@zapier/zapier-sdk-cli 0.15.14 → 0.16.1
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 +23 -0
- package/dist/cli.cjs +36 -16
- package/dist/cli.mjs +36 -16
- package/dist/index.cjs +1 -1
- package/dist/index.d.mts +5 -23
- package/dist/index.d.ts +5 -23
- package/dist/index.mjs +1 -1
- package/dist/package.json +2 -2
- package/dist/src/plugins/add/schemas.d.ts +3 -13
- package/dist/src/plugins/buildManifest/schemas.d.ts +2 -10
- package/dist/src/plugins/bundleCode/schemas.d.ts +1 -15
- package/dist/src/plugins/generateAppTypes/schemas.d.ts +3 -13
- package/dist/src/plugins/getLoginConfigPath/schemas.d.ts +1 -1
- package/dist/src/plugins/login/schemas.d.ts +1 -5
- package/dist/src/plugins/logout/schemas.d.ts +1 -1
- package/dist/src/plugins/mcp/schemas.d.ts +1 -5
- package/dist/src/utils/cli-generator-utils.js +4 -4
- package/dist/src/utils/cli-generator.js +37 -19
- package/dist/src/utils/parameter-resolver.js +2 -2
- package/dist/src/utils/schema-formatter.js +4 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/utils/cli-generator-utils.ts +4 -4
- package/src/utils/cli-generator.test.ts +152 -0
- package/src/utils/cli-generator.ts +49 -27
- package/src/utils/parameter-resolver.ts +2 -2
- package/src/utils/schema-formatter.ts +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zapier/zapier-sdk-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "Command line interface for Zapier SDK",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"pkce-challenge": "^5.0.0",
|
|
47
47
|
"semver": "^7.7.3",
|
|
48
48
|
"typescript": "^5.8.3",
|
|
49
|
-
"zod": "
|
|
50
|
-
"@zapier/zapier-sdk-mcp": "0.3.39",
|
|
49
|
+
"zod": "4.2.1",
|
|
51
50
|
"@zapier/zapier-sdk-cli-login": "0.3.5",
|
|
52
|
-
"@zapier/zapier-sdk": "0.
|
|
51
|
+
"@zapier/zapier-sdk-mcp": "0.4.0",
|
|
52
|
+
"@zapier/zapier-sdk": "0.16.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/express": "^5.0.3",
|
|
@@ -52,13 +52,13 @@ function analyzeZodField(
|
|
|
52
52
|
// Unwrap optional and default wrappers
|
|
53
53
|
if (baseSchema instanceof z.ZodOptional) {
|
|
54
54
|
required = false;
|
|
55
|
-
baseSchema = baseSchema.
|
|
55
|
+
baseSchema = baseSchema._zod.def.innerType as z.ZodSchema;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
if (baseSchema instanceof z.ZodDefault) {
|
|
59
59
|
required = false;
|
|
60
|
-
defaultValue = baseSchema.
|
|
61
|
-
baseSchema = baseSchema.
|
|
60
|
+
defaultValue = (baseSchema._zod.def.defaultValue as () => unknown)();
|
|
61
|
+
baseSchema = baseSchema._zod.def.innerType as z.ZodSchema;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Determine parameter type
|
|
@@ -75,7 +75,7 @@ function analyzeZodField(
|
|
|
75
75
|
paramType = "array";
|
|
76
76
|
} else if (baseSchema instanceof z.ZodEnum) {
|
|
77
77
|
paramType = "string";
|
|
78
|
-
choices = baseSchema.
|
|
78
|
+
choices = baseSchema.options as string[];
|
|
79
79
|
} else if (baseSchema instanceof z.ZodRecord) {
|
|
80
80
|
// Handle Record<string, any> as JSON string input
|
|
81
81
|
paramType = "string";
|
|
@@ -119,6 +119,158 @@ const mockSdk = {
|
|
|
119
119
|
listApps: vi.fn().mockResolvedValue({ data: [] }),
|
|
120
120
|
} as unknown as ZapierSdk;
|
|
121
121
|
|
|
122
|
+
// Schema for request command testing
|
|
123
|
+
const requestSchema = z
|
|
124
|
+
.object({
|
|
125
|
+
url: z.string().describe("The URL to request"),
|
|
126
|
+
method: z.string().optional().describe("HTTP method"),
|
|
127
|
+
authenticationId: z.number().optional(),
|
|
128
|
+
})
|
|
129
|
+
.describe("Make authenticated HTTP requests");
|
|
130
|
+
|
|
131
|
+
describe("CLI Response Body Handling", () => {
|
|
132
|
+
let consoleSpy: ReturnType<typeof vi.spyOn>;
|
|
133
|
+
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
vi.clearAllMocks();
|
|
136
|
+
consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
afterEach(() => {
|
|
140
|
+
consoleSpy.mockRestore();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should wrap Response objects in structured envelope with statusCode, headers, body", async () => {
|
|
144
|
+
const bodyData = { ok: true, users: [{ id: 1, name: "Test User" }] };
|
|
145
|
+
|
|
146
|
+
// Create a mock Response object
|
|
147
|
+
const mockResponse = new Response(JSON.stringify(bodyData), {
|
|
148
|
+
status: 200,
|
|
149
|
+
headers: { "Content-Type": "application/json" },
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const mockSdkWithRequest = {
|
|
153
|
+
getRegistry: vi.fn(() => ({
|
|
154
|
+
functions: [
|
|
155
|
+
{
|
|
156
|
+
name: "request",
|
|
157
|
+
type: "single",
|
|
158
|
+
inputSchema: requestSchema,
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
categories: [],
|
|
162
|
+
})),
|
|
163
|
+
request: vi.fn().mockResolvedValue(mockResponse),
|
|
164
|
+
} as unknown as ZapierSdk;
|
|
165
|
+
|
|
166
|
+
const program = new Command();
|
|
167
|
+
program.exitOverride();
|
|
168
|
+
|
|
169
|
+
generateCliCommands(program, mockSdkWithRequest);
|
|
170
|
+
|
|
171
|
+
await program.parseAsync([
|
|
172
|
+
"node",
|
|
173
|
+
"test",
|
|
174
|
+
"request",
|
|
175
|
+
"https://api.example.com/users",
|
|
176
|
+
"--json",
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
// Verify the SDK method was called
|
|
180
|
+
expect(mockSdkWithRequest.request).toHaveBeenCalled();
|
|
181
|
+
|
|
182
|
+
// Verify output includes the structured envelope with statusCode, headers, and body
|
|
183
|
+
const expectedEnvelope = {
|
|
184
|
+
statusCode: 200,
|
|
185
|
+
headers: { "content-type": "application/json" },
|
|
186
|
+
body: bodyData,
|
|
187
|
+
};
|
|
188
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
189
|
+
JSON.stringify(expectedEnvelope, null, 2),
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("should handle invalid JSON in Response with helpful error", async () => {
|
|
194
|
+
// Create a mock Response with invalid JSON
|
|
195
|
+
const mockResponse = new Response("not valid json {", {
|
|
196
|
+
status: 200,
|
|
197
|
+
headers: { "Content-Type": "application/json" },
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const mockSdkWithBadResponse = {
|
|
201
|
+
getRegistry: vi.fn(() => ({
|
|
202
|
+
functions: [
|
|
203
|
+
{
|
|
204
|
+
name: "request",
|
|
205
|
+
type: "single",
|
|
206
|
+
inputSchema: requestSchema,
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
categories: [],
|
|
210
|
+
})),
|
|
211
|
+
request: vi.fn().mockResolvedValue(mockResponse),
|
|
212
|
+
} as unknown as ZapierSdk;
|
|
213
|
+
|
|
214
|
+
const program = new Command();
|
|
215
|
+
program.exitOverride();
|
|
216
|
+
const consoleErrorSpy = vi
|
|
217
|
+
.spyOn(console, "error")
|
|
218
|
+
.mockImplementation(() => {});
|
|
219
|
+
|
|
220
|
+
generateCliCommands(program, mockSdkWithBadResponse);
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
await program.parseAsync([
|
|
224
|
+
"node",
|
|
225
|
+
"test",
|
|
226
|
+
"request",
|
|
227
|
+
"https://api.example.com/users",
|
|
228
|
+
"--json",
|
|
229
|
+
]);
|
|
230
|
+
} catch {
|
|
231
|
+
// Expected to throw
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Verify error message includes helpful context
|
|
235
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
236
|
+
expect.stringContaining("❌"),
|
|
237
|
+
expect.stringContaining("Failed to parse response as JSON"),
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
consoleErrorSpy.mockRestore();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("should handle non-Response objects normally", async () => {
|
|
244
|
+
const regularData = { apps: [{ id: 1, name: "Test App" }] };
|
|
245
|
+
|
|
246
|
+
const mockSdkRegular = {
|
|
247
|
+
getRegistry: vi.fn(() => ({
|
|
248
|
+
functions: [
|
|
249
|
+
{
|
|
250
|
+
name: "getApp",
|
|
251
|
+
type: "single",
|
|
252
|
+
inputSchema: z.object({ appKey: z.string() }).describe("Get app"),
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
categories: [],
|
|
256
|
+
})),
|
|
257
|
+
getApp: vi.fn().mockResolvedValue(regularData),
|
|
258
|
+
} as unknown as ZapierSdk;
|
|
259
|
+
|
|
260
|
+
const program = new Command();
|
|
261
|
+
program.exitOverride();
|
|
262
|
+
|
|
263
|
+
generateCliCommands(program, mockSdkRegular);
|
|
264
|
+
|
|
265
|
+
await program.parseAsync(["node", "test", "get-app", "test-app", "--json"]);
|
|
266
|
+
|
|
267
|
+
expect(mockSdkRegular.getApp).toHaveBeenCalled();
|
|
268
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
269
|
+
JSON.stringify(regularData, null, 2),
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
122
274
|
describe("CLI Command Generation", () => {
|
|
123
275
|
beforeEach(() => {
|
|
124
276
|
vi.clearAllMocks();
|
|
@@ -45,15 +45,14 @@ function analyzeZodSchema(
|
|
|
45
45
|
const parameters: CliParameter[] = [];
|
|
46
46
|
|
|
47
47
|
// Handle ZodEffects (schemas with .refine(), .transform(), etc.)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return analyzeZodSchema(innerSchema, functionInfo);
|
|
48
|
+
// In Zod, effects have type "effect" and inner schema is accessed via innerType
|
|
49
|
+
const schemaDef = (
|
|
50
|
+
schema as unknown as {
|
|
51
|
+
_zod?: { def?: { type?: string; innerType?: z.ZodSchema } };
|
|
52
|
+
}
|
|
53
|
+
)._zod?.def;
|
|
54
|
+
if (schemaDef?.type === "effect" && schemaDef.innerType) {
|
|
55
|
+
return analyzeZodSchema(schemaDef.innerType, functionInfo);
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
if (schema instanceof z.ZodObject) {
|
|
@@ -87,26 +86,26 @@ function analyzeZodField(
|
|
|
87
86
|
while (true) {
|
|
88
87
|
if (baseSchema instanceof z.ZodOptional) {
|
|
89
88
|
required = false;
|
|
90
|
-
baseSchema = baseSchema.
|
|
89
|
+
baseSchema = baseSchema._zod.def.innerType as z.ZodSchema;
|
|
91
90
|
} else if (baseSchema instanceof z.ZodDefault) {
|
|
92
91
|
required = false;
|
|
93
|
-
defaultValue = baseSchema.
|
|
94
|
-
baseSchema = baseSchema.
|
|
95
|
-
} else
|
|
96
|
-
|
|
92
|
+
defaultValue = (baseSchema._zod.def.defaultValue as () => unknown)();
|
|
93
|
+
baseSchema = baseSchema._zod.def.innerType as z.ZodSchema;
|
|
94
|
+
} else {
|
|
95
|
+
// Check for ZodNullable - nullable doesn't affect CLI required status, but we need to unwrap it to get the base type
|
|
96
|
+
const zodDef = (
|
|
97
97
|
baseSchema as unknown as {
|
|
98
|
-
|
|
98
|
+
_zod?: { def?: { typeName?: string; innerType?: z.ZodSchema } };
|
|
99
99
|
}
|
|
100
|
-
).
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
break; // No more wrappers to unwrap
|
|
100
|
+
)._zod?.def;
|
|
101
|
+
|
|
102
|
+
if (zodDef?.typeName === "ZodNullable" && zodDef.innerType) {
|
|
103
|
+
baseSchema = zodDef.innerType;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// No more wrappers to unwrap
|
|
108
|
+
break;
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
111
|
|
|
@@ -124,7 +123,7 @@ function analyzeZodField(
|
|
|
124
123
|
paramType = "array";
|
|
125
124
|
} else if (baseSchema instanceof z.ZodEnum) {
|
|
126
125
|
paramType = "string";
|
|
127
|
-
choices = baseSchema.
|
|
126
|
+
choices = baseSchema.options as string[];
|
|
128
127
|
} else if (baseSchema instanceof z.ZodRecord) {
|
|
129
128
|
// Handle Record<string, any> as JSON string input
|
|
130
129
|
paramType = "string";
|
|
@@ -351,7 +350,30 @@ function createCommandConfig(
|
|
|
351
350
|
string,
|
|
352
351
|
(params: unknown) => Promise<unknown>
|
|
353
352
|
>;
|
|
354
|
-
|
|
353
|
+
let result: unknown = await sdkObj[functionInfo.name](resolvedParams);
|
|
354
|
+
|
|
355
|
+
// Handle Response objects by wrapping in a structured envelope
|
|
356
|
+
if (result instanceof Response) {
|
|
357
|
+
const response = result;
|
|
358
|
+
let body: unknown;
|
|
359
|
+
try {
|
|
360
|
+
body = await response.json();
|
|
361
|
+
} catch {
|
|
362
|
+
// If JSON parsing fails, try to get text for error context
|
|
363
|
+
const text = response.bodyUsed
|
|
364
|
+
? "[body already consumed]"
|
|
365
|
+
: await response.text().catch(() => "[unable to read body]");
|
|
366
|
+
throw new Error(
|
|
367
|
+
`Failed to parse response as JSON (status: ${response.status}). Body: ${text.slice(0, 200)}`,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
result = {
|
|
371
|
+
statusCode: response.status,
|
|
372
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
373
|
+
body,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
355
377
|
const items = (result as { data?: unknown })?.data
|
|
356
378
|
? (result as { data: unknown }).data
|
|
357
379
|
: result;
|
|
@@ -364,12 +364,12 @@ export class SchemaParameterResolver {
|
|
|
364
364
|
// Check if field is optional or has default
|
|
365
365
|
if (baseSchema instanceof z.ZodOptional) {
|
|
366
366
|
isRequired = false;
|
|
367
|
-
baseSchema = baseSchema.
|
|
367
|
+
baseSchema = baseSchema._zod.def.innerType as z.ZodSchema;
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
if (baseSchema instanceof z.ZodDefault) {
|
|
371
371
|
isRequired = false;
|
|
372
|
-
baseSchema = baseSchema.
|
|
372
|
+
baseSchema = baseSchema._zod.def.innerType as z.ZodSchema;
|
|
373
373
|
}
|
|
374
374
|
|
|
375
375
|
return this.createResolvableParameter([fieldName], baseSchema, isRequired);
|
|
@@ -6,12 +6,13 @@ import type { FormattedItem, FormatMetadata } from "@zapier/zapier-sdk";
|
|
|
6
6
|
// TODO: Consider exposing these utilities or implementing proper CLI formatting
|
|
7
7
|
|
|
8
8
|
function getFormatMetadata(schema: unknown): FormatMetadata | undefined {
|
|
9
|
-
return (schema as {
|
|
10
|
-
?.formatMeta;
|
|
9
|
+
return (schema as { _zod?: { def?: { formatMeta?: FormatMetadata } } })?._zod
|
|
10
|
+
?.def?.formatMeta;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function getOutputSchema(schema: unknown): unknown {
|
|
14
|
-
return (schema as {
|
|
14
|
+
return (schema as { _zod?: { def?: { outputSchema?: unknown } } })?._zod?.def
|
|
15
|
+
?.outputSchema;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
// ============================================================================
|