@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { loginPlugin } from "./login";
|
|
2
2
|
export { logoutPlugin } from "./logout";
|
|
3
3
|
export { mcpPlugin } from "./mcp";
|
|
4
|
-
export { generateTypesPlugin } from "./generateTypes";
|
|
5
4
|
export { bundleCodePlugin } from "./bundleCode";
|
|
6
|
-
export {
|
|
5
|
+
export { getLoginConfigPathPlugin } from "./getLoginConfigPath";
|
|
6
|
+
export { addPlugin } from "./add";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { loginPlugin } from "./login";
|
|
2
2
|
export { logoutPlugin } from "./logout";
|
|
3
3
|
export { mcpPlugin } from "./mcp";
|
|
4
|
-
export { generateTypesPlugin } from "./generateTypes";
|
|
5
4
|
export { bundleCodePlugin } from "./bundleCode";
|
|
6
|
-
export {
|
|
5
|
+
export { getLoginConfigPathPlugin } from "./getLoginConfigPath";
|
|
6
|
+
export { addPlugin } from "./add";
|
package/dist/src/sdk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createZapierSdkWithoutRegistry, registryPlugin, } from "@zapier/zapier-sdk";
|
|
2
|
-
import { loginPlugin, logoutPlugin, mcpPlugin,
|
|
2
|
+
import { loginPlugin, logoutPlugin, mcpPlugin, bundleCodePlugin, getLoginConfigPathPlugin, addPlugin, } from "./plugins/index";
|
|
3
3
|
/**
|
|
4
4
|
* Create a Zapier SDK instance configured specifically for the CLI
|
|
5
5
|
* Includes all CLI-specific plugins in addition to the standard SDK functionality
|
|
@@ -10,9 +10,9 @@ export function createZapierCliSdk(options = {}) {
|
|
|
10
10
|
debug: options.debug,
|
|
11
11
|
});
|
|
12
12
|
// Add CLI-specific plugins before registry
|
|
13
|
-
sdk = sdk.addPlugin(generateTypesPlugin);
|
|
14
13
|
sdk = sdk.addPlugin(bundleCodePlugin);
|
|
15
|
-
sdk = sdk.addPlugin(
|
|
14
|
+
sdk = sdk.addPlugin(getLoginConfigPathPlugin);
|
|
15
|
+
sdk = sdk.addPlugin(addPlugin);
|
|
16
16
|
sdk = sdk.addPlugin(mcpPlugin);
|
|
17
17
|
sdk = sdk.addPlugin(loginPlugin);
|
|
18
18
|
sdk = sdk.addPlugin(logoutPlugin);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { type FunctionRegistryEntry } from "@zapier/zapier-sdk";
|
|
2
3
|
export interface CliParameter {
|
|
3
4
|
name: string;
|
|
4
5
|
type: "string" | "number" | "boolean" | "array";
|
|
@@ -9,5 +10,5 @@ export interface CliParameter {
|
|
|
9
10
|
hasResolver?: boolean;
|
|
10
11
|
isPositional?: boolean;
|
|
11
12
|
}
|
|
12
|
-
export declare function analyzeZodSchema(schema: z.ZodSchema): CliParameter[];
|
|
13
|
+
export declare function analyzeZodSchema(schema: z.ZodSchema, functionInfo?: FunctionRegistryEntry): CliParameter[];
|
|
13
14
|
export declare function convertCliArgsToSdkParams(parameters: CliParameter[], positionalArgs: unknown[], options: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { isPositional } from "@zapier/zapier-sdk";
|
|
3
3
|
// ============================================================================
|
|
4
4
|
// Schema Analysis
|
|
5
5
|
// ============================================================================
|
|
6
|
-
export function analyzeZodSchema(schema) {
|
|
6
|
+
export function analyzeZodSchema(schema, functionInfo) {
|
|
7
7
|
const parameters = [];
|
|
8
8
|
if (schema instanceof z.ZodObject) {
|
|
9
9
|
const shape = schema.shape;
|
|
10
10
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
11
|
-
const param = analyzeZodField(key, fieldSchema);
|
|
11
|
+
const param = analyzeZodField(key, fieldSchema, functionInfo);
|
|
12
12
|
if (param) {
|
|
13
13
|
parameters.push(param);
|
|
14
14
|
}
|
|
@@ -16,7 +16,7 @@ export function analyzeZodSchema(schema) {
|
|
|
16
16
|
}
|
|
17
17
|
return parameters;
|
|
18
18
|
}
|
|
19
|
-
function analyzeZodField(name, schema) {
|
|
19
|
+
function analyzeZodField(name, schema, functionInfo) {
|
|
20
20
|
let baseSchema = schema;
|
|
21
21
|
let required = true;
|
|
22
22
|
let defaultValue = undefined;
|
|
@@ -53,6 +53,12 @@ function analyzeZodField(name, schema) {
|
|
|
53
53
|
// Handle Record<string, any> as JSON string input
|
|
54
54
|
paramType = "string";
|
|
55
55
|
}
|
|
56
|
+
// Check if this parameter has a resolver
|
|
57
|
+
let paramHasResolver = false;
|
|
58
|
+
// Check function-specific resolvers first
|
|
59
|
+
if (functionInfo?.resolvers?.[name]) {
|
|
60
|
+
paramHasResolver = true;
|
|
61
|
+
}
|
|
56
62
|
// Extract resolver metadata
|
|
57
63
|
return {
|
|
58
64
|
name,
|
|
@@ -61,7 +67,7 @@ function analyzeZodField(name, schema) {
|
|
|
61
67
|
description: schema.description,
|
|
62
68
|
default: defaultValue,
|
|
63
69
|
choices,
|
|
64
|
-
hasResolver:
|
|
70
|
+
hasResolver: paramHasResolver,
|
|
65
71
|
isPositional: isPositional(schema),
|
|
66
72
|
};
|
|
67
73
|
}
|
|
@@ -1,34 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { isPositional, formatErrorMessage, ZapierError, } from "@zapier/zapier-sdk";
|
|
3
3
|
import { SchemaParameterResolver } from "./parameter-resolver";
|
|
4
|
-
import { formatItemsFromSchema } from "./schema-formatter";
|
|
4
|
+
import { formatItemsFromSchema, formatJsonOutput } from "./schema-formatter";
|
|
5
5
|
import chalk from "chalk";
|
|
6
|
-
import util from "util";
|
|
7
6
|
import inquirer from "inquirer";
|
|
8
7
|
// ============================================================================
|
|
9
|
-
// JSON Formatting
|
|
10
|
-
// ============================================================================
|
|
11
|
-
function formatJsonOutput(data) {
|
|
12
|
-
// Don't print anything for undefined results (commands that just perform actions)
|
|
13
|
-
if (data === undefined) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
// Show success message for action results
|
|
17
|
-
if (data &&
|
|
18
|
-
typeof data === "object" &&
|
|
19
|
-
!Array.isArray(data) &&
|
|
20
|
-
(data.success !== undefined ||
|
|
21
|
-
data.id ||
|
|
22
|
-
data.status)) {
|
|
23
|
-
console.log(chalk.green("ā
Action completed successfully!\n"));
|
|
24
|
-
}
|
|
25
|
-
// Use util.inspect for colored output
|
|
26
|
-
console.log(util.inspect(data, { colors: true, depth: null, breakLength: 80 }));
|
|
27
|
-
}
|
|
28
|
-
// ============================================================================
|
|
29
8
|
// Schema Analysis
|
|
30
9
|
// ============================================================================
|
|
31
|
-
function analyzeZodSchema(schema) {
|
|
10
|
+
function analyzeZodSchema(schema, functionInfo) {
|
|
32
11
|
const parameters = [];
|
|
33
12
|
// Handle ZodEffects (schemas with .refine(), .transform(), etc.)
|
|
34
13
|
if (schema._def &&
|
|
@@ -37,12 +16,12 @@ function analyzeZodSchema(schema) {
|
|
|
37
16
|
// Get the underlying schema
|
|
38
17
|
const innerSchema = schema
|
|
39
18
|
._def.schema;
|
|
40
|
-
return analyzeZodSchema(innerSchema);
|
|
19
|
+
return analyzeZodSchema(innerSchema, functionInfo);
|
|
41
20
|
}
|
|
42
21
|
if (schema instanceof z.ZodObject) {
|
|
43
22
|
const shape = schema.shape;
|
|
44
23
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
45
|
-
const param = analyzeZodField(key, fieldSchema);
|
|
24
|
+
const param = analyzeZodField(key, fieldSchema, functionInfo);
|
|
46
25
|
if (param) {
|
|
47
26
|
parameters.push(param);
|
|
48
27
|
}
|
|
@@ -50,7 +29,7 @@ function analyzeZodSchema(schema) {
|
|
|
50
29
|
}
|
|
51
30
|
return parameters;
|
|
52
31
|
}
|
|
53
|
-
function analyzeZodField(name, schema) {
|
|
32
|
+
function analyzeZodField(name, schema, functionInfo) {
|
|
54
33
|
let baseSchema = schema;
|
|
55
34
|
let required = true;
|
|
56
35
|
let defaultValue = undefined;
|
|
@@ -98,6 +77,12 @@ function analyzeZodField(name, schema) {
|
|
|
98
77
|
// Handle Record<string, any> as JSON string input
|
|
99
78
|
paramType = "string";
|
|
100
79
|
}
|
|
80
|
+
// Check if this parameter has a resolver
|
|
81
|
+
let paramHasResolver = false;
|
|
82
|
+
// Check function-specific resolvers first
|
|
83
|
+
if (functionInfo?.resolvers?.[name]) {
|
|
84
|
+
paramHasResolver = true;
|
|
85
|
+
}
|
|
101
86
|
// Extract resolver metadata
|
|
102
87
|
return {
|
|
103
88
|
name,
|
|
@@ -106,7 +91,7 @@ function analyzeZodField(name, schema) {
|
|
|
106
91
|
description: schema.description,
|
|
107
92
|
default: defaultValue,
|
|
108
93
|
choices,
|
|
109
|
-
hasResolver:
|
|
94
|
+
hasResolver: paramHasResolver,
|
|
110
95
|
isPositional: isPositional(schema),
|
|
111
96
|
};
|
|
112
97
|
}
|
|
@@ -145,7 +130,7 @@ export function generateCliCommands(program, sdk) {
|
|
|
145
130
|
}
|
|
146
131
|
// Convert methodName to kebab-case CLI command
|
|
147
132
|
const cliCommandName = methodNameToCliCommand(fnInfo.name);
|
|
148
|
-
const config = createCommandConfig(cliCommandName, fnInfo
|
|
133
|
+
const config = createCommandConfig(cliCommandName, fnInfo, sdk);
|
|
149
134
|
addCommand(program, cliCommandName, config);
|
|
150
135
|
});
|
|
151
136
|
// Override the help display to show commands grouped by category
|
|
@@ -200,8 +185,9 @@ export function generateCliCommands(program, sdk) {
|
|
|
200
185
|
},
|
|
201
186
|
});
|
|
202
187
|
}
|
|
203
|
-
function createCommandConfig(cliCommandName,
|
|
204
|
-
const
|
|
188
|
+
function createCommandConfig(cliCommandName, functionInfo, sdk) {
|
|
189
|
+
const schema = functionInfo.inputSchema;
|
|
190
|
+
const parameters = analyzeZodSchema(schema, functionInfo);
|
|
205
191
|
const description = schema.description || `${cliCommandName} command`;
|
|
206
192
|
const handler = async (...args) => {
|
|
207
193
|
try {
|
|
@@ -209,7 +195,7 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
|
209
195
|
const commandObj = args[args.length - 1];
|
|
210
196
|
const options = commandObj.opts();
|
|
211
197
|
// Check if this is a list command for pagination
|
|
212
|
-
const isListCommand =
|
|
198
|
+
const isListCommand = functionInfo.type === "list";
|
|
213
199
|
const hasPaginationParams = parameters.some((p) => p.name === "maxItems" || p.name === "pageSize");
|
|
214
200
|
const hasUserSpecifiedMaxItems = "maxItems" in options && options.maxItems !== undefined;
|
|
215
201
|
const shouldUseJson = options.json;
|
|
@@ -217,7 +203,7 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
|
217
203
|
const rawParams = convertCliArgsToSdkParams(parameters, args.slice(0, -1), options);
|
|
218
204
|
// Resolve missing parameters interactively using schema metadata
|
|
219
205
|
const resolver = new SchemaParameterResolver();
|
|
220
|
-
const resolvedParams = await resolver.resolveParameters(schema, rawParams, sdk);
|
|
206
|
+
const resolvedParams = await resolver.resolveParameters(schema, rawParams, sdk, functionInfo.name);
|
|
221
207
|
// Handle paginated list commands with async iteration
|
|
222
208
|
if (isListCommand &&
|
|
223
209
|
hasPaginationParams &&
|
|
@@ -225,8 +211,8 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
|
225
211
|
!hasUserSpecifiedMaxItems) {
|
|
226
212
|
// Get the async iterable directly from the SDK method call (don't await it! that breaks the next page behavior)
|
|
227
213
|
const sdkObj = sdk;
|
|
228
|
-
const sdkIterator = sdkObj[
|
|
229
|
-
await handlePaginatedListWithAsyncIteration(
|
|
214
|
+
const sdkIterator = sdkObj[functionInfo.name](resolvedParams);
|
|
215
|
+
await handlePaginatedListWithAsyncIteration(functionInfo.name, sdkIterator, functionInfo);
|
|
230
216
|
}
|
|
231
217
|
else {
|
|
232
218
|
// Special handling for commands that write to files
|
|
@@ -234,14 +220,14 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
|
234
220
|
if (hasOutputFile) {
|
|
235
221
|
// Call the SDK method for file output commands
|
|
236
222
|
const sdkObj = sdk;
|
|
237
|
-
await sdkObj[
|
|
223
|
+
await sdkObj[functionInfo.name](resolvedParams);
|
|
238
224
|
console.log(chalk.green(`ā
${cliCommandName} completed successfully!`));
|
|
239
225
|
console.log(chalk.gray(`Output written to: ${hasOutputFile}`));
|
|
240
226
|
return;
|
|
241
227
|
}
|
|
242
228
|
// Call the SDK method and handle non-paginated results
|
|
243
229
|
const sdkObj = sdk;
|
|
244
|
-
const result = await sdkObj[
|
|
230
|
+
const result = await sdkObj[functionInfo.name](resolvedParams);
|
|
245
231
|
const items = result?.data
|
|
246
232
|
? result.data
|
|
247
233
|
: result;
|
|
@@ -249,7 +235,7 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
|
249
235
|
console.log(JSON.stringify(items, null, 2));
|
|
250
236
|
}
|
|
251
237
|
else if (isListCommand) {
|
|
252
|
-
formatNonPaginatedResults(items, resolvedParams.maxItems, hasUserSpecifiedMaxItems, shouldUseJson,
|
|
238
|
+
formatNonPaginatedResults(items, resolvedParams.maxItems, hasUserSpecifiedMaxItems, shouldUseJson, functionInfo);
|
|
253
239
|
}
|
|
254
240
|
else {
|
|
255
241
|
formatJsonOutput(items);
|
|
@@ -294,6 +280,8 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
|
294
280
|
}
|
|
295
281
|
function addCommand(program, commandName, config) {
|
|
296
282
|
const command = program.command(commandName).description(config.description);
|
|
283
|
+
// Track whether we've already used a positional array parameter
|
|
284
|
+
let hasPositionalArray = false;
|
|
297
285
|
// Add parameters to command
|
|
298
286
|
config.parameters.forEach((param) => {
|
|
299
287
|
// Convert camelCase to kebab-case for CLI display
|
|
@@ -302,6 +290,19 @@ function addCommand(program, commandName, config) {
|
|
|
302
290
|
// Required parameters with resolvers become optional positional arguments (resolver handles prompting)
|
|
303
291
|
command.argument(`[${kebabName}]`, param.description || `${kebabName} parameter`);
|
|
304
292
|
}
|
|
293
|
+
else if (param.required &&
|
|
294
|
+
param.type === "array" &&
|
|
295
|
+
!hasPositionalArray) {
|
|
296
|
+
// First required array parameter becomes a variadic positional argument
|
|
297
|
+
hasPositionalArray = true;
|
|
298
|
+
command.argument(`<${kebabName}...>`, param.description || `${kebabName} parameter`);
|
|
299
|
+
}
|
|
300
|
+
else if (param.required && param.type === "array") {
|
|
301
|
+
// Subsequent required array parameters become required flags
|
|
302
|
+
const flags = [`--${kebabName}`];
|
|
303
|
+
const flagSignature = flags.join(", ") + ` <values...>`;
|
|
304
|
+
command.requiredOption(flagSignature, param.description || `${kebabName} parameter (required)`);
|
|
305
|
+
}
|
|
305
306
|
else if (param.required) {
|
|
306
307
|
// Required parameters without resolvers become required positional arguments
|
|
307
308
|
command.argument(`<${kebabName}>`, param.description || `${kebabName} parameter`);
|
|
@@ -358,6 +359,10 @@ function convertCliArgsToSdkParams(parameters, positionalArgs, options) {
|
|
|
358
359
|
return sdkParams;
|
|
359
360
|
}
|
|
360
361
|
function convertValue(value, type) {
|
|
362
|
+
// Don't convert undefined values - let the resolver system handle them
|
|
363
|
+
if (value === undefined) {
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
361
366
|
switch (type) {
|
|
362
367
|
case "number":
|
|
363
368
|
return Number(value);
|
|
@@ -383,11 +388,11 @@ function convertValue(value, type) {
|
|
|
383
388
|
// ============================================================================
|
|
384
389
|
// Pagination Handlers
|
|
385
390
|
// ============================================================================
|
|
386
|
-
async function handlePaginatedListWithAsyncIteration(sdkMethodName, sdkResult,
|
|
387
|
-
const itemName = getItemNameFromMethod(
|
|
391
|
+
async function handlePaginatedListWithAsyncIteration(sdkMethodName, sdkResult, functionInfo) {
|
|
392
|
+
const itemName = getItemNameFromMethod(functionInfo);
|
|
388
393
|
let totalShown = 0;
|
|
389
394
|
let pageCount = 0;
|
|
390
|
-
console.log(chalk.blue(`š ${getListTitleFromMethod(sdkMethodName)}\n`));
|
|
395
|
+
console.log(chalk.blue(`š ${getListTitleFromMethod(sdkMethodName, functionInfo)}\n`));
|
|
391
396
|
try {
|
|
392
397
|
// Use async iteration to go through pages
|
|
393
398
|
for await (const page of sdkResult) {
|
|
@@ -407,11 +412,11 @@ async function handlePaginatedListWithAsyncIteration(sdkMethodName, sdkResult, s
|
|
|
407
412
|
// Clear screen for subsequent pages (not the first)
|
|
408
413
|
if (pageCount > 1) {
|
|
409
414
|
console.clear();
|
|
410
|
-
console.log(chalk.blue(`š ${getListTitleFromMethod(sdkMethodName)}\n`));
|
|
415
|
+
console.log(chalk.blue(`š ${getListTitleFromMethod(sdkMethodName, functionInfo)}\n`));
|
|
411
416
|
}
|
|
412
|
-
// Format and display items using
|
|
413
|
-
if (
|
|
414
|
-
formatItemsFromSchema(
|
|
417
|
+
// Format and display items using function info
|
|
418
|
+
if (functionInfo) {
|
|
419
|
+
formatItemsFromSchema(functionInfo, items, totalShown);
|
|
415
420
|
}
|
|
416
421
|
else {
|
|
417
422
|
formatItemsGeneric(items, totalShown);
|
|
@@ -447,8 +452,8 @@ async function handlePaginatedListWithAsyncIteration(sdkMethodName, sdkResult, s
|
|
|
447
452
|
console.log(chalk.yellow(`No ${itemName} found.`));
|
|
448
453
|
return;
|
|
449
454
|
}
|
|
450
|
-
if (
|
|
451
|
-
formatItemsFromSchema(
|
|
455
|
+
if (functionInfo) {
|
|
456
|
+
formatItemsFromSchema(functionInfo, items, 0);
|
|
452
457
|
}
|
|
453
458
|
else {
|
|
454
459
|
formatItemsGeneric(items, 0);
|
|
@@ -460,7 +465,7 @@ async function handlePaginatedListWithAsyncIteration(sdkMethodName, sdkResult, s
|
|
|
460
465
|
}
|
|
461
466
|
}
|
|
462
467
|
}
|
|
463
|
-
function formatNonPaginatedResults(result, requestedMaxItems, userSpecifiedMaxItems, useRawJson,
|
|
468
|
+
function formatNonPaginatedResults(result, requestedMaxItems, userSpecifiedMaxItems, useRawJson, functionInfo) {
|
|
464
469
|
if (!Array.isArray(result)) {
|
|
465
470
|
if (useRawJson) {
|
|
466
471
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -474,15 +479,15 @@ function formatNonPaginatedResults(result, requestedMaxItems, userSpecifiedMaxIt
|
|
|
474
479
|
console.log(JSON.stringify(result, null, 2));
|
|
475
480
|
return;
|
|
476
481
|
}
|
|
477
|
-
const itemName =
|
|
482
|
+
const itemName = functionInfo ? getItemNameFromMethod(functionInfo) : "items";
|
|
478
483
|
if (result.length === 0) {
|
|
479
484
|
console.log(chalk.yellow(`No ${itemName} found.`));
|
|
480
485
|
return;
|
|
481
486
|
}
|
|
482
487
|
console.log(chalk.green(`\nā
Found ${result.length} ${itemName}:\n`));
|
|
483
|
-
// Use
|
|
484
|
-
if (
|
|
485
|
-
formatItemsFromSchema(
|
|
488
|
+
// Use function info for formatting
|
|
489
|
+
if (functionInfo) {
|
|
490
|
+
formatItemsFromSchema(functionInfo, result);
|
|
486
491
|
}
|
|
487
492
|
else {
|
|
488
493
|
// Fallback to generic formatting
|
|
@@ -509,20 +514,15 @@ function formatItemsGeneric(items, startingNumber = 0) {
|
|
|
509
514
|
});
|
|
510
515
|
}
|
|
511
516
|
// Generic helper functions that infer from schema description or method name
|
|
512
|
-
function getItemNameFromMethod(
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
if (listMatch) {
|
|
516
|
-
return listMatch[1].toLowerCase();
|
|
517
|
+
function getItemNameFromMethod(functionInfo) {
|
|
518
|
+
if (functionInfo.itemType) {
|
|
519
|
+
return `${functionInfo.itemType} items`;
|
|
517
520
|
}
|
|
518
|
-
// Fallback to generic
|
|
519
521
|
return "items";
|
|
520
522
|
}
|
|
521
|
-
function getListTitleFromMethod(methodName) {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const capitalized = itemName.charAt(0).toUpperCase() + itemName.slice(1);
|
|
527
|
-
return `Available ${capitalized}`;
|
|
523
|
+
function getListTitleFromMethod(methodName, functionInfo) {
|
|
524
|
+
if (functionInfo.itemType) {
|
|
525
|
+
return `Available ${functionInfo.itemType} items`;
|
|
526
|
+
}
|
|
527
|
+
return `${methodName} items`;
|
|
528
528
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { ZapierSdk } from "@zapier/zapier-sdk";
|
|
3
3
|
export declare class SchemaParameterResolver {
|
|
4
|
-
resolveParameters(schema: z.ZodSchema, providedParams: unknown, sdk: ZapierSdk): Promise<unknown>;
|
|
4
|
+
resolveParameters(schema: z.ZodSchema, providedParams: unknown, sdk: ZapierSdk, functionName?: string): Promise<unknown>;
|
|
5
5
|
private extractParametersFromSchema;
|
|
6
6
|
private analyzeFieldSchema;
|
|
7
7
|
private createResolvableParameter;
|
|
@@ -11,4 +11,7 @@ export declare class SchemaParameterResolver {
|
|
|
11
11
|
private setNestedValue;
|
|
12
12
|
private promptForField;
|
|
13
13
|
private isUserCancellation;
|
|
14
|
+
private hasResolver;
|
|
15
|
+
private getResolver;
|
|
16
|
+
private getLocalResolvers;
|
|
14
17
|
}
|
|
@@ -1,17 +1,59 @@
|
|
|
1
1
|
import inquirer from "inquirer";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Local Resolution Helper Functions
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* Resolve dependency chain for a parameter using local resolvers
|
|
9
|
+
* Returns parameters in the order they need to be resolved
|
|
10
|
+
*/
|
|
11
|
+
function getLocalResolutionOrder(paramName, resolvers, resolved = new Set()) {
|
|
12
|
+
const resolver = resolvers[paramName];
|
|
13
|
+
if (!resolver || resolver.type === "static") {
|
|
14
|
+
return [paramName];
|
|
15
|
+
}
|
|
16
|
+
const order = [];
|
|
17
|
+
if ("depends" in resolver && resolver.depends) {
|
|
18
|
+
for (const dependency of resolver.depends) {
|
|
19
|
+
if (!resolved.has(dependency)) {
|
|
20
|
+
order.push(...getLocalResolutionOrder(dependency, resolvers, resolved));
|
|
21
|
+
resolved.add(dependency);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!resolved.has(paramName)) {
|
|
26
|
+
order.push(paramName);
|
|
27
|
+
resolved.add(paramName);
|
|
28
|
+
}
|
|
29
|
+
return order;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get resolution order for multiple parameters using local resolvers
|
|
33
|
+
*/
|
|
34
|
+
function getLocalResolutionOrderForParams(paramNames, resolvers) {
|
|
35
|
+
const resolved = new Set();
|
|
36
|
+
const order = [];
|
|
37
|
+
for (const paramName of paramNames) {
|
|
38
|
+
const paramOrder = getLocalResolutionOrder(paramName, resolvers, resolved);
|
|
39
|
+
for (const param of paramOrder) {
|
|
40
|
+
if (!order.includes(param)) {
|
|
41
|
+
order.push(param);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return order;
|
|
46
|
+
}
|
|
5
47
|
// ============================================================================
|
|
6
48
|
// Schema Parameter Resolver
|
|
7
49
|
// ============================================================================
|
|
8
50
|
export class SchemaParameterResolver {
|
|
9
|
-
async resolveParameters(schema, providedParams, sdk) {
|
|
51
|
+
async resolveParameters(schema, providedParams, sdk, functionName) {
|
|
10
52
|
// 1. Try to parse with current parameters
|
|
11
53
|
const parseResult = schema.safeParse(providedParams);
|
|
12
54
|
// Get all schema parameters to check which ones have resolvers
|
|
13
55
|
const allParams = this.extractParametersFromSchema(schema);
|
|
14
|
-
const resolvableParams = allParams.filter((param) => hasResolver(param.name));
|
|
56
|
+
const resolvableParams = allParams.filter((param) => this.hasResolver(param.name, sdk, functionName));
|
|
15
57
|
// Get all missing parameters that have resolvers
|
|
16
58
|
const missingResolvable = resolvableParams.filter((param) => {
|
|
17
59
|
const hasValue = this.getNestedValue(providedParams, param.path) !== undefined;
|
|
@@ -61,10 +103,13 @@ export class SchemaParameterResolver {
|
|
|
61
103
|
sdk,
|
|
62
104
|
currentParams: providedParams,
|
|
63
105
|
resolvedParams,
|
|
106
|
+
functionName,
|
|
64
107
|
};
|
|
108
|
+
// Get local resolvers for this function
|
|
109
|
+
const localResolvers = this.getLocalResolvers(sdk, functionName);
|
|
65
110
|
if (functionallyRequired.length > 0) {
|
|
66
111
|
const requiredParamNames = functionallyRequired.map((p) => p.name);
|
|
67
|
-
const requiredResolutionOrder =
|
|
112
|
+
const requiredResolutionOrder = getLocalResolutionOrderForParams(requiredParamNames, localResolvers);
|
|
68
113
|
// Find all parameters that need to be resolved (including dependencies)
|
|
69
114
|
// from the available resolvable parameters
|
|
70
115
|
const orderedRequiredParams = requiredResolutionOrder
|
|
@@ -84,7 +129,7 @@ export class SchemaParameterResolver {
|
|
|
84
129
|
.filter((param) => param !== undefined);
|
|
85
130
|
for (const param of orderedRequiredParams) {
|
|
86
131
|
try {
|
|
87
|
-
const value = await this.resolveParameter(param, context);
|
|
132
|
+
const value = await this.resolveParameter(param, context, functionName);
|
|
88
133
|
this.setNestedValue(resolvedParams, param.path, value);
|
|
89
134
|
// Update context with newly resolved value
|
|
90
135
|
context.resolvedParams = resolvedParams;
|
|
@@ -105,13 +150,13 @@ export class SchemaParameterResolver {
|
|
|
105
150
|
// 3. Resolve parameters that should always be prompted for (but can be skipped)
|
|
106
151
|
if (alwaysPrompt.length > 0) {
|
|
107
152
|
const alwaysPromptNames = alwaysPrompt.map((p) => p.name);
|
|
108
|
-
const alwaysPromptResolutionOrder =
|
|
153
|
+
const alwaysPromptResolutionOrder = getLocalResolutionOrderForParams(alwaysPromptNames, localResolvers);
|
|
109
154
|
const orderedAlwaysPromptParams = alwaysPromptResolutionOrder
|
|
110
155
|
.map((paramName) => alwaysPrompt.find((p) => p.name === paramName))
|
|
111
156
|
.filter((param) => param !== undefined);
|
|
112
157
|
for (const param of orderedAlwaysPromptParams) {
|
|
113
158
|
try {
|
|
114
|
-
const value = await this.resolveParameter(param, context);
|
|
159
|
+
const value = await this.resolveParameter(param, context, functionName);
|
|
115
160
|
this.setNestedValue(resolvedParams, param.path, value);
|
|
116
161
|
// Update context with newly resolved value
|
|
117
162
|
context.resolvedParams = resolvedParams;
|
|
@@ -139,13 +184,13 @@ export class SchemaParameterResolver {
|
|
|
139
184
|
if (shouldResolveOptional.resolveOptional) {
|
|
140
185
|
// Resolve optional parameters using their resolvers
|
|
141
186
|
const optionalParamNames = trulyOptional.map((p) => p.name);
|
|
142
|
-
const optionalResolutionOrder =
|
|
187
|
+
const optionalResolutionOrder = getLocalResolutionOrderForParams(optionalParamNames, localResolvers);
|
|
143
188
|
const orderedOptionalParams = optionalResolutionOrder
|
|
144
189
|
.map((paramName) => trulyOptional.find((p) => p.name === paramName))
|
|
145
190
|
.filter((param) => param !== undefined);
|
|
146
191
|
for (const param of orderedOptionalParams) {
|
|
147
192
|
try {
|
|
148
|
-
const value = await this.resolveParameter(param, context);
|
|
193
|
+
const value = await this.resolveParameter(param, context, functionName);
|
|
149
194
|
this.setNestedValue(resolvedParams, param.path, value);
|
|
150
195
|
// Update context with newly resolved value
|
|
151
196
|
context.resolvedParams = resolvedParams;
|
|
@@ -208,8 +253,8 @@ export class SchemaParameterResolver {
|
|
|
208
253
|
isRequired,
|
|
209
254
|
};
|
|
210
255
|
}
|
|
211
|
-
async resolveParameter(param, context) {
|
|
212
|
-
const resolver = getResolver(param.name);
|
|
256
|
+
async resolveParameter(param, context, functionName) {
|
|
257
|
+
const resolver = this.getResolver(param.name, context.sdk, functionName);
|
|
213
258
|
if (!resolver) {
|
|
214
259
|
throw new Error(`No resolver found for parameter: ${param.name}`);
|
|
215
260
|
}
|
|
@@ -285,8 +330,8 @@ export class SchemaParameterResolver {
|
|
|
285
330
|
break;
|
|
286
331
|
}
|
|
287
332
|
// Separate new required and optional fields
|
|
288
|
-
const newRequiredFields = newFields.filter((field) => field.
|
|
289
|
-
const newOptionalFields = newFields.filter((field) => !field.
|
|
333
|
+
const newRequiredFields = newFields.filter((field) => field.is_required);
|
|
334
|
+
const newOptionalFields = newFields.filter((field) => !field.is_required);
|
|
290
335
|
// Prompt for new required fields
|
|
291
336
|
if (newRequiredFields.length > 0) {
|
|
292
337
|
console.log(chalk.blue(`\nš Please provide values for the following ${iteration === 1 ? "" : "additional "}input fields:`));
|
|
@@ -358,7 +403,7 @@ export class SchemaParameterResolver {
|
|
|
358
403
|
const fieldPrompt = {
|
|
359
404
|
type: fieldObj.type === "boolean" ? "confirm" : "input",
|
|
360
405
|
name: fieldObj.key,
|
|
361
|
-
message: `${fieldObj.label || fieldObj.key}${fieldObj.
|
|
406
|
+
message: `${fieldObj.label || fieldObj.key}${fieldObj.is_required ? " (required)" : " (optional)"}:`,
|
|
362
407
|
};
|
|
363
408
|
if (fieldObj.helpText) {
|
|
364
409
|
fieldPrompt.prefix = chalk.gray(`ā¹ ${fieldObj.helpText}\n`);
|
|
@@ -381,7 +426,7 @@ export class SchemaParameterResolver {
|
|
|
381
426
|
if (answer[fieldObj.key] !== undefined && answer[fieldObj.key] !== "") {
|
|
382
427
|
inputs[fieldObj.key] = answer[fieldObj.key];
|
|
383
428
|
}
|
|
384
|
-
else if (fieldObj.
|
|
429
|
+
else if (fieldObj.is_required) {
|
|
385
430
|
throw new Error(`Required field ${fieldObj.key} cannot be empty`);
|
|
386
431
|
}
|
|
387
432
|
}
|
|
@@ -399,4 +444,36 @@ export class SchemaParameterResolver {
|
|
|
399
444
|
errorObj?.message?.includes("User force closed") ||
|
|
400
445
|
errorObj?.isTTYError === true);
|
|
401
446
|
}
|
|
447
|
+
hasResolver(paramName, sdk, functionName) {
|
|
448
|
+
// Check plugin-specific resolvers first
|
|
449
|
+
if (functionName && typeof sdk.getRegistry === "function") {
|
|
450
|
+
const registry = sdk.getRegistry();
|
|
451
|
+
const functionInfo = registry.functions.find((f) => f.name === functionName);
|
|
452
|
+
if (functionInfo && functionInfo.resolvers?.[paramName]) {
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// No global registry fallback
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
getResolver(paramName, sdk, functionName) {
|
|
460
|
+
// Check plugin-specific resolvers first
|
|
461
|
+
if (functionName && typeof sdk.getRegistry === "function") {
|
|
462
|
+
const registry = sdk.getRegistry();
|
|
463
|
+
const functionInfo = registry.functions.find((f) => f.name === functionName);
|
|
464
|
+
if (functionInfo && functionInfo.resolvers?.[paramName]) {
|
|
465
|
+
return functionInfo.resolvers[paramName];
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// No global registry fallback
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
getLocalResolvers(sdk, functionName) {
|
|
472
|
+
if (!functionName || typeof sdk.getRegistry !== "function") {
|
|
473
|
+
return {};
|
|
474
|
+
}
|
|
475
|
+
const registry = sdk.getRegistry();
|
|
476
|
+
const functionInfo = registry.functions.find((f) => f.name === functionName);
|
|
477
|
+
return functionInfo?.resolvers || {};
|
|
478
|
+
}
|
|
402
479
|
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function formatJsonOutput(data: unknown): void;
|
|
3
|
+
export declare function formatItemsFromSchema(functionInfo: {
|
|
4
|
+
inputSchema: z.ZodType;
|
|
5
|
+
outputSchema?: z.ZodType;
|
|
6
|
+
}, items: unknown[], startingNumber?: number): void;
|