@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.
@@ -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
- if (authenticationId) {
62
- for (const action of actions) {
63
- try {
64
- const fieldsResult = await sdk.listInputFields({
65
- appKey: app.implementation_id,
66
- actionKey: action.key,
67
- actionType: action.action_type,
68
- authenticationId: authenticationId,
69
- });
70
-
71
- const fields = fieldsResult.data as InputFieldItem[];
72
- actionsWithFields.push({
73
- ...action,
74
- inputFields: fields,
75
- name: action.title || action.key,
76
- });
77
- } catch {
78
- // If we can't get fields for an action, include it without fields
79
- actionsWithFields.push({
80
- ...action,
81
- inputFields: [],
82
- name: action.title || action.key,
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
- } else {
87
- // Convert actions to have empty input fields (will generate generic types)
88
- actions.forEach(
89
- (action: {
90
- key: string;
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
- const loginWithSdk = createFunction(async function loginWithSdk(
33
- options: LoginOptions,
34
- ): Promise<void> {
35
- const timeoutSeconds = options.timeout ? parseInt(options.timeout, 10) : 300;
36
- if (isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
37
- throw new Error("Timeout must be a positive number");
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
- await login(timeoutSeconds * 1000); // Convert to milliseconds
41
- const user = await getLoggedInUser();
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
- console.log(`✅ Successfully logged in as ${user.email}`);
56
+ console.log(`✅ Successfully logged in as ${user.email}`);
44
57
 
45
- // Force immediate exit to prevent hanging (especially in development with tsx)
46
- setTimeout(() => process.exit(0), 100);
47
- }, LoginSchema);
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(options);
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;
@@ -6,16 +6,20 @@ import type { Socket } from "net";
6
6
 
7
7
  import {
8
8
  AUTH_MODE_HEADER,
9
- LOGIN_CLIENT_ID,
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 { updateLogin, logout } from "@zapier/zapier-sdk-cli-login";
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
- const login = async (timeoutMs: number = LOGIN_TIMEOUT_MS): Promise<string> => {
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 = `${ZAPIER_BASE}/oauth/authorize/?${new URLSearchParams({
128
+ const authUrl = `${authorizeUrl}?${new URLSearchParams({
110
129
  response_type: "code",
111
- client_id: LOGIN_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
- `${ZAPIER_BASE}/oauth/token/`,
187
+ tokenUrl,
169
188
  {
170
189
  grant_type: "authorization_code",
171
190
  code: await promisedCode,
172
191
  redirect_uri: redirectUri,
173
- client_id: LOGIN_CLIENT_ID,
192
+ client_id: authClientId || ZAPIER_AUTH_CLIENT_ID,
174
193
  code_verifier: codeVerifier,
175
194
  },
176
195
  {
@@ -1,7 +1,6 @@
1
1
  // Import shared OAuth constants from login package
2
2
  export {
3
- ZAPIER_BASE,
4
- LOGIN_CLIENT_ID,
3
+ ZAPIER_AUTH_CLIENT_ID,
5
4
  AUTH_MODE_HEADER,
6
5
  } from "@zapier/zapier-sdk-cli-login";
7
6