@zapier/zapier-sdk-cli 0.44.0 → 0.45.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 +20 -0
- package/README.md +392 -40
- package/bin/zapier-sdk-experimental.mjs +14 -0
- package/dist/cli.cjs +585 -37
- package/dist/cli.mjs +584 -36
- package/dist/experimental.cjs +3519 -0
- package/dist/experimental.d.mts +39 -0
- package/dist/experimental.d.ts +39 -0
- package/dist/experimental.mjs +3483 -0
- package/dist/index.cjs +507 -26
- package/dist/index.d.mts +3 -514
- package/dist/index.d.ts +3 -514
- package/dist/index.mjs +505 -24
- package/dist/package.json +14 -2
- package/dist/sdk-B3nKAZdN.d.mts +516 -0
- package/dist/sdk-B3nKAZdN.d.ts +516 -0
- package/dist/src/cli.js +26 -2
- package/dist/src/experimental.d.ts +33 -0
- package/dist/src/experimental.js +83 -0
- package/dist/src/generators/ast-generator.d.ts +2 -2
- package/dist/src/generators/ast-generator.js +1 -1
- package/dist/src/plugins/add/index.d.ts +2 -2
- package/dist/src/plugins/bundleCode/index.js +3 -12
- package/dist/src/plugins/curl/index.js +2 -2
- package/dist/src/plugins/curl/utils.d.ts +11 -1
- package/dist/src/plugins/curl/utils.js +14 -5
- package/dist/src/plugins/drainTriggerInbox/index.d.ts +46 -0
- package/dist/src/plugins/drainTriggerInbox/index.js +178 -0
- package/dist/src/plugins/generateAppTypes/index.d.ts +2 -2
- package/dist/src/plugins/index.d.ts +2 -0
- package/dist/src/plugins/index.js +2 -0
- package/dist/src/plugins/mcp/index.d.ts +1 -0
- package/dist/src/plugins/mcp/index.js +5 -1
- package/dist/src/plugins/watchTriggerInbox/index.d.ts +45 -0
- package/dist/src/plugins/watchTriggerInbox/index.js +157 -0
- package/dist/src/sdk.js +5 -1
- package/dist/src/utils/cli-generator.js +18 -1
- package/dist/src/utils/cli-renderer.d.ts +12 -0
- package/dist/src/utils/cli-renderer.js +22 -1
- package/dist/src/utils/parameter-resolver.d.ts +1 -0
- package/dist/src/utils/parameter-resolver.js +55 -9
- package/dist/src/utils/triggerDrain.d.ts +144 -0
- package/dist/src/utils/triggerDrain.js +351 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -4
|
@@ -224,6 +224,17 @@ function analyzeZodField(name, schema, functionInfo) {
|
|
|
224
224
|
break;
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
|
+
// Some schema kinds aren't expressible as CLI flags and would
|
|
228
|
+
// otherwise fall through to a bogus `--name <string>` flag:
|
|
229
|
+
// - z.function() — onMessage / onError-style callbacks
|
|
230
|
+
// - z.custom<AbortSignal>() and other custom predicates — passed
|
|
231
|
+
// programmatically by callers, or filled in by a CLI-side
|
|
232
|
+
// decorator plugin (closure-capture + override).
|
|
233
|
+
// Skip them so the auto-generated CLI surface stays clean.
|
|
234
|
+
const baseSchemaDef = baseSchema._zod?.def;
|
|
235
|
+
if (baseSchema instanceof z.ZodFunction || baseSchemaDef?.type === "custom") {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
227
238
|
// Determine parameter type
|
|
228
239
|
let paramType = "string";
|
|
229
240
|
let elementType;
|
|
@@ -448,9 +459,15 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
|
|
|
448
459
|
}
|
|
449
460
|
}
|
|
450
461
|
}
|
|
451
|
-
const
|
|
462
|
+
const baseDescription = functionInfo.description ||
|
|
452
463
|
schema?.description ||
|
|
453
464
|
`${cliCommandName} command`;
|
|
465
|
+
// Mark experimental commands clearly in --help so users know what they're
|
|
466
|
+
// touching. The command only reaches here at all when the experimental SDK
|
|
467
|
+
// factory is loaded, so we don't need to gate help visibility separately.
|
|
468
|
+
const description = functionInfo.experimental
|
|
469
|
+
? `${baseDescription} (experimental)`
|
|
470
|
+
: baseDescription;
|
|
454
471
|
const handler = async (...args) => {
|
|
455
472
|
const startTime = Date.now();
|
|
456
473
|
let success = true;
|
|
@@ -25,6 +25,18 @@ export interface CliRenderer {
|
|
|
25
25
|
renderError(error: unknown): never;
|
|
26
26
|
}
|
|
27
27
|
export declare function buildJsonErrors(error: unknown): JsonErrorEntry[];
|
|
28
|
+
/**
|
|
29
|
+
* Replacer for JSON.stringify that coerces Error instances to a plain
|
|
30
|
+
* object with `{ name, message, stack }`. Without this, `JSON.stringify`
|
|
31
|
+
* renders an Error as `{}` (its useful properties are non-enumerable),
|
|
32
|
+
* which surfaces in any command that returns Error-like values — most
|
|
33
|
+
* notably `drainTriggerInbox` whose `errors[].reason` is typed `unknown`
|
|
34
|
+
* and is usually an Error.
|
|
35
|
+
*
|
|
36
|
+
* Walks recursively because the replacer is invoked for every nested
|
|
37
|
+
* key/value pair, not just the top-level value.
|
|
38
|
+
*/
|
|
39
|
+
export declare function jsonReplacer(_key: string, value: unknown): unknown;
|
|
28
40
|
export declare function createJsonRenderer(): CliRenderer;
|
|
29
41
|
export declare function createInteractiveRenderer(): CliRenderer;
|
|
30
42
|
export {};
|
|
@@ -50,8 +50,29 @@ async function unwrapHttpResponse(response) {
|
|
|
50
50
|
// ============================================================================
|
|
51
51
|
// JSON renderer
|
|
52
52
|
// ============================================================================
|
|
53
|
+
/**
|
|
54
|
+
* Replacer for JSON.stringify that coerces Error instances to a plain
|
|
55
|
+
* object with `{ name, message, stack }`. Without this, `JSON.stringify`
|
|
56
|
+
* renders an Error as `{}` (its useful properties are non-enumerable),
|
|
57
|
+
* which surfaces in any command that returns Error-like values — most
|
|
58
|
+
* notably `drainTriggerInbox` whose `errors[].reason` is typed `unknown`
|
|
59
|
+
* and is usually an Error.
|
|
60
|
+
*
|
|
61
|
+
* Walks recursively because the replacer is invoked for every nested
|
|
62
|
+
* key/value pair, not just the top-level value.
|
|
63
|
+
*/
|
|
64
|
+
export function jsonReplacer(_key, value) {
|
|
65
|
+
if (value instanceof Error) {
|
|
66
|
+
return {
|
|
67
|
+
name: value.name,
|
|
68
|
+
message: value.message,
|
|
69
|
+
...(value.stack ? { stack: value.stack } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
53
74
|
function outputJson(envelope) {
|
|
54
|
-
console.log(JSON.stringify(envelope,
|
|
75
|
+
console.log(JSON.stringify(envelope, jsonReplacer, 2));
|
|
55
76
|
}
|
|
56
77
|
export function createJsonRenderer() {
|
|
57
78
|
return {
|
|
@@ -24,7 +24,7 @@ function formatZodError(error) {
|
|
|
24
24
|
*/
|
|
25
25
|
function getLocalResolutionOrder(paramName, resolvers, resolved = new Set()) {
|
|
26
26
|
const resolver = resolvers[paramName];
|
|
27
|
-
if (!resolver || resolver.type === "static") {
|
|
27
|
+
if (!resolver || resolver.type === "static" || resolver.type === "constant") {
|
|
28
28
|
return [paramName];
|
|
29
29
|
}
|
|
30
30
|
const order = [];
|
|
@@ -119,7 +119,16 @@ export class SchemaParameterResolver {
|
|
|
119
119
|
return parseResult.data;
|
|
120
120
|
}
|
|
121
121
|
// 2. Resolve required parameters
|
|
122
|
-
|
|
122
|
+
// Seed resolvedParams with constant resolvers — entries in `resolvers`
|
|
123
|
+
// typed `{ type: "constant", value }` for parameters not in the schema.
|
|
124
|
+
// Used when a dependent resolver needs a value the user never sets
|
|
125
|
+
// (e.g., trigger plugins pin actionType="read" so actionKeyResolver
|
|
126
|
+
// and inputsResolver work without actionType being a real input).
|
|
127
|
+
const resolverConstants = this.getResolverConstants(sdk, functionName);
|
|
128
|
+
const resolvedParams = {
|
|
129
|
+
...resolverConstants,
|
|
130
|
+
...providedParams,
|
|
131
|
+
};
|
|
123
132
|
const context = {
|
|
124
133
|
sdk,
|
|
125
134
|
currentParams: providedParams,
|
|
@@ -263,11 +272,17 @@ export class SchemaParameterResolver {
|
|
|
263
272
|
const missingParams = [];
|
|
264
273
|
for (const param of params) {
|
|
265
274
|
const resolver = this.getResolver(param.name, context.sdk, functionName);
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
275
|
+
let autoResolved = null;
|
|
276
|
+
if (resolver?.type === "constant") {
|
|
277
|
+
autoResolved = {
|
|
278
|
+
resolvedValue: resolver.value,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
else if (resolver?.type === "dynamic") {
|
|
282
|
+
autoResolved = await this.tryAutoResolve(resolver, context);
|
|
283
|
+
}
|
|
284
|
+
if (autoResolved != null) {
|
|
285
|
+
this.setNestedValue(resolvedParams, param.path, autoResolved.resolvedValue);
|
|
271
286
|
context.resolvedParams = resolvedParams;
|
|
272
287
|
}
|
|
273
288
|
else {
|
|
@@ -299,7 +314,12 @@ export class SchemaParameterResolver {
|
|
|
299
314
|
: param.name;
|
|
300
315
|
const promptName = inArrayContext ? "value" : param.name;
|
|
301
316
|
this.debugLog(`Resolving ${promptLabel}${isOptional ? " (optional)" : ""}`);
|
|
302
|
-
if (resolver.type === "
|
|
317
|
+
if (resolver.type === "constant") {
|
|
318
|
+
const constantResolver = resolver;
|
|
319
|
+
this.stopSpinner();
|
|
320
|
+
return constantResolver.value;
|
|
321
|
+
}
|
|
322
|
+
else if (resolver.type === "static") {
|
|
303
323
|
const staticResolver = resolver;
|
|
304
324
|
const promptConfig = {
|
|
305
325
|
type: staticResolver.inputType === "password" ? "password" : "input",
|
|
@@ -370,6 +390,15 @@ export class SchemaParameterResolver {
|
|
|
370
390
|
while (true) {
|
|
371
391
|
const promptConfig = dynamicResolver.prompt(items, context.resolvedParams);
|
|
372
392
|
promptConfig.name = promptName;
|
|
393
|
+
// Friendlier than inquirer's default "No selectable choices. All
|
|
394
|
+
// choices are disabled." when the resolver returns no items (or
|
|
395
|
+
// filters everything out in `prompt`). Use ZapierCliValidationError
|
|
396
|
+
// so the message renders to the user; ZapierCliExitError exits
|
|
397
|
+
// silently and would be confusing here.
|
|
398
|
+
const hasSelectableChoice = promptConfig.choices?.some((c) => !c.disabled);
|
|
399
|
+
if (!hasSelectableChoice && !hasMore) {
|
|
400
|
+
throw new ZapierCliValidationError(`No ${promptLabel} available to select.`);
|
|
401
|
+
}
|
|
373
402
|
if (isOptional && promptConfig.choices) {
|
|
374
403
|
promptConfig.choices.unshift({
|
|
375
404
|
name: chalk.dim("(Skip)"),
|
|
@@ -711,7 +740,7 @@ export class SchemaParameterResolver {
|
|
|
711
740
|
? `Fetching more choices for ${fieldMeta.title}`
|
|
712
741
|
: `Fetching choices for ${fieldMeta.title}`);
|
|
713
742
|
this.startSpinner();
|
|
714
|
-
const page = await context.sdk.
|
|
743
|
+
const page = await context.sdk.listActionInputFieldChoices({
|
|
715
744
|
app: context.resolvedParams.app,
|
|
716
745
|
action: context.resolvedParams.action,
|
|
717
746
|
actionType: context.resolvedParams.actionType,
|
|
@@ -956,4 +985,21 @@ export class SchemaParameterResolver {
|
|
|
956
985
|
const functionInfo = registry.functions.find((f) => f.name === functionName);
|
|
957
986
|
return functionInfo?.resolvers || {};
|
|
958
987
|
}
|
|
988
|
+
getResolverConstants(sdk, functionName) {
|
|
989
|
+
if (!functionName || typeof sdk.getRegistry !== "function") {
|
|
990
|
+
return {};
|
|
991
|
+
}
|
|
992
|
+
const registry = sdk.getRegistry();
|
|
993
|
+
const functionInfo = registry.functions.find((f) => f.name === functionName);
|
|
994
|
+
const resolvers = functionInfo?.resolvers ?? {};
|
|
995
|
+
const constants = {};
|
|
996
|
+
for (const [key, resolver] of Object.entries(resolvers)) {
|
|
997
|
+
if (resolver &&
|
|
998
|
+
typeof resolver === "object" &&
|
|
999
|
+
resolver.type === "constant") {
|
|
1000
|
+
constants[key] = resolver.value;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
return constants;
|
|
1004
|
+
}
|
|
959
1005
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared CLI helpers for the `drain-trigger-inbox` and
|
|
3
|
+
* `watch-trigger-inbox` commands. Both wrap the eager SDK callback API
|
|
4
|
+
* and dispatch on the same flag set: interactive default (TTY),
|
|
5
|
+
* `--exec` (binary + argv, no shell), `--exec-shell` (shell handler),
|
|
6
|
+
* `--json` (drain: collect-and-print; watch: NDJSON streaming).
|
|
7
|
+
*/
|
|
8
|
+
import { type LeasedTriggerMessageItem } from "@zapier/zapier-sdk";
|
|
9
|
+
export type Callback = (message: LeasedTriggerMessageItem) => Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Marker error thrown by `createInteractiveCallback` when the user
|
|
12
|
+
* picks "Skip (let lease expire)". The SDK treats it like any other
|
|
13
|
+
* Error subclass — leave-leased per default `releaseOnError: false`
|
|
14
|
+
* — so the lease-timeout recovery path is preserved. The CLI uses
|
|
15
|
+
* the marker type to distinguish a deliberate skip from an unhandled
|
|
16
|
+
* exception when counting summary lines. Not a `ZapierSignal`: the
|
|
17
|
+
* SDK's signal-handling auto-releases the message, which is exactly
|
|
18
|
+
* what skip-expire does *not* want.
|
|
19
|
+
*/
|
|
20
|
+
export declare class CliSkipLeaseExpireError extends Error {
|
|
21
|
+
readonly name = "CliSkipLeaseExpireError";
|
|
22
|
+
constructor();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Prompts the user per message. Choices:
|
|
26
|
+
*
|
|
27
|
+
* - **Ack**: returns normally so the SDK acks the message.
|
|
28
|
+
* - **Skip (release after draining)**: throws
|
|
29
|
+
* `ZapierReleaseTriggerMessageSignal`. The SDK marks the message for
|
|
30
|
+
* release; the actual release call is deferred until the drain
|
|
31
|
+
* finishes so the same drain doesn't immediately re-lease it. After
|
|
32
|
+
* drain ends, the message returns to `available` for a future drain
|
|
33
|
+
* run or another consumer.
|
|
34
|
+
* - **Skip (let lease expire)**: throws `CliSkipLeaseExpireError` so
|
|
35
|
+
* the SDK leaves the message leased (it's not a `ZapierSignal`,
|
|
36
|
+
* so the SDK's catch-all "leave on error" path applies); the lease
|
|
37
|
+
* timeout (~5 min default) is the recovery path. Use this when you
|
|
38
|
+
* want backpressure on reprocessing. The CLI catches the marker
|
|
39
|
+
* to count it as a skip in the drain summary.
|
|
40
|
+
* - **Quit**: throws `ZapierAbortDrainSignal` so the SDK stops draining
|
|
41
|
+
* after the current batch finishes (in-flight callbacks complete;
|
|
42
|
+
* not-yet-started messages in the batch are released).
|
|
43
|
+
*
|
|
44
|
+
* Ctrl-C during the prompt is translated to `ZapierAbortDrainSignal`
|
|
45
|
+
* (same as Quit) — inquirer raises `ExitPromptError`, which we catch
|
|
46
|
+
* and re-throw as the abort signal.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createInteractiveCallback(): Callback;
|
|
49
|
+
/**
|
|
50
|
+
* Writes one JSON object per line to stdout (NDJSON) for piping. Acks
|
|
51
|
+
* each message after the write resolves; if stdout is closed mid-drain
|
|
52
|
+
* (broken pipe), the write throws and the message stays unacked.
|
|
53
|
+
*
|
|
54
|
+
* The write callback alone provides backpressure: it doesn't fire until
|
|
55
|
+
* the chunk is fully committed to the underlying resource, so awaiting
|
|
56
|
+
* it pauses the drain when stdout is busy. No separate `drain` listener
|
|
57
|
+
* needed — and adding one would race with the callback (drain fires at
|
|
58
|
+
* highWaterMark, callback fires after full commit; if drain wins, the
|
|
59
|
+
* later callback's error gets dropped on the floor).
|
|
60
|
+
*/
|
|
61
|
+
export declare function createNdjsonCallback(): Callback;
|
|
62
|
+
export declare function runShellCommand(command: string, message: LeasedTriggerMessageItem, signal?: AbortSignal): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Run a binary per message with no shell interpretation. `argv[0]` is
|
|
65
|
+
* the executable; the rest are passed literally as argv to the child.
|
|
66
|
+
* Otherwise identical to `runShellCommand`: stdin = message JSON, exit
|
|
67
|
+
* code 0 acks, non-zero rejects, abort signal kills the child.
|
|
68
|
+
*/
|
|
69
|
+
export declare function runExecCommand(argv: string[], message: LeasedTriggerMessageItem, signal?: AbortSignal): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Print a single per-message error to stderr. Used as the live
|
|
72
|
+
* `onError` body so failures surface as they happen (vs accumulated
|
|
73
|
+
* and dumped at the end, which is broken for `watch-trigger-inbox`
|
|
74
|
+
* where "the end" is Ctrl-C).
|
|
75
|
+
*/
|
|
76
|
+
export declare function printDrainError(reason: unknown, message: LeasedTriggerMessageItem): void;
|
|
77
|
+
/**
|
|
78
|
+
* One-line summary printed when the CLI finished a drain in a mode
|
|
79
|
+
* where the data array is uninformative (interactive, exec, exec-shell).
|
|
80
|
+
* Both the bounded and forever variants use the same format.
|
|
81
|
+
*
|
|
82
|
+
* `skipped` is the interactive-mode count of user-driven Skip choices
|
|
83
|
+
* (release-after-draining + let-lease-expire). Omitted when zero so the
|
|
84
|
+
* subprocess modes — which have no skip semantics — keep their original
|
|
85
|
+
* two-part summary.
|
|
86
|
+
*/
|
|
87
|
+
export declare function printDrainSummary(counts: {
|
|
88
|
+
fulfilled: number;
|
|
89
|
+
rejected: number;
|
|
90
|
+
skipped?: number;
|
|
91
|
+
}): void;
|
|
92
|
+
/**
|
|
93
|
+
* Warn on stderr that interactive mode is overriding an explicit
|
|
94
|
+
* `continueOnError: false`. Only fires when the caller (programmatic
|
|
95
|
+
* SDK consumer or future flag form that takes `=false`) opted into
|
|
96
|
+
* fail-fast — bare `--continue-on-error` flag absence leaves it
|
|
97
|
+
* `undefined` and doesn't trigger this. Interactive mode mechanically
|
|
98
|
+
* requires continue-on-error: the "Skip (let lease expire)" choice
|
|
99
|
+
* throws a plain Error to leave the message leased, and a fail-fast
|
|
100
|
+
* drain would tear down on the first skip.
|
|
101
|
+
*/
|
|
102
|
+
export declare function warnInteractiveContinueOnErrorOverride(): void;
|
|
103
|
+
/**
|
|
104
|
+
* Throw a CliValidationError if the current process isn't attached to
|
|
105
|
+
* a real terminal on both stdin and stdout. Used by the interactive
|
|
106
|
+
* default to fail fast with a useful message instead of letting
|
|
107
|
+
* inquirer fall over later.
|
|
108
|
+
*/
|
|
109
|
+
export declare function requireInteractiveTty(commandName: string): void;
|
|
110
|
+
/**
|
|
111
|
+
* Reject more than one of `--exec`, `--exec-shell`, and `--json`.
|
|
112
|
+
* Each is a non-interactive output path and choosing implicitly is
|
|
113
|
+
* ambiguous.
|
|
114
|
+
*/
|
|
115
|
+
export declare function rejectExecJsonMutex(opts: {
|
|
116
|
+
exec?: unknown;
|
|
117
|
+
execShell?: unknown;
|
|
118
|
+
json?: unknown;
|
|
119
|
+
}): void;
|
|
120
|
+
/**
|
|
121
|
+
* Pull argv tokens after the first `--` separator out of `process.argv`.
|
|
122
|
+
* Returns `[]` when there's no `--`. Used by drain/watch to append
|
|
123
|
+
* post-`--` args to the `--exec` binary, mirroring `xargs`, `gh`, and
|
|
124
|
+
* `npm run -- ...` semantics.
|
|
125
|
+
*
|
|
126
|
+
* Read from `process.argv` rather than threading via cli-generator
|
|
127
|
+
* because Commander already consumes `--` at parse time and there's
|
|
128
|
+
* no per-command API for "the args after `--`" exposed through the
|
|
129
|
+
* SDK options object.
|
|
130
|
+
*/
|
|
131
|
+
export declare function getPostDashArgs(argv?: string[]): string[];
|
|
132
|
+
/**
|
|
133
|
+
* Combine an optional user signal with an internal one (typically the
|
|
134
|
+
* SIGINT controller) into a single AbortSignal that fires when either
|
|
135
|
+
* source aborts. Returns a `dispose` to detach the listeners; safe to
|
|
136
|
+
* call regardless of whether the combined signal fired.
|
|
137
|
+
*
|
|
138
|
+
* Plain shape rather than the SDK's `combineAbortSignals`, which works
|
|
139
|
+
* over `DisposableAbortSignal` handles and returns a different shape.
|
|
140
|
+
*/
|
|
141
|
+
export declare function combineSignals(a: AbortSignal | undefined, b: AbortSignal): {
|
|
142
|
+
signal: AbortSignal;
|
|
143
|
+
dispose: () => void;
|
|
144
|
+
};
|