@zapier/zapier-sdk-cli 0.13.10 → 0.13.12
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/dist/cli.cjs +106 -56
- package/dist/cli.mjs +108 -58
- package/dist/index.cjs +84 -52
- package/dist/index.mjs +86 -54
- package/dist/package.json +1 -1
- package/dist/src/cli.js +23 -3
- package/dist/src/generators/ast-generator.js +33 -29
- package/dist/src/plugins/login/index.js +17 -5
- package/dist/src/utils/auth/login.d.ts +7 -1
- package/dist/src/utils/auth/login.js +10 -7
- package/dist/src/utils/constants.d.ts +1 -1
- package/dist/src/utils/constants.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/cli.test.ts +10 -0
- package/src/cli.ts +35 -3
- package/src/generators/ast-generator.test.ts +908 -0
- package/src/generators/ast-generator.ts +54 -46
- package/src/plugins/login/index.ts +33 -14
- package/src/utils/auth/login.ts +27 -8
- package/src/utils/constants.ts +1 -2
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
ManifestPluginProvides,
|
|
9
9
|
AppItem,
|
|
10
10
|
} from "@zapier/zapier-sdk";
|
|
11
|
-
import { toSnakeCase } from "@zapier/zapier-sdk";
|
|
11
|
+
import { toSnakeCase, batch } from "@zapier/zapier-sdk";
|
|
12
12
|
|
|
13
13
|
interface ActionWithInputFields extends Omit<Action, "inputFields" | "type"> {
|
|
14
14
|
inputFields: InputFieldItem[];
|
|
@@ -58,53 +58,61 @@ export class AstTypeGenerator {
|
|
|
58
58
|
// Fetch input fields for each action
|
|
59
59
|
const actionsWithFields: ActionWithInputFields[] = [];
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
61
|
+
// Fetch all input fields with concurrency limiting for better reliability
|
|
62
|
+
// Using batch() instead of Promise.allSettled() prevents overwhelming the API
|
|
63
|
+
// and triggering rate limits when apps have many actions
|
|
64
|
+
const inputFieldsTasks = actions.map(
|
|
65
|
+
(action) => () =>
|
|
66
|
+
sdk.listInputFields({
|
|
67
|
+
appKey: app.implementation_id,
|
|
68
|
+
actionKey: action.key,
|
|
69
|
+
actionType: action.action_type,
|
|
70
|
+
authenticationId: authenticationId,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const results = await batch(inputFieldsTasks, {
|
|
75
|
+
concurrency: 50, // Limit to 50 concurrent requests
|
|
76
|
+
retry: true, // Automatically retry transient failures
|
|
77
|
+
timeoutMs: 180_000, // 3 minute overall timeout
|
|
78
|
+
taskTimeoutMs: 30_000, // 30 seconds per-task timeout (API calls shouldn't take longer)
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const failedActions: string[] = [];
|
|
82
|
+
|
|
83
|
+
results.forEach((result, i) => {
|
|
84
|
+
const action = actions[i];
|
|
85
|
+
|
|
86
|
+
if (result.status === "fulfilled") {
|
|
87
|
+
actionsWithFields.push({
|
|
88
|
+
...action,
|
|
89
|
+
inputFields: result.value.data as InputFieldItem[],
|
|
90
|
+
name: action.title || action.key,
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
failedActions.push(
|
|
94
|
+
`${action.key} (${action.action_type}): ${result.reason?.message || "Unknown error"}`,
|
|
95
|
+
);
|
|
96
|
+
actionsWithFields.push({
|
|
97
|
+
...action,
|
|
98
|
+
inputFields: [],
|
|
99
|
+
name: action.title || action.key,
|
|
100
|
+
app_key: action.app_key || app.implementation_id,
|
|
101
|
+
action_type: (action.action_type as Action["type"]) || "write",
|
|
102
|
+
title: action.title || action.key,
|
|
103
|
+
type: "action" as const,
|
|
104
|
+
description: action.description || "",
|
|
105
|
+
});
|
|
85
106
|
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
title?: string;
|
|
92
|
-
app_key?: string;
|
|
93
|
-
action_type?: string;
|
|
94
|
-
description?: string;
|
|
95
|
-
}) => {
|
|
96
|
-
actionsWithFields.push({
|
|
97
|
-
...action,
|
|
98
|
-
inputFields: [],
|
|
99
|
-
name: action.title || action.key,
|
|
100
|
-
app_key: action.app_key || app.implementation_id,
|
|
101
|
-
action_type: (action.action_type as Action["type"]) || "write",
|
|
102
|
-
title: action.title || action.key,
|
|
103
|
-
type: "action" as const,
|
|
104
|
-
description: action.description || "",
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (failedActions.length > 0) {
|
|
110
|
+
console.warn(
|
|
111
|
+
`Failed to fetch input fields for ${failedActions.length} action(s):`,
|
|
107
112
|
);
|
|
113
|
+
failedActions.forEach((failedAction) => {
|
|
114
|
+
console.warn(` - ${failedAction}`);
|
|
115
|
+
});
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
// Generate TypeScript AST nodes
|
|
@@ -29,22 +29,36 @@ interface LoginPluginProvides {
|
|
|
29
29
|
const CLI_COMMAND_EXECUTED_EVENT_SUBJECT =
|
|
30
30
|
"platform.sdk.CliCommandExecutedEvent";
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
// Internal type for loginWithSdk that includes SDK URL options
|
|
33
|
+
interface LoginWithSdkOptions extends LoginOptions {
|
|
34
|
+
baseUrl?: string;
|
|
35
|
+
authBaseUrl?: string;
|
|
36
|
+
authClientId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const loginWithSdk = createFunction(
|
|
40
|
+
async (options: LoginWithSdkOptions): Promise<void> => {
|
|
41
|
+
const timeoutSeconds = options.timeout
|
|
42
|
+
? parseInt(options.timeout, 10)
|
|
43
|
+
: 300;
|
|
44
|
+
if (isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
|
|
45
|
+
throw new Error("Timeout must be a positive number");
|
|
46
|
+
}
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
await login({
|
|
49
|
+
timeoutMs: timeoutSeconds * 1000,
|
|
50
|
+
baseUrl: options.baseUrl,
|
|
51
|
+
authBaseUrl: options.authBaseUrl,
|
|
52
|
+
authClientId: options.authClientId,
|
|
53
|
+
});
|
|
54
|
+
const user = await getLoggedInUser();
|
|
42
55
|
|
|
43
|
-
|
|
56
|
+
console.log(`✅ Successfully logged in as ${user.email}`);
|
|
44
57
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
},
|
|
58
|
+
// Force immediate exit to prevent hanging (especially in development with tsx)
|
|
59
|
+
setTimeout(() => process.exit(0), 100);
|
|
60
|
+
},
|
|
61
|
+
);
|
|
48
62
|
|
|
49
63
|
export const loginPlugin: Plugin<
|
|
50
64
|
{},
|
|
@@ -58,7 +72,12 @@ export const loginPlugin: Plugin<
|
|
|
58
72
|
let errorMessage: string | null = null;
|
|
59
73
|
|
|
60
74
|
try {
|
|
61
|
-
await loginWithSdk(
|
|
75
|
+
await loginWithSdk({
|
|
76
|
+
...options,
|
|
77
|
+
baseUrl: context.options?.baseUrl,
|
|
78
|
+
authBaseUrl: context.options?.authBaseUrl,
|
|
79
|
+
authClientId: context.options?.authClientId,
|
|
80
|
+
});
|
|
62
81
|
success = true;
|
|
63
82
|
} catch (error) {
|
|
64
83
|
success = false;
|
package/src/utils/auth/login.ts
CHANGED
|
@@ -6,16 +6,20 @@ import type { Socket } from "net";
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
AUTH_MODE_HEADER,
|
|
9
|
-
|
|
9
|
+
ZAPIER_AUTH_CLIENT_ID,
|
|
10
10
|
LOGIN_PORTS,
|
|
11
11
|
LOGIN_TIMEOUT_MS,
|
|
12
|
-
ZAPIER_BASE,
|
|
13
12
|
} from "../constants";
|
|
14
13
|
import { spinPromise } from "../spinner";
|
|
15
14
|
import log from "../log";
|
|
16
15
|
import api from "../api/client";
|
|
17
16
|
import getCallablePromise from "../getCallablePromise";
|
|
18
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
updateLogin,
|
|
19
|
+
logout,
|
|
20
|
+
getAuthTokenUrl,
|
|
21
|
+
getAuthAuthorizeUrl,
|
|
22
|
+
} from "@zapier/zapier-sdk-cli-login";
|
|
19
23
|
|
|
20
24
|
const findAvailablePort = (): Promise<number> => {
|
|
21
25
|
return new Promise((resolve, reject) => {
|
|
@@ -62,7 +66,22 @@ const generateRandomString = () => {
|
|
|
62
66
|
).join("");
|
|
63
67
|
};
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
interface LoginOptions {
|
|
70
|
+
timeoutMs?: number;
|
|
71
|
+
baseUrl?: string;
|
|
72
|
+
authBaseUrl?: string;
|
|
73
|
+
authClientId?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const login = async ({
|
|
77
|
+
timeoutMs = LOGIN_TIMEOUT_MS,
|
|
78
|
+
baseUrl,
|
|
79
|
+
authBaseUrl,
|
|
80
|
+
authClientId,
|
|
81
|
+
}: LoginOptions): Promise<string> => {
|
|
82
|
+
const authOptions = { baseUrl, authBaseUrl };
|
|
83
|
+
const tokenUrl = getAuthTokenUrl(authOptions);
|
|
84
|
+
const authorizeUrl = getAuthAuthorizeUrl(authOptions);
|
|
66
85
|
// Force logout
|
|
67
86
|
logout();
|
|
68
87
|
|
|
@@ -106,9 +125,9 @@ const login = async (timeoutMs: number = LOGIN_TIMEOUT_MS): Promise<string> => {
|
|
|
106
125
|
const { code_verifier: codeVerifier, code_challenge: codeChallenge } =
|
|
107
126
|
await pkceChallenge();
|
|
108
127
|
|
|
109
|
-
const authUrl = `${
|
|
128
|
+
const authUrl = `${authorizeUrl}?${new URLSearchParams({
|
|
110
129
|
response_type: "code",
|
|
111
|
-
client_id:
|
|
130
|
+
client_id: authClientId || ZAPIER_AUTH_CLIENT_ID,
|
|
112
131
|
redirect_uri: redirectUri,
|
|
113
132
|
scope: "internal offline_access",
|
|
114
133
|
state: generateRandomString(),
|
|
@@ -165,12 +184,12 @@ const login = async (timeoutMs: number = LOGIN_TIMEOUT_MS): Promise<string> => {
|
|
|
165
184
|
refresh_token: string;
|
|
166
185
|
expires_in: number;
|
|
167
186
|
}>(
|
|
168
|
-
|
|
187
|
+
tokenUrl,
|
|
169
188
|
{
|
|
170
189
|
grant_type: "authorization_code",
|
|
171
190
|
code: await promisedCode,
|
|
172
191
|
redirect_uri: redirectUri,
|
|
173
|
-
client_id:
|
|
192
|
+
client_id: authClientId || ZAPIER_AUTH_CLIENT_ID,
|
|
174
193
|
code_verifier: codeVerifier,
|
|
175
194
|
},
|
|
176
195
|
{
|