@zapier/zapier-sdk-cli 0.36.2 → 0.37.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/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command, CommanderError } from 'commander';
3
3
  import { z } from 'zod';
4
- import { createFunction, OutputPropertySchema, DEFAULT_CONFIG_PATH, injectCliLogin, createZapierSdkWithoutRegistry, registryPlugin, ZapierError, ZapierValidationError, ZapierUnknownError, batch, toSnakeCase, buildApplicationLifecycleEvent, isCredentialsObject, isPositional, runWithTelemetryContext, formatErrorMessage, getOsInfo, getPlatformVersions, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId } from '@zapier/zapier-sdk';
4
+ import { createFunction, OutputPropertySchema, DEFAULT_CONFIG_PATH, injectCliLogin, BaseSdkOptionsSchema, createZapierSdkWithoutRegistry, registryPlugin, ZapierError, ZapierValidationError, ZapierUnknownError, batch, toSnakeCase, buildApplicationLifecycleEvent, isCredentialsObject, isPositional, runWithTelemetryContext, buildCapabilityMessage, formatErrorMessage, getOsInfo, getPlatformVersions, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId } from '@zapier/zapier-sdk';
5
5
  import inquirer from 'inquirer';
6
6
  import chalk7 from 'chalk';
7
7
  import ora from 'ora';
@@ -192,10 +192,13 @@ var SchemaParameterResolver = class {
192
192
  const value = await this.resolveParameter(
193
193
  param,
194
194
  context,
195
- functionName
195
+ functionName,
196
+ { isOptional: !param.isRequired }
196
197
  );
197
- this.setNestedValue(resolvedParams, param.path, value);
198
- context.resolvedParams = resolvedParams;
198
+ if (param.isRequired || value !== void 0) {
199
+ this.setNestedValue(resolvedParams, param.path, value);
200
+ context.resolvedParams = resolvedParams;
201
+ }
199
202
  } catch (error) {
200
203
  if (this.isUserCancellation(error)) {
201
204
  console.log(chalk7.yellow("\n\nOperation cancelled by user"));
@@ -388,28 +391,104 @@ var SchemaParameterResolver = class {
388
391
  context.sdk,
389
392
  context.resolvedParams
390
393
  );
391
- const items = Array.isArray(fetchResult) ? fetchResult : fetchResult?.data ?? [];
392
- const promptConfig = dynamicResolver.prompt(
393
- items,
394
- context.resolvedParams
395
- );
396
- promptConfig.name = promptName;
394
+ let pageIterator = null;
395
+ let items;
396
+ let hasMore = false;
397
+ if (fetchResult != null && typeof fetchResult === "object" && Symbol.asyncIterator in fetchResult) {
398
+ pageIterator = fetchResult[Symbol.asyncIterator]();
399
+ const first = await pageIterator.next();
400
+ if (!first.done && first.value) {
401
+ items = first.value.data;
402
+ hasMore = first.value.nextCursor != null;
403
+ } else {
404
+ items = [];
405
+ }
406
+ } else if (fetchResult != null && typeof fetchResult === "object" && "data" in fetchResult) {
407
+ const page = fetchResult;
408
+ items = page.data;
409
+ hasMore = page.nextCursor != null;
410
+ if (hasMore) {
411
+ this.debugLog(
412
+ `Resolver for ${promptLabel} has more pages but no iterator. Use toIterable() to enable "Load more..." support.`
413
+ );
414
+ }
415
+ } else {
416
+ items = fetchResult || [];
417
+ pageIterator = null;
418
+ }
419
+ const LOAD_MORE_SENTINEL = Symbol("LOAD_MORE");
420
+ const SKIP_SENTINEL = Symbol("SKIP");
421
+ let newItemsStartIndex = -1;
397
422
  this.stopSpinner();
398
- if (isOptional && promptConfig.choices) {
399
- const SKIP_SENTINEL = Symbol("SKIP");
400
- promptConfig.choices = [
401
- { name: chalk7.dim("(Skip)"), value: SKIP_SENTINEL },
402
- ...promptConfig.choices
403
- ];
404
- const answers2 = await inquirer.prompt([promptConfig]);
405
- const value = answers2[promptName];
406
- if (value === SKIP_SENTINEL) {
423
+ while (true) {
424
+ const promptConfig = dynamicResolver.prompt(
425
+ items,
426
+ context.resolvedParams
427
+ );
428
+ promptConfig.name = promptName;
429
+ if (isOptional && promptConfig.choices) {
430
+ promptConfig.choices.unshift({
431
+ name: chalk7.dim("(Skip)"),
432
+ value: SKIP_SENTINEL
433
+ });
434
+ }
435
+ if (hasMore && pageIterator && promptConfig.choices) {
436
+ promptConfig.choices.push({
437
+ name: chalk7.dim("(Load more...)"),
438
+ value: LOAD_MORE_SENTINEL
439
+ });
440
+ }
441
+ if (!hasMore && promptConfig.choices && dynamicResolver.requireCapabilities) {
442
+ const capContext = context.sdk.getContext();
443
+ if (capContext.hasCapability) {
444
+ for (const cap of dynamicResolver.requireCapabilities) {
445
+ const enabled = await capContext.hasCapability(cap);
446
+ if (!enabled) {
447
+ promptConfig.choices.push({
448
+ name: chalk7.dim(buildCapabilityMessage(cap)),
449
+ value: SKIP_SENTINEL,
450
+ disabled: true
451
+ });
452
+ }
453
+ }
454
+ }
455
+ }
456
+ if (newItemsStartIndex >= 0 && promptConfig.choices) {
457
+ const injectedBefore = isOptional ? 1 : 0;
458
+ const adjustedIndex = newItemsStartIndex + injectedBefore;
459
+ if (promptConfig.choices[adjustedIndex]) {
460
+ promptConfig.default = promptConfig.choices[adjustedIndex].value;
461
+ }
462
+ newItemsStartIndex = -1;
463
+ }
464
+ const answers = await inquirer.prompt([promptConfig]);
465
+ let selected = answers[promptName];
466
+ if (selected === SKIP_SENTINEL) {
407
467
  return void 0;
408
468
  }
409
- return value;
469
+ const wantsMore = Array.isArray(selected) ? selected.includes(LOAD_MORE_SENTINEL) : selected === LOAD_MORE_SENTINEL;
470
+ if (wantsMore && pageIterator) {
471
+ if (Array.isArray(selected)) {
472
+ selected = selected.filter(
473
+ (v) => v !== LOAD_MORE_SENTINEL
474
+ );
475
+ }
476
+ const prevLength = items.length;
477
+ this.startSpinner();
478
+ this.debugLog("Fetching more options...");
479
+ const next = await pageIterator.next();
480
+ this.stopSpinner();
481
+ if (!next.done && next.value) {
482
+ items = [...items, ...next.value.data];
483
+ hasMore = next.value.nextCursor != null;
484
+ newItemsStartIndex = prevLength;
485
+ } else {
486
+ hasMore = false;
487
+ }
488
+ continue;
489
+ }
490
+ return selected;
410
491
  }
411
- const answers = await inquirer.prompt([promptConfig]);
412
- return answers[promptName];
413
492
  } else if (resolver.type === "fields") {
414
493
  if (isOptional && !inArrayContext) {
415
494
  this.stopSpinner();
@@ -986,7 +1065,7 @@ var SHARED_COMMAND_CLI_OPTIONS = [
986
1065
 
987
1066
  // package.json
988
1067
  var package_default = {
989
- version: "0.36.2"};
1068
+ version: "0.37.0"};
990
1069
 
991
1070
  // src/telemetry/builders.ts
992
1071
  function createCliBaseEvent(context = {}) {
@@ -4521,7 +4600,7 @@ function createZapierCliSdk(options = {}) {
4521
4600
  // package.json with { type: 'json' }
4522
4601
  var package_default2 = {
4523
4602
  name: "@zapier/zapier-sdk-cli",
4524
- version: "0.36.2"};
4603
+ version: "0.37.0"};
4525
4604
  var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
4526
4605
  var CACHE_RESET_INTERVAL_MS = (() => {
4527
4606
  const { ZAPIER_SDK_UPDATE_CHECK_INTERVAL_MS = `${ONE_DAY_MS}` } = process.env;
@@ -4666,8 +4745,6 @@ async function checkAndNotifyUpdates({
4666
4745
  const versionInfo = await checkForUpdates({ packageName, currentVersion });
4667
4746
  displayUpdateNotification(versionInfo, packageName);
4668
4747
  }
4669
-
4670
- // src/cli.ts
4671
4748
  var EXIT_GRACE_PERIOD_MS = 500;
4672
4749
  var program = new Command();
4673
4750
  var versionOption = getReservedCliOption("version" /* Version */);
@@ -4688,7 +4765,24 @@ program.name("zapier-sdk").description("CLI for Zapier SDK").version(
4688
4765
  ).option(
4689
4766
  "--max-network-retry-delay-ms <ms>",
4690
4767
  "Max delay in ms to wait for rate limit retry (default: 60000)"
4691
- ).helpOption(
4768
+ );
4769
+ var booleanFlags = [];
4770
+ for (const [key, fieldSchema] of Object.entries(
4771
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4772
+ BaseSdkOptionsSchema.shape
4773
+ )) {
4774
+ let inner = fieldSchema;
4775
+ if (inner instanceof z.ZodOptional) {
4776
+ inner = inner._zod.def.innerType;
4777
+ }
4778
+ if (inner instanceof z.ZodBoolean && key !== "debug") {
4779
+ const kebab = key.replace(/([A-Z])/g, "-$1").toLowerCase();
4780
+ const description = fieldSchema._zod?.def?.description ?? "";
4781
+ booleanFlags.push({ camelName: key, kebabFlag: `--${kebab}` });
4782
+ program.option(`--${kebab}`, description);
4783
+ }
4784
+ }
4785
+ program.helpOption(
4692
4786
  `${helpOption.short}, ${helpOption.flag}`,
4693
4787
  helpOption.description
4694
4788
  );
@@ -4730,13 +4824,20 @@ function buildCredentialsFromFlags() {
4730
4824
  return void 0;
4731
4825
  }
4732
4826
  var credentials = buildCredentialsFromFlags();
4827
+ var flagOverrides = {};
4828
+ for (const { camelName, kebabFlag } of booleanFlags) {
4829
+ if (process.argv.includes(kebabFlag)) {
4830
+ flagOverrides[camelName] = true;
4831
+ }
4832
+ }
4733
4833
  var sdk = createZapierCliSdk({
4734
4834
  debug: isDebugMode,
4735
4835
  credentials,
4736
4836
  baseUrl,
4737
4837
  trackingBaseUrl,
4738
4838
  maxNetworkRetries,
4739
- maxNetworkRetryDelayMs
4839
+ maxNetworkRetryDelayMs,
4840
+ ...flagOverrides
4740
4841
  });
4741
4842
  generateCliCommands(program, sdk);
4742
4843
  program.exitOverride();
package/dist/index.cjs CHANGED
@@ -2512,7 +2512,7 @@ function createZapierCliSdk(options = {}) {
2512
2512
 
2513
2513
  // package.json
2514
2514
  var package_default = {
2515
- version: "0.36.2"};
2515
+ version: "0.37.0"};
2516
2516
 
2517
2517
  // src/telemetry/builders.ts
2518
2518
  function createCliBaseEvent(context = {}) {
package/dist/index.mjs CHANGED
@@ -2479,7 +2479,7 @@ function createZapierCliSdk(options = {}) {
2479
2479
 
2480
2480
  // package.json
2481
2481
  var package_default = {
2482
- version: "0.36.2"};
2482
+ version: "0.37.0"};
2483
2483
 
2484
2484
  // src/telemetry/builders.ts
2485
2485
  function createCliBaseEvent(context = {}) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.36.2",
3
+ "version": "0.37.0",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
package/dist/src/cli.js CHANGED
@@ -5,6 +5,8 @@ import { createZapierCliSdk } from "./sdk";
5
5
  import packageJson from "../package.json" with { type: "json" };
6
6
  import { ZapierCliError } from "./utils/errors";
7
7
  import { checkAndNotifyUpdates } from "./utils/version-checker";
8
+ import { BaseSdkOptionsSchema } from "@zapier/zapier-sdk";
9
+ import { z } from "zod";
8
10
  import { ReservedCliParameter, getReservedCliOption, } from "./utils/cli-options";
9
11
  const EXIT_GRACE_PERIOD_MS = 500;
10
12
  const program = new Command();
@@ -22,8 +24,24 @@ program
22
24
  .option("--credentials-base-url <url>", "Base URL for authentication endpoints")
23
25
  .option("--tracking-base-url <url>", "Base URL for Zapier tracking endpoints")
24
26
  .option("--max-network-retries <count>", "Max retries for rate-limited requests (default: 3)")
25
- .option("--max-network-retry-delay-ms <ms>", "Max delay in ms to wait for rate limit retry (default: 60000)")
26
- .helpOption(`${helpOption.short}, ${helpOption.flag}`, helpOption.description);
27
+ .option("--max-network-retry-delay-ms <ms>", "Max delay in ms to wait for rate limit retry (default: 60000)");
28
+ // Dynamically register boolean flags from BaseSdkOptionsSchema
29
+ const booleanFlags = [];
30
+ for (const [key, fieldSchema] of Object.entries(
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ BaseSdkOptionsSchema.shape)) {
33
+ let inner = fieldSchema;
34
+ if (inner instanceof z.ZodOptional) {
35
+ inner = inner._zod.def.innerType;
36
+ }
37
+ if (inner instanceof z.ZodBoolean && key !== "debug") {
38
+ const kebab = key.replace(/([A-Z])/g, "-$1").toLowerCase();
39
+ const description = fieldSchema._zod?.def?.description ?? "";
40
+ booleanFlags.push({ camelName: key, kebabFlag: `--${kebab}` });
41
+ program.option(`--${kebab}`, description);
42
+ }
43
+ }
44
+ program.helpOption(`${helpOption.short}, ${helpOption.flag}`, helpOption.description);
27
45
  // Check for debug flag early (support both flag and env var)
28
46
  const isDebugMode = process.env.DEBUG === "true" || process.argv.includes("--debug");
29
47
  // Helper to get flag value from argv
@@ -77,6 +95,13 @@ function buildCredentialsFromFlags() {
77
95
  return undefined;
78
96
  }
79
97
  const credentials = buildCredentialsFromFlags();
98
+ // Extract boolean flags from argv
99
+ const flagOverrides = {};
100
+ for (const { camelName, kebabFlag } of booleanFlags) {
101
+ if (process.argv.includes(kebabFlag)) {
102
+ flagOverrides[camelName] = true;
103
+ }
104
+ }
80
105
  // Create CLI SDK instance with all plugins and URL options
81
106
  const sdk = createZapierCliSdk({
82
107
  debug: isDebugMode,
@@ -85,6 +110,7 @@ const sdk = createZapierCliSdk({
85
110
  trackingBaseUrl,
86
111
  maxNetworkRetries,
87
112
  maxNetworkRetryDelayMs,
113
+ ...flagOverrides,
88
114
  });
89
115
  // Auth commands now handled by plugins
90
116
  // Generate CLI commands from SDK schemas (including CLI plugins)
@@ -3,7 +3,7 @@ import chalk from "chalk";
3
3
  import ora from "ora";
4
4
  import { z } from "zod";
5
5
  import { runWithTelemetryContext, } from "@zapier/zapier-sdk";
6
- import { isPositional } from "@zapier/zapier-sdk";
6
+ import { isPositional, buildCapabilityMessage } from "@zapier/zapier-sdk";
7
7
  import { ZapierCliUserCancellationError, ZapierCliMissingParametersError, ZapierCliValidationError, } from "./errors";
8
8
  // Zod validation errors are structured objects; flatten them into a
9
9
  // readable "field: reason" string for CLI error messages.
@@ -146,9 +146,11 @@ export class SchemaParameterResolver {
146
146
  else {
147
147
  for (const param of orderedRequiredParams) {
148
148
  try {
149
- const value = await this.resolveParameter(param, context, functionName);
150
- this.setNestedValue(resolvedParams, param.path, value);
151
- context.resolvedParams = resolvedParams;
149
+ const value = await this.resolveParameter(param, context, functionName, { isOptional: !param.isRequired });
150
+ if (param.isRequired || value !== undefined) {
151
+ this.setNestedValue(resolvedParams, param.path, value);
152
+ context.resolvedParams = resolvedParams;
153
+ }
152
154
  }
153
155
  catch (error) {
154
156
  if (this.isUserCancellation(error)) {
@@ -325,28 +327,116 @@ export class SchemaParameterResolver {
325
327
  }
326
328
  this.debugLog(`Fetching options for ${promptLabel}`);
327
329
  const fetchResult = await dynamicResolver.fetch(context.sdk, context.resolvedParams);
328
- const items = Array.isArray(fetchResult)
329
- ? fetchResult
330
- : (fetchResult?.data ?? []);
331
- const promptConfig = dynamicResolver.prompt(items, context.resolvedParams);
332
- promptConfig.name = promptName;
333
- // Inject a skip option for optional parameters
330
+ // Check if the result is an AsyncIterable of pages (from toIterable())
331
+ let pageIterator = null;
332
+ let items;
333
+ let hasMore = false;
334
+ if (fetchResult != null &&
335
+ typeof fetchResult === "object" &&
336
+ Symbol.asyncIterator in fetchResult) {
337
+ // Paginated: pull the first page from the iterator
338
+ pageIterator = fetchResult[Symbol.asyncIterator]();
339
+ const first = await pageIterator.next();
340
+ if (!first.done && first.value) {
341
+ items = first.value.data;
342
+ hasMore = first.value.nextCursor != null;
343
+ }
344
+ else {
345
+ items = [];
346
+ }
347
+ }
348
+ else if (fetchResult != null &&
349
+ typeof fetchResult === "object" &&
350
+ "data" in fetchResult) {
351
+ const page = fetchResult;
352
+ items = page.data;
353
+ hasMore = page.nextCursor != null;
354
+ if (hasMore) {
355
+ this.debugLog(`Resolver for ${promptLabel} has more pages but no iterator. ` +
356
+ `Use toIterable() to enable "Load more..." support.`);
357
+ }
358
+ }
359
+ else {
360
+ items = fetchResult || [];
361
+ pageIterator = null;
362
+ }
363
+ const LOAD_MORE_SENTINEL = Symbol("LOAD_MORE");
364
+ const SKIP_SENTINEL = Symbol("SKIP");
365
+ let newItemsStartIndex = -1;
334
366
  this.stopSpinner();
335
- if (isOptional && promptConfig.choices) {
336
- const SKIP_SENTINEL = Symbol("SKIP");
337
- promptConfig.choices = [
338
- { name: chalk.dim("(Skip)"), value: SKIP_SENTINEL },
339
- ...promptConfig.choices,
340
- ];
367
+ while (true) {
368
+ const promptConfig = dynamicResolver.prompt(items, context.resolvedParams);
369
+ promptConfig.name = promptName;
370
+ if (isOptional && promptConfig.choices) {
371
+ promptConfig.choices.unshift({
372
+ name: chalk.dim("(Skip)"),
373
+ value: SKIP_SENTINEL,
374
+ });
375
+ }
376
+ // Add "Load more..." option if paginated
377
+ if (hasMore && pageIterator && promptConfig.choices) {
378
+ promptConfig.choices.push({
379
+ name: chalk.dim("(Load more...)"),
380
+ value: LOAD_MORE_SENTINEL,
381
+ });
382
+ }
383
+ // Show hints for capabilities that would expand results
384
+ if (!hasMore &&
385
+ promptConfig.choices &&
386
+ dynamicResolver.requireCapabilities) {
387
+ const capContext = context.sdk.getContext();
388
+ if (capContext.hasCapability) {
389
+ for (const cap of dynamicResolver.requireCapabilities) {
390
+ const enabled = await capContext.hasCapability(cap);
391
+ if (!enabled) {
392
+ promptConfig.choices.push({
393
+ name: chalk.dim(buildCapabilityMessage(cap)),
394
+ value: SKIP_SENTINEL,
395
+ disabled: true,
396
+ });
397
+ }
398
+ }
399
+ }
400
+ }
401
+ // After loading more, jump cursor to first new item.
402
+ // Offset by the number of injected choices before the data items (e.g. Skip).
403
+ if (newItemsStartIndex >= 0 && promptConfig.choices) {
404
+ const injectedBefore = isOptional ? 1 : 0;
405
+ const adjustedIndex = newItemsStartIndex + injectedBefore;
406
+ if (promptConfig.choices[adjustedIndex]) {
407
+ promptConfig.default = promptConfig.choices[adjustedIndex].value;
408
+ }
409
+ newItemsStartIndex = -1;
410
+ }
341
411
  const answers = await inquirer.prompt([promptConfig]);
342
- const value = answers[promptName];
343
- if (value === SKIP_SENTINEL) {
412
+ let selected = answers[promptName];
413
+ if (selected === SKIP_SENTINEL) {
344
414
  return undefined;
345
415
  }
346
- return value;
416
+ const wantsMore = Array.isArray(selected)
417
+ ? selected.includes(LOAD_MORE_SENTINEL)
418
+ : selected === LOAD_MORE_SENTINEL;
419
+ if (wantsMore && pageIterator) {
420
+ if (Array.isArray(selected)) {
421
+ selected = selected.filter((v) => v !== LOAD_MORE_SENTINEL);
422
+ }
423
+ const prevLength = items.length;
424
+ this.startSpinner();
425
+ this.debugLog("Fetching more options...");
426
+ const next = await pageIterator.next();
427
+ this.stopSpinner();
428
+ if (!next.done && next.value) {
429
+ items = [...items, ...next.value.data];
430
+ hasMore = next.value.nextCursor != null;
431
+ newItemsStartIndex = prevLength;
432
+ }
433
+ else {
434
+ hasMore = false;
435
+ }
436
+ continue;
437
+ }
438
+ return selected;
347
439
  }
348
- const answers = await inquirer.prompt([promptConfig]);
349
- return answers[promptName];
350
440
  }
351
441
  else if (resolver.type === "fields") {
352
442
  if (isOptional && !inArrayContext) {