@zapier/zapier-sdk-cli 0.10.0 → 0.11.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -42,9 +42,9 @@
42
42
  "ora": "^8.2.0",
43
43
  "pkce-challenge": "^5.0.0",
44
44
  "zod": "^3.25.67",
45
- "@zapier/zapier-sdk": "0.10.0",
45
+ "@zapier/zapier-sdk": "0.11.1",
46
46
  "@zapier/zapier-sdk-cli-login": "0.3.2",
47
- "@zapier/zapier-sdk-mcp": "0.3.7"
47
+ "@zapier/zapier-sdk-mcp": "0.3.9"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/express": "^5.0.3",
@@ -459,7 +459,7 @@ export class SchemaParameterResolver {
459
459
  const inputs: Record<string, unknown> = {};
460
460
  let processedFieldKeys = new Set<string>();
461
461
  let iteration = 0;
462
- const maxIterations = 5; // Prevent infinite loops
462
+ const maxIterations = 10; // Prevent infinite loops
463
463
 
464
464
  while (iteration < maxIterations) {
465
465
  iteration++;
@@ -479,12 +479,12 @@ export class SchemaParameterResolver {
479
479
  ),
480
480
  );
481
481
 
482
- const fields = await typedResolver.fetch(
482
+ const rootFieldItems = await typedResolver.fetch(
483
483
  updatedContext.sdk,
484
484
  updatedContext.resolvedParams,
485
485
  );
486
486
 
487
- if (!fields || fields.length === 0) {
487
+ if (!rootFieldItems || rootFieldItems.length === 0) {
488
488
  if (iteration === 1) {
489
489
  console.log(
490
490
  chalk.yellow(`No input fields required for this action.`),
@@ -493,59 +493,180 @@ export class SchemaParameterResolver {
493
493
  break;
494
494
  }
495
495
 
496
- // Find new fields that we haven't processed yet
497
- const newFields = fields.filter(
498
- (field: unknown) =>
499
- !(
500
- (field as { key: string }).key &&
501
- processedFieldKeys.has((field as { key: string }).key)
502
- ),
496
+ // Process fields recursively, maintaining fieldset structure
497
+ const fieldStats = await this.processFieldItems(
498
+ rootFieldItems,
499
+ inputs,
500
+ processedFieldKeys,
501
+ [],
502
+ iteration,
503
503
  );
504
504
 
505
- if (newFields.length === 0) {
506
- // No new fields, we're done
505
+ // If no new fields were processed, we're done
506
+ if (fieldStats.newRequired === 0 && fieldStats.newOptional === 0) {
507
507
  break;
508
508
  }
509
509
 
510
- // Separate new required and optional fields
511
- const newRequiredFields = newFields.filter(
512
- (field: unknown) => (field as { is_required: boolean }).is_required,
513
- );
514
- const newOptionalFields = newFields.filter(
515
- (field: unknown) => !(field as { is_required: boolean }).is_required,
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
+ ),
516
521
  );
522
+ }
523
+
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
+ };
517
554
 
518
- // Prompt for new required fields
519
- if (newRequiredFields.length > 0) {
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(" > ")})` : "";
520
560
  console.log(
521
- chalk.blue(
522
- `\n📝 Please provide values for the following ${iteration === 1 ? "" : "additional "}input fields:`,
561
+ chalk.cyan(
562
+ `\n📁 Processing fieldset: ${fieldsetTitle}${pathDisplay}`,
523
563
  ),
524
564
  );
525
- for (const field of newRequiredFields) {
526
- await this.promptForField(field, inputs);
527
- processedFieldKeys.add((field as { key: string }).key);
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++;
528
615
  }
529
616
  }
617
+ // Skip info fields - they're for display only
618
+ }
530
619
 
531
- // Ask about optional fields
532
- let shouldConfigureOptional = { configure: false };
533
- if (newOptionalFields.length > 0) {
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
+ });
635
+
636
+ if (optionalFields.length > 0) {
637
+ const pathContext =
638
+ fieldsetPath.length > 0 ? ` in ${fieldsetPath.join(" > ")}` : "";
534
639
  console.log(
535
640
  chalk.gray(
536
- `\nThere are ${newOptionalFields.length} ${iteration === 1 ? "" : "additional "}optional field(s) available.`,
641
+ `\nThere are ${optionalFields.length} ${iteration === 1 ? "" : "additional "}optional field(s) available${pathContext}.`,
537
642
  ),
538
643
  );
539
644
 
540
645
  try {
541
- shouldConfigureOptional = await inquirer.prompt([
646
+ const shouldConfigureOptional = await inquirer.prompt([
542
647
  {
543
648
  type: "confirm",
544
649
  name: "configure",
545
- 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}?`,
546
651
  default: false,
547
652
  },
548
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
+ }
549
670
  } catch (error) {
550
671
  if (this.isUserCancellation(error)) {
551
672
  console.log(chalk.yellow("\n\nOperation cancelled by user"));
@@ -553,39 +674,14 @@ export class SchemaParameterResolver {
553
674
  }
554
675
  throw error;
555
676
  }
556
-
557
- if (shouldConfigureOptional.configure) {
558
- console.log(chalk.cyan(`\nOptional fields:`));
559
- for (const field of newOptionalFields) {
560
- await this.promptForField(field, inputs);
561
- processedFieldKeys.add((field as { key: string }).key);
562
- }
563
- } else {
564
- // Mark these fields as processed even if skipped to avoid re-asking
565
- newOptionalFields.forEach((field: unknown) =>
566
- processedFieldKeys.add((field as { key: string }).key),
567
- );
568
- }
569
- }
570
-
571
- // If we only processed optional fields and skipped them, no need to re-fetch
572
- if (
573
- newRequiredFields.length === 0 &&
574
- (!newOptionalFields.length || !shouldConfigureOptional.configure)
575
- ) {
576
- break;
577
677
  }
578
678
  }
579
679
 
580
- if (iteration >= maxIterations) {
581
- console.log(
582
- chalk.yellow(
583
- `\n⚠️ Maximum field resolution iterations reached. Some dynamic fields may not have been discovered.`,
584
- ),
585
- );
586
- }
587
-
588
- return inputs;
680
+ return {
681
+ newRequired: newRequiredCount,
682
+ newOptional: newOptionalCount,
683
+ optionalSkipped,
684
+ };
589
685
  }
590
686
 
591
687
  private getNestedValue(obj: unknown, path: string[]): unknown {