@zapier/zapier-sdk-cli 0.2.1 → 0.4.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/bin/zapier-sdk.js +1 -1
- package/bin/zsdk.js +1 -1
- package/dist/cli.js +1099 -16
- package/dist/index.js +0 -3
- package/package.json +6 -3
- package/src/cli.ts +1 -1
- package/src/commands/configPath.ts +1 -1
- package/src/commands/login.ts +1 -1
- package/src/commands/logout.ts +1 -1
- package/src/commands/whoami.ts +1 -1
- package/src/utils/auth/login.ts +1 -1
- package/tsconfig.json +1 -1
- package/tsup.config.ts +17 -0
- package/dist/cli.d.ts +0 -2
- package/dist/commands/configPath.d.ts +0 -2
- package/dist/commands/configPath.js +0 -9
- package/dist/commands/index.d.ts +0 -4
- package/dist/commands/index.js +0 -4
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +0 -25
- package/dist/commands/logout.d.ts +0 -2
- package/dist/commands/logout.js +0 -16
- package/dist/commands/whoami.d.ts +0 -2
- package/dist/commands/whoami.js +0 -17
- package/dist/index.d.ts +0 -0
- package/dist/utils/api/client.d.ts +0 -15
- package/dist/utils/api/client.js +0 -27
- package/dist/utils/auth/login.d.ts +0 -2
- package/dist/utils/auth/login.js +0 -134
- package/dist/utils/cli-generator.d.ts +0 -3
- package/dist/utils/cli-generator.js +0 -388
- package/dist/utils/constants.d.ts +0 -5
- package/dist/utils/constants.js +0 -6
- package/dist/utils/getCallablePromise.d.ts +0 -6
- package/dist/utils/getCallablePromise.js +0 -14
- package/dist/utils/log.d.ts +0 -7
- package/dist/utils/log.js +0 -16
- package/dist/utils/pager.d.ts +0 -48
- package/dist/utils/pager.js +0 -137
- package/dist/utils/parameter-resolver.d.ts +0 -13
- package/dist/utils/parameter-resolver.js +0 -300
- package/dist/utils/schema-formatter.d.ts +0 -2
- package/dist/utils/schema-formatter.js +0 -71
- package/dist/utils/serializeAsync.d.ts +0 -2
- package/dist/utils/serializeAsync.js +0 -16
- package/dist/utils/spinner.d.ts +0 -1
- package/dist/utils/spinner.js +0 -13
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import inquirer from "inquirer";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { getResolver, hasResolver, getResolutionOrderForParams, } from "@zapier/zapier-sdk";
|
|
5
|
-
// ============================================================================
|
|
6
|
-
// Schema Parameter Resolver
|
|
7
|
-
// ============================================================================
|
|
8
|
-
export class SchemaParameterResolver {
|
|
9
|
-
async resolveParameters(schema, providedParams, sdk) {
|
|
10
|
-
// 1. Try to parse with current parameters
|
|
11
|
-
const parseResult = schema.safeParse(providedParams);
|
|
12
|
-
// Get all schema parameters to check which ones have resolvers
|
|
13
|
-
const allParams = this.extractParametersFromSchema(schema);
|
|
14
|
-
const resolvableParams = allParams.filter((param) => hasResolver(param.name));
|
|
15
|
-
// Get all missing parameters that have resolvers
|
|
16
|
-
const missingResolvable = resolvableParams.filter((param) => {
|
|
17
|
-
const hasValue = this.getNestedValue(providedParams, param.path) !== undefined;
|
|
18
|
-
return !hasValue;
|
|
19
|
-
});
|
|
20
|
-
// Determine which parameters are "functionally required" vs truly optional
|
|
21
|
-
// For run-action: appKey, actionType, actionKey are schema-required
|
|
22
|
-
// authenticationId and inputs are schema-optional but functionally needed
|
|
23
|
-
const functionallyRequired = missingResolvable.filter((param) => {
|
|
24
|
-
// Schema-required parameters are always functionally required
|
|
25
|
-
if (param.isRequired)
|
|
26
|
-
return true;
|
|
27
|
-
// For run-action, inputs and authenticationId are functionally required
|
|
28
|
-
// even though schema-optional
|
|
29
|
-
if (param.name === "inputs" || param.name === "authenticationId") {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
33
|
-
});
|
|
34
|
-
const trulyOptional = missingResolvable.filter((param) => !functionallyRequired.includes(param));
|
|
35
|
-
if (parseResult.success && functionallyRequired.length === 0) {
|
|
36
|
-
return parseResult.data;
|
|
37
|
-
}
|
|
38
|
-
if (functionallyRequired.length === 0) {
|
|
39
|
-
// No functionally required parameters missing, but check if we can parse
|
|
40
|
-
if (!parseResult.success) {
|
|
41
|
-
throw parseResult.error;
|
|
42
|
-
}
|
|
43
|
-
return parseResult.data;
|
|
44
|
-
}
|
|
45
|
-
// 2. Resolve functionally required parameters first
|
|
46
|
-
const resolvedParams = { ...providedParams };
|
|
47
|
-
const context = {
|
|
48
|
-
sdk,
|
|
49
|
-
currentParams: providedParams,
|
|
50
|
-
resolvedParams,
|
|
51
|
-
};
|
|
52
|
-
if (functionallyRequired.length > 0) {
|
|
53
|
-
const requiredParamNames = functionallyRequired.map((p) => p.name);
|
|
54
|
-
const requiredResolutionOrder = getResolutionOrderForParams(requiredParamNames);
|
|
55
|
-
const orderedRequiredParams = requiredResolutionOrder
|
|
56
|
-
.map((paramName) => functionallyRequired.find((p) => p.name === paramName))
|
|
57
|
-
.filter((param) => param !== undefined);
|
|
58
|
-
for (const param of orderedRequiredParams) {
|
|
59
|
-
try {
|
|
60
|
-
const value = await this.resolveParameter(param, context);
|
|
61
|
-
this.setNestedValue(resolvedParams, param.path, value);
|
|
62
|
-
// Update context with newly resolved value
|
|
63
|
-
context.resolvedParams = resolvedParams;
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
if (this.isUserCancellation(error)) {
|
|
67
|
-
console.log(chalk.yellow("\n\nOperation cancelled by user"));
|
|
68
|
-
process.exit(0);
|
|
69
|
-
}
|
|
70
|
-
console.error(chalk.red(`Failed to resolve ${param.name}:`), error);
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// 3. Ask user if they want to resolve truly optional parameters
|
|
76
|
-
if (trulyOptional.length > 0) {
|
|
77
|
-
const optionalNames = trulyOptional.map((p) => p.name).join(", ");
|
|
78
|
-
const shouldResolveOptional = await inquirer.prompt([
|
|
79
|
-
{
|
|
80
|
-
type: "confirm",
|
|
81
|
-
name: "resolveOptional",
|
|
82
|
-
message: `Would you like to be prompted for optional parameters (${optionalNames})?`,
|
|
83
|
-
default: false,
|
|
84
|
-
},
|
|
85
|
-
]);
|
|
86
|
-
if (shouldResolveOptional.resolveOptional) {
|
|
87
|
-
// Resolve optional parameters using their resolvers
|
|
88
|
-
const optionalParamNames = trulyOptional.map((p) => p.name);
|
|
89
|
-
const optionalResolutionOrder = getResolutionOrderForParams(optionalParamNames);
|
|
90
|
-
const orderedOptionalParams = optionalResolutionOrder
|
|
91
|
-
.map((paramName) => trulyOptional.find((p) => p.name === paramName))
|
|
92
|
-
.filter((param) => param !== undefined);
|
|
93
|
-
for (const param of orderedOptionalParams) {
|
|
94
|
-
try {
|
|
95
|
-
const value = await this.resolveParameter(param, context);
|
|
96
|
-
this.setNestedValue(resolvedParams, param.path, value);
|
|
97
|
-
// Update context with newly resolved value
|
|
98
|
-
context.resolvedParams = resolvedParams;
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
if (this.isUserCancellation(error)) {
|
|
102
|
-
console.log(chalk.yellow("\n\nOperation cancelled by user"));
|
|
103
|
-
process.exit(0);
|
|
104
|
-
}
|
|
105
|
-
console.error(chalk.red(`Failed to resolve ${param.name}:`), error);
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
// 3. Validate final parameters
|
|
112
|
-
const finalResult = schema.safeParse(resolvedParams);
|
|
113
|
-
if (!finalResult.success) {
|
|
114
|
-
console.error(chalk.red("❌ Parameter validation failed after resolution:"));
|
|
115
|
-
throw finalResult.error;
|
|
116
|
-
}
|
|
117
|
-
return finalResult.data;
|
|
118
|
-
}
|
|
119
|
-
extractParametersFromSchema(schema) {
|
|
120
|
-
const parameters = [];
|
|
121
|
-
// Only handle ZodObject at the top level
|
|
122
|
-
if (schema instanceof z.ZodObject) {
|
|
123
|
-
const shape = schema.shape;
|
|
124
|
-
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
125
|
-
const param = this.analyzeFieldSchema(key, fieldSchema);
|
|
126
|
-
if (param) {
|
|
127
|
-
parameters.push(param);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return parameters;
|
|
132
|
-
}
|
|
133
|
-
analyzeFieldSchema(fieldName, fieldSchema) {
|
|
134
|
-
let baseSchema = fieldSchema;
|
|
135
|
-
let isRequired = true;
|
|
136
|
-
// Check if field is optional or has default
|
|
137
|
-
if (baseSchema instanceof z.ZodOptional) {
|
|
138
|
-
isRequired = false;
|
|
139
|
-
baseSchema = baseSchema._def.innerType;
|
|
140
|
-
}
|
|
141
|
-
if (baseSchema instanceof z.ZodDefault) {
|
|
142
|
-
isRequired = false;
|
|
143
|
-
baseSchema = baseSchema._def.innerType;
|
|
144
|
-
}
|
|
145
|
-
return this.createResolvableParameter([fieldName], baseSchema, isRequired);
|
|
146
|
-
}
|
|
147
|
-
createResolvableParameter(path, schema, isRequired) {
|
|
148
|
-
if (path.length === 0)
|
|
149
|
-
return null;
|
|
150
|
-
const name = path[path.length - 1];
|
|
151
|
-
return {
|
|
152
|
-
name,
|
|
153
|
-
path,
|
|
154
|
-
schema,
|
|
155
|
-
description: schema.description,
|
|
156
|
-
isRequired,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
async resolveParameter(param, context) {
|
|
160
|
-
const resolver = getResolver(param.name);
|
|
161
|
-
if (!resolver) {
|
|
162
|
-
throw new Error(`No resolver found for parameter: ${param.name}`);
|
|
163
|
-
}
|
|
164
|
-
console.log(chalk.blue(`\n🔍 Resolving ${param.name}...`));
|
|
165
|
-
if (resolver.type === "static") {
|
|
166
|
-
// Static resolver - just prompt for input
|
|
167
|
-
const promptConfig = {
|
|
168
|
-
type: resolver.inputType === "password" ? "password" : "input",
|
|
169
|
-
name: param.name,
|
|
170
|
-
message: `Enter ${param.name}:`,
|
|
171
|
-
...(resolver.placeholder && { default: resolver.placeholder }),
|
|
172
|
-
};
|
|
173
|
-
const answers = await inquirer.prompt([promptConfig]);
|
|
174
|
-
return answers[param.name];
|
|
175
|
-
}
|
|
176
|
-
else if (resolver.type === "dynamic") {
|
|
177
|
-
// Dynamic resolver - fetch options and prompt for selection
|
|
178
|
-
try {
|
|
179
|
-
console.log(chalk.gray(`Fetching options for ${param.name}...`));
|
|
180
|
-
const items = await resolver.fetch(context.sdk, context.resolvedParams);
|
|
181
|
-
if (!items || items.length === 0) {
|
|
182
|
-
throw new Error(`No options available for ${param.name}`);
|
|
183
|
-
}
|
|
184
|
-
const promptConfig = resolver.prompt(items, context.resolvedParams);
|
|
185
|
-
const answers = await inquirer.prompt([promptConfig]);
|
|
186
|
-
return answers[param.name];
|
|
187
|
-
}
|
|
188
|
-
catch (error) {
|
|
189
|
-
console.error(chalk.red(`Failed to fetch options for ${param.name}:`), error instanceof Error ? error.message : error);
|
|
190
|
-
throw error;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
else if (resolver.type === "fields") {
|
|
194
|
-
// Fields resolver - fetch field definitions and prompt for each input
|
|
195
|
-
try {
|
|
196
|
-
console.log(chalk.gray(`Fetching input fields for ${param.name}...`));
|
|
197
|
-
const fields = await resolver.fetch(context.sdk, context.resolvedParams);
|
|
198
|
-
if (!fields || fields.length === 0) {
|
|
199
|
-
console.log(chalk.yellow(`No input fields required for this action.`));
|
|
200
|
-
return {};
|
|
201
|
-
}
|
|
202
|
-
const inputs = {};
|
|
203
|
-
// Separate required and optional fields
|
|
204
|
-
const requiredFields = fields.filter((field) => field.required);
|
|
205
|
-
const optionalFields = fields.filter((field) => !field.required);
|
|
206
|
-
// First, prompt for all required fields
|
|
207
|
-
if (requiredFields.length > 0) {
|
|
208
|
-
console.log(chalk.blue(`\n📝 Please provide values for the following input fields:`));
|
|
209
|
-
for (const field of requiredFields) {
|
|
210
|
-
await this.promptForField(field, inputs);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// Then ask if user wants to configure optional fields
|
|
214
|
-
if (optionalFields.length > 0) {
|
|
215
|
-
console.log(chalk.gray(`\nThere are ${optionalFields.length} optional field(s) available.`));
|
|
216
|
-
let shouldConfigureOptional;
|
|
217
|
-
try {
|
|
218
|
-
shouldConfigureOptional = await inquirer.prompt([
|
|
219
|
-
{
|
|
220
|
-
type: "confirm",
|
|
221
|
-
name: "configure",
|
|
222
|
-
message: "Would you like to configure optional fields?",
|
|
223
|
-
default: false,
|
|
224
|
-
},
|
|
225
|
-
]);
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
if (this.isUserCancellation(error)) {
|
|
229
|
-
console.log(chalk.yellow("\n\nOperation cancelled by user"));
|
|
230
|
-
process.exit(0);
|
|
231
|
-
}
|
|
232
|
-
throw error;
|
|
233
|
-
}
|
|
234
|
-
if (shouldConfigureOptional.configure) {
|
|
235
|
-
console.log(chalk.cyan(`\nOptional fields:`));
|
|
236
|
-
for (const field of optionalFields) {
|
|
237
|
-
await this.promptForField(field, inputs);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return inputs;
|
|
242
|
-
}
|
|
243
|
-
catch (error) {
|
|
244
|
-
console.error(chalk.red(`Failed to fetch fields for ${param.name}:`), error instanceof Error ? error.message : error);
|
|
245
|
-
throw error;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
throw new Error(`Unknown resolver type for ${param.name}`);
|
|
249
|
-
}
|
|
250
|
-
getNestedValue(obj, path) {
|
|
251
|
-
return path.reduce((current, key) => current?.[key], obj);
|
|
252
|
-
}
|
|
253
|
-
setNestedValue(obj, path, value) {
|
|
254
|
-
const lastKey = path[path.length - 1];
|
|
255
|
-
const parent = path.slice(0, -1).reduce((current, key) => {
|
|
256
|
-
if (!(key in current)) {
|
|
257
|
-
current[key] = {};
|
|
258
|
-
}
|
|
259
|
-
return current[key];
|
|
260
|
-
}, obj);
|
|
261
|
-
parent[lastKey] = value;
|
|
262
|
-
}
|
|
263
|
-
async promptForField(field, inputs) {
|
|
264
|
-
const fieldPrompt = {
|
|
265
|
-
type: field.type === "boolean" ? "confirm" : "input",
|
|
266
|
-
name: field.key,
|
|
267
|
-
message: `${field.label || field.key}${field.required ? " (required)" : " (optional)"}:`,
|
|
268
|
-
...(field.helpText && { prefix: chalk.gray(`ℹ ${field.helpText}\n`) }),
|
|
269
|
-
...(field.default && { default: field.default }),
|
|
270
|
-
};
|
|
271
|
-
if (field.choices && field.choices.length > 0) {
|
|
272
|
-
fieldPrompt.type = "list";
|
|
273
|
-
fieldPrompt.choices = field.choices.map((choice) => ({
|
|
274
|
-
name: choice.label || choice.value,
|
|
275
|
-
value: choice.value,
|
|
276
|
-
}));
|
|
277
|
-
}
|
|
278
|
-
try {
|
|
279
|
-
const answer = await inquirer.prompt([fieldPrompt]);
|
|
280
|
-
if (answer[field.key] !== undefined && answer[field.key] !== "") {
|
|
281
|
-
inputs[field.key] = answer[field.key];
|
|
282
|
-
}
|
|
283
|
-
else if (field.required) {
|
|
284
|
-
throw new Error(`Required field ${field.key} cannot be empty`);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
catch (error) {
|
|
288
|
-
if (this.isUserCancellation(error)) {
|
|
289
|
-
console.log(chalk.yellow("\n\nOperation cancelled by user"));
|
|
290
|
-
process.exit(0);
|
|
291
|
-
}
|
|
292
|
-
throw error;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
isUserCancellation(error) {
|
|
296
|
-
return (error?.name === "ExitPromptError" ||
|
|
297
|
-
error?.message?.includes("User force closed") ||
|
|
298
|
-
error?.isTTYError);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
function getFormatMetadata(schema) {
|
|
3
|
-
return schema?._def?.formatMeta;
|
|
4
|
-
}
|
|
5
|
-
function getOutputSchema(schema) {
|
|
6
|
-
return schema?._def?.outputSchema;
|
|
7
|
-
}
|
|
8
|
-
// ============================================================================
|
|
9
|
-
// Generic Schema-Driven Formatter
|
|
10
|
-
// ============================================================================
|
|
11
|
-
export function formatItemsFromSchema(inputSchema, items) {
|
|
12
|
-
// Get the output schema and its format metadata
|
|
13
|
-
const outputSchema = getOutputSchema(inputSchema);
|
|
14
|
-
if (!outputSchema) {
|
|
15
|
-
// Fallback to generic formatting if no output schema
|
|
16
|
-
formatItemsGeneric(items);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
const formatMeta = getFormatMetadata(outputSchema);
|
|
20
|
-
if (!formatMeta) {
|
|
21
|
-
// Fallback to generic formatting if no format metadata
|
|
22
|
-
formatItemsGeneric(items);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
// Format each item using the schema metadata
|
|
26
|
-
items.forEach((item, index) => {
|
|
27
|
-
formatSingleItem(item, index, formatMeta);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
function formatSingleItem(item, index, formatMeta) {
|
|
31
|
-
// Get the formatted item from the format function
|
|
32
|
-
const formatted = formatMeta.format(item);
|
|
33
|
-
// Build the main title line
|
|
34
|
-
let titleLine = `${chalk.gray(`${index + 1}.`)} ${chalk.cyan(formatted.title)}`;
|
|
35
|
-
if (formatted.subtitle) {
|
|
36
|
-
titleLine += ` ${chalk.gray(formatted.subtitle)}`;
|
|
37
|
-
}
|
|
38
|
-
console.log(titleLine);
|
|
39
|
-
// Format detail lines
|
|
40
|
-
for (const detail of formatted.details) {
|
|
41
|
-
const styledText = applyStyle(detail.text, detail.style);
|
|
42
|
-
console.log(` ${styledText}`);
|
|
43
|
-
}
|
|
44
|
-
console.log(); // Empty line between items
|
|
45
|
-
}
|
|
46
|
-
function applyStyle(value, style) {
|
|
47
|
-
switch (style) {
|
|
48
|
-
case "dim":
|
|
49
|
-
return chalk.dim(value);
|
|
50
|
-
case "accent":
|
|
51
|
-
return chalk.magenta(value);
|
|
52
|
-
case "warning":
|
|
53
|
-
return chalk.red(value);
|
|
54
|
-
case "success":
|
|
55
|
-
return chalk.green(value);
|
|
56
|
-
case "normal":
|
|
57
|
-
default:
|
|
58
|
-
return chalk.blue(value);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function formatItemsGeneric(items) {
|
|
62
|
-
// Fallback formatting for items without schema metadata
|
|
63
|
-
items.forEach((item, index) => {
|
|
64
|
-
const name = item.name || item.key || item.id || "Item";
|
|
65
|
-
console.log(`${chalk.gray(`${index + 1}.`)} ${chalk.cyan(name)}`);
|
|
66
|
-
if (item.description) {
|
|
67
|
-
console.log(` ${chalk.dim(item.description)}`);
|
|
68
|
-
}
|
|
69
|
-
console.log();
|
|
70
|
-
});
|
|
71
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
const promises = {};
|
|
3
|
-
const serializeAsync = (fn) => async (...args) => {
|
|
4
|
-
const hash = createHash("sha256")
|
|
5
|
-
.update(fn.toString(), "utf8")
|
|
6
|
-
.digest()
|
|
7
|
-
.toString("hex");
|
|
8
|
-
const promise = promises[hash];
|
|
9
|
-
if (!promise) {
|
|
10
|
-
promises[hash] = fn(...args).finally(() => {
|
|
11
|
-
delete promises[hash];
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
return promises[hash];
|
|
15
|
-
};
|
|
16
|
-
export default serializeAsync;
|
package/dist/utils/spinner.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const spinPromise: <T>(promise: Promise<T>, text: string) => Promise<T>;
|
package/dist/utils/spinner.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import ora from "ora";
|
|
2
|
-
export const spinPromise = async (promise, text) => {
|
|
3
|
-
const spinner = ora(text).start();
|
|
4
|
-
try {
|
|
5
|
-
const result = await promise;
|
|
6
|
-
spinner.succeed();
|
|
7
|
-
return result;
|
|
8
|
-
}
|
|
9
|
-
catch (error) {
|
|
10
|
-
spinner.fail();
|
|
11
|
-
throw error;
|
|
12
|
-
}
|
|
13
|
-
};
|