@zapier/zapier-sdk 0.8.2 → 0.9.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.
- package/CHANGELOG.md +12 -0
- package/README.md +10 -33
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +1 -2
- package/dist/api/polling.d.ts +36 -6
- package/dist/api/polling.d.ts.map +1 -1
- package/dist/api/polling.js +132 -28
- package/dist/api/polling.test.d.ts +2 -0
- package/dist/api/polling.test.d.ts.map +1 -0
- package/dist/api/polling.test.js +318 -0
- package/dist/api/types.d.ts +1 -2
- package/dist/api/types.d.ts.map +1 -1
- package/dist/index.cjs +489 -252
- package/dist/index.d.mts +182 -187
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.mjs +486 -251
- package/dist/plugins/apps/index.d.ts +4 -0
- package/dist/plugins/apps/index.d.ts.map +1 -1
- package/dist/plugins/getApp/index.d.ts +2 -7
- package/dist/plugins/getApp/index.d.ts.map +1 -1
- package/dist/plugins/getApp/index.js +9 -9
- package/dist/plugins/getApp/index.test.js +1 -1
- package/dist/plugins/getAuthentication/index.test.js +1 -1
- package/dist/plugins/listActions/index.d.ts +2 -4
- package/dist/plugins/listActions/index.d.ts.map +1 -1
- package/dist/plugins/listActions/index.js +1 -1
- package/dist/plugins/listActions/index.test.js +4 -4
- package/dist/plugins/listApps/index.d.ts +4 -7
- package/dist/plugins/listApps/index.d.ts.map +1 -1
- package/dist/plugins/listApps/index.js +33 -17
- package/dist/plugins/listApps/index.test.js +22 -2
- package/dist/plugins/listAuthentications/index.d.ts +2 -4
- package/dist/plugins/listAuthentications/index.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/index.js +4 -0
- package/dist/plugins/listAuthentications/index.test.js +39 -13
- package/dist/plugins/listAuthentications/schemas.d.ts +3 -0
- package/dist/plugins/listAuthentications/schemas.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/schemas.js +4 -0
- package/dist/plugins/manifest/index.d.ts +25 -9
- package/dist/plugins/manifest/index.d.ts.map +1 -1
- package/dist/plugins/manifest/index.js +239 -67
- package/dist/plugins/manifest/index.test.js +426 -171
- package/dist/plugins/manifest/schemas.d.ts +5 -1
- package/dist/plugins/manifest/schemas.d.ts.map +1 -1
- package/dist/plugins/manifest/schemas.js +1 -0
- package/dist/sdk.d.ts +5 -11
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +1 -4
- package/dist/types/plugin.d.ts +1 -0
- package/dist/types/plugin.d.ts.map +1 -1
- package/dist/types/sdk.d.ts +6 -3
- package/dist/types/sdk.d.ts.map +1 -1
- package/dist/utils/domain-utils.d.ts +16 -0
- package/dist/utils/domain-utils.d.ts.map +1 -1
- package/dist/utils/domain-utils.js +46 -27
- package/dist/utils/domain-utils.test.js +157 -3
- package/dist/utils/file-utils.d.ts +4 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +74 -0
- package/dist/utils/file-utils.test.d.ts +2 -0
- package/dist/utils/file-utils.test.d.ts.map +1 -0
- package/dist/utils/file-utils.test.js +51 -0
- package/package.json +1 -1
- package/src/api/client.ts +5 -4
- package/src/api/polling.test.ts +405 -0
- package/src/api/polling.ts +224 -44
- package/src/api/types.ts +1 -2
- package/src/index.ts +1 -1
- package/src/plugins/apps/index.ts +9 -2
- package/src/plugins/getApp/index.test.ts +1 -1
- package/src/plugins/getApp/index.ts +12 -14
- package/src/plugins/getAuthentication/index.test.ts +1 -1
- package/src/plugins/listActions/index.test.ts +8 -7
- package/src/plugins/listActions/index.ts +3 -3
- package/src/plugins/listApps/index.test.ts +23 -2
- package/src/plugins/listApps/index.ts +46 -25
- package/src/plugins/listAuthentications/index.test.ts +52 -15
- package/src/plugins/listAuthentications/index.ts +7 -2
- package/src/plugins/listAuthentications/schemas.ts +4 -0
- package/src/plugins/manifest/index.test.ts +503 -197
- package/src/plugins/manifest/index.ts +338 -82
- package/src/plugins/manifest/schemas.ts +9 -2
- package/src/sdk.ts +1 -5
- package/src/types/plugin.ts +3 -0
- package/src/types/sdk.ts +26 -21
- package/src/utils/domain-utils.test.ts +196 -2
- package/src/utils/domain-utils.ts +68 -35
- package/src/utils/file-utils.test.ts +73 -0
- package/src/utils/file-utils.ts +94 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/plugins/lockVersion/index.d.ts +0 -24
- package/dist/plugins/lockVersion/index.d.ts.map +0 -1
- package/dist/plugins/lockVersion/index.js +0 -72
- package/dist/plugins/lockVersion/index.test.d.ts +0 -2
- package/dist/plugins/lockVersion/index.test.d.ts.map +0 -1
- package/dist/plugins/lockVersion/index.test.js +0 -129
- package/dist/plugins/lockVersion/schemas.d.ts +0 -10
- package/dist/plugins/lockVersion/schemas.d.ts.map +0 -1
- package/dist/plugins/lockVersion/schemas.js +0 -6
- package/src/plugins/lockVersion/index.test.ts +0 -176
- package/src/plugins/lockVersion/index.ts +0 -112
- package/src/plugins/lockVersion/schemas.ts +0 -9
|
@@ -1,30 +1,41 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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 {
|
|
15
|
-
import type { ImplementationsResponse } from "../../api/types";
|
|
13
|
+
import type { ImplementationsMetaResponse } from "../../api/types";
|
|
16
14
|
import {
|
|
17
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
*
|
|
58
|
-
* Supports local files (Node.js
|
|
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
|
|
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 =
|
|
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
|
|
80
|
+
console.warn(`⚠️ Failed to read manifest from ${filePath}`);
|
|
68
81
|
return null;
|
|
69
82
|
}
|
|
70
83
|
}
|
|
71
84
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
324
|
+
{}, // no SDK dependencies
|
|
86
325
|
{ api: ApiClient },
|
|
87
326
|
ManifestPluginProvides
|
|
88
327
|
> = (params) => {
|
|
89
|
-
const {
|
|
328
|
+
const { context } = params;
|
|
90
329
|
const { api, options } = context;
|
|
91
|
-
const { manifestPath =
|
|
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
|
|
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
|
|
116
|
-
|
|
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
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
const app = apps[0];
|
|
160
|
-
return app;
|
|
161
|
-
};
|
|
402
|
+
let manifestKey: string;
|
|
162
403
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
package/src/sdk.ts
CHANGED
|
@@ -32,7 +32,6 @@ import { listInputFieldsPlugin } from "./plugins/listInputFields";
|
|
|
32
32
|
import { listInputFieldChoicesPlugin } from "./plugins/listInputFieldChoices";
|
|
33
33
|
import { requestPlugin } from "./plugins/request";
|
|
34
34
|
import { manifestPlugin } from "./plugins/manifest";
|
|
35
|
-
import { lockVersionPlugin } from "./plugins/lockVersion";
|
|
36
35
|
|
|
37
36
|
// Full SDK interface with plugins applied
|
|
38
37
|
// Note: ZapierSdk is now defined as ReturnType<typeof createZapierSdk> at the bottom of this file
|
|
@@ -125,10 +124,10 @@ export function createZapierSdkWithoutRegistry(options: ZapierSdkOptions = {}) {
|
|
|
125
124
|
.addPlugin(apiPlugin)
|
|
126
125
|
|
|
127
126
|
// Manifest plugin (provides version locking context)
|
|
127
|
+
.addPlugin(manifestPlugin)
|
|
128
128
|
|
|
129
129
|
// Apps/actions/fields
|
|
130
130
|
.addPlugin(listAppsPlugin)
|
|
131
|
-
.addPlugin(manifestPlugin)
|
|
132
131
|
.addPlugin(getAppPlugin)
|
|
133
132
|
.addPlugin(listActionsPlugin)
|
|
134
133
|
.addPlugin(getActionPlugin)
|
|
@@ -138,9 +137,6 @@ export function createZapierSdkWithoutRegistry(options: ZapierSdkOptions = {}) {
|
|
|
138
137
|
// Run action
|
|
139
138
|
.addPlugin(runActionPlugin)
|
|
140
139
|
|
|
141
|
-
// Version locking
|
|
142
|
-
.addPlugin(lockVersionPlugin)
|
|
143
|
-
|
|
144
140
|
// Authentications
|
|
145
141
|
.addPlugin(listAuthenticationsPlugin)
|
|
146
142
|
.addPlugin(getAuthenticationPlugin)
|
package/src/types/plugin.ts
CHANGED
|
@@ -41,6 +41,9 @@ export type GetSdkType<TPluginProvides extends PluginProvides> =
|
|
|
41
41
|
getContext(): NonNullable<ExtractContextProperties<TPluginProvides>>;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
export type GetContextType<TPluginProvides extends PluginProvides> =
|
|
45
|
+
NonNullable<ExtractContextProperties<TPluginProvides>>;
|
|
46
|
+
|
|
44
47
|
// Helper type for standard plugin options with SDK dependency and meta context
|
|
45
48
|
export type PluginOptions<TSdk = {}, TContext = {}> = {
|
|
46
49
|
sdk: TSdk;
|
package/src/types/sdk.ts
CHANGED
|
@@ -29,12 +29,12 @@ import type { GetAuthenticationSdkFunction } from "../plugins/getAuthentication/
|
|
|
29
29
|
import type { FindFirstAuthenticationSdkFunction } from "../plugins/findFirstAuthentication/schemas";
|
|
30
30
|
import type { FindUniqueAuthenticationSdkFunction } from "../plugins/findUniqueAuthentication/schemas";
|
|
31
31
|
import type { RelayRequestSdkFunction } from "../plugins/request/schemas";
|
|
32
|
-
import type { LockVersionPluginProvides } from "../plugins/lockVersion";
|
|
33
32
|
|
|
34
33
|
import type { z } from "zod";
|
|
35
34
|
import type { RegistryPluginProvides } from "../plugins/registry";
|
|
36
35
|
import type { GetProfilePluginProvides } from "../plugins/getProfile";
|
|
37
|
-
import type { AppsPluginProvides } from "../plugins/apps";
|
|
36
|
+
import type { AppsPluginProvides, ZapierSdkApps } from "../plugins/apps";
|
|
37
|
+
import type { ActionProxy } from "../plugins/apps/types";
|
|
38
38
|
import type { FetchPluginProvides } from "../plugins/fetch";
|
|
39
39
|
import type { ListAppsPluginProvides } from "../plugins/listApps";
|
|
40
40
|
import type { GetAppPluginProvides } from "../plugins/getApp";
|
|
@@ -46,6 +46,7 @@ import type { GetAuthenticationPluginProvides } from "../plugins/getAuthenticati
|
|
|
46
46
|
import type { FindFirstAuthenticationPluginProvides } from "../plugins/findFirstAuthentication";
|
|
47
47
|
import type { FindUniqueAuthenticationPluginProvides } from "../plugins/findUniqueAuthentication";
|
|
48
48
|
import type { ListInputFieldsPluginProvides } from "../plugins/listInputFields";
|
|
49
|
+
import type { ListInputFieldChoicesPluginProvides } from "../plugins/listInputFieldChoices";
|
|
49
50
|
import type { RequestPluginProvides } from "../plugins/request";
|
|
50
51
|
import type { GetSdkType } from "./plugin";
|
|
51
52
|
import type { ManifestPluginProvides } from "../plugins/manifest";
|
|
@@ -78,22 +79,26 @@ export interface ZapierSdkFunctions
|
|
|
78
79
|
// Note: ZapierSdk type removed - use ReturnType<typeof createZapierSdk> instead
|
|
79
80
|
// This ensures the type matches exactly what the builder produces
|
|
80
81
|
|
|
81
|
-
export
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
82
|
+
export interface ZapierSdk
|
|
83
|
+
extends GetSdkType<
|
|
84
|
+
RegistryPluginProvides &
|
|
85
|
+
FetchPluginProvides &
|
|
86
|
+
AppsPluginProvides &
|
|
87
|
+
ListAppsPluginProvides &
|
|
88
|
+
ManifestPluginProvides &
|
|
89
|
+
GetAppPluginProvides &
|
|
90
|
+
ListActionsPluginProvides &
|
|
91
|
+
GetActionPluginProvides &
|
|
92
|
+
RunActionPluginProvides &
|
|
93
|
+
ListAuthenticationsPluginProvides &
|
|
94
|
+
GetAuthenticationPluginProvides &
|
|
95
|
+
FindFirstAuthenticationPluginProvides &
|
|
96
|
+
FindUniqueAuthenticationPluginProvides &
|
|
97
|
+
ListInputFieldsPluginProvides &
|
|
98
|
+
ListInputFieldChoicesPluginProvides &
|
|
99
|
+
RequestPluginProvides &
|
|
100
|
+
GetProfilePluginProvides
|
|
101
|
+
> {
|
|
102
|
+
// Override apps property to intersect ActionProxy with our typed apps
|
|
103
|
+
apps: ActionProxy & ZapierSdkApps;
|
|
104
|
+
}
|