@zapier/zapier-sdk-cli 0.9.0 → 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.
@@ -1,17 +1,59 @@
1
1
  import inquirer from "inquirer";
2
2
  import chalk from "chalk";
3
3
  import { z } from "zod";
4
- import { getResolver, hasResolver, getResolutionOrderForParams, } from "@zapier/zapier-sdk";
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 = getResolutionOrderForParams(requiredParamNames);
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 = getResolutionOrderForParams(alwaysPromptNames);
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 = getResolutionOrderForParams(optionalParamNames);
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.required);
289
- const newOptionalFields = newFields.filter((field) => !field.required);
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.required ? " (required)" : " (optional)"}:`,
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.required) {
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 formatItemsFromSchema(inputSchema: z.ZodType, items: unknown[], startingNumber?: number): void;
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;
@@ -1,4 +1,5 @@
1
1
  import chalk from "chalk";
2
+ import util from "util";
2
3
  function getFormatMetadata(schema) {
3
4
  return schema?._def
4
5
  ?.formatMeta;
@@ -7,11 +8,22 @@ function getOutputSchema(schema) {
7
8
  return schema?._def?.outputSchema;
8
9
  }
9
10
  // ============================================================================
11
+ // JSON Formatting
12
+ // ============================================================================
13
+ export function formatJsonOutput(data) {
14
+ // Don't print anything for undefined results (commands that just perform actions)
15
+ if (data === undefined) {
16
+ return;
17
+ }
18
+ // Use util.inspect for colored output
19
+ console.log(util.inspect(data, { colors: true, depth: null, breakLength: 80 }));
20
+ }
21
+ // ============================================================================
10
22
  // Generic Schema-Driven Formatter
11
23
  // ============================================================================
12
- export function formatItemsFromSchema(inputSchema, items, startingNumber = 0) {
13
- // Get the output schema and its format metadata
14
- const outputSchema = getOutputSchema(inputSchema);
24
+ export function formatItemsFromSchema(functionInfo, items, startingNumber = 0) {
25
+ // Get the output schema from function info or fall back to input schema output schema
26
+ const outputSchema = functionInfo.outputSchema || getOutputSchema(functionInfo.inputSchema);
15
27
  if (!outputSchema) {
16
28
  // Fallback to generic formatting if no output schema
17
29
  formatItemsGeneric(items, startingNumber);
@@ -25,18 +37,31 @@ export function formatItemsFromSchema(inputSchema, items, startingNumber = 0) {
25
37
  }
26
38
  // Format each item using the schema metadata
27
39
  items.forEach((item, index) => {
28
- formatSingleItem(item, startingNumber + index, formatMeta);
40
+ const formatted = formatMeta.format(item);
41
+ formatSingleItem(formatted, startingNumber + index);
29
42
  });
30
43
  }
31
- function formatSingleItem(item, itemNumber, formatMeta) {
32
- // Get the formatted item from the format function
33
- const formatted = formatMeta.format(item);
34
- // Build the main title line
44
+ function formatSingleItem(formatted, itemNumber) {
45
+ // Build the main title line with optional subtitle
35
46
  let titleLine = `${chalk.gray(`${itemNumber + 1}.`)} ${chalk.cyan(formatted.title)}`;
36
- if (formatted.subtitle) {
37
- titleLine += ` ${chalk.gray(formatted.subtitle)}`;
47
+ // Generate subtitle from id or key
48
+ if (formatted.id) {
49
+ titleLine += ` ${chalk.gray(`(ID: ${formatted.id})`)}`;
50
+ }
51
+ else if (formatted.key) {
52
+ titleLine += ` ${chalk.gray(`(${formatted.key})`)}`;
38
53
  }
39
54
  console.log(titleLine);
55
+ // Show description if available
56
+ if (formatted.description) {
57
+ console.log(` ${chalk.dim(formatted.description)}`);
58
+ }
59
+ // If data is provided, use JSON formatting instead of details
60
+ if (formatted.data !== undefined) {
61
+ formatJsonOutput(formatted.data);
62
+ console.log(); // Empty line between items
63
+ return;
64
+ }
40
65
  // Format detail lines
41
66
  for (const detail of formatted.details) {
42
67
  const styledText = applyStyle(detail.text, detail.style);
@@ -59,15 +84,20 @@ function applyStyle(value, style) {
59
84
  return chalk.blue(value);
60
85
  }
61
86
  }
87
+ function convertGenericItemToFormattedItem(item) {
88
+ const itemObj = item;
89
+ return {
90
+ title: itemObj.title || itemObj.name || itemObj.key || itemObj.id || "Item",
91
+ id: itemObj.id,
92
+ key: itemObj.key,
93
+ description: itemObj.description,
94
+ details: [],
95
+ };
96
+ }
62
97
  function formatItemsGeneric(items, startingNumber = 0) {
63
- // Fallback formatting for items without schema metadata
98
+ // Convert generic items to FormattedItem and use formatSingleItem
64
99
  items.forEach((item, index) => {
65
- const itemObj = item;
66
- const name = itemObj.title || itemObj.name || itemObj.key || itemObj.id || "Item";
67
- console.log(`${chalk.gray(`${startingNumber + index + 1}.`)} ${chalk.cyan(name)}`);
68
- if (itemObj.description) {
69
- console.log(` ${chalk.dim(itemObj.description)}`);
70
- }
71
- console.log();
100
+ const formatted = convertGenericItemToFormattedItem(item);
101
+ formatSingleItem(formatted, startingNumber + index);
72
102
  });
73
103
  }