@zapier/zapier-sdk 0.8.3 → 0.10.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.
Files changed (222) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +17 -40
  3. package/dist/api/client.d.ts.map +1 -1
  4. package/dist/api/client.js +14 -0
  5. package/dist/api/debug.d.ts +1 -0
  6. package/dist/api/debug.d.ts.map +1 -1
  7. package/dist/api/debug.js +42 -1
  8. package/dist/api/debug.test.d.ts +2 -0
  9. package/dist/api/debug.test.d.ts.map +1 -0
  10. package/dist/api/debug.test.js +59 -0
  11. package/dist/api/schemas.d.ts +451 -251
  12. package/dist/api/schemas.d.ts.map +1 -1
  13. package/dist/api/schemas.js +51 -29
  14. package/dist/index.cjs +1149 -751
  15. package/dist/index.d.mts +2359 -2161
  16. package/dist/index.d.ts +3 -5
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +2 -4
  19. package/dist/index.mjs +1143 -743
  20. package/dist/plugins/apps/index.d.ts +4 -0
  21. package/dist/plugins/apps/index.d.ts.map +1 -1
  22. package/dist/plugins/findFirstAuthentication/index.d.ts +1 -1
  23. package/dist/plugins/findFirstAuthentication/index.d.ts.map +1 -1
  24. package/dist/plugins/findFirstAuthentication/index.js +9 -1
  25. package/dist/plugins/findFirstAuthentication/index.test.js +3 -4
  26. package/dist/plugins/findFirstAuthentication/schemas.d.ts +5 -3
  27. package/dist/plugins/findFirstAuthentication/schemas.d.ts.map +1 -1
  28. package/dist/plugins/findUniqueAuthentication/index.d.ts.map +1 -1
  29. package/dist/plugins/findUniqueAuthentication/index.js +4 -0
  30. package/dist/plugins/findUniqueAuthentication/schemas.d.ts +5 -3
  31. package/dist/plugins/findUniqueAuthentication/schemas.d.ts.map +1 -1
  32. package/dist/plugins/getAction/index.d.ts.map +1 -1
  33. package/dist/plugins/getAction/index.js +10 -0
  34. package/dist/plugins/getAction/schemas.d.ts +5 -3
  35. package/dist/plugins/getAction/schemas.d.ts.map +1 -1
  36. package/dist/plugins/getApp/index.d.ts +2 -7
  37. package/dist/plugins/getApp/index.d.ts.map +1 -1
  38. package/dist/plugins/getApp/index.js +17 -9
  39. package/dist/plugins/getApp/index.test.js +3 -3
  40. package/dist/plugins/getApp/schemas.d.ts +3 -1
  41. package/dist/plugins/getApp/schemas.d.ts.map +1 -1
  42. package/dist/plugins/getApp/schemas.js +2 -4
  43. package/dist/plugins/getAuthentication/index.d.ts.map +1 -1
  44. package/dist/plugins/getAuthentication/index.js +8 -0
  45. package/dist/plugins/getAuthentication/index.test.js +1 -1
  46. package/dist/plugins/getProfile/index.d.ts.map +1 -1
  47. package/dist/plugins/getProfile/index.js +4 -0
  48. package/dist/plugins/getProfile/schemas.d.ts.map +1 -1
  49. package/dist/plugins/getProfile/schemas.js +4 -3
  50. package/dist/plugins/listActions/index.d.ts +2 -4
  51. package/dist/plugins/listActions/index.d.ts.map +1 -1
  52. package/dist/plugins/listActions/index.js +10 -1
  53. package/dist/plugins/listActions/index.test.js +4 -4
  54. package/dist/plugins/listActions/schemas.d.ts +5 -3
  55. package/dist/plugins/listActions/schemas.d.ts.map +1 -1
  56. package/dist/plugins/listActions/schemas.js +2 -4
  57. package/dist/plugins/listApps/index.d.ts +4 -7
  58. package/dist/plugins/listApps/index.d.ts.map +1 -1
  59. package/dist/plugins/listApps/index.js +37 -17
  60. package/dist/plugins/listApps/index.test.js +23 -3
  61. package/dist/plugins/listApps/schemas.d.ts.map +1 -1
  62. package/dist/plugins/listApps/schemas.js +3 -9
  63. package/dist/plugins/listAuthentications/index.d.ts +2 -4
  64. package/dist/plugins/listAuthentications/index.d.ts.map +1 -1
  65. package/dist/plugins/listAuthentications/index.js +12 -0
  66. package/dist/plugins/listAuthentications/index.test.js +39 -13
  67. package/dist/plugins/listAuthentications/schemas.d.ts +8 -3
  68. package/dist/plugins/listAuthentications/schemas.d.ts.map +1 -1
  69. package/dist/plugins/listAuthentications/schemas.js +4 -0
  70. package/dist/plugins/listInputFieldChoices/index.d.ts.map +1 -1
  71. package/dist/plugins/listInputFieldChoices/index.js +14 -2
  72. package/dist/plugins/listInputFieldChoices/schemas.d.ts +5 -3
  73. package/dist/plugins/listInputFieldChoices/schemas.d.ts.map +1 -1
  74. package/dist/plugins/listInputFieldChoices/schemas.js +10 -19
  75. package/dist/plugins/listInputFields/index.d.ts.map +1 -1
  76. package/dist/plugins/listInputFields/index.js +14 -2
  77. package/dist/plugins/listInputFields/index.test.js +5 -9
  78. package/dist/plugins/listInputFields/schemas.d.ts +5 -3
  79. package/dist/plugins/listInputFields/schemas.d.ts.map +1 -1
  80. package/dist/plugins/manifest/index.d.ts +25 -9
  81. package/dist/plugins/manifest/index.d.ts.map +1 -1
  82. package/dist/plugins/manifest/index.js +239 -67
  83. package/dist/plugins/manifest/index.test.js +426 -171
  84. package/dist/plugins/manifest/schemas.d.ts +5 -1
  85. package/dist/plugins/manifest/schemas.d.ts.map +1 -1
  86. package/dist/plugins/manifest/schemas.js +1 -0
  87. package/dist/plugins/registry/index.d.ts.map +1 -1
  88. package/dist/plugins/registry/index.js +8 -2
  89. package/dist/plugins/request/index.d.ts.map +1 -1
  90. package/dist/plugins/request/index.js +1 -0
  91. package/dist/plugins/runAction/index.d.ts.map +1 -1
  92. package/dist/plugins/runAction/index.js +12 -0
  93. package/dist/plugins/runAction/schemas.d.ts +5 -3
  94. package/dist/plugins/runAction/schemas.d.ts.map +1 -1
  95. package/dist/resolvers/actionKey.d.ts +13 -7
  96. package/dist/resolvers/actionKey.d.ts.map +1 -1
  97. package/dist/resolvers/actionType.d.ts +8 -7
  98. package/dist/resolvers/actionType.d.ts.map +1 -1
  99. package/dist/resolvers/appKey.d.ts +2 -6
  100. package/dist/resolvers/appKey.d.ts.map +1 -1
  101. package/dist/resolvers/authenticationId.d.ts +7 -7
  102. package/dist/resolvers/authenticationId.d.ts.map +1 -1
  103. package/dist/resolvers/authenticationId.js +16 -7
  104. package/dist/resolvers/index.d.ts +3 -35
  105. package/dist/resolvers/index.d.ts.map +1 -1
  106. package/dist/resolvers/index.js +4 -87
  107. package/dist/resolvers/inputFieldKey.d.ts +11 -0
  108. package/dist/resolvers/inputFieldKey.d.ts.map +1 -0
  109. package/dist/resolvers/inputFieldKey.js +23 -0
  110. package/dist/resolvers/inputs.d.ts +11 -6
  111. package/dist/resolvers/inputs.d.ts.map +1 -1
  112. package/dist/resolvers/inputs.js +17 -0
  113. package/dist/schemas/Action.d.ts +8 -8
  114. package/dist/schemas/Action.d.ts.map +1 -1
  115. package/dist/schemas/Action.js +8 -3
  116. package/dist/schemas/App.d.ts +183 -11
  117. package/dist/schemas/App.d.ts.map +1 -1
  118. package/dist/schemas/App.js +7 -9
  119. package/dist/schemas/Auth.d.ts +12 -12
  120. package/dist/schemas/Auth.js +1 -1
  121. package/dist/schemas/Field.d.ts +5 -98
  122. package/dist/schemas/Field.d.ts.map +1 -1
  123. package/dist/schemas/Field.js +24 -52
  124. package/dist/schemas/Run.d.ts +3 -0
  125. package/dist/schemas/Run.d.ts.map +1 -0
  126. package/dist/schemas/Run.js +31 -0
  127. package/dist/schemas/UserProfile.d.ts +11 -11
  128. package/dist/schemas/UserProfile.d.ts.map +1 -1
  129. package/dist/schemas/UserProfile.js +21 -7
  130. package/dist/sdk.d.ts +15 -14
  131. package/dist/sdk.d.ts.map +1 -1
  132. package/dist/sdk.js +1 -4
  133. package/dist/types/plugin.d.ts +6 -0
  134. package/dist/types/plugin.d.ts.map +1 -1
  135. package/dist/types/properties.d.ts +3 -1
  136. package/dist/types/properties.d.ts.map +1 -1
  137. package/dist/types/sdk.d.ts +11 -3
  138. package/dist/types/sdk.d.ts.map +1 -1
  139. package/dist/utils/domain-utils.d.ts +17 -16
  140. package/dist/utils/domain-utils.d.ts.map +1 -1
  141. package/dist/utils/domain-utils.js +53 -78
  142. package/dist/utils/domain-utils.test.js +157 -3
  143. package/dist/utils/file-utils.d.ts +4 -0
  144. package/dist/utils/file-utils.d.ts.map +1 -0
  145. package/dist/utils/file-utils.js +74 -0
  146. package/dist/utils/file-utils.test.d.ts +2 -0
  147. package/dist/utils/file-utils.test.d.ts.map +1 -0
  148. package/dist/utils/file-utils.test.js +51 -0
  149. package/dist/utils/schema-utils.d.ts +44 -21
  150. package/dist/utils/schema-utils.d.ts.map +1 -1
  151. package/dist/utils/schema-utils.js +17 -11
  152. package/package.json +1 -1
  153. package/src/api/client.ts +12 -0
  154. package/src/api/debug.test.ts +76 -0
  155. package/src/api/debug.ts +46 -2
  156. package/src/api/schemas.ts +51 -29
  157. package/src/index.ts +5 -6
  158. package/src/plugins/apps/index.ts +9 -2
  159. package/src/plugins/findFirstAuthentication/index.test.ts +8 -5
  160. package/src/plugins/findFirstAuthentication/index.ts +14 -2
  161. package/src/plugins/findUniqueAuthentication/index.ts +4 -0
  162. package/src/plugins/getAction/index.ts +14 -0
  163. package/src/plugins/getApp/index.test.ts +3 -3
  164. package/src/plugins/getApp/index.ts +20 -14
  165. package/src/plugins/getApp/schemas.ts +7 -12
  166. package/src/plugins/getAuthentication/index.test.ts +1 -1
  167. package/src/plugins/getAuthentication/index.ts +8 -0
  168. package/src/plugins/getProfile/index.ts +4 -0
  169. package/src/plugins/getProfile/schemas.ts +4 -6
  170. package/src/plugins/listActions/index.test.ts +8 -7
  171. package/src/plugins/listActions/index.ts +12 -3
  172. package/src/plugins/listActions/schemas.ts +20 -25
  173. package/src/plugins/listApps/index.test.ts +24 -3
  174. package/src/plugins/listApps/index.ts +50 -25
  175. package/src/plugins/listApps/schemas.ts +17 -26
  176. package/src/plugins/listAuthentications/index.test.ts +52 -15
  177. package/src/plugins/listAuthentications/index.ts +15 -2
  178. package/src/plugins/listAuthentications/schemas.ts +4 -0
  179. package/src/plugins/listInputFieldChoices/index.ts +21 -1
  180. package/src/plugins/listInputFieldChoices/schemas.ts +61 -76
  181. package/src/plugins/listInputFields/index.test.ts +5 -9
  182. package/src/plugins/listInputFields/index.ts +20 -2
  183. package/src/plugins/manifest/index.test.ts +503 -197
  184. package/src/plugins/manifest/index.ts +338 -82
  185. package/src/plugins/manifest/schemas.ts +9 -2
  186. package/src/plugins/registry/index.ts +8 -2
  187. package/src/plugins/request/index.ts +1 -0
  188. package/src/plugins/runAction/index.ts +18 -0
  189. package/src/resolvers/actionKey.ts +15 -13
  190. package/src/resolvers/actionType.ts +10 -12
  191. package/src/resolvers/appKey.ts +2 -6
  192. package/src/resolvers/authenticationId.ts +25 -19
  193. package/src/resolvers/index.ts +7 -113
  194. package/src/resolvers/inputFieldKey.ts +38 -0
  195. package/src/resolvers/inputs.ts +28 -10
  196. package/src/schemas/Action.ts +8 -3
  197. package/src/schemas/App.ts +7 -9
  198. package/src/schemas/Auth.ts +1 -1
  199. package/src/schemas/Field.ts +24 -57
  200. package/src/schemas/Run.ts +40 -0
  201. package/src/schemas/UserProfile.ts +24 -7
  202. package/src/sdk.ts +18 -12
  203. package/src/types/plugin.ts +8 -0
  204. package/src/types/sdk.ts +31 -21
  205. package/src/utils/domain-utils.test.ts +196 -2
  206. package/src/utils/domain-utils.ts +77 -102
  207. package/src/utils/file-utils.test.ts +73 -0
  208. package/src/utils/file-utils.ts +94 -0
  209. package/src/utils/schema-utils.ts +96 -44
  210. package/tsconfig.tsbuildinfo +1 -1
  211. package/dist/plugins/lockVersion/index.d.ts +0 -24
  212. package/dist/plugins/lockVersion/index.d.ts.map +0 -1
  213. package/dist/plugins/lockVersion/index.js +0 -72
  214. package/dist/plugins/lockVersion/index.test.d.ts +0 -2
  215. package/dist/plugins/lockVersion/index.test.d.ts.map +0 -1
  216. package/dist/plugins/lockVersion/index.test.js +0 -129
  217. package/dist/plugins/lockVersion/schemas.d.ts +0 -10
  218. package/dist/plugins/lockVersion/schemas.d.ts.map +0 -1
  219. package/dist/plugins/lockVersion/schemas.js +0 -6
  220. package/src/plugins/lockVersion/index.test.ts +0 -176
  221. package/src/plugins/lockVersion/index.ts +0 -112
  222. package/src/plugins/lockVersion/schemas.ts +0 -9
@@ -1,30 +1,41 @@
1
- import { readFileSync } from "fs";
2
- import { resolve } from "path";
1
+ import { readFile, writeFile, resolve } from "../../utils/file-utils";
3
2
  import type {
4
- GetImplementation,
5
- GetManifestEntry,
6
3
  GetVersionedImplementationId,
7
4
  Manifest,
5
+ ManifestEntry,
8
6
  ManifestPluginOptionsSchema,
7
+ ResolveAppKeys,
9
8
  } from "./schemas";
10
- import { ManifestSchema } from "./schemas";
11
- import type { GetSdkType, Plugin } from "../../types/plugin";
9
+ import { ManifestSchema, DEFAULT_CONFIG_PATH } from "./schemas";
10
+ import type { Plugin } from "../../types/plugin";
12
11
  import type { z } from "zod";
13
12
  import type { ApiClient } from "../../api";
14
- import type { ListAppsPluginProvides } from "../listApps";
15
- import type { ImplementationsResponse } from "../../api/types";
13
+ import type { ImplementationsMetaResponse } from "../../api/types";
16
14
  import {
17
- normalizeImplementationToAppItem,
15
+ normalizeImplementationMetaToAppItem,
18
16
  splitVersionedKey,
17
+ isSnakeCasedSlug,
18
+ dashifySnakeCasedSlug,
19
+ toAppLocator,
20
+ type ResolvedAppLocator,
21
+ isResolvedAppLocator,
19
22
  } from "../../utils/domain-utils";
23
+ import type { AppItem } from "../../types/domain";
24
+ import { extractCursor } from "../../utils/function-utils";
25
+ import { paginate } from "../../utils/pagination-utils";
26
+ import { toArrayFromAsync } from "../../utils/array-utils";
20
27
 
21
28
  export type ManifestPluginOptions = z.infer<typeof ManifestPluginOptionsSchema>;
22
29
 
23
30
  export interface ManifestPluginProvides {
24
31
  context: {
25
32
  getVersionedImplementationId: GetVersionedImplementationId;
26
- getManifestEntry: GetManifestEntry;
27
- getImplementation: GetImplementation;
33
+ resolveAppKeys: ResolveAppKeys;
34
+ updateManifestEntry: (
35
+ appKey: string,
36
+ entry: ManifestEntry,
37
+ configPath?: string,
38
+ ) => Promise<[string, ManifestEntry]>;
28
39
  };
29
40
  }
30
41
 
@@ -54,127 +65,372 @@ function parseManifestContent(
54
65
  }
55
66
 
56
67
  /**
57
- * Load manifest from a file path synchronously
58
- * Supports local files (Node.js only)
68
+ * Read manifest from a file path asynchronously
69
+ * Supports local files (Node.js) and browser environments (fallback to global filesystem)
59
70
  */
60
- export function loadManifestFromFile(filePath: string): Manifest | null {
71
+ export async function readManifestFromFile(
72
+ filePath: string,
73
+ ): Promise<Manifest | null> {
61
74
  try {
62
75
  // Resolve relative paths relative to current working directory
63
- const resolvedPath = resolve(filePath);
64
- const content = readFileSync(resolvedPath, "utf8");
76
+ const resolvedPath = await resolve(filePath);
77
+ const content = await readFile(resolvedPath);
65
78
  return parseManifestContent(content, resolvedPath);
66
79
  } catch {
67
- console.warn(`⚠️ Failed to load manifest from ${filePath}`);
80
+ console.warn(`⚠️ Failed to read manifest from ${filePath}`);
68
81
  return null;
69
82
  }
70
83
  }
71
84
 
72
- const emitWarning = (appKey: string) => {
73
- console.warn(
74
- `\n${"⚠️".padEnd(3)} ${"WARNING".padEnd(8)} No manifest version found for '${appKey}'`,
75
- );
76
- console.warn(
77
- ` ${"↳".padEnd(3)} Using a manifest ensures version locking and prevents unexpected behavior due to version changes.`,
85
+ /**
86
+ * Write manifest to a file path asynchronously
87
+ * Supports local files (Node.js) and browser environments (fallback to global filesystem)
88
+ */
89
+ async function writeManifestToFile(
90
+ manifest: Manifest,
91
+ filePath: string,
92
+ ): Promise<void> {
93
+ const resolvedPath = await resolve(filePath);
94
+ await writeFile(resolvedPath, JSON.stringify(manifest, null, 2));
95
+ }
96
+
97
+ /**
98
+ * Get the preferred key for storing an app in the manifest
99
+ * Extracted from plugin to make it easier to test
100
+ */
101
+ export async function getPreferredManifestEntryKey({
102
+ appKey,
103
+ api,
104
+ }: {
105
+ appKey: string;
106
+ api: ApiClient;
107
+ }): Promise<string> {
108
+ const locator = toAppLocator(appKey);
109
+
110
+ // If we have a slug, prefer it
111
+ if (locator.slug) {
112
+ return locator.slug;
113
+ }
114
+
115
+ // If we have implementation name, look up the latest to get the slug
116
+ if (locator.implementationName) {
117
+ try {
118
+ // API call to get app metadata by implementation name using selected_apis parameter
119
+ const implementationsEnvelope: ImplementationsMetaResponse =
120
+ await api.get(`/api/v4/implementations-meta/lookup/`, {
121
+ searchParams: {
122
+ selected_apis: locator.implementationName,
123
+ },
124
+ });
125
+
126
+ if (
127
+ implementationsEnvelope.results.length > 0 &&
128
+ implementationsEnvelope.results[0].slug
129
+ ) {
130
+ return implementationsEnvelope.results[0].slug;
131
+ }
132
+ } catch {
133
+ // Fall back to implementation name if lookup fails
134
+ }
135
+
136
+ return locator.implementationName;
137
+ }
138
+
139
+ // Fall back to original key if we can't determine anything better
140
+ return locator.lookupAppKey;
141
+ }
142
+
143
+ async function listAppsForSlugsPage({
144
+ slugs,
145
+ cursor,
146
+ api,
147
+ }: {
148
+ slugs: string[];
149
+ cursor?: string;
150
+ api: ApiClient;
151
+ }) {
152
+ const searchParams: Record<string, string> = {};
153
+
154
+ if (slugs.length > 0) {
155
+ searchParams.slugs = slugs.join(",");
156
+ }
157
+
158
+ if (cursor) {
159
+ searchParams.offset = cursor;
160
+ }
161
+
162
+ const implementationsEnvelope: ImplementationsMetaResponse = await api.get(
163
+ "/api/v4/implementations-meta/lookup/",
164
+ {
165
+ searchParams,
166
+ },
78
167
  );
79
- console.warn(
80
- ` ${"↳".padEnd(3)} Generate/update the manifest with: \`zapier-sdk lock-version ${appKey}\`\n`,
168
+
169
+ return {
170
+ data: implementationsEnvelope.results.map(
171
+ normalizeImplementationMetaToAppItem,
172
+ ),
173
+ nextCursor: extractCursor(implementationsEnvelope),
174
+ };
175
+ }
176
+
177
+ /**
178
+ * Find a manifest entry by any app key (implementation name, slug, etc.).
179
+ */
180
+ export function findManifestEntry({
181
+ appKey,
182
+ manifest,
183
+ }: {
184
+ appKey: string;
185
+ manifest: Manifest;
186
+ }): [string, ManifestEntry] | null {
187
+ const [appKeyWithoutVersion] = splitVersionedKey(appKey);
188
+
189
+ // Direct match by key
190
+ if (manifest.apps[appKeyWithoutVersion]) {
191
+ return [appKeyWithoutVersion, manifest.apps[appKeyWithoutVersion]];
192
+ }
193
+
194
+ // Handle snake-cased slugs (convert to dashed)
195
+ if (isSnakeCasedSlug(appKey)) {
196
+ const slug = dashifySnakeCasedSlug(appKey);
197
+ if (manifest.apps[slug]) {
198
+ return [slug, manifest.apps[slug]];
199
+ }
200
+ }
201
+
202
+ // Search by implementation name in manifest values
203
+ for (const [key, entry] of Object.entries(manifest.apps)) {
204
+ if (entry.implementationName === appKeyWithoutVersion) {
205
+ return [key, entry];
206
+ }
207
+ }
208
+
209
+ // No match found
210
+ return null;
211
+ }
212
+
213
+ /**
214
+ * Turn any app keys (slugs, implementation names, etc.) into app locators, which are objects that will be guaranteed to
215
+ * have implementation names. Note that this function will not actually guarantee that implementation names are valid!
216
+ * It will attempt to find the app key in the manifest, or if it's a slug, actually do an API call, or otherwise just
217
+ * assume it's an implementation name that will be used for an API call.
218
+ */
219
+ async function resolveAppKeys({
220
+ appKeys,
221
+ api,
222
+ manifest,
223
+ }: {
224
+ appKeys: string[];
225
+ api: ApiClient;
226
+ manifest: Manifest;
227
+ }): Promise<ResolvedAppLocator[]> {
228
+ // Step 1: Convert all app keys to locators
229
+ const locators = appKeys.map(toAppLocator);
230
+
231
+ // Step 2: Map to resolved/unresolved via manifest
232
+ const locatorsWithManifest = locators.map((locator) => {
233
+ const manifestEntryResult = findManifestEntry({
234
+ appKey: locator.lookupAppKey,
235
+ manifest,
236
+ });
237
+
238
+ if (manifestEntryResult) {
239
+ const [, manifestEntry] = manifestEntryResult;
240
+ // Use manifest entry to resolve implementation name
241
+ const resolvedVersion = locator.version || manifestEntry.version;
242
+
243
+ const resolvedLocator: ResolvedAppLocator = {
244
+ ...locator,
245
+ implementationName: manifestEntry.implementationName,
246
+ version: resolvedVersion,
247
+ };
248
+
249
+ return resolvedLocator;
250
+ }
251
+
252
+ // If these have an implementationName, they're already resolved.
253
+ return locator;
254
+ });
255
+
256
+ // Step 3: Separate resolved from unresolved
257
+ const unresolvedLocators = locatorsWithManifest.filter(
258
+ (locator) => !isResolvedAppLocator(locator),
81
259
  );
82
- };
260
+
261
+ // Step 4: Resolve slugs via API
262
+ const slugsToResolve = unresolvedLocators
263
+ .map((locator) => locator.slug)
264
+ .filter((slug): slug is string => !!slug)
265
+ .filter((slug, index, array) => array.indexOf(slug) === index); // dedupe
266
+
267
+ if (slugsToResolve.length === 0) {
268
+ return locatorsWithManifest.filter(isResolvedAppLocator);
269
+ }
270
+
271
+ const iterator = paginate(listAppsForSlugsPage, {
272
+ slugs: slugsToResolve,
273
+ api,
274
+ });
275
+
276
+ const pages = await toArrayFromAsync(iterator);
277
+ const apps = pages.flatMap((page) => page.data);
278
+
279
+ const slugToAppData = new Map<string, AppItem>();
280
+ for (const app of apps) {
281
+ if (app.slug) {
282
+ slugToAppData.set(app.slug, app);
283
+ }
284
+ }
285
+
286
+ const slugResolvedLocators = locatorsWithManifest.map((locator) => {
287
+ if (isResolvedAppLocator(locator)) {
288
+ return locator;
289
+ }
290
+ if (locator.slug) {
291
+ const appData = slugToAppData.get(locator.slug);
292
+ if (appData) {
293
+ // After resolving from API, check if we have a manifest entry for this implementation name
294
+ const manifestEntryByImplementationName = findManifestEntry({
295
+ appKey: appData.key, // appData.key is the implementation name
296
+ manifest,
297
+ });
298
+
299
+ let version = locator.version;
300
+ if (!version) {
301
+ // Prefer manifest version if available, otherwise use API version
302
+ version = manifestEntryByImplementationName
303
+ ? manifestEntryByImplementationName[1].version || appData.version
304
+ : appData.version;
305
+ }
306
+
307
+ return {
308
+ ...locator,
309
+ implementationName: appData.key,
310
+ version,
311
+ };
312
+ }
313
+ }
314
+ return locator;
315
+ });
316
+
317
+ // Return all resolved locators.
318
+ return slugResolvedLocators.filter(isResolvedAppLocator);
319
+ }
320
+
321
+ export { DEFAULT_CONFIG_PATH } from "./schemas";
83
322
 
84
323
  export const manifestPlugin: Plugin<
85
- GetSdkType<ListAppsPluginProvides>,
324
+ {}, // no SDK dependencies
86
325
  { api: ApiClient },
87
326
  ManifestPluginProvides
88
327
  > = (params) => {
89
- const { sdk, context } = params;
328
+ const { context } = params;
90
329
  const { api, options } = context;
91
- const { manifestPath = ".zapierrc", manifest } = options || {};
330
+ const { manifestPath = DEFAULT_CONFIG_PATH, manifest } = options || {};
92
331
 
93
332
  let resolvedManifest: Manifest | undefined | null;
94
333
 
95
- function resolveManifest() {
334
+ async function resolveManifest(): Promise<Manifest | null> {
96
335
  // If manifest is provided directly, use it
97
336
  if (manifest) {
98
337
  return manifest;
99
338
  }
100
339
  // If manifestPath is provided, load from file
101
340
  if (manifestPath) {
102
- return loadManifestFromFile(manifestPath);
341
+ return await readManifestFromFile(manifestPath);
103
342
  }
104
343
  return null;
105
344
  }
106
345
 
107
- const getResolvedManifest = () => {
346
+ const getResolvedManifest = async (): Promise<Manifest | null> => {
108
347
  if (typeof resolvedManifest === "undefined") {
109
- resolvedManifest = resolveManifest() ?? null;
348
+ resolvedManifest = (await resolveManifest()) ?? null;
110
349
  }
111
350
 
112
351
  return resolvedManifest;
113
352
  };
114
353
 
115
- const getManifestEntry = (appKey: string) => {
116
- return getResolvedManifest()?.apps?.[appKey] || null;
354
+ const getVersionedImplementationId = async (appKey: string) => {
355
+ const resolvedApps = await resolveAppKeys({
356
+ appKeys: [appKey],
357
+ api,
358
+ manifest: (await getResolvedManifest()) ?? { apps: {} },
359
+ });
360
+
361
+ const resolvedApp = resolvedApps[0];
362
+ if (!resolvedApp) return null;
363
+
364
+ return `${resolvedApp.implementationName}@${resolvedApp.version || "latest"}`;
117
365
  };
118
366
 
119
- const getImplementation = async (appKey: string) => {
120
- let selectedApi = null;
121
- const manifestImplementation = getResolvedManifest()?.apps?.[appKey];
122
- const [versionlessAppKey, version] = splitVersionedKey(appKey);
123
-
124
- // Use versioned app key if provided
125
- if (version) {
126
- selectedApi = `${versionlessAppKey}@${version}`;
127
- // Otherwise, use manifest entry if available
128
- } else if (manifestImplementation) {
129
- selectedApi = `${manifestImplementation.implementationName}@${manifestImplementation.version || "latest"}`;
130
- }
367
+ const updateManifestEntry = async (
368
+ appKey: string,
369
+ entry: ManifestEntry,
370
+ configPath: string = DEFAULT_CONFIG_PATH,
371
+ ): Promise<[string, ManifestEntry]> => {
372
+ const manifest = (await readManifestFromFile(configPath)) || { apps: {} };
131
373
 
132
- if (selectedApi) {
133
- const searchParams = {
134
- selected_apis: selectedApi,
135
- };
136
- const implementationData: ImplementationsResponse = await api.get(
137
- "/api/v4/implementations/",
138
- {
139
- searchParams,
140
- },
141
- );
142
- const implementationResults = implementationData.results[0];
143
- if (!implementationResults) return null;
144
- return normalizeImplementationToAppItem(implementationResults);
145
- }
146
- emitWarning(appKey);
374
+ // Try to find existing entry by direct key first
375
+ let existingEntry = findManifestEntry({
376
+ appKey,
377
+ manifest,
378
+ });
147
379
 
148
- const appsIterator = sdk.listApps({ appKeys: [appKey] }).items();
380
+ // If not found directly, try to resolve the app key and search by implementation name
381
+ if (!existingEntry) {
382
+ try {
383
+ const resolvedApps = await resolveAppKeys({
384
+ appKeys: [appKey],
385
+ api,
386
+ manifest,
387
+ });
149
388
 
150
- const apps = [];
151
- for await (const app of appsIterator) {
152
- apps.push(app);
153
- break; // Only need the first result
389
+ if (resolvedApps.length > 0) {
390
+ const resolvedImplementationName = resolvedApps[0].implementationName;
391
+ // Try to find entry using the resolved implementation name
392
+ existingEntry = findManifestEntry({
393
+ appKey: resolvedImplementationName,
394
+ manifest,
395
+ });
396
+ }
397
+ } catch {
398
+ // If resolution fails, continue with original logic
399
+ }
154
400
  }
155
401
 
156
- if (apps.length === 0) {
157
- return null;
158
- }
159
- const app = apps[0];
160
- return app;
161
- };
402
+ let manifestKey: string;
162
403
 
163
- const getVersionedImplementationId = async (appKey: string) => {
164
- const manifestEntry = getManifestEntry(appKey);
165
- if (manifestEntry) {
166
- return `${manifestEntry.implementationName}@${manifestEntry.version || "latest"}`;
404
+ if (existingEntry) {
405
+ // Use existing key to maintain consistency with types files
406
+ manifestKey = existingEntry[0];
407
+ } else {
408
+ // Get preferred key for new entries
409
+ manifestKey = await getPreferredManifestEntryKey({
410
+ appKey,
411
+ api,
412
+ });
167
413
  }
168
- const implementation = await getImplementation(appKey);
169
- if (!implementation) return null;
170
- return implementation.current_implementation_id;
414
+
415
+ manifest.apps[manifestKey] = entry;
416
+ await writeManifestToFile(manifest, configPath);
417
+
418
+ // Clear the cached manifest so it gets reloaded with the new data
419
+ resolvedManifest = undefined;
420
+
421
+ return [manifestKey, entry];
171
422
  };
172
423
 
173
424
  return {
174
425
  context: {
175
426
  getVersionedImplementationId,
176
- getManifestEntry,
177
- getImplementation,
427
+ resolveAppKeys: async ({ appKeys }: { appKeys: string[] }) =>
428
+ resolveAppKeys({
429
+ appKeys,
430
+ api,
431
+ manifest: (await getResolvedManifest()) ?? { apps: {} },
432
+ }),
433
+ updateManifestEntry,
178
434
  },
179
435
  };
180
436
  };
@@ -1,13 +1,14 @@
1
1
  import { z } from "zod";
2
+ import type { ResolvedAppLocator } from "../../utils/domain-utils";
2
3
  import type { AppItem } from "../../types/domain";
3
4
 
5
+ export const DEFAULT_CONFIG_PATH = ".zapierrc" as const;
6
+
4
7
  export type ManifestEntry = {
5
8
  implementationName: string;
6
9
  version?: string;
7
10
  };
8
11
 
9
- export type GetManifestEntry = (appKey: string) => ManifestEntry | null;
10
-
11
12
  export type GetVersionedImplementationId = (
12
13
  appKey: string,
13
14
  ) => Promise<string | null>;
@@ -18,6 +19,12 @@ export type Manifest = {
18
19
  apps: Record<string, ManifestEntry>;
19
20
  };
20
21
 
22
+ export type ResolveAppKeys = ({
23
+ appKeys,
24
+ }: {
25
+ appKeys: string[];
26
+ }) => Promise<ResolvedAppLocator[]>;
27
+
21
28
  /**
22
29
  * Manifest schema for version locking
23
30
  * Maps app keys to their locked version information
@@ -55,10 +55,16 @@ export const registryPlugin: Plugin<
55
55
  const functions = metaKeys
56
56
  .filter((key) => typeof sdk[key as keyof typeof sdk] === "function")
57
57
  .map((key) => {
58
+ const meta = context.meta[key];
58
59
  return {
59
- ...context.meta[key],
60
- categories: context.meta[key].categories || [],
61
60
  name: key,
61
+ type: meta.type,
62
+ itemType: meta.itemType,
63
+ returnType: meta.returnType,
64
+ inputSchema: meta.inputSchema,
65
+ outputSchema: meta.outputSchema,
66
+ categories: meta.categories || [],
67
+ resolvers: meta.resolvers,
62
68
  };
63
69
  })
64
70
  .sort((a, b) => a.name.localeCompare(b.name));
@@ -96,6 +96,7 @@ export const requestPlugin: Plugin<
96
96
  meta: {
97
97
  request: {
98
98
  categories: ["http"],
99
+ returnType: "Response",
99
100
  inputSchema: RelayRequestSchema,
100
101
  },
101
102
  },
@@ -5,6 +5,7 @@ import {
5
5
  type RunActionOptions,
6
6
  type RunActionPage,
7
7
  } from "./schemas";
8
+ import { ActionResultItemSchema } from "../../schemas/Run";
8
9
  import {
9
10
  ZapierValidationError,
10
11
  ZapierConfigurationError,
@@ -13,6 +14,13 @@ import {
13
14
  import { createPaginatedFunction } from "../../utils/function-utils";
14
15
  import type { GetActionPluginProvides } from "../getAction";
15
16
  import type { GetAppPluginProvides } from "../getApp";
17
+ import {
18
+ appKeyResolver,
19
+ actionTypeResolver,
20
+ actionKeyResolver,
21
+ authenticationIdResolver,
22
+ inputsResolver,
23
+ } from "../../resolvers";
16
24
  import type { GetVersionedImplementationId } from "../manifest/schemas";
17
25
 
18
26
  export interface RunActionPluginProvides {
@@ -177,7 +185,17 @@ export const runActionPlugin: Plugin<
177
185
  meta: {
178
186
  runAction: {
179
187
  categories: ["action"],
188
+ type: "list",
189
+ itemType: "ActionResult",
180
190
  inputSchema: RunActionSchema,
191
+ outputSchema: ActionResultItemSchema,
192
+ resolvers: {
193
+ appKey: appKeyResolver,
194
+ actionType: actionTypeResolver,
195
+ actionKey: actionKeyResolver,
196
+ authenticationId: authenticationIdResolver,
197
+ inputs: inputsResolver,
198
+ },
181
199
  },
182
200
  },
183
201
  },
@@ -1,27 +1,29 @@
1
- import type { ZapierSdk } from "../types/sdk";
1
+ import type { DynamicResolver } from "../utils/schema-utils";
2
+ import type { ActionTypeProperty } from "../types/properties";
2
3
 
3
- export interface ActionKeyResolver {
4
- type: "dynamic";
5
- depends: readonly string[];
6
- fetch: (
7
- sdk: ZapierSdk,
8
- resolvedParams: Record<string, any>,
9
- ) => Promise<any[]>;
10
- prompt: (items: any[], params: Record<string, any>) => any;
4
+ interface ActionItem {
5
+ key: string;
6
+ title?: string;
7
+ name?: string;
8
+ description?: string;
9
+ action_type: string;
11
10
  }
12
11
 
13
- export const actionKeyResolver: ActionKeyResolver = {
12
+ export const actionKeyResolver: DynamicResolver<
13
+ ActionItem,
14
+ { appKey: string; actionType: ActionTypeProperty }
15
+ > = {
14
16
  type: "dynamic",
15
17
  depends: ["appKey", "actionType"] as const,
16
- fetch: async (sdk: ZapierSdk, resolvedParams: Record<string, any>) => {
18
+ fetch: async (sdk, resolvedParams) => {
17
19
  const actionsResponse = await sdk.listActions({
18
20
  appKey: resolvedParams.appKey,
19
21
  });
20
22
  return actionsResponse.data.filter(
21
- (action: any) => action.action_type === resolvedParams.actionType,
23
+ (action: ActionItem) => action.action_type === resolvedParams.actionType,
22
24
  );
23
25
  },
24
- prompt: (actions: any[]) => ({
26
+ prompt: (actions) => ({
25
27
  type: "list",
26
28
  name: "actionKey",
27
29
  message: "Select action:",
@@ -1,19 +1,17 @@
1
- import type { ZapierSdk } from "../types/sdk";
1
+ import type { DynamicResolver } from "../utils/schema-utils";
2
2
 
3
- export interface ActionTypeResolver {
4
- type: "dynamic";
5
- depends: readonly string[];
6
- fetch: (
7
- sdk: ZapierSdk,
8
- resolvedParams: Record<string, any>,
9
- ) => Promise<any[]>;
10
- prompt: (items: any[], params: Record<string, any>) => any;
3
+ interface ActionTypeItem {
4
+ key: string;
5
+ name: string;
11
6
  }
12
7
 
13
- export const actionTypeResolver: ActionTypeResolver = {
8
+ export const actionTypeResolver: DynamicResolver<
9
+ ActionTypeItem,
10
+ { appKey: string }
11
+ > = {
14
12
  type: "dynamic",
15
13
  depends: ["appKey"] as const,
16
- fetch: async (sdk: ZapierSdk, resolvedParams: Record<string, any>) => {
14
+ fetch: async (sdk, resolvedParams) => {
17
15
  const actionsResponse = await sdk.listActions({
18
16
  appKey: resolvedParams.appKey,
19
17
  });
@@ -23,7 +21,7 @@ export const actionTypeResolver: ActionTypeResolver = {
23
21
  ];
24
22
  return types.map((type) => ({ key: type, name: type }));
25
23
  },
26
- prompt: (types: any[]) => ({
24
+ prompt: (types) => ({
27
25
  type: "list",
28
26
  name: "actionType",
29
27
  message: "Select action type:",
@@ -1,10 +1,6 @@
1
- export interface AppKeyResolver {
2
- type: "static";
3
- inputType?: "text" | "password" | "email";
4
- placeholder?: string;
5
- }
1
+ import type { StaticResolver } from "../utils/schema-utils";
6
2
 
7
- export const appKeyResolver: AppKeyResolver = {
3
+ export const appKeyResolver: StaticResolver = {
8
4
  type: "static",
9
5
  inputType: "text",
10
6
  placeholder: "Enter app key (e.g., 'SlackCLIAPI' or slug like 'github')",