@zapier/zapier-sdk 0.0.2 → 0.0.3
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/actions-sdk.d.ts +0 -4
- package/dist/actions-sdk.js +43 -1029
- package/dist/api.d.ts +62 -0
- package/dist/api.js +227 -0
- package/dist/functions/bundleCode.d.ts +18 -0
- package/dist/functions/bundleCode.js +91 -0
- package/dist/functions/generateTypes.d.ts +16 -0
- package/dist/functions/generateTypes.js +271 -0
- package/dist/functions/getAction.d.ts +16 -0
- package/dist/functions/getAction.js +25 -0
- package/dist/functions/getApp.d.ts +14 -0
- package/dist/functions/getApp.js +41 -0
- package/dist/functions/listActions.d.ts +15 -0
- package/dist/functions/listActions.js +127 -0
- package/dist/functions/listApps.d.ts +16 -0
- package/dist/functions/listApps.js +50 -0
- package/dist/functions/listAuths.d.ts +18 -0
- package/dist/functions/listAuths.js +118 -0
- package/dist/functions/listFields.d.ts +18 -0
- package/dist/functions/listFields.js +67 -0
- package/dist/functions/runAction.d.ts +18 -0
- package/dist/functions/runAction.js +156 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +20 -1
- package/dist/output-schemas.d.ts +4 -4
- package/dist/schemas.d.ts +24 -24
- package/dist/types.d.ts +12 -0
- package/package.json +1 -1
- package/src/actions-sdk.ts +47 -1376
- package/src/api.ts +361 -0
- package/src/functions/bundleCode.ts +85 -0
- package/src/functions/generateTypes.ts +309 -0
- package/src/functions/getAction.ts +34 -0
- package/src/functions/getApp.ts +47 -0
- package/src/functions/listActions.ts +151 -0
- package/src/functions/listApps.ts +65 -0
- package/src/functions/listAuths.ts +161 -0
- package/src/functions/listFields.ts +95 -0
- package/src/functions/runAction.ts +256 -0
- package/src/index.ts +11 -0
- package/src/types.ts +13 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import type { Action, ActionField, FunctionConfig } from "../types";
|
|
2
|
+
import { listActions } from "./listActions";
|
|
3
|
+
import { listFields } from "./listFields";
|
|
4
|
+
|
|
5
|
+
export interface GenerateTypesOptions extends FunctionConfig {
|
|
6
|
+
appKey: string;
|
|
7
|
+
authId?: number;
|
|
8
|
+
output?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ActionWithActionFields extends Omit<Action, "inputFields"> {
|
|
12
|
+
inputFields: ActionField[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Generate TypeScript types for a specific app
|
|
17
|
+
*
|
|
18
|
+
* This function can be used standalone without instantiating a full SDK,
|
|
19
|
+
* which enables better tree-shaking in applications that only need this functionality.
|
|
20
|
+
*
|
|
21
|
+
* @param options - App key, auth ID, output path, and API configuration options
|
|
22
|
+
* @returns Promise<string> - Generated TypeScript code
|
|
23
|
+
*/
|
|
24
|
+
export async function generateTypes(
|
|
25
|
+
options: GenerateTypesOptions,
|
|
26
|
+
): Promise<string> {
|
|
27
|
+
const { appKey, authId, output = `./types/${appKey}.d.ts` } = options;
|
|
28
|
+
|
|
29
|
+
// Parse app identifier (support app@version format)
|
|
30
|
+
const { app, version } = parseAppIdentifier(appKey);
|
|
31
|
+
|
|
32
|
+
// Fetch all actions for the app
|
|
33
|
+
const actions = await listActions({
|
|
34
|
+
...options,
|
|
35
|
+
appKey: app,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (actions.length === 0) {
|
|
39
|
+
const typeDefinitions = generateEmptyTypesFile(app, version);
|
|
40
|
+
|
|
41
|
+
if (output) {
|
|
42
|
+
const fs = await import("fs");
|
|
43
|
+
const path = await import("path");
|
|
44
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
45
|
+
fs.writeFileSync(output, typeDefinitions, "utf8");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return typeDefinitions;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Fetch input fields for each action
|
|
52
|
+
const actionsWithFields: ActionWithActionFields[] = [];
|
|
53
|
+
|
|
54
|
+
if (authId) {
|
|
55
|
+
for (const action of actions) {
|
|
56
|
+
try {
|
|
57
|
+
const fields = await listFields({
|
|
58
|
+
...options,
|
|
59
|
+
app: action.appKey,
|
|
60
|
+
action: action.key,
|
|
61
|
+
type: action.type,
|
|
62
|
+
authId: authId,
|
|
63
|
+
});
|
|
64
|
+
actionsWithFields.push({ ...action, inputFields: fields });
|
|
65
|
+
} catch {
|
|
66
|
+
// If we can't get fields for an action, include it without fields
|
|
67
|
+
actionsWithFields.push({ ...action, inputFields: [] });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// Convert actions to have empty input fields (will generate generic types)
|
|
72
|
+
actions.forEach((action) => {
|
|
73
|
+
actionsWithFields.push({ ...action, inputFields: [] });
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Generate TypeScript types
|
|
78
|
+
const typeDefinitions = generateTypeDefinitions(
|
|
79
|
+
app,
|
|
80
|
+
actionsWithFields,
|
|
81
|
+
version,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Write to file if output path specified
|
|
85
|
+
if (output) {
|
|
86
|
+
const fs = await import("fs");
|
|
87
|
+
const path = await import("path");
|
|
88
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
89
|
+
fs.writeFileSync(output, typeDefinitions, "utf8");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return typeDefinitions;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function parseAppIdentifier(identifier: string): {
|
|
96
|
+
app: string;
|
|
97
|
+
version?: string;
|
|
98
|
+
} {
|
|
99
|
+
const parts = identifier.split("@");
|
|
100
|
+
return {
|
|
101
|
+
app: parts[0],
|
|
102
|
+
version: parts[1],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function generateTypeDefinitions(
|
|
107
|
+
appKey: string,
|
|
108
|
+
actions: ActionWithActionFields[],
|
|
109
|
+
version?: string,
|
|
110
|
+
): string {
|
|
111
|
+
// Handle empty actions
|
|
112
|
+
if (actions.length === 0) {
|
|
113
|
+
return generateEmptyTypesFile(appKey, version);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Group actions by type
|
|
117
|
+
const actionsByType = actions.reduce(
|
|
118
|
+
(acc, action) => {
|
|
119
|
+
if (!acc[action.type]) {
|
|
120
|
+
acc[action.type] = [];
|
|
121
|
+
}
|
|
122
|
+
acc[action.type].push(action);
|
|
123
|
+
return acc;
|
|
124
|
+
},
|
|
125
|
+
{} as Record<string, ActionWithActionFields[]>,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const appName = capitalize(appKey);
|
|
129
|
+
const versionComment = version
|
|
130
|
+
? ` * Generated for ${appKey}@${version}`
|
|
131
|
+
: ` * Generated for ${appKey}`;
|
|
132
|
+
|
|
133
|
+
let output = `/* eslint-disable @typescript-eslint/naming-convention */
|
|
134
|
+
/**
|
|
135
|
+
* Auto-generated TypeScript types for Zapier ${appKey} actions
|
|
136
|
+
${versionComment}
|
|
137
|
+
* Generated on: ${new Date().toISOString()}
|
|
138
|
+
*
|
|
139
|
+
* Usage:
|
|
140
|
+
* import type { ${appName}Actions } from './path/to/this/file'
|
|
141
|
+
* const sdk: ActionsSdk & { actions: ${appName}Actions } = createActionsSdk(...)
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
import type { ActionExecutionOptions, ActionExecutionResult } from '@zapier/zapier-sdk'
|
|
145
|
+
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
// Generate input types for each action
|
|
149
|
+
actions.forEach((action) => {
|
|
150
|
+
if (action.inputFields.length > 0) {
|
|
151
|
+
const inputTypeName = `${appName}${capitalize(action.type)}${capitalize(
|
|
152
|
+
sanitizeActionName(action.key),
|
|
153
|
+
)}Inputs`;
|
|
154
|
+
|
|
155
|
+
output += `interface ${inputTypeName} {\n`;
|
|
156
|
+
|
|
157
|
+
action.inputFields.forEach((field) => {
|
|
158
|
+
const isOptional = !field.required;
|
|
159
|
+
const fieldType = mapFieldTypeToTypeScript(field);
|
|
160
|
+
const description = field.helpText
|
|
161
|
+
? ` /** ${escapeComment(field.helpText)} */\n`
|
|
162
|
+
: "";
|
|
163
|
+
|
|
164
|
+
output += `${description} ${sanitizeFieldName(field.key)}${
|
|
165
|
+
isOptional ? "?" : ""
|
|
166
|
+
}: ${fieldType}\n`;
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
output += `}\n\n`;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Generate action type interfaces for each action type
|
|
174
|
+
Object.entries(actionsByType).forEach(([actionType, typeActions]) => {
|
|
175
|
+
const typeName = `${appName}${capitalize(actionType)}Actions`;
|
|
176
|
+
|
|
177
|
+
output += `interface ${typeName} {\n`;
|
|
178
|
+
|
|
179
|
+
typeActions.forEach((action: ActionWithActionFields) => {
|
|
180
|
+
const actionName = sanitizeActionName(action.key);
|
|
181
|
+
const description = action.description
|
|
182
|
+
? ` /** ${escapeComment(action.description)} */\n`
|
|
183
|
+
: "";
|
|
184
|
+
|
|
185
|
+
// Generate type-safe action method signature
|
|
186
|
+
if (action.inputFields.length > 0) {
|
|
187
|
+
const inputTypeName = `${appName}${capitalize(action.type)}${capitalize(
|
|
188
|
+
sanitizeActionName(action.key),
|
|
189
|
+
)}Inputs`;
|
|
190
|
+
output += `${description} ${actionName}: (options: { inputs: ${inputTypeName} } & Omit<ActionExecutionOptions, 'inputs'>) => Promise<ActionExecutionResult>\n`;
|
|
191
|
+
} else {
|
|
192
|
+
// No specific input fields available - use generic Record<string, any> for inputs
|
|
193
|
+
output += `${description} ${actionName}: (options?: { inputs?: Record<string, any> } & ActionExecutionOptions) => Promise<ActionExecutionResult>\n`;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
output += `}\n\n`;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Generate the main app actions interface
|
|
201
|
+
output += `export interface ${appName}Actions {\n`;
|
|
202
|
+
output += ` run: {\n`;
|
|
203
|
+
output += ` ${appKey}: {\n`;
|
|
204
|
+
|
|
205
|
+
Object.keys(actionsByType).forEach((actionType) => {
|
|
206
|
+
const typeName = `${appName}${capitalize(actionType)}Actions`;
|
|
207
|
+
output += ` ${actionType}: ${typeName}\n`;
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
output += ` }\n`;
|
|
211
|
+
output += ` }\n`;
|
|
212
|
+
output += `}\n`;
|
|
213
|
+
|
|
214
|
+
return output;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function generateEmptyTypesFile(appKey: string, version?: string): string {
|
|
218
|
+
const appName = capitalize(appKey);
|
|
219
|
+
const versionComment = version
|
|
220
|
+
? ` * Generated for ${appKey}@${version}`
|
|
221
|
+
: ` * Generated for ${appKey}`;
|
|
222
|
+
|
|
223
|
+
return `/* eslint-disable @typescript-eslint/naming-convention */
|
|
224
|
+
/**
|
|
225
|
+
* Auto-generated TypeScript types for Zapier ${appKey} actions
|
|
226
|
+
${versionComment}
|
|
227
|
+
* Generated on: ${new Date().toISOString()}
|
|
228
|
+
*
|
|
229
|
+
* No actions found for this app.
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
import type { ActionExecutionOptions, ActionExecutionResult } from '@zapier/zapier-sdk'
|
|
233
|
+
|
|
234
|
+
export interface ${appName}Actions {
|
|
235
|
+
run: {
|
|
236
|
+
${appKey}: {
|
|
237
|
+
// No actions available
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function capitalize(str: string): string {
|
|
245
|
+
return str.charAt(0).toUpperCase() + str.slice(1).replace(/[-_]/g, "");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function sanitizeActionName(actionKey: string): string {
|
|
249
|
+
// Ensure the action name is a valid TypeScript identifier
|
|
250
|
+
return actionKey.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function sanitizeFieldName(fieldKey: string): string {
|
|
254
|
+
// Ensure the field name is a valid TypeScript identifier
|
|
255
|
+
return fieldKey.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function escapeComment(comment: string): string {
|
|
259
|
+
// Escape comment text to prevent breaking the JSDoc comment
|
|
260
|
+
return comment.replace(/\*\//g, "*\\/").replace(/\r?\n/g, " ");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function mapFieldTypeToTypeScript(field: ActionField): string {
|
|
264
|
+
// Handle choices (enum-like fields)
|
|
265
|
+
if (field.choices && field.choices.length > 0) {
|
|
266
|
+
const choiceValues = field.choices
|
|
267
|
+
.filter(
|
|
268
|
+
(choice) =>
|
|
269
|
+
choice.value !== undefined &&
|
|
270
|
+
choice.value !== null &&
|
|
271
|
+
choice.value !== "",
|
|
272
|
+
)
|
|
273
|
+
.map((choice) =>
|
|
274
|
+
typeof choice.value === "string" ? `"${choice.value}"` : choice.value,
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
if (choiceValues.length > 0) {
|
|
278
|
+
return choiceValues.join(" | ");
|
|
279
|
+
}
|
|
280
|
+
// If all choices were filtered out, fall through to default type handling
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Map Zapier field types to TypeScript types
|
|
284
|
+
switch (field.type?.toLowerCase()) {
|
|
285
|
+
case "string":
|
|
286
|
+
case "text":
|
|
287
|
+
case "email":
|
|
288
|
+
case "url":
|
|
289
|
+
case "password":
|
|
290
|
+
return "string";
|
|
291
|
+
case "integer":
|
|
292
|
+
case "number":
|
|
293
|
+
return "number";
|
|
294
|
+
case "boolean":
|
|
295
|
+
return "boolean";
|
|
296
|
+
case "datetime":
|
|
297
|
+
case "date":
|
|
298
|
+
return "string"; // ISO date strings
|
|
299
|
+
case "file":
|
|
300
|
+
return "string"; // File URL or content
|
|
301
|
+
case "array":
|
|
302
|
+
return "any[]";
|
|
303
|
+
case "object":
|
|
304
|
+
return "Record<string, any>";
|
|
305
|
+
default:
|
|
306
|
+
// Default to string for unknown types, with union for common cases
|
|
307
|
+
return "string | number | boolean";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Action, FunctionConfig } from "../types";
|
|
2
|
+
import { listActions } from "./listActions";
|
|
3
|
+
|
|
4
|
+
export interface GetActionOptions extends FunctionConfig {
|
|
5
|
+
app: string;
|
|
6
|
+
action: string;
|
|
7
|
+
type: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get a specific action by app, action key, and type
|
|
12
|
+
*
|
|
13
|
+
* This function can be used standalone without instantiating a full SDK,
|
|
14
|
+
* which enables better tree-shaking in applications that only need this functionality.
|
|
15
|
+
*
|
|
16
|
+
* @param options - App key, action key, type, and API configuration options
|
|
17
|
+
* @returns Promise<Action>
|
|
18
|
+
*/
|
|
19
|
+
export async function getAction(options: GetActionOptions): Promise<Action> {
|
|
20
|
+
const { app, action: actionKey, type } = options;
|
|
21
|
+
|
|
22
|
+
const actions = await listActions({
|
|
23
|
+
...options,
|
|
24
|
+
appKey: app,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const action = actions.find((a) => a.key === actionKey && a.type === type);
|
|
28
|
+
|
|
29
|
+
if (!action) {
|
|
30
|
+
throw new Error(`Action not found: ${actionKey} with type ${type}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return action;
|
|
34
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getOrCreateApiClient } from "../api";
|
|
2
|
+
import type { Integration, FunctionConfig } from "../types";
|
|
3
|
+
|
|
4
|
+
export interface GetAppOptions extends FunctionConfig {
|
|
5
|
+
key: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get a specific app by key
|
|
10
|
+
*
|
|
11
|
+
* This function can be used standalone without instantiating a full SDK,
|
|
12
|
+
* which enables better tree-shaking in applications that only need this functionality.
|
|
13
|
+
*
|
|
14
|
+
* @param options - App key and API configuration options
|
|
15
|
+
* @returns Promise<Integration>
|
|
16
|
+
*/
|
|
17
|
+
export async function getApp(options: GetAppOptions): Promise<Integration> {
|
|
18
|
+
const api = getOrCreateApiClient(options);
|
|
19
|
+
|
|
20
|
+
const { key } = options;
|
|
21
|
+
|
|
22
|
+
const app = await api.get(`/api/v4/apps/${key}/`, {
|
|
23
|
+
customErrorHandler: (response) => {
|
|
24
|
+
if (response.status === 404) {
|
|
25
|
+
const AppNotFoundError = class extends Error {
|
|
26
|
+
constructor(appKey: string) {
|
|
27
|
+
super(`App not found: ${appKey}`);
|
|
28
|
+
this.name = "AppNotFoundError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return new AppNotFoundError(key);
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
key: app.slug,
|
|
39
|
+
name: app.name,
|
|
40
|
+
description: app.description,
|
|
41
|
+
version: "1.0.0",
|
|
42
|
+
category: app.category?.name,
|
|
43
|
+
actions: [],
|
|
44
|
+
triggers: [],
|
|
45
|
+
current_implementation_id: app.current_implementation_id,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { getOrCreateApiClient } from "../api";
|
|
2
|
+
import type { Action, FunctionConfig } from "../types";
|
|
3
|
+
import { getApp } from "./getApp";
|
|
4
|
+
|
|
5
|
+
export interface ListActionsOptions extends FunctionConfig {
|
|
6
|
+
appKey?: string;
|
|
7
|
+
type?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List available actions with optional filtering
|
|
12
|
+
*
|
|
13
|
+
* This function can be used standalone without instantiating a full SDK,
|
|
14
|
+
* which enables better tree-shaking in applications that only need this functionality.
|
|
15
|
+
*
|
|
16
|
+
* @param options - Filtering and API configuration options
|
|
17
|
+
* @returns Promise<Action[]> with pagination metadata
|
|
18
|
+
*/
|
|
19
|
+
export async function listActions(
|
|
20
|
+
options: ListActionsOptions = {},
|
|
21
|
+
): Promise<Action[]> {
|
|
22
|
+
const api = getOrCreateApiClient(options);
|
|
23
|
+
|
|
24
|
+
// If filtering by appKey, use optimized approach with selected_apis parameter
|
|
25
|
+
if (options.appKey) {
|
|
26
|
+
try {
|
|
27
|
+
// Use the standalone getApp function
|
|
28
|
+
const appData = await getApp({
|
|
29
|
+
key: options.appKey,
|
|
30
|
+
api,
|
|
31
|
+
token: options.token,
|
|
32
|
+
baseUrl: options.baseUrl,
|
|
33
|
+
debug: options.debug,
|
|
34
|
+
fetch: options.fetch,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Extract implementation name from current_implementation_id (e.g., "SlackCLIAPI@1.20.0" -> "SlackCLIAPI")
|
|
38
|
+
const implementationId = appData.current_implementation_id?.split("@")[0];
|
|
39
|
+
if (!implementationId) {
|
|
40
|
+
throw new Error("No current_implementation_id found for app");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const searchParams = {
|
|
44
|
+
global: "true",
|
|
45
|
+
public_only: "true",
|
|
46
|
+
selected_apis: implementationId,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const data = await api.get("/api/v4/implementations/", {
|
|
50
|
+
searchParams,
|
|
51
|
+
});
|
|
52
|
+
const actions: Action[] = [];
|
|
53
|
+
|
|
54
|
+
// Transform implementations to actions
|
|
55
|
+
for (const implementation of data.results || []) {
|
|
56
|
+
if (implementation.actions) {
|
|
57
|
+
for (const action of implementation.actions) {
|
|
58
|
+
const transformedAction: Action = {
|
|
59
|
+
key: action.key,
|
|
60
|
+
name: action.name || action.label,
|
|
61
|
+
description: action.description || "",
|
|
62
|
+
appKey: implementation.slug || "",
|
|
63
|
+
type: action.type || action.type_of || "read",
|
|
64
|
+
inputFields: [], // Would need additional API call for detailed fields
|
|
65
|
+
outputFields: [],
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Apply type filter if provided
|
|
69
|
+
if (options?.type && transformedAction.type !== options.type) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
actions.push(transformedAction);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Add pagination metadata for app-specific queries
|
|
79
|
+
if (actions.length > 0) {
|
|
80
|
+
(actions as any).__pagination = {
|
|
81
|
+
count: data.count,
|
|
82
|
+
hasNext: !!data.next,
|
|
83
|
+
hasPrevious: !!data.previous,
|
|
84
|
+
nextUrl: data.next,
|
|
85
|
+
previousUrl: data.previous,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return actions;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
// If it's an AppNotFoundError, don't try fallback - just re-throw
|
|
92
|
+
if (error instanceof Error && error.name === "AppNotFoundError") {
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
// Fallback to original approach if optimized approach fails
|
|
96
|
+
console.warn(
|
|
97
|
+
"Optimized app lookup failed, falling back to full scan:",
|
|
98
|
+
error,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Original approach for general queries or when optimization fails
|
|
104
|
+
const searchParams = {
|
|
105
|
+
global: "true",
|
|
106
|
+
public_only: "true",
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const data = await api.get("/api/v4/implementations/", { searchParams });
|
|
110
|
+
const actions: Action[] = [];
|
|
111
|
+
|
|
112
|
+
// Transform implementations to actions
|
|
113
|
+
for (const implementation of data.results || []) {
|
|
114
|
+
if (implementation.actions) {
|
|
115
|
+
for (const action of implementation.actions) {
|
|
116
|
+
const transformedAction: Action = {
|
|
117
|
+
key: action.key,
|
|
118
|
+
name: action.name || action.label,
|
|
119
|
+
description: action.description || "",
|
|
120
|
+
appKey: implementation.slug || "",
|
|
121
|
+
type: action.type || action.type_of || "read",
|
|
122
|
+
inputFields: [], // Would need additional API call for detailed fields
|
|
123
|
+
outputFields: [],
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Apply filters
|
|
127
|
+
if (options?.appKey && transformedAction.appKey !== options.appKey) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (options?.type && transformedAction.type !== options.type) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
actions.push(transformedAction);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Add pagination metadata for general queries
|
|
140
|
+
if (actions.length > 0) {
|
|
141
|
+
(actions as any).__pagination = {
|
|
142
|
+
count: data.count,
|
|
143
|
+
hasNext: !!data.next,
|
|
144
|
+
hasPrevious: !!data.previous,
|
|
145
|
+
nextUrl: data.next,
|
|
146
|
+
previousUrl: data.previous,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return actions;
|
|
151
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { getOrCreateApiClient } from "../api";
|
|
2
|
+
import type { Integration, FunctionConfig } from "../types";
|
|
3
|
+
|
|
4
|
+
export interface ListAppsOptions extends FunctionConfig {
|
|
5
|
+
category?: string;
|
|
6
|
+
limit?: number;
|
|
7
|
+
offset?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List available apps with optional filtering
|
|
12
|
+
*
|
|
13
|
+
* This function can be used standalone without instantiating a full SDK,
|
|
14
|
+
* which enables better tree-shaking in applications that only need this functionality.
|
|
15
|
+
*
|
|
16
|
+
* @param options - Filtering, pagination, and API configuration options
|
|
17
|
+
* @returns Promise<Integration[]> with pagination metadata
|
|
18
|
+
*/
|
|
19
|
+
export async function listApps(
|
|
20
|
+
options: ListAppsOptions = {},
|
|
21
|
+
): Promise<Integration[]> {
|
|
22
|
+
const api = getOrCreateApiClient(options);
|
|
23
|
+
|
|
24
|
+
// Build search parameters
|
|
25
|
+
const searchParams: Record<string, string> = {};
|
|
26
|
+
if (options.category) {
|
|
27
|
+
searchParams.category = options.category;
|
|
28
|
+
}
|
|
29
|
+
if (options.limit) {
|
|
30
|
+
searchParams.limit = options.limit.toString();
|
|
31
|
+
}
|
|
32
|
+
if (options.offset) {
|
|
33
|
+
searchParams.offset = options.offset.toString();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const data = await api.get("/api/v4/apps/", { searchParams });
|
|
37
|
+
|
|
38
|
+
// Transform API response to our Integration interface
|
|
39
|
+
const apps =
|
|
40
|
+
data.results?.map(
|
|
41
|
+
(app: any): Integration => ({
|
|
42
|
+
key: app.slug,
|
|
43
|
+
name: app.name,
|
|
44
|
+
description: app.description,
|
|
45
|
+
version: "1.0.0", // API doesn't provide version
|
|
46
|
+
category: app.category?.name,
|
|
47
|
+
actions: [], // Will be populated separately
|
|
48
|
+
triggers: [],
|
|
49
|
+
current_implementation_id: app.current_implementation_id,
|
|
50
|
+
}),
|
|
51
|
+
) || [];
|
|
52
|
+
|
|
53
|
+
// Add pagination metadata to the response
|
|
54
|
+
if (apps.length > 0) {
|
|
55
|
+
(apps as any).__pagination = {
|
|
56
|
+
count: data.count,
|
|
57
|
+
hasNext: !!data.next,
|
|
58
|
+
hasPrevious: !!data.previous,
|
|
59
|
+
nextUrl: data.next,
|
|
60
|
+
previousUrl: data.previous,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return apps;
|
|
65
|
+
}
|