@zapier/zapier-sdk-cli 0.15.0 → 0.15.2
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 +15 -0
- package/dist/cli.cjs +197 -34
- package/dist/cli.mjs +197 -34
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/package.json +1 -1
- package/dist/src/plugins/buildManifest/index.js +1 -1
- package/dist/src/utils/parameter-resolver.d.ts +20 -0
- package/dist/src/utils/parameter-resolver.js +189 -29
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/plugins/buildManifest/index.test.ts +39 -15
- package/src/plugins/buildManifest/index.ts +1 -1
- package/src/utils/parameter-resolver.ts +275 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zapier/zapier-sdk-cli",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "Command line interface for Zapier SDK",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"semver": "^7.7.3",
|
|
48
48
|
"typescript": "^5.8.3",
|
|
49
49
|
"zod": "^3.25.67",
|
|
50
|
-
"@zapier/zapier-sdk": "0.15.
|
|
51
|
-
"@zapier/zapier-sdk-
|
|
52
|
-
"@zapier/zapier-sdk-
|
|
50
|
+
"@zapier/zapier-sdk": "0.15.3",
|
|
51
|
+
"@zapier/zapier-sdk-mcp": "0.3.27",
|
|
52
|
+
"@zapier/zapier-sdk-cli-login": "0.3.4"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/express": "^5.0.3",
|
|
@@ -4,7 +4,6 @@ import { BuildManifestSchema } from "./schemas";
|
|
|
4
4
|
import type {
|
|
5
5
|
AppItem,
|
|
6
6
|
Manifest,
|
|
7
|
-
ManifestEntry,
|
|
8
7
|
UpdateManifestEntryOptions,
|
|
9
8
|
} from "@zapier/zapier-sdk";
|
|
10
9
|
import {
|
|
@@ -49,20 +48,20 @@ describe("buildManifest plugin", () => {
|
|
|
49
48
|
|
|
50
49
|
mockUpdateManifestEntry = vi
|
|
51
50
|
.fn()
|
|
52
|
-
.mockImplementation(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
);
|
|
51
|
+
.mockImplementation(async (options: UpdateManifestEntryOptions) => {
|
|
52
|
+
const manifestKey = options.entry.implementationName;
|
|
53
|
+
const updatedManifest: Manifest = {
|
|
54
|
+
apps: {
|
|
55
|
+
...options.manifest?.apps,
|
|
56
|
+
[manifestKey]: options.entry,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
key: manifestKey,
|
|
61
|
+
entry: options.entry,
|
|
62
|
+
manifest: updatedManifest,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
66
65
|
|
|
67
66
|
mockListAppsItems = vi.fn();
|
|
68
67
|
});
|
|
@@ -81,6 +80,31 @@ describe("buildManifest plugin", () => {
|
|
|
81
80
|
.addPlugin(() => ({
|
|
82
81
|
context: {
|
|
83
82
|
updateManifestEntry: mockUpdateManifestEntry,
|
|
83
|
+
addActionEntry: vi
|
|
84
|
+
.fn()
|
|
85
|
+
.mockResolvedValue(["test-key", {}, { apps: {} }]),
|
|
86
|
+
findActionEntry: vi.fn().mockResolvedValue({
|
|
87
|
+
key: "test-key",
|
|
88
|
+
entry: {},
|
|
89
|
+
manifest: { apps: {} },
|
|
90
|
+
}),
|
|
91
|
+
hasActionEntry: vi.fn().mockResolvedValue(true),
|
|
92
|
+
listActionEntries: vi
|
|
93
|
+
.fn()
|
|
94
|
+
.mockResolvedValue([
|
|
95
|
+
[
|
|
96
|
+
"test-key",
|
|
97
|
+
{ key: "test-key", entry: {}, manifest: { apps: {} } },
|
|
98
|
+
],
|
|
99
|
+
]),
|
|
100
|
+
deleteActionEntry: vi.fn().mockResolvedValue({ apps: {} }),
|
|
101
|
+
findManifestEntry: vi
|
|
102
|
+
.fn()
|
|
103
|
+
.mockResolvedValue([
|
|
104
|
+
"test-key",
|
|
105
|
+
{ key: "test-key", entry: {}, manifest: { apps: {} } },
|
|
106
|
+
]),
|
|
107
|
+
readManifestFromFile: vi.fn().mockResolvedValue({ apps: {} }),
|
|
84
108
|
},
|
|
85
109
|
}))
|
|
86
110
|
.addPlugin(() => ({
|
|
@@ -76,7 +76,7 @@ export const buildManifestPlugin: Plugin<
|
|
|
76
76
|
version: manifestEntry.version || "",
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
const
|
|
79
|
+
const { key: updatedManifestKey, manifest } =
|
|
80
80
|
await context.updateManifestEntry({
|
|
81
81
|
appKey: app.key,
|
|
82
82
|
entry: manifestEntry,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import inquirer from "inquirer";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import type { ZapierSdk } from "@zapier/zapier-sdk";
|
|
4
|
+
import type { ZapierSdk, ActionTypeProperty } from "@zapier/zapier-sdk";
|
|
5
5
|
import { ZapierCliUserCancellationError } from "./errors";
|
|
6
6
|
|
|
7
7
|
// ============================================================================
|
|
@@ -23,6 +23,17 @@ interface ResolverContext {
|
|
|
23
23
|
functionName?: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
interface FieldMetadata {
|
|
27
|
+
key: string;
|
|
28
|
+
title: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
isRequired: boolean;
|
|
31
|
+
defaultValue?: unknown;
|
|
32
|
+
valueType: string;
|
|
33
|
+
hasDropdown: boolean;
|
|
34
|
+
isMultiSelect: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
// ============================================================================
|
|
27
38
|
// Local Resolution Helper Functions
|
|
28
39
|
// ============================================================================
|
|
@@ -501,6 +512,7 @@ export class SchemaParameterResolver {
|
|
|
501
512
|
processedFieldKeys,
|
|
502
513
|
[],
|
|
503
514
|
iteration,
|
|
515
|
+
updatedContext,
|
|
504
516
|
);
|
|
505
517
|
|
|
506
518
|
// If no new fields were processed, we're done
|
|
@@ -535,6 +547,7 @@ export class SchemaParameterResolver {
|
|
|
535
547
|
processedFieldKeys: Set<string>,
|
|
536
548
|
fieldsetPath: string[] = [],
|
|
537
549
|
iteration: number = 1,
|
|
550
|
+
context?: ResolverContext,
|
|
538
551
|
): Promise<{
|
|
539
552
|
newRequired: number;
|
|
540
553
|
newOptional: number;
|
|
@@ -581,6 +594,7 @@ export class SchemaParameterResolver {
|
|
|
581
594
|
processedFieldKeys,
|
|
582
595
|
nestedPath,
|
|
583
596
|
iteration,
|
|
597
|
+
context,
|
|
584
598
|
);
|
|
585
599
|
|
|
586
600
|
newRequiredCount += nestedStats.newRequired;
|
|
@@ -608,7 +622,7 @@ export class SchemaParameterResolver {
|
|
|
608
622
|
);
|
|
609
623
|
}
|
|
610
624
|
|
|
611
|
-
await this.promptForField(typedItem, targetInputs);
|
|
625
|
+
await this.promptForField(typedItem, targetInputs, context);
|
|
612
626
|
processedFieldKeys.add(typedItem.key);
|
|
613
627
|
} else {
|
|
614
628
|
// Collect optional fields for batch processing
|
|
@@ -656,7 +670,7 @@ export class SchemaParameterResolver {
|
|
|
656
670
|
if (shouldConfigureOptional.configure) {
|
|
657
671
|
console.log(chalk.cyan(`\nOptional fields${pathContext}:`));
|
|
658
672
|
for (const field of optionalFields) {
|
|
659
|
-
await this.promptForField(field, targetInputs);
|
|
673
|
+
await this.promptForField(field, targetInputs, context);
|
|
660
674
|
const typedField = field as { key: string };
|
|
661
675
|
processedFieldKeys.add(typedField.key);
|
|
662
676
|
}
|
|
@@ -704,54 +718,244 @@ export class SchemaParameterResolver {
|
|
|
704
718
|
parent[lastKey] = value;
|
|
705
719
|
}
|
|
706
720
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
):
|
|
721
|
+
/**
|
|
722
|
+
* Extract and normalize field metadata from raw field object
|
|
723
|
+
*/
|
|
724
|
+
private extractFieldMetadata(field: unknown): FieldMetadata {
|
|
711
725
|
const fieldObj = field as {
|
|
712
726
|
type?: string;
|
|
713
727
|
key: string;
|
|
728
|
+
title?: string;
|
|
714
729
|
label?: string;
|
|
715
730
|
is_required?: boolean;
|
|
731
|
+
description?: string;
|
|
716
732
|
helpText?: string;
|
|
733
|
+
default_value?: unknown;
|
|
717
734
|
default?: unknown;
|
|
718
|
-
|
|
735
|
+
value_type?: string;
|
|
736
|
+
format?: string;
|
|
737
|
+
items?: { type: string };
|
|
719
738
|
};
|
|
720
739
|
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
740
|
+
const valueType = fieldObj.value_type || "string";
|
|
741
|
+
|
|
742
|
+
return {
|
|
743
|
+
key: fieldObj.key,
|
|
744
|
+
title: fieldObj.title || fieldObj.label || fieldObj.key,
|
|
745
|
+
description: fieldObj.description || fieldObj.helpText,
|
|
746
|
+
isRequired: fieldObj.is_required || false,
|
|
747
|
+
defaultValue: fieldObj.default_value ?? fieldObj.default,
|
|
748
|
+
valueType,
|
|
749
|
+
hasDropdown: fieldObj.format === "SELECT",
|
|
750
|
+
isMultiSelect: Boolean(
|
|
751
|
+
valueType === "array" ||
|
|
752
|
+
(fieldObj.items && fieldObj.items.type !== undefined),
|
|
753
|
+
),
|
|
725
754
|
};
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Fetch a page of choices for a dropdown field
|
|
759
|
+
*/
|
|
760
|
+
private async fetchChoices(
|
|
761
|
+
fieldMeta: FieldMetadata,
|
|
762
|
+
inputs: Record<string, unknown>,
|
|
763
|
+
context: ResolverContext,
|
|
764
|
+
cursor?: string,
|
|
765
|
+
): Promise<{
|
|
766
|
+
choices: Array<{ label: string; value: unknown }>;
|
|
767
|
+
nextCursor?: string;
|
|
768
|
+
}> {
|
|
769
|
+
try {
|
|
770
|
+
console.log(
|
|
771
|
+
chalk.gray(
|
|
772
|
+
cursor
|
|
773
|
+
? ` Fetching more choices...`
|
|
774
|
+
: ` Fetching choices for ${fieldMeta.title}...`,
|
|
775
|
+
),
|
|
776
|
+
);
|
|
777
|
+
|
|
778
|
+
const page = await context.sdk.listInputFieldChoices({
|
|
779
|
+
appKey: context.resolvedParams.appKey as string,
|
|
780
|
+
actionKey: context.resolvedParams.actionKey as string,
|
|
781
|
+
actionType: context.resolvedParams.actionType as ActionTypeProperty,
|
|
782
|
+
authenticationId: context.resolvedParams.authenticationId as
|
|
783
|
+
| number
|
|
784
|
+
| null,
|
|
785
|
+
inputFieldKey: fieldMeta.key,
|
|
786
|
+
inputs,
|
|
787
|
+
...(cursor && { cursor }),
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
const choices = page.data.map((choice) => ({
|
|
791
|
+
label: choice.label || choice.key || String(choice.value),
|
|
792
|
+
value: choice.value ?? choice.key,
|
|
793
|
+
}));
|
|
794
|
+
|
|
795
|
+
if (choices.length === 0 && !cursor) {
|
|
796
|
+
console.log(
|
|
797
|
+
chalk.yellow(` No choices available for ${fieldMeta.title}`),
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
return {
|
|
802
|
+
choices,
|
|
803
|
+
nextCursor: page.nextCursor,
|
|
804
|
+
};
|
|
805
|
+
} catch (error) {
|
|
806
|
+
console.warn(
|
|
807
|
+
chalk.yellow(` ⚠️ Failed to fetch choices for ${fieldMeta.title}:`),
|
|
808
|
+
error,
|
|
809
|
+
);
|
|
810
|
+
return { choices: [] };
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Prompt user with choices (handles both single and multi-select with pagination)
|
|
816
|
+
*/
|
|
817
|
+
private async promptWithChoices({
|
|
818
|
+
fieldMeta,
|
|
819
|
+
choices: initialChoices,
|
|
820
|
+
nextCursor: initialCursor,
|
|
821
|
+
inputs,
|
|
822
|
+
context,
|
|
823
|
+
}: {
|
|
824
|
+
fieldMeta: FieldMetadata;
|
|
825
|
+
choices: Array<{ label: string; value: unknown }>;
|
|
826
|
+
nextCursor?: string;
|
|
827
|
+
inputs: Record<string, unknown>;
|
|
828
|
+
context?: ResolverContext;
|
|
829
|
+
}): Promise<unknown> {
|
|
830
|
+
const choices = [...initialChoices];
|
|
831
|
+
let nextCursor = initialCursor;
|
|
832
|
+
const LOAD_MORE_SENTINEL = Symbol("LOAD_MORE");
|
|
833
|
+
|
|
834
|
+
// Progressive loading loop
|
|
835
|
+
while (true) {
|
|
836
|
+
const promptChoices = choices.map((choice) => ({
|
|
837
|
+
name: choice.label,
|
|
838
|
+
value: choice.value,
|
|
839
|
+
}));
|
|
840
|
+
|
|
841
|
+
// Add "(Load more...)" option if there are more pages
|
|
842
|
+
if (nextCursor) {
|
|
843
|
+
promptChoices.push({
|
|
844
|
+
name: chalk.dim("(Load more...)"),
|
|
845
|
+
value: LOAD_MORE_SENTINEL,
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// Add skip option for optional fields (single-select only)
|
|
850
|
+
if (!fieldMeta.isRequired && !fieldMeta.isMultiSelect) {
|
|
851
|
+
promptChoices.push({ name: "(Skip)", value: undefined });
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
const promptConfig = {
|
|
855
|
+
type: fieldMeta.isMultiSelect ? "checkbox" : "list",
|
|
856
|
+
name: fieldMeta.key,
|
|
857
|
+
message: `${fieldMeta.title}${fieldMeta.isRequired ? " (required)" : " (optional)"}:`,
|
|
858
|
+
choices: promptChoices,
|
|
859
|
+
...(fieldMeta.isMultiSelect && {
|
|
860
|
+
validate: (input: unknown[]) => {
|
|
861
|
+
if (fieldMeta.isRequired && (!input || input.length === 0)) {
|
|
862
|
+
return "At least one selection is required";
|
|
863
|
+
}
|
|
864
|
+
return true;
|
|
865
|
+
},
|
|
866
|
+
}),
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
const answer = await inquirer.prompt([promptConfig as any]);
|
|
870
|
+
let selectedValue = answer[fieldMeta.key];
|
|
871
|
+
|
|
872
|
+
// Check if user selected "Load more..."
|
|
873
|
+
const wantsMore = fieldMeta.isMultiSelect
|
|
874
|
+
? Array.isArray(selectedValue) &&
|
|
875
|
+
selectedValue.includes(LOAD_MORE_SENTINEL)
|
|
876
|
+
: selectedValue === LOAD_MORE_SENTINEL;
|
|
877
|
+
|
|
878
|
+
if (wantsMore && nextCursor && context) {
|
|
879
|
+
// Remove sentinel from multi-select
|
|
880
|
+
if (fieldMeta.isMultiSelect && Array.isArray(selectedValue)) {
|
|
881
|
+
selectedValue = selectedValue.filter((v) => v !== LOAD_MORE_SENTINEL);
|
|
882
|
+
}
|
|
726
883
|
|
|
727
|
-
|
|
728
|
-
|
|
884
|
+
// Fetch next page
|
|
885
|
+
const result = await this.fetchChoices(
|
|
886
|
+
fieldMeta,
|
|
887
|
+
inputs,
|
|
888
|
+
context,
|
|
889
|
+
nextCursor,
|
|
890
|
+
);
|
|
891
|
+
choices.push(...result.choices);
|
|
892
|
+
nextCursor = result.nextCursor;
|
|
893
|
+
|
|
894
|
+
// Re-prompt with updated choices
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
return selectedValue;
|
|
729
899
|
}
|
|
900
|
+
}
|
|
730
901
|
|
|
731
|
-
|
|
732
|
-
|
|
902
|
+
/**
|
|
903
|
+
* Prompt user for free-form input (text or boolean)
|
|
904
|
+
*/
|
|
905
|
+
private async promptFreeForm(fieldMeta: FieldMetadata): Promise<unknown> {
|
|
906
|
+
const promptConfig: Record<string, unknown> = {
|
|
907
|
+
name: fieldMeta.key,
|
|
908
|
+
message: `${fieldMeta.title}${fieldMeta.isRequired ? " (required)" : " (optional)"}:`,
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
if (fieldMeta.valueType === "boolean") {
|
|
912
|
+
promptConfig.type = "confirm";
|
|
913
|
+
promptConfig.default =
|
|
914
|
+
fieldMeta.defaultValue !== undefined
|
|
915
|
+
? Boolean(fieldMeta.defaultValue)
|
|
916
|
+
: undefined;
|
|
917
|
+
} else {
|
|
918
|
+
promptConfig.type = "input";
|
|
919
|
+
promptConfig.default = fieldMeta.defaultValue;
|
|
920
|
+
promptConfig.validate = (input: string) => {
|
|
921
|
+
if (fieldMeta.isRequired && !input) {
|
|
922
|
+
return "This field is required";
|
|
923
|
+
}
|
|
924
|
+
return true;
|
|
925
|
+
};
|
|
733
926
|
}
|
|
734
927
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
(choice: unknown) => {
|
|
739
|
-
const choiceObj = choice as { label?: unknown; value: unknown };
|
|
740
|
-
return {
|
|
741
|
-
name: choiceObj.label || choiceObj.value,
|
|
742
|
-
value: choiceObj.value,
|
|
743
|
-
};
|
|
744
|
-
},
|
|
745
|
-
);
|
|
928
|
+
// Add help text if available
|
|
929
|
+
if (fieldMeta.description) {
|
|
930
|
+
promptConfig.prefix = chalk.gray(`ℹ ${fieldMeta.description}\n`);
|
|
746
931
|
}
|
|
747
932
|
|
|
748
933
|
try {
|
|
749
|
-
const answer = await inquirer.prompt([
|
|
934
|
+
const answer = await inquirer.prompt([promptConfig as any]);
|
|
935
|
+
return answer[fieldMeta.key];
|
|
936
|
+
} catch (error) {
|
|
937
|
+
if (this.isUserCancellation(error)) {
|
|
938
|
+
console.log(chalk.yellow("\n\nOperation cancelled by user"));
|
|
939
|
+
throw new ZapierCliUserCancellationError();
|
|
940
|
+
}
|
|
941
|
+
throw error;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
750
944
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
945
|
+
/**
|
|
946
|
+
* Store field value in inputs object with validation
|
|
947
|
+
*/
|
|
948
|
+
private storeFieldValue(
|
|
949
|
+
inputs: Record<string, unknown>,
|
|
950
|
+
key: string,
|
|
951
|
+
value: unknown,
|
|
952
|
+
isRequired: boolean,
|
|
953
|
+
): void {
|
|
954
|
+
try {
|
|
955
|
+
if (value !== undefined && value !== "") {
|
|
956
|
+
inputs[key] = value;
|
|
957
|
+
} else if (isRequired) {
|
|
958
|
+
throw new Error(`Required field ${key} cannot be empty`);
|
|
755
959
|
}
|
|
756
960
|
} catch (error) {
|
|
757
961
|
if (this.isUserCancellation(error)) {
|
|
@@ -762,6 +966,45 @@ export class SchemaParameterResolver {
|
|
|
762
966
|
}
|
|
763
967
|
}
|
|
764
968
|
|
|
969
|
+
private async promptForField(
|
|
970
|
+
field: unknown,
|
|
971
|
+
inputs: Record<string, unknown>,
|
|
972
|
+
context?: ResolverContext,
|
|
973
|
+
): Promise<void> {
|
|
974
|
+
const fieldMeta = this.extractFieldMetadata(field);
|
|
975
|
+
|
|
976
|
+
// Fetch choices if field has dropdown
|
|
977
|
+
let choices: Array<{ label: string; value: unknown }> = [];
|
|
978
|
+
let nextCursor: string | undefined;
|
|
979
|
+
if (fieldMeta.hasDropdown && context) {
|
|
980
|
+
const result = await this.fetchChoices(fieldMeta, inputs, context);
|
|
981
|
+
choices = result.choices;
|
|
982
|
+
nextCursor = result.nextCursor;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// Prompt user based on field type
|
|
986
|
+
let selectedValue: unknown;
|
|
987
|
+
if (choices.length > 0) {
|
|
988
|
+
selectedValue = await this.promptWithChoices({
|
|
989
|
+
fieldMeta,
|
|
990
|
+
choices,
|
|
991
|
+
nextCursor,
|
|
992
|
+
inputs,
|
|
993
|
+
context,
|
|
994
|
+
});
|
|
995
|
+
} else {
|
|
996
|
+
selectedValue = await this.promptFreeForm(fieldMeta);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Store result
|
|
1000
|
+
this.storeFieldValue(
|
|
1001
|
+
inputs,
|
|
1002
|
+
fieldMeta.key,
|
|
1003
|
+
selectedValue,
|
|
1004
|
+
fieldMeta.isRequired,
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
765
1008
|
private isUserCancellation(error: unknown): boolean {
|
|
766
1009
|
const errorObj = error as {
|
|
767
1010
|
name?: string;
|