@zapier/zapier-sdk 0.6.3 → 0.8.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 +18 -0
- package/README.md +73 -41
- package/dist/api/schemas.d.ts +114 -0
- package/dist/api/schemas.d.ts.map +1 -1
- package/dist/api/schemas.js +45 -0
- package/dist/api/types.d.ts +5 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/index.cjs +2208 -2022
- package/dist/index.d.mts +327 -10
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -1
- package/dist/index.mjs +2191 -2024
- package/dist/plugins/findFirstAuthentication/index.test.js +3 -3
- package/dist/plugins/findFirstAuthentication/schemas.d.ts +3 -3
- package/dist/plugins/findFirstAuthentication/schemas.js +1 -1
- package/dist/plugins/findUniqueAuthentication/index.test.js +3 -3
- package/dist/plugins/findUniqueAuthentication/schemas.d.ts +3 -3
- package/dist/plugins/findUniqueAuthentication/schemas.js +1 -1
- package/dist/plugins/getProfile/index.js +1 -1
- package/dist/plugins/listAuthentications/index.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/index.js +2 -3
- package/dist/plugins/listAuthentications/index.test.js +5 -5
- package/dist/plugins/listAuthentications/schemas.d.ts +3 -3
- package/dist/plugins/listAuthentications/schemas.js +1 -1
- package/dist/plugins/listInputFieldChoices/index.d.ts +28 -0
- package/dist/plugins/listInputFieldChoices/index.d.ts.map +1 -0
- package/dist/plugins/listInputFieldChoices/index.js +78 -0
- package/dist/plugins/listInputFieldChoices/index.test.d.ts +2 -0
- package/dist/plugins/listInputFieldChoices/index.test.d.ts.map +1 -0
- package/dist/plugins/listInputFieldChoices/index.test.js +537 -0
- package/dist/plugins/listInputFieldChoices/schemas.d.ts +61 -0
- package/dist/plugins/listInputFieldChoices/schemas.d.ts.map +1 -0
- package/dist/plugins/listInputFieldChoices/schemas.js +73 -0
- package/dist/plugins/registry/index.js +3 -3
- package/dist/sdk.d.ts +93 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +7 -2
- package/package.json +1 -1
- package/src/api/schemas.ts +62 -0
- package/src/api/types.ts +14 -0
- package/src/index.ts +25 -1
- package/src/plugins/findFirstAuthentication/index.test.ts +3 -3
- package/src/plugins/findFirstAuthentication/schemas.ts +1 -1
- package/src/plugins/findUniqueAuthentication/index.test.ts +3 -3
- package/src/plugins/findUniqueAuthentication/schemas.ts +1 -1
- package/src/plugins/getProfile/index.ts +1 -1
- package/src/plugins/listAuthentications/index.test.ts +5 -5
- package/src/plugins/listAuthentications/index.ts +2 -3
- package/src/plugins/listAuthentications/schemas.ts +1 -1
- package/src/plugins/listInputFieldChoices/index.test.ts +653 -0
- package/src/plugins/listInputFieldChoices/index.ts +152 -0
- package/src/plugins/listInputFieldChoices/schemas.ts +139 -0
- package/src/plugins/registry/index.ts +3 -3
- package/src/sdk.ts +8 -1
- package/tsconfig.tsbuildinfo +1 -1
package/dist/index.cjs
CHANGED
|
@@ -320,1003 +320,541 @@ var fetchPlugin = ({ sdk }) => {
|
|
|
320
320
|
};
|
|
321
321
|
};
|
|
322
322
|
|
|
323
|
-
// src/
|
|
324
|
-
var
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
// src/resolvers/actionType.ts
|
|
331
|
-
var actionTypeResolver = {
|
|
332
|
-
type: "dynamic",
|
|
333
|
-
depends: ["appKey"],
|
|
334
|
-
fetch: async (sdk, resolvedParams) => {
|
|
335
|
-
const actionsResponse = await sdk.listActions({
|
|
336
|
-
appKey: resolvedParams.appKey
|
|
337
|
-
});
|
|
338
|
-
const types = [
|
|
339
|
-
...new Set(actionsResponse.data.map((action) => action.action_type))
|
|
340
|
-
];
|
|
341
|
-
return types.map((type) => ({ key: type, name: type }));
|
|
342
|
-
},
|
|
343
|
-
prompt: (types) => ({
|
|
344
|
-
type: "list",
|
|
345
|
-
name: "actionType",
|
|
346
|
-
message: "Select action type:",
|
|
347
|
-
choices: types.map((type) => ({
|
|
348
|
-
name: type.name,
|
|
349
|
-
value: type.key
|
|
350
|
-
}))
|
|
351
|
-
})
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
// src/resolvers/actionKey.ts
|
|
355
|
-
var actionKeyResolver = {
|
|
356
|
-
type: "dynamic",
|
|
357
|
-
depends: ["appKey", "actionType"],
|
|
358
|
-
fetch: async (sdk, resolvedParams) => {
|
|
359
|
-
const actionsResponse = await sdk.listActions({
|
|
360
|
-
appKey: resolvedParams.appKey
|
|
361
|
-
});
|
|
362
|
-
return actionsResponse.data.filter(
|
|
363
|
-
(action) => action.action_type === resolvedParams.actionType
|
|
364
|
-
);
|
|
365
|
-
},
|
|
366
|
-
prompt: (actions) => ({
|
|
367
|
-
type: "list",
|
|
368
|
-
name: "actionKey",
|
|
369
|
-
message: "Select action:",
|
|
370
|
-
choices: actions.map((action) => ({
|
|
371
|
-
name: `${action.title || action.name || action.key} - ${action.description || "No description"}`,
|
|
372
|
-
value: action.key
|
|
373
|
-
}))
|
|
374
|
-
})
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
// src/resolvers/authenticationId.ts
|
|
378
|
-
var authenticationIdResolver = {
|
|
379
|
-
type: "dynamic",
|
|
380
|
-
depends: ["appKey"],
|
|
381
|
-
fetch: async (sdk, resolvedParams) => {
|
|
382
|
-
const myAuths = await sdk.listAuthentications({
|
|
383
|
-
appKey: resolvedParams.appKey,
|
|
384
|
-
maxItems: 1e3,
|
|
385
|
-
owner: "me"
|
|
386
|
-
});
|
|
387
|
-
const allAuths = await sdk.listAuthentications({
|
|
388
|
-
appKey: resolvedParams.appKey,
|
|
389
|
-
maxItems: 1e3
|
|
390
|
-
});
|
|
391
|
-
const otherAuths = allAuths.data.filter(
|
|
392
|
-
(auth) => !myAuths.data.some((myAuth) => myAuth.id === auth.id)
|
|
393
|
-
);
|
|
394
|
-
return [...myAuths.data, ...otherAuths];
|
|
395
|
-
},
|
|
396
|
-
prompt: (auths, params) => ({
|
|
397
|
-
type: "list",
|
|
398
|
-
name: "authenticationId",
|
|
399
|
-
message: `Select authentication for ${params.appKey}:`,
|
|
400
|
-
choices: [
|
|
401
|
-
...auths.map((auth) => ({
|
|
402
|
-
name: `${auth.title || auth.label || "Authentication"} (ID: ${auth.id})`,
|
|
403
|
-
value: auth.id
|
|
404
|
-
})),
|
|
405
|
-
{
|
|
406
|
-
name: "\u2197 Skip authentication (may fail)",
|
|
407
|
-
value: null
|
|
408
|
-
}
|
|
409
|
-
]
|
|
410
|
-
})
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
// src/resolvers/inputs.ts
|
|
414
|
-
var inputsResolver = {
|
|
415
|
-
type: "fields",
|
|
416
|
-
depends: ["appKey", "actionKey", "actionType", "authenticationId"],
|
|
417
|
-
fetch: async (sdk, resolvedParams) => {
|
|
418
|
-
const fieldsResponse = await sdk.listInputFields({
|
|
419
|
-
appKey: resolvedParams.appKey,
|
|
420
|
-
actionKey: resolvedParams.actionKey,
|
|
421
|
-
actionType: resolvedParams.actionType,
|
|
422
|
-
authenticationId: resolvedParams.authenticationId,
|
|
423
|
-
inputs: resolvedParams.inputs
|
|
424
|
-
// Pass along currently resolved inputs
|
|
425
|
-
});
|
|
426
|
-
return fieldsResponse.data;
|
|
323
|
+
// src/utils/pagination-utils.ts
|
|
324
|
+
var offsetCursorMarker = "$$offset$$";
|
|
325
|
+
function splitOffsetCursor(cursor) {
|
|
326
|
+
if (!cursor) {
|
|
327
|
+
return [0, cursor];
|
|
427
328
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
inputs: inputsResolver
|
|
437
|
-
};
|
|
438
|
-
function getResolver(name) {
|
|
439
|
-
return resolverRegistry[name];
|
|
440
|
-
}
|
|
441
|
-
function getResolversForMissingParams(missingParams) {
|
|
442
|
-
const resolvers = {};
|
|
443
|
-
for (const param of missingParams) {
|
|
444
|
-
const resolver = resolverRegistry[param];
|
|
445
|
-
if (resolver) {
|
|
446
|
-
resolvers[param] = resolver;
|
|
329
|
+
try {
|
|
330
|
+
const parsedCursor = JSON.parse(cursor);
|
|
331
|
+
if (!Array.isArray(parsedCursor)) {
|
|
332
|
+
return [0, cursor];
|
|
333
|
+
}
|
|
334
|
+
const [marker, offset, currentCursor] = parsedCursor;
|
|
335
|
+
if (marker !== offsetCursorMarker) {
|
|
336
|
+
return [0, cursor];
|
|
447
337
|
}
|
|
338
|
+
if (typeof offset !== "number") {
|
|
339
|
+
return [0, cursor];
|
|
340
|
+
}
|
|
341
|
+
return [offset, currentCursor];
|
|
342
|
+
} catch {
|
|
343
|
+
return [0, cursor];
|
|
448
344
|
}
|
|
449
|
-
return resolvers;
|
|
450
|
-
}
|
|
451
|
-
function hasResolver(paramName) {
|
|
452
|
-
return paramName in resolverRegistry;
|
|
453
345
|
}
|
|
454
|
-
function
|
|
455
|
-
return
|
|
346
|
+
function createOffsetCursor(offset, currentCursor) {
|
|
347
|
+
return JSON.stringify([offsetCursorMarker, offset, currentCursor]);
|
|
456
348
|
}
|
|
457
|
-
function
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
349
|
+
async function* paginateMaxItems(pageFunction, pageOptions) {
|
|
350
|
+
let cursor = pageOptions?.cursor;
|
|
351
|
+
let totalItemsYielded = 0;
|
|
352
|
+
const maxItems = pageOptions?.maxItems;
|
|
353
|
+
const pageSize = pageOptions?.pageSize;
|
|
354
|
+
do {
|
|
355
|
+
const options = {
|
|
356
|
+
...pageOptions || {},
|
|
357
|
+
cursor,
|
|
358
|
+
pageSize: maxItems !== void 0 && pageSize !== void 0 ? Math.min(pageSize, maxItems) : pageSize
|
|
359
|
+
};
|
|
360
|
+
const page = await pageFunction(options);
|
|
361
|
+
if (maxItems !== void 0) {
|
|
362
|
+
const remainingItems = maxItems - totalItemsYielded;
|
|
363
|
+
if (page.data.length >= remainingItems) {
|
|
364
|
+
const yieldedPage = {
|
|
365
|
+
...page,
|
|
366
|
+
data: page.data.slice(0, remainingItems),
|
|
367
|
+
nextCursor: void 0
|
|
368
|
+
};
|
|
369
|
+
yield yieldedPage;
|
|
370
|
+
break;
|
|
468
371
|
}
|
|
469
372
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
return order;
|
|
373
|
+
yield page;
|
|
374
|
+
totalItemsYielded += page.data.length;
|
|
375
|
+
cursor = page.nextCursor;
|
|
376
|
+
} while (cursor);
|
|
476
377
|
}
|
|
477
|
-
function
|
|
478
|
-
const
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
378
|
+
async function* paginateBuffered(pageFunction, pageOptions) {
|
|
379
|
+
const pageSize = pageOptions?.pageSize;
|
|
380
|
+
const [cursorOffset, currentCursor] = splitOffsetCursor(pageOptions?.cursor);
|
|
381
|
+
const options = {
|
|
382
|
+
...pageOptions || {},
|
|
383
|
+
cursor: currentCursor
|
|
384
|
+
};
|
|
385
|
+
const iterator = paginateMaxItems(pageFunction, options);
|
|
386
|
+
let bufferedPages = [];
|
|
387
|
+
let isFirstPage = true;
|
|
388
|
+
let cursor;
|
|
389
|
+
for await (let page of iterator) {
|
|
390
|
+
if (isFirstPage) {
|
|
391
|
+
isFirstPage = false;
|
|
392
|
+
if (cursorOffset) {
|
|
393
|
+
page = {
|
|
394
|
+
...page,
|
|
395
|
+
data: page.data.slice(cursorOffset)
|
|
396
|
+
};
|
|
485
397
|
}
|
|
486
398
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
// src/auth.ts
|
|
492
|
-
function getTokenFromEnv() {
|
|
493
|
-
return process.env.ZAPIER_TOKEN;
|
|
494
|
-
}
|
|
495
|
-
async function getTokenFromCliLogin(options = {}) {
|
|
496
|
-
try {
|
|
497
|
-
const { getToken } = await import('@zapier/zapier-sdk-cli-login');
|
|
498
|
-
return await getToken(options);
|
|
499
|
-
} catch {
|
|
500
|
-
return void 0;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
async function getTokenFromEnvOrConfig(options = {}) {
|
|
504
|
-
const envToken = getTokenFromEnv();
|
|
505
|
-
if (envToken) {
|
|
506
|
-
return envToken;
|
|
507
|
-
}
|
|
508
|
-
return getTokenFromCliLogin(options);
|
|
509
|
-
}
|
|
510
|
-
var RelayRequestSchema = zod.z.object({
|
|
511
|
-
url: zod.z.string().url().describe("The URL to request (will be proxied through Relay)"),
|
|
512
|
-
method: zod.z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]).optional().describe("HTTP method"),
|
|
513
|
-
body: zod.z.any().optional().describe("Request body as a string"),
|
|
514
|
-
authenticationId: zod.z.number().int().optional().describe("Zapier authentication ID to use for the request"),
|
|
515
|
-
callbackUrl: zod.z.string().url().optional().describe("URL to send async response to (makes request async)"),
|
|
516
|
-
authenticationTemplate: zod.z.string().optional().describe(
|
|
517
|
-
"Optional JSON string authentication template to bypass Notary lookup"
|
|
518
|
-
),
|
|
519
|
-
headers: zod.z.union([
|
|
520
|
-
zod.z.record(zod.z.string()),
|
|
521
|
-
zod.z.instanceof(Headers),
|
|
522
|
-
zod.z.array(zod.z.tuple([zod.z.string(), zod.z.string()]))
|
|
523
|
-
]).optional().describe("Request headers")
|
|
524
|
-
}).extend({
|
|
525
|
-
relayBaseUrl: zod.z.string().optional().describe("Base URL for Relay service")
|
|
526
|
-
}).describe("Make authenticated HTTP requests through Zapier's Relay service");
|
|
527
|
-
var RelayFetchSchema = RelayRequestSchema;
|
|
528
|
-
|
|
529
|
-
// src/plugins/registry/index.ts
|
|
530
|
-
var registryPlugin = ({ sdk, context }) => {
|
|
531
|
-
const metaKeys = Object.keys(context.meta || {});
|
|
532
|
-
const categoryDefinitions = {
|
|
533
|
-
app: {
|
|
534
|
-
title: "App",
|
|
535
|
-
titlePlural: "Apps"
|
|
536
|
-
},
|
|
537
|
-
authentication: {
|
|
538
|
-
title: "Authentication"
|
|
539
|
-
},
|
|
540
|
-
action: {
|
|
541
|
-
title: "Action"
|
|
542
|
-
},
|
|
543
|
-
http: {
|
|
544
|
-
title: "HTTP Request"
|
|
545
|
-
},
|
|
546
|
-
user: {
|
|
547
|
-
title: "User"
|
|
548
|
-
},
|
|
549
|
-
utility: {
|
|
550
|
-
title: "Utility",
|
|
551
|
-
titlePlural: "Utilities"
|
|
552
|
-
},
|
|
553
|
-
other: {
|
|
554
|
-
title: "Other"
|
|
399
|
+
if (!pageSize) {
|
|
400
|
+
yield page;
|
|
401
|
+
cursor = page.nextCursor;
|
|
402
|
+
continue;
|
|
555
403
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
404
|
+
const bufferedLength = bufferedPages.reduce(
|
|
405
|
+
(acc, page2) => acc + page2.data.length,
|
|
406
|
+
0
|
|
407
|
+
);
|
|
408
|
+
if (bufferedLength + page.data.length < pageSize) {
|
|
409
|
+
bufferedPages.push(page);
|
|
410
|
+
cursor = page.nextCursor;
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const bufferedItems = bufferedPages.map((p) => p.data).flat();
|
|
414
|
+
const allItems = [...bufferedItems, ...page.data];
|
|
415
|
+
const pageItems = allItems.slice(0, pageSize);
|
|
416
|
+
const remainingItems = allItems.slice(pageItems.length);
|
|
417
|
+
if (remainingItems.length === 0) {
|
|
418
|
+
yield {
|
|
419
|
+
...page,
|
|
420
|
+
data: pageItems,
|
|
421
|
+
nextCursor: page.nextCursor
|
|
422
|
+
};
|
|
423
|
+
bufferedPages = [];
|
|
424
|
+
cursor = page.nextCursor;
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
yield {
|
|
428
|
+
...page,
|
|
429
|
+
data: pageItems,
|
|
430
|
+
nextCursor: createOffsetCursor(
|
|
431
|
+
page.data.length - remainingItems.length,
|
|
432
|
+
cursor
|
|
433
|
+
)
|
|
583
434
|
};
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
435
|
+
while (remainingItems.length > pageSize) {
|
|
436
|
+
const pageItems2 = remainingItems.splice(0, pageSize);
|
|
437
|
+
yield {
|
|
438
|
+
...page,
|
|
439
|
+
data: pageItems2,
|
|
440
|
+
nextCursor: createOffsetCursor(
|
|
441
|
+
page.data.length - remainingItems.length,
|
|
442
|
+
cursor
|
|
443
|
+
)
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
bufferedPages = [
|
|
447
|
+
{
|
|
448
|
+
...page,
|
|
449
|
+
data: remainingItems
|
|
450
|
+
}
|
|
451
|
+
];
|
|
452
|
+
cursor = page.nextCursor;
|
|
453
|
+
}
|
|
454
|
+
if (bufferedPages.length > 0) {
|
|
455
|
+
const lastBufferedPage = bufferedPages.slice(-1)[0];
|
|
456
|
+
const bufferedItems = bufferedPages.map((p) => p.data).flat();
|
|
457
|
+
yield {
|
|
458
|
+
...lastBufferedPage,
|
|
459
|
+
data: bufferedItems
|
|
589
460
|
};
|
|
590
461
|
}
|
|
591
|
-
|
|
592
|
-
|
|
462
|
+
}
|
|
463
|
+
var paginate = paginateBuffered;
|
|
464
|
+
|
|
465
|
+
// src/utils/validation.ts
|
|
466
|
+
var validate = (schema, input) => {
|
|
467
|
+
const result = schema.safeParse(input);
|
|
468
|
+
if (!result.success) {
|
|
469
|
+
const errorMessages = result.error.errors.map((error) => {
|
|
470
|
+
const path = error.path.length > 0 ? error.path.join(".") : "input";
|
|
471
|
+
return `${path}: ${error.message}`;
|
|
472
|
+
});
|
|
473
|
+
const message = `Validation failed:
|
|
474
|
+
${errorMessages.join("\n ")}`;
|
|
475
|
+
throw new ZapierValidationError(message, {
|
|
476
|
+
details: {
|
|
477
|
+
zodErrors: result.error.errors,
|
|
478
|
+
input
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return result.data;
|
|
483
|
+
};
|
|
484
|
+
function createValidator(schema) {
|
|
485
|
+
return function validateFn(input) {
|
|
486
|
+
return validate(schema, input);
|
|
593
487
|
};
|
|
488
|
+
}
|
|
489
|
+
var validateOptions = (schema, options) => {
|
|
490
|
+
return validate(schema, options);
|
|
594
491
|
};
|
|
595
492
|
|
|
596
|
-
// src/
|
|
597
|
-
function
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return false;
|
|
493
|
+
// src/utils/function-utils.ts
|
|
494
|
+
function extractCursor(data) {
|
|
495
|
+
if (!data?.next) {
|
|
496
|
+
return void 0;
|
|
601
497
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
return
|
|
498
|
+
try {
|
|
499
|
+
const urlObj = new URL(data.next);
|
|
500
|
+
const offset = urlObj.searchParams.get("offset");
|
|
501
|
+
return offset || void 0;
|
|
502
|
+
} catch {
|
|
503
|
+
return void 0;
|
|
608
504
|
}
|
|
609
|
-
return `Bearer ${token}`;
|
|
610
505
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
if (!enabled) {
|
|
615
|
-
return () => {
|
|
616
|
-
};
|
|
506
|
+
function normalizeError(error) {
|
|
507
|
+
if (error instanceof ZapierError) {
|
|
508
|
+
return error;
|
|
617
509
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
};
|
|
510
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
511
|
+
return new ZapierUnknownError(`Unknown error: ${message}`, { cause: error });
|
|
621
512
|
}
|
|
622
|
-
function
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
return options2.body;
|
|
513
|
+
function createFunction(coreFn, schema) {
|
|
514
|
+
const functionName = coreFn.name;
|
|
515
|
+
const namedFunctions = {
|
|
516
|
+
[functionName]: async function(options) {
|
|
517
|
+
try {
|
|
518
|
+
const normalizedOptions = options ?? {};
|
|
519
|
+
if (schema) {
|
|
520
|
+
const validatedOptions = validateOptions(schema, normalizedOptions);
|
|
521
|
+
return await coreFn({
|
|
522
|
+
...normalizedOptions,
|
|
523
|
+
...validatedOptions
|
|
524
|
+
});
|
|
635
525
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const duration = Date.now() - startTime;
|
|
641
|
-
debugLog(`\u2190 ${response.status} ${response.statusText} (${duration}ms)`, {
|
|
642
|
-
url,
|
|
643
|
-
method,
|
|
644
|
-
status: response.status
|
|
645
|
-
});
|
|
646
|
-
return response;
|
|
647
|
-
} catch (error) {
|
|
648
|
-
const duration = Date.now() - startTime;
|
|
649
|
-
debugLog(`\u2716 Request failed (${duration}ms)`, {
|
|
650
|
-
url,
|
|
651
|
-
method,
|
|
652
|
-
error: error instanceof Error ? error.message : error
|
|
653
|
-
});
|
|
654
|
-
throw error;
|
|
526
|
+
return await coreFn(normalizedOptions);
|
|
527
|
+
} catch (error) {
|
|
528
|
+
throw normalizeError(error);
|
|
529
|
+
}
|
|
655
530
|
}
|
|
656
531
|
};
|
|
532
|
+
return namedFunctions[functionName];
|
|
657
533
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
return resultExtractor(result);
|
|
678
|
-
} else if (response.status === pendingStatus) {
|
|
679
|
-
errorCount = 0;
|
|
680
|
-
if (attempt < maxAttempts - 1) {
|
|
681
|
-
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
682
|
-
delay = Math.min(delay * 2, maxDelay);
|
|
683
|
-
continue;
|
|
684
|
-
}
|
|
685
|
-
} else {
|
|
686
|
-
errorCount++;
|
|
687
|
-
if (errorCount >= 3) {
|
|
688
|
-
throw new ZapierApiError(
|
|
689
|
-
`Poll request failed: ${response.status} ${response.statusText}`,
|
|
690
|
-
{ statusCode: response.status }
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
if (attempt < maxAttempts - 1) {
|
|
694
|
-
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
695
|
-
delay = Math.min(delay * 2, maxDelay);
|
|
696
|
-
continue;
|
|
534
|
+
function createPageFunction(coreFn) {
|
|
535
|
+
const functionName = coreFn.name + "Page";
|
|
536
|
+
const namedFunctions = {
|
|
537
|
+
[functionName]: async function(options) {
|
|
538
|
+
try {
|
|
539
|
+
const result = await coreFn(options);
|
|
540
|
+
if (result && typeof result === "object" && "data" in result) {
|
|
541
|
+
const data = result.data;
|
|
542
|
+
return {
|
|
543
|
+
data: Array.isArray(data) ? data : [data],
|
|
544
|
+
nextCursor: result.nextCursor
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
if (Array.isArray(result)) {
|
|
548
|
+
return { data: result };
|
|
549
|
+
}
|
|
550
|
+
return { data: [result] };
|
|
551
|
+
} catch (error) {
|
|
552
|
+
throw normalizeError(error);
|
|
697
553
|
}
|
|
698
554
|
}
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
`Operation timed out after ${maxAttempts} attempts`,
|
|
702
|
-
{ attempts: maxAttempts, maxAttempts }
|
|
703
|
-
);
|
|
555
|
+
};
|
|
556
|
+
return namedFunctions[functionName];
|
|
704
557
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
};
|
|
728
|
-
this.delete = async (path, options = {}) => {
|
|
729
|
-
return this.fetchJson("DELETE", path, void 0, options);
|
|
730
|
-
};
|
|
731
|
-
this.poll = async (path, options = {}) => {
|
|
732
|
-
return pollUntilComplete({
|
|
733
|
-
fetchPoll: () => this.plainFetch(path, {
|
|
734
|
-
method: "GET",
|
|
735
|
-
searchParams: options.searchParams,
|
|
736
|
-
authRequired: options.authRequired
|
|
737
|
-
}),
|
|
738
|
-
maxAttempts: options.maxAttempts,
|
|
739
|
-
initialDelay: options.initialDelay,
|
|
740
|
-
maxDelay: options.maxDelay,
|
|
741
|
-
successStatus: options.successStatus,
|
|
742
|
-
pendingStatus: options.pendingStatus,
|
|
743
|
-
resultExtractor: options.resultExtractor
|
|
558
|
+
function createPaginatedFunction(coreFn, schema) {
|
|
559
|
+
const pageFunction = createPageFunction(coreFn);
|
|
560
|
+
const functionName = coreFn.name;
|
|
561
|
+
const validator = schema ? createValidator(schema) : null;
|
|
562
|
+
const namedFunctions = {
|
|
563
|
+
[functionName]: function(options) {
|
|
564
|
+
const normalizedOptions = options ?? {};
|
|
565
|
+
const validatedOptions = {
|
|
566
|
+
...normalizedOptions,
|
|
567
|
+
...validator ? validator(normalizedOptions) : normalizedOptions
|
|
568
|
+
};
|
|
569
|
+
const pageSize = validatedOptions.pageSize || 100;
|
|
570
|
+
const optimizedOptions = {
|
|
571
|
+
...validatedOptions,
|
|
572
|
+
pageSize
|
|
573
|
+
};
|
|
574
|
+
const iterator = paginate(pageFunction, optimizedOptions);
|
|
575
|
+
const firstPagePromise = iterator.next().then((result) => {
|
|
576
|
+
if (result.done) {
|
|
577
|
+
throw new Error("Paginate should always iterate at least once");
|
|
578
|
+
}
|
|
579
|
+
return result.value;
|
|
744
580
|
});
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
return getTokenFromEnvOrConfig({
|
|
767
|
-
onEvent: this.options.onEvent,
|
|
768
|
-
fetch: this.options.fetch
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
// Helper to handle responses
|
|
772
|
-
async handleResponse(params) {
|
|
773
|
-
const { response, customErrorHandler, wasMissingAuthToken } = params;
|
|
774
|
-
const { data: responseData } = await this.parseResult(response);
|
|
775
|
-
if (response.ok) {
|
|
776
|
-
return responseData;
|
|
777
|
-
}
|
|
778
|
-
const errorInfo = {
|
|
779
|
-
status: response.status,
|
|
780
|
-
statusText: response.statusText,
|
|
781
|
-
data: responseData
|
|
782
|
-
};
|
|
783
|
-
if (customErrorHandler) {
|
|
784
|
-
const customError = customErrorHandler(errorInfo);
|
|
785
|
-
if (customError) {
|
|
786
|
-
if (customError instanceof Error) {
|
|
787
|
-
throw customError;
|
|
788
|
-
} else {
|
|
789
|
-
throw new Error(
|
|
790
|
-
`customErrorHandler returned a non-Error: ${JSON.stringify(customError)}`
|
|
791
|
-
);
|
|
581
|
+
return Object.assign(firstPagePromise, {
|
|
582
|
+
[Symbol.asyncIterator]: async function* () {
|
|
583
|
+
yield await firstPagePromise;
|
|
584
|
+
for await (const page of iterator) {
|
|
585
|
+
yield page;
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
items: function() {
|
|
589
|
+
return {
|
|
590
|
+
[Symbol.asyncIterator]: async function* () {
|
|
591
|
+
const firstPage = await firstPagePromise;
|
|
592
|
+
for (const item of firstPage.data) {
|
|
593
|
+
yield item;
|
|
594
|
+
}
|
|
595
|
+
for await (const page of iterator) {
|
|
596
|
+
for (const item of page.data) {
|
|
597
|
+
yield item;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
};
|
|
792
602
|
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
const { message, errors } = this.parseErrorResponse(errorInfo);
|
|
796
|
-
const errorOptions = {
|
|
797
|
-
statusCode: response.status,
|
|
798
|
-
errors
|
|
799
|
-
};
|
|
800
|
-
if (response.status === 404) {
|
|
801
|
-
throw new ZapierNotFoundError(message, errorOptions);
|
|
802
|
-
}
|
|
803
|
-
if (response.status === 401 || response.status === 403) {
|
|
804
|
-
if (wasMissingAuthToken) {
|
|
805
|
-
throw new ZapierAuthenticationError(
|
|
806
|
-
`Authentication required (HTTP ${response.status}). Please provide a token in options or set ZAPIER_TOKEN environment variable.`,
|
|
807
|
-
errorOptions
|
|
808
|
-
);
|
|
809
|
-
}
|
|
810
|
-
throw new ZapierAuthenticationError(message, errorOptions);
|
|
603
|
+
});
|
|
811
604
|
}
|
|
812
|
-
|
|
813
|
-
|
|
605
|
+
};
|
|
606
|
+
return namedFunctions[functionName];
|
|
607
|
+
}
|
|
608
|
+
var AppItemSchema = withFormatter(
|
|
609
|
+
zod.z.object({
|
|
610
|
+
// Essential properties only
|
|
611
|
+
title: zod.z.string(),
|
|
612
|
+
// Mapped from name
|
|
613
|
+
key: zod.z.string(),
|
|
614
|
+
// Mapped from selected_api
|
|
615
|
+
current_implementation_id: zod.z.string(),
|
|
616
|
+
// From id, keeps the full version
|
|
617
|
+
version: zod.z.string().optional(),
|
|
618
|
+
// Extracted from implementation ID
|
|
619
|
+
description: zod.z.string().optional(),
|
|
620
|
+
slug: zod.z.string().optional()
|
|
621
|
+
}),
|
|
622
|
+
{
|
|
623
|
+
format: (item) => {
|
|
624
|
+
return {
|
|
625
|
+
title: item.title,
|
|
626
|
+
subtitle: `(${item.key})`,
|
|
627
|
+
details: []
|
|
628
|
+
};
|
|
814
629
|
}
|
|
815
|
-
throw new ZapierApiError(message, errorOptions);
|
|
816
630
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
// src/plugins/listApps/schemas.ts
|
|
634
|
+
var ListAppsSchema = withOutputSchema(
|
|
635
|
+
zod.z.object({
|
|
636
|
+
appKeys: zod.z.array(zod.z.string()).optional().describe(
|
|
637
|
+
"Filter apps by app keys (e.g., 'SlackCLIAPI' or slug like 'github')"
|
|
638
|
+
),
|
|
639
|
+
search: zod.z.string().optional().describe("Search for apps by name"),
|
|
640
|
+
pageSize: zod.z.number().min(1).optional().describe("Number of apps per page"),
|
|
641
|
+
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
642
|
+
}).describe("List all available apps with optional filtering"),
|
|
643
|
+
AppItemSchema
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
// src/utils/domain-utils.ts
|
|
647
|
+
function splitVersionedKey(versionedKey) {
|
|
648
|
+
const parts = versionedKey.split("@");
|
|
649
|
+
if (parts.length >= 2) {
|
|
650
|
+
const baseKey = parts[0];
|
|
651
|
+
const version = parts.slice(1).join("@");
|
|
652
|
+
return [baseKey, version];
|
|
824
653
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
654
|
+
return [versionedKey, void 0];
|
|
655
|
+
}
|
|
656
|
+
function normalizeImplementationToAppItem(implementation) {
|
|
657
|
+
const [selectedApi, appVersion] = implementation.selected_api ? splitVersionedKey(implementation.selected_api) : [implementation.selected_api || "", void 0];
|
|
658
|
+
return {
|
|
659
|
+
title: implementation.name || selectedApi,
|
|
660
|
+
key: selectedApi,
|
|
661
|
+
current_implementation_id: implementation.selected_api || "",
|
|
662
|
+
version: appVersion
|
|
663
|
+
// Extract version separately
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function normalizeImplementationMetaToAppItem(implementationMeta) {
|
|
667
|
+
const [selectedApi, appVersion] = splitVersionedKey(implementationMeta.id);
|
|
668
|
+
return {
|
|
669
|
+
title: implementationMeta.name,
|
|
670
|
+
key: selectedApi,
|
|
671
|
+
current_implementation_id: implementationMeta.id,
|
|
672
|
+
// Keep the full versioned ID
|
|
673
|
+
version: appVersion,
|
|
674
|
+
// Extract version separately
|
|
675
|
+
slug: implementationMeta.slug
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function normalizeAuthenticationItem(auth, options = {}) {
|
|
679
|
+
let appKey = options.app_key;
|
|
680
|
+
let version = options.version;
|
|
681
|
+
if (auth.selected_api) {
|
|
682
|
+
const [extractedAppKey, extractedVersion] = splitVersionedKey(
|
|
683
|
+
auth.selected_api
|
|
684
|
+
);
|
|
685
|
+
if (!appKey) {
|
|
686
|
+
appKey = extractedAppKey;
|
|
687
|
+
}
|
|
688
|
+
if (!version) {
|
|
689
|
+
version = extractedVersion;
|
|
853
690
|
}
|
|
854
|
-
return void 0;
|
|
855
691
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
692
|
+
const {
|
|
693
|
+
selected_api: selectedApi,
|
|
694
|
+
customuser_id: userId,
|
|
695
|
+
...restOfAuth
|
|
696
|
+
} = auth;
|
|
697
|
+
return {
|
|
698
|
+
...restOfAuth,
|
|
699
|
+
// Pass through all other API response fields except selected_api
|
|
700
|
+
implementation_id: selectedApi,
|
|
701
|
+
// Rename selected_api to implementation_id
|
|
702
|
+
title: auth.title || auth.label || void 0,
|
|
703
|
+
// Coerce title from label if missing
|
|
704
|
+
is_expired: auth.is_stale,
|
|
705
|
+
// Map is_stale to is_expired
|
|
706
|
+
expired_at: auth.marked_stale_at,
|
|
707
|
+
// Map marked_stale_at to expired_at
|
|
708
|
+
app_key: appKey,
|
|
709
|
+
// App key from implementations endpoint or parsed from selected_api
|
|
710
|
+
version,
|
|
711
|
+
// Version from selected_api or provided
|
|
712
|
+
user_id: userId
|
|
713
|
+
// Map customuser_id to user_id
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
function normalizeActionItem(action) {
|
|
717
|
+
const { name, type, selected_api: appKey, ...restOfAction } = action;
|
|
718
|
+
return {
|
|
719
|
+
...restOfAction,
|
|
720
|
+
app_key: appKey || "",
|
|
721
|
+
action_type: type,
|
|
722
|
+
title: name,
|
|
723
|
+
// Map name to title
|
|
724
|
+
type: "action"
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function groupVersionedAppKeysByType(appKeys) {
|
|
728
|
+
const result = {
|
|
729
|
+
selectedApi: [],
|
|
730
|
+
slug: []
|
|
731
|
+
};
|
|
732
|
+
const seenSelectedApi = /* @__PURE__ */ new Set();
|
|
733
|
+
const seenSlugs = /* @__PURE__ */ new Set();
|
|
734
|
+
for (const key of appKeys) {
|
|
735
|
+
const [keyWithoutVersion, version] = splitVersionedKey(key);
|
|
736
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
737
|
+
if (uuidRegex.test(keyWithoutVersion)) {
|
|
738
|
+
throw new Error(
|
|
739
|
+
`UUID app keys are not supported. Use app slug or implementation ID instead of: ${key}`
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
if (isSnakeCasedSlug(keyWithoutVersion)) {
|
|
743
|
+
const dashified = dashifySnakeCasedSlug(keyWithoutVersion);
|
|
744
|
+
const slugWithVersion = version ? `${dashified}@${version}` : dashified;
|
|
745
|
+
if (!seenSlugs.has(slugWithVersion)) {
|
|
746
|
+
seenSlugs.add(slugWithVersion);
|
|
747
|
+
result.slug.push(slugWithVersion);
|
|
881
748
|
}
|
|
882
|
-
|
|
883
|
-
} catch {
|
|
884
|
-
return { message: fallbackMessage };
|
|
749
|
+
continue;
|
|
885
750
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
const pathSegments = path.split("/").filter(Boolean);
|
|
891
|
-
if (pathSegments.length > 0 && pathSegments[0] in SubdomainConfigMap) {
|
|
892
|
-
const domainPrefix = pathSegments[0];
|
|
893
|
-
const subdomainConfig = SubdomainConfigMap[domainPrefix];
|
|
894
|
-
const originalBaseUrl = new URL(this.options.baseUrl);
|
|
895
|
-
const finalBaseUrl = `https://${domainPrefix}.${originalBaseUrl.hostname}`;
|
|
896
|
-
const pathWithoutPrefix = "/" + pathSegments.slice(1).join("/");
|
|
897
|
-
return { url: new URL(pathWithoutPrefix, finalBaseUrl), subdomainConfig };
|
|
751
|
+
if (keyWithoutVersion.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)) {
|
|
752
|
+
seenSlugs.add(key);
|
|
753
|
+
result.slug.push(key);
|
|
754
|
+
continue;
|
|
898
755
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
};
|
|
903
|
-
}
|
|
904
|
-
// Helper to build full URLs and return routing info
|
|
905
|
-
buildUrl(path, searchParams) {
|
|
906
|
-
const { url, subdomainConfig } = this.applySubdomainBehavior(path);
|
|
907
|
-
if (searchParams) {
|
|
908
|
-
Object.entries(searchParams).forEach(([key, value]) => {
|
|
909
|
-
url.searchParams.set(key, value);
|
|
910
|
-
});
|
|
756
|
+
if (!seenSelectedApi.has(key)) {
|
|
757
|
+
seenSelectedApi.add(key);
|
|
758
|
+
result.selectedApi.push(key);
|
|
911
759
|
}
|
|
912
|
-
return { url: url.toString(), subdomainConfig };
|
|
913
760
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
761
|
+
return result;
|
|
762
|
+
}
|
|
763
|
+
function groupAppKeysByType(appKeys) {
|
|
764
|
+
const grouped = groupVersionedAppKeysByType(appKeys);
|
|
765
|
+
return {
|
|
766
|
+
selectedApi: [
|
|
767
|
+
...new Set(grouped.selectedApi.map((key) => key.split("@")[0]))
|
|
768
|
+
],
|
|
769
|
+
slug: [...new Set(grouped.slug.map((key) => key.split("@")[0]))]
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
function isSnakeCasedSlug(slug) {
|
|
773
|
+
if (slug.match(/^_[0-9]/)) {
|
|
774
|
+
slug = slug.slice(1);
|
|
775
|
+
}
|
|
776
|
+
return !!slug.match(/^[a-z0-9]+(?:_[a-z0-9]+)*$/);
|
|
777
|
+
}
|
|
778
|
+
function dashifySnakeCasedSlug(slug) {
|
|
779
|
+
if (!isSnakeCasedSlug(slug)) {
|
|
780
|
+
return slug;
|
|
781
|
+
}
|
|
782
|
+
if (slug.startsWith("_")) {
|
|
783
|
+
slug = slug.slice(1);
|
|
784
|
+
}
|
|
785
|
+
return slug.replace(/_/g, "-");
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/plugins/listApps/index.ts
|
|
789
|
+
var listAppsPlugin = ({ context }) => {
|
|
790
|
+
const listApps = createPaginatedFunction(async function listAppsPage(options) {
|
|
791
|
+
const api = context.api;
|
|
792
|
+
const opts = options;
|
|
793
|
+
const appKeys = [...opts.appKeys ?? []].map(
|
|
794
|
+
(key) => splitVersionedKey(key)[0]
|
|
795
|
+
);
|
|
796
|
+
if (opts.search) {
|
|
797
|
+
const searchParams2 = {};
|
|
798
|
+
searchParams2.term = opts.search;
|
|
799
|
+
const searchEnvelope = await api.get(
|
|
800
|
+
"/api/v4/implementations-meta/search/",
|
|
801
|
+
{
|
|
802
|
+
searchParams: searchParams2
|
|
803
|
+
}
|
|
804
|
+
);
|
|
805
|
+
const implementations = searchEnvelope.results.map(
|
|
806
|
+
normalizeImplementationMetaToAppItem
|
|
807
|
+
);
|
|
808
|
+
const appKeysSet = new Set(appKeys);
|
|
809
|
+
for (const implementation of implementations) {
|
|
810
|
+
if (!appKeysSet.has(implementation.key)) {
|
|
811
|
+
appKeysSet.add(implementation.key);
|
|
812
|
+
appKeys.push(implementation.key);
|
|
813
|
+
}
|
|
927
814
|
}
|
|
928
815
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
async fetchJson(method, path, data, options = {}) {
|
|
933
|
-
const headers = { ...options.headers };
|
|
934
|
-
if (data && typeof data === "object") {
|
|
935
|
-
headers["Content-Type"] = "application/json";
|
|
816
|
+
const searchParams = {};
|
|
817
|
+
if (opts.pageSize) {
|
|
818
|
+
searchParams.limit = opts.pageSize.toString();
|
|
936
819
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
method,
|
|
941
|
-
body: data != null ? JSON.stringify(data) : void 0,
|
|
942
|
-
headers
|
|
943
|
-
});
|
|
944
|
-
const result = await this.handleResponse({
|
|
945
|
-
response,
|
|
946
|
-
customErrorHandler: options.customErrorHandler,
|
|
947
|
-
wasMissingAuthToken
|
|
948
|
-
});
|
|
949
|
-
if (typeof result === "string") {
|
|
950
|
-
throw new ZapierValidationError(
|
|
951
|
-
`Response could not be parsed as JSON: ${result}`
|
|
952
|
-
);
|
|
953
|
-
}
|
|
954
|
-
return result;
|
|
955
|
-
}
|
|
956
|
-
// Plain fetch method for API paths (must start with /)
|
|
957
|
-
async plainFetch(path, fetchOptions) {
|
|
958
|
-
if (!path.startsWith("/")) {
|
|
959
|
-
throw new ZapierValidationError(
|
|
960
|
-
`plainFetch expects a path starting with '/', got: ${path}`
|
|
961
|
-
);
|
|
820
|
+
searchParams.latest_only = "true";
|
|
821
|
+
if (opts.cursor) {
|
|
822
|
+
searchParams.offset = opts.cursor;
|
|
962
823
|
}
|
|
963
|
-
if (
|
|
964
|
-
|
|
824
|
+
if (appKeys.length > 0) {
|
|
825
|
+
const groupedAppKeys = groupAppKeysByType(appKeys);
|
|
826
|
+
if (groupedAppKeys.selectedApi.length > 0) {
|
|
827
|
+
searchParams.selected_apis = groupedAppKeys.selectedApi.join(",");
|
|
828
|
+
}
|
|
829
|
+
if (groupedAppKeys.slug.length > 0) {
|
|
830
|
+
searchParams.slugs = groupedAppKeys.slug.join(",");
|
|
831
|
+
}
|
|
965
832
|
}
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
fetchOptions,
|
|
972
|
-
subdomainConfig
|
|
833
|
+
const implementationsEnvelope = await api.get(
|
|
834
|
+
"/api/v4/implementations-meta/lookup/",
|
|
835
|
+
{
|
|
836
|
+
searchParams
|
|
837
|
+
}
|
|
973
838
|
);
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
});
|
|
982
|
-
return await this.options.fetch(url, {
|
|
983
|
-
...fetchOptions,
|
|
984
|
-
headers: mergedHeaders
|
|
985
|
-
});
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
var createZapierApi = (options) => {
|
|
989
|
-
const {
|
|
990
|
-
baseUrl,
|
|
991
|
-
token,
|
|
992
|
-
getToken,
|
|
993
|
-
debug = false,
|
|
994
|
-
fetch: originalFetch = globalThis.fetch,
|
|
995
|
-
onEvent
|
|
996
|
-
} = options;
|
|
997
|
-
const debugLog = createDebugLogger(debug);
|
|
998
|
-
const debugFetch = createDebugFetch({ originalFetch, debugLog });
|
|
999
|
-
return new ZapierApiClient({
|
|
1000
|
-
baseUrl,
|
|
1001
|
-
token,
|
|
1002
|
-
getToken,
|
|
1003
|
-
debug,
|
|
1004
|
-
fetch: debugFetch,
|
|
1005
|
-
onEvent
|
|
1006
|
-
});
|
|
1007
|
-
};
|
|
1008
|
-
|
|
1009
|
-
// src/plugins/api/index.ts
|
|
1010
|
-
var apiPlugin = (params) => {
|
|
1011
|
-
const {
|
|
1012
|
-
fetch: customFetch = globalThis.fetch,
|
|
1013
|
-
baseUrl = "https://zapier.com",
|
|
1014
|
-
token,
|
|
1015
|
-
getToken,
|
|
1016
|
-
onEvent,
|
|
1017
|
-
debug = false
|
|
1018
|
-
} = params.context.options;
|
|
1019
|
-
const api = createZapierApi({
|
|
1020
|
-
baseUrl,
|
|
1021
|
-
token,
|
|
1022
|
-
getToken,
|
|
1023
|
-
debug,
|
|
1024
|
-
fetch: customFetch,
|
|
1025
|
-
onEvent
|
|
1026
|
-
});
|
|
839
|
+
return {
|
|
840
|
+
data: implementationsEnvelope.results.map(
|
|
841
|
+
normalizeImplementationMetaToAppItem
|
|
842
|
+
),
|
|
843
|
+
nextCursor: extractCursor(implementationsEnvelope)
|
|
844
|
+
};
|
|
845
|
+
}, ListAppsSchema);
|
|
1027
846
|
return {
|
|
847
|
+
listApps,
|
|
1028
848
|
context: {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
// src/utils/pagination-utils.ts
|
|
1036
|
-
var offsetCursorMarker = "$$offset$$";
|
|
1037
|
-
function splitOffsetCursor(cursor) {
|
|
1038
|
-
if (!cursor) {
|
|
1039
|
-
return [0, cursor];
|
|
1040
|
-
}
|
|
1041
|
-
try {
|
|
1042
|
-
const parsedCursor = JSON.parse(cursor);
|
|
1043
|
-
if (!Array.isArray(parsedCursor)) {
|
|
1044
|
-
return [0, cursor];
|
|
1045
|
-
}
|
|
1046
|
-
const [marker, offset, currentCursor] = parsedCursor;
|
|
1047
|
-
if (marker !== offsetCursorMarker) {
|
|
1048
|
-
return [0, cursor];
|
|
1049
|
-
}
|
|
1050
|
-
if (typeof offset !== "number") {
|
|
1051
|
-
return [0, cursor];
|
|
1052
|
-
}
|
|
1053
|
-
return [offset, currentCursor];
|
|
1054
|
-
} catch {
|
|
1055
|
-
return [0, cursor];
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
function createOffsetCursor(offset, currentCursor) {
|
|
1059
|
-
return JSON.stringify([offsetCursorMarker, offset, currentCursor]);
|
|
1060
|
-
}
|
|
1061
|
-
async function* paginateMaxItems(pageFunction, pageOptions) {
|
|
1062
|
-
let cursor = pageOptions?.cursor;
|
|
1063
|
-
let totalItemsYielded = 0;
|
|
1064
|
-
const maxItems = pageOptions?.maxItems;
|
|
1065
|
-
const pageSize = pageOptions?.pageSize;
|
|
1066
|
-
do {
|
|
1067
|
-
const options = {
|
|
1068
|
-
...pageOptions || {},
|
|
1069
|
-
cursor,
|
|
1070
|
-
pageSize: maxItems !== void 0 && pageSize !== void 0 ? Math.min(pageSize, maxItems) : pageSize
|
|
1071
|
-
};
|
|
1072
|
-
const page = await pageFunction(options);
|
|
1073
|
-
if (maxItems !== void 0) {
|
|
1074
|
-
const remainingItems = maxItems - totalItemsYielded;
|
|
1075
|
-
if (page.data.length >= remainingItems) {
|
|
1076
|
-
const yieldedPage = {
|
|
1077
|
-
...page,
|
|
1078
|
-
data: page.data.slice(0, remainingItems),
|
|
1079
|
-
nextCursor: void 0
|
|
1080
|
-
};
|
|
1081
|
-
yield yieldedPage;
|
|
1082
|
-
break;
|
|
849
|
+
meta: {
|
|
850
|
+
listApps: {
|
|
851
|
+
categories: ["app"],
|
|
852
|
+
inputSchema: ListAppsSchema
|
|
853
|
+
}
|
|
1083
854
|
}
|
|
1084
855
|
}
|
|
1085
|
-
yield page;
|
|
1086
|
-
totalItemsYielded += page.data.length;
|
|
1087
|
-
cursor = page.nextCursor;
|
|
1088
|
-
} while (cursor);
|
|
1089
|
-
}
|
|
1090
|
-
async function* paginateBuffered(pageFunction, pageOptions) {
|
|
1091
|
-
const pageSize = pageOptions?.pageSize;
|
|
1092
|
-
const [cursorOffset, currentCursor] = splitOffsetCursor(pageOptions?.cursor);
|
|
1093
|
-
const options = {
|
|
1094
|
-
...pageOptions || {},
|
|
1095
|
-
cursor: currentCursor
|
|
1096
856
|
};
|
|
1097
|
-
const iterator = paginateMaxItems(pageFunction, options);
|
|
1098
|
-
let bufferedPages = [];
|
|
1099
|
-
let isFirstPage = true;
|
|
1100
|
-
let cursor;
|
|
1101
|
-
for await (let page of iterator) {
|
|
1102
|
-
if (isFirstPage) {
|
|
1103
|
-
isFirstPage = false;
|
|
1104
|
-
if (cursorOffset) {
|
|
1105
|
-
page = {
|
|
1106
|
-
...page,
|
|
1107
|
-
data: page.data.slice(cursorOffset)
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
if (!pageSize) {
|
|
1112
|
-
yield page;
|
|
1113
|
-
cursor = page.nextCursor;
|
|
1114
|
-
continue;
|
|
1115
|
-
}
|
|
1116
|
-
const bufferedLength = bufferedPages.reduce(
|
|
1117
|
-
(acc, page2) => acc + page2.data.length,
|
|
1118
|
-
0
|
|
1119
|
-
);
|
|
1120
|
-
if (bufferedLength + page.data.length < pageSize) {
|
|
1121
|
-
bufferedPages.push(page);
|
|
1122
|
-
cursor = page.nextCursor;
|
|
1123
|
-
continue;
|
|
1124
|
-
}
|
|
1125
|
-
const bufferedItems = bufferedPages.map((p) => p.data).flat();
|
|
1126
|
-
const allItems = [...bufferedItems, ...page.data];
|
|
1127
|
-
const pageItems = allItems.slice(0, pageSize);
|
|
1128
|
-
const remainingItems = allItems.slice(pageItems.length);
|
|
1129
|
-
if (remainingItems.length === 0) {
|
|
1130
|
-
yield {
|
|
1131
|
-
...page,
|
|
1132
|
-
data: pageItems,
|
|
1133
|
-
nextCursor: page.nextCursor
|
|
1134
|
-
};
|
|
1135
|
-
bufferedPages = [];
|
|
1136
|
-
cursor = page.nextCursor;
|
|
1137
|
-
continue;
|
|
1138
|
-
}
|
|
1139
|
-
yield {
|
|
1140
|
-
...page,
|
|
1141
|
-
data: pageItems,
|
|
1142
|
-
nextCursor: createOffsetCursor(
|
|
1143
|
-
page.data.length - remainingItems.length,
|
|
1144
|
-
cursor
|
|
1145
|
-
)
|
|
1146
|
-
};
|
|
1147
|
-
while (remainingItems.length > pageSize) {
|
|
1148
|
-
const pageItems2 = remainingItems.splice(0, pageSize);
|
|
1149
|
-
yield {
|
|
1150
|
-
...page,
|
|
1151
|
-
data: pageItems2,
|
|
1152
|
-
nextCursor: createOffsetCursor(
|
|
1153
|
-
page.data.length - remainingItems.length,
|
|
1154
|
-
cursor
|
|
1155
|
-
)
|
|
1156
|
-
};
|
|
1157
|
-
}
|
|
1158
|
-
bufferedPages = [
|
|
1159
|
-
{
|
|
1160
|
-
...page,
|
|
1161
|
-
data: remainingItems
|
|
1162
|
-
}
|
|
1163
|
-
];
|
|
1164
|
-
cursor = page.nextCursor;
|
|
1165
|
-
}
|
|
1166
|
-
if (bufferedPages.length > 0) {
|
|
1167
|
-
const lastBufferedPage = bufferedPages.slice(-1)[0];
|
|
1168
|
-
const bufferedItems = bufferedPages.map((p) => p.data).flat();
|
|
1169
|
-
yield {
|
|
1170
|
-
...lastBufferedPage,
|
|
1171
|
-
data: bufferedItems
|
|
1172
|
-
};
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
var paginate = paginateBuffered;
|
|
1176
|
-
|
|
1177
|
-
// src/utils/validation.ts
|
|
1178
|
-
var validate = (schema, input) => {
|
|
1179
|
-
const result = schema.safeParse(input);
|
|
1180
|
-
if (!result.success) {
|
|
1181
|
-
const errorMessages = result.error.errors.map((error) => {
|
|
1182
|
-
const path = error.path.length > 0 ? error.path.join(".") : "input";
|
|
1183
|
-
return `${path}: ${error.message}`;
|
|
1184
|
-
});
|
|
1185
|
-
const message = `Validation failed:
|
|
1186
|
-
${errorMessages.join("\n ")}`;
|
|
1187
|
-
throw new ZapierValidationError(message, {
|
|
1188
|
-
details: {
|
|
1189
|
-
zodErrors: result.error.errors,
|
|
1190
|
-
input
|
|
1191
|
-
}
|
|
1192
|
-
});
|
|
1193
|
-
}
|
|
1194
|
-
return result.data;
|
|
1195
|
-
};
|
|
1196
|
-
function createValidator(schema) {
|
|
1197
|
-
return function validateFn(input) {
|
|
1198
|
-
return validate(schema, input);
|
|
1199
|
-
};
|
|
1200
|
-
}
|
|
1201
|
-
var validateOptions = (schema, options) => {
|
|
1202
|
-
return validate(schema, options);
|
|
1203
857
|
};
|
|
1204
|
-
|
|
1205
|
-
// src/utils/function-utils.ts
|
|
1206
|
-
function extractCursor(data) {
|
|
1207
|
-
if (!data?.next) {
|
|
1208
|
-
return void 0;
|
|
1209
|
-
}
|
|
1210
|
-
try {
|
|
1211
|
-
const urlObj = new URL(data.next);
|
|
1212
|
-
const offset = urlObj.searchParams.get("offset");
|
|
1213
|
-
return offset || void 0;
|
|
1214
|
-
} catch {
|
|
1215
|
-
return void 0;
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
function normalizeError(error) {
|
|
1219
|
-
if (error instanceof ZapierError) {
|
|
1220
|
-
return error;
|
|
1221
|
-
}
|
|
1222
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1223
|
-
return new ZapierUnknownError(`Unknown error: ${message}`, { cause: error });
|
|
1224
|
-
}
|
|
1225
|
-
function createFunction(coreFn, schema) {
|
|
1226
|
-
const functionName = coreFn.name;
|
|
1227
|
-
const namedFunctions = {
|
|
1228
|
-
[functionName]: async function(options) {
|
|
1229
|
-
try {
|
|
1230
|
-
const normalizedOptions = options ?? {};
|
|
1231
|
-
if (schema) {
|
|
1232
|
-
const validatedOptions = validateOptions(schema, normalizedOptions);
|
|
1233
|
-
return await coreFn({
|
|
1234
|
-
...normalizedOptions,
|
|
1235
|
-
...validatedOptions
|
|
1236
|
-
});
|
|
1237
|
-
}
|
|
1238
|
-
return await coreFn(normalizedOptions);
|
|
1239
|
-
} catch (error) {
|
|
1240
|
-
throw normalizeError(error);
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
};
|
|
1244
|
-
return namedFunctions[functionName];
|
|
1245
|
-
}
|
|
1246
|
-
function createPageFunction(coreFn) {
|
|
1247
|
-
const functionName = coreFn.name + "Page";
|
|
1248
|
-
const namedFunctions = {
|
|
1249
|
-
[functionName]: async function(options) {
|
|
1250
|
-
try {
|
|
1251
|
-
const result = await coreFn(options);
|
|
1252
|
-
if (result && typeof result === "object" && "data" in result) {
|
|
1253
|
-
const data = result.data;
|
|
1254
|
-
return {
|
|
1255
|
-
data: Array.isArray(data) ? data : [data],
|
|
1256
|
-
nextCursor: result.nextCursor
|
|
1257
|
-
};
|
|
1258
|
-
}
|
|
1259
|
-
if (Array.isArray(result)) {
|
|
1260
|
-
return { data: result };
|
|
1261
|
-
}
|
|
1262
|
-
return { data: [result] };
|
|
1263
|
-
} catch (error) {
|
|
1264
|
-
throw normalizeError(error);
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
};
|
|
1268
|
-
return namedFunctions[functionName];
|
|
1269
|
-
}
|
|
1270
|
-
function createPaginatedFunction(coreFn, schema) {
|
|
1271
|
-
const pageFunction = createPageFunction(coreFn);
|
|
1272
|
-
const functionName = coreFn.name;
|
|
1273
|
-
const validator = schema ? createValidator(schema) : null;
|
|
1274
|
-
const namedFunctions = {
|
|
1275
|
-
[functionName]: function(options) {
|
|
1276
|
-
const normalizedOptions = options ?? {};
|
|
1277
|
-
const validatedOptions = {
|
|
1278
|
-
...normalizedOptions,
|
|
1279
|
-
...validator ? validator(normalizedOptions) : normalizedOptions
|
|
1280
|
-
};
|
|
1281
|
-
const pageSize = validatedOptions.pageSize || 100;
|
|
1282
|
-
const optimizedOptions = {
|
|
1283
|
-
...validatedOptions,
|
|
1284
|
-
pageSize
|
|
1285
|
-
};
|
|
1286
|
-
const iterator = paginate(pageFunction, optimizedOptions);
|
|
1287
|
-
const firstPagePromise = iterator.next().then((result) => {
|
|
1288
|
-
if (result.done) {
|
|
1289
|
-
throw new Error("Paginate should always iterate at least once");
|
|
1290
|
-
}
|
|
1291
|
-
return result.value;
|
|
1292
|
-
});
|
|
1293
|
-
return Object.assign(firstPagePromise, {
|
|
1294
|
-
[Symbol.asyncIterator]: async function* () {
|
|
1295
|
-
yield await firstPagePromise;
|
|
1296
|
-
for await (const page of iterator) {
|
|
1297
|
-
yield page;
|
|
1298
|
-
}
|
|
1299
|
-
},
|
|
1300
|
-
items: function() {
|
|
1301
|
-
return {
|
|
1302
|
-
[Symbol.asyncIterator]: async function* () {
|
|
1303
|
-
const firstPage = await firstPagePromise;
|
|
1304
|
-
for (const item of firstPage.data) {
|
|
1305
|
-
yield item;
|
|
1306
|
-
}
|
|
1307
|
-
for await (const page of iterator) {
|
|
1308
|
-
for (const item of page.data) {
|
|
1309
|
-
yield item;
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
};
|
|
1314
|
-
}
|
|
1315
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
};
|
|
1318
|
-
return namedFunctions[functionName];
|
|
1319
|
-
}
|
|
1320
858
|
var NeedChoicesSchema = zod.z.object({
|
|
1321
859
|
key: zod.z.string().optional(),
|
|
1322
860
|
label: zod.z.string().optional(),
|
|
@@ -1651,1223 +1189,1849 @@ zod.z.object({
|
|
|
1651
1189
|
previous: zod.z.string().nullable().optional(),
|
|
1652
1190
|
results: zod.z.array(ImplementationMetaSchema)
|
|
1653
1191
|
});
|
|
1192
|
+
var NeedChoicesResponseMetaSchema = zod.z.object({
|
|
1193
|
+
page: zod.z.string().nullable().optional()
|
|
1194
|
+
});
|
|
1195
|
+
var NeedChoicesResponseLinksSchema = zod.z.object({
|
|
1196
|
+
next: zod.z.string().nullable().optional(),
|
|
1197
|
+
prev: zod.z.string().nullable().optional()
|
|
1198
|
+
});
|
|
1199
|
+
zod.z.object({
|
|
1200
|
+
selected_api: zod.z.string().optional().describe(
|
|
1201
|
+
"Something like `SlackAPI` (for Python apps) or `SplitwiseCLIAPI@1.0.0` (for CLI apps). Non-public apps are fine as long as the authed user can access them."
|
|
1202
|
+
),
|
|
1203
|
+
authentication_id: zod.z.number().optional().describe(
|
|
1204
|
+
"If the app needs auth, provide an `authentication_id` that has the `selected_api` of the app you want to run. Can be any auth visible to the user (including shared)."
|
|
1205
|
+
),
|
|
1206
|
+
params: zod.z.record(zod.z.any()).optional().describe(
|
|
1207
|
+
"Object that matches the input the node would normally get. Has all the same keys/types as the `needs` of the action."
|
|
1208
|
+
),
|
|
1209
|
+
page: zod.z.number().optional().default(0),
|
|
1210
|
+
prefill: zod.z.string().optional().describe(
|
|
1211
|
+
"The prefill string to indicate what we're fetching choices for. Likely something like `spreadsheet.id.title`. Must be provided alongside `selected_api` if both `action_id` and `input_field_id` are not."
|
|
1212
|
+
),
|
|
1213
|
+
action_id: zod.z.string().optional().describe(
|
|
1214
|
+
"The id that will be used to lookup the Action for prefill lookup. If provided, `input_field_id` is required, else `prefill` must be provided."
|
|
1215
|
+
),
|
|
1216
|
+
input_field_id: zod.z.string().optional().describe(
|
|
1217
|
+
"The id (key) of the input field (Need) that dynamic choices are being retrieved for. If provided, `action_id` is required, else `prefill` must be provided."
|
|
1218
|
+
)
|
|
1219
|
+
});
|
|
1220
|
+
zod.z.object({
|
|
1221
|
+
success: zod.z.boolean(),
|
|
1222
|
+
choices: zod.z.array(NeedChoicesSchema).optional(),
|
|
1223
|
+
next_page: zod.z.number().optional(),
|
|
1224
|
+
errors: zod.z.array(zod.z.string()).optional(),
|
|
1225
|
+
meta: NeedChoicesResponseMetaSchema.optional(),
|
|
1226
|
+
links: NeedChoicesResponseLinksSchema.optional()
|
|
1227
|
+
});
|
|
1654
1228
|
|
|
1655
|
-
// src/schemas/
|
|
1656
|
-
var
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
//
|
|
1229
|
+
// src/schemas/Action.ts
|
|
1230
|
+
var ActionItemSchema = withFormatter(
|
|
1231
|
+
ActionSchema.omit({ type: true, name: true }).extend({
|
|
1232
|
+
app_key: zod.z.string(),
|
|
1233
|
+
// Mapped from selected_api
|
|
1234
|
+
action_type: ActionSchema.shape.type,
|
|
1235
|
+
// Mapped from original 'type' field
|
|
1236
|
+
title: zod.z.string(),
|
|
1237
|
+
// Mapped from original 'name' field
|
|
1238
|
+
type: zod.z.literal("action")
|
|
1239
|
+
// Fixed type identifier
|
|
1660
1240
|
}),
|
|
1661
1241
|
{
|
|
1662
1242
|
format: (item) => {
|
|
1663
1243
|
const details = [];
|
|
1664
|
-
details.push({
|
|
1665
|
-
|
|
1244
|
+
details.push({
|
|
1245
|
+
text: `Type: ${item.action_type}`,
|
|
1246
|
+
style: "accent"
|
|
1247
|
+
});
|
|
1248
|
+
if (item.app_key) {
|
|
1666
1249
|
details.push({
|
|
1667
|
-
text: `
|
|
1668
|
-
style: "
|
|
1250
|
+
text: `App: ${item.app_key}`,
|
|
1251
|
+
style: "normal"
|
|
1669
1252
|
});
|
|
1670
1253
|
}
|
|
1671
|
-
|
|
1672
|
-
text:
|
|
1673
|
-
|
|
1254
|
+
if (item.description) {
|
|
1255
|
+
details.push({ text: item.description, style: "dim" });
|
|
1256
|
+
}
|
|
1257
|
+
return {
|
|
1258
|
+
title: item.title || item.name || item.key,
|
|
1259
|
+
subtitle: `(${item.key})`,
|
|
1260
|
+
details
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
);
|
|
1265
|
+
|
|
1266
|
+
// src/plugins/listActions/schemas.ts
|
|
1267
|
+
var ListActionsSchema = withOutputSchema(
|
|
1268
|
+
zod.z.object({
|
|
1269
|
+
appKey: AppKeyPropertySchema.describe(
|
|
1270
|
+
"App key of actions to list (e.g., 'SlackCLIAPI')"
|
|
1271
|
+
),
|
|
1272
|
+
actionType: ActionTypePropertySchema.optional().describe(
|
|
1273
|
+
"Filter actions by type"
|
|
1274
|
+
),
|
|
1275
|
+
pageSize: zod.z.number().min(1).optional().describe("Number of actions per page"),
|
|
1276
|
+
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
1277
|
+
}).describe("List all actions for a specific app"),
|
|
1278
|
+
ActionItemSchema
|
|
1279
|
+
);
|
|
1280
|
+
|
|
1281
|
+
// src/plugins/listActions/index.ts
|
|
1282
|
+
var listActionsPlugin = ({ context }) => {
|
|
1283
|
+
const listActions = createPaginatedFunction(async function listActionsPage(options) {
|
|
1284
|
+
const { api, getVersionedImplementationId } = context;
|
|
1285
|
+
const selectedApi = await getVersionedImplementationId(options.appKey);
|
|
1286
|
+
if (!selectedApi) {
|
|
1287
|
+
throw new ZapierConfigurationError(
|
|
1288
|
+
"No current_implementation_id found for app",
|
|
1289
|
+
{ configType: "current_implementation_id" }
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
const searchParams = {
|
|
1293
|
+
global: "true",
|
|
1294
|
+
public_only: "true",
|
|
1295
|
+
selected_apis: selectedApi
|
|
1296
|
+
};
|
|
1297
|
+
const data = await api.get(
|
|
1298
|
+
"/api/v4/implementations/",
|
|
1299
|
+
{
|
|
1300
|
+
searchParams,
|
|
1301
|
+
customErrorHandler: ({ status }) => {
|
|
1302
|
+
if (status === 401) {
|
|
1303
|
+
return new ZapierAuthenticationError(
|
|
1304
|
+
`Authentication failed. Your token may not have permission to access implementations or may be expired. (HTTP ${status})`,
|
|
1305
|
+
{ statusCode: status }
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
if (status === 403) {
|
|
1309
|
+
return new ZapierAuthenticationError(
|
|
1310
|
+
`Access forbidden. Your token may not have the required scopes to list implementations. (HTTP ${status})`,
|
|
1311
|
+
{ statusCode: status }
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
return void 0;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
);
|
|
1318
|
+
let allActions = [];
|
|
1319
|
+
for (const implementation of data.results || []) {
|
|
1320
|
+
if (implementation.actions) {
|
|
1321
|
+
for (const action of implementation.actions) {
|
|
1322
|
+
const actionWithContext = {
|
|
1323
|
+
...action,
|
|
1324
|
+
selected_api: action.selected_api || implementation.selected_api
|
|
1325
|
+
};
|
|
1326
|
+
allActions.push(normalizeActionItem(actionWithContext));
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
if (options.actionType) {
|
|
1331
|
+
allActions = allActions.filter(
|
|
1332
|
+
(action) => action.action_type === options.actionType
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
return {
|
|
1336
|
+
data: allActions,
|
|
1337
|
+
nextCursor: void 0
|
|
1338
|
+
};
|
|
1339
|
+
}, ListActionsSchema);
|
|
1340
|
+
return {
|
|
1341
|
+
listActions,
|
|
1342
|
+
context: {
|
|
1343
|
+
meta: {
|
|
1344
|
+
listActions: {
|
|
1345
|
+
categories: ["action"],
|
|
1346
|
+
inputSchema: ListActionsSchema
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
};
|
|
1352
|
+
var ListInputFieldsSchema = zod.z.object({
|
|
1353
|
+
appKey: AppKeyPropertySchema,
|
|
1354
|
+
actionType: ActionTypePropertySchema,
|
|
1355
|
+
actionKey: ActionKeyPropertySchema,
|
|
1356
|
+
authenticationId: AuthenticationIdPropertySchema.nullable().optional(),
|
|
1357
|
+
inputs: InputsPropertySchema.optional().describe(
|
|
1358
|
+
"Current input values that may affect available fields"
|
|
1359
|
+
),
|
|
1360
|
+
pageSize: zod.z.number().min(1).optional().describe("Number of input fields per page"),
|
|
1361
|
+
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
1362
|
+
}).describe("Get the input fields required for a specific action");
|
|
1363
|
+
|
|
1364
|
+
// src/plugins/listInputFields/index.ts
|
|
1365
|
+
function getInputFieldTypeFromNeed(need) {
|
|
1366
|
+
if (need.list) {
|
|
1367
|
+
return "ARRAY" /* ARRAY */;
|
|
1368
|
+
}
|
|
1369
|
+
const typeMap = {
|
|
1370
|
+
string: "STRING" /* STRING */,
|
|
1371
|
+
decimal: "NUMBER" /* NUMBER */,
|
|
1372
|
+
integer: "INTEGER" /* INTEGER */,
|
|
1373
|
+
boolean: "BOOLEAN" /* BOOLEAN */,
|
|
1374
|
+
dict: "OBJECT" /* OBJECT */
|
|
1375
|
+
};
|
|
1376
|
+
return typeMap[need.type || ""] || "STRING" /* STRING */;
|
|
1377
|
+
}
|
|
1378
|
+
function getInputFieldFormatFromNeed(need) {
|
|
1379
|
+
if (need.prefill || need.choices) {
|
|
1380
|
+
return "SELECT" /* SELECT */;
|
|
1381
|
+
}
|
|
1382
|
+
const formatMap = {
|
|
1383
|
+
text: "MULTILINE" /* MULTILINE */,
|
|
1384
|
+
datetime: "DATETIME" /* DATETIME */,
|
|
1385
|
+
file: "FILE" /* FILE */,
|
|
1386
|
+
password: "PASSWORD" /* PASSWORD */,
|
|
1387
|
+
code: "CODE" /* CODE */
|
|
1388
|
+
};
|
|
1389
|
+
return formatMap[need.type || ""];
|
|
1390
|
+
}
|
|
1391
|
+
function getItemsTypeFromNeed(need) {
|
|
1392
|
+
if (!need.list) {
|
|
1393
|
+
return void 0;
|
|
1394
|
+
}
|
|
1395
|
+
const typeMap = {
|
|
1396
|
+
string: "STRING" /* STRING */,
|
|
1397
|
+
decimal: "NUMBER" /* NUMBER */,
|
|
1398
|
+
integer: "INTEGER" /* INTEGER */,
|
|
1399
|
+
boolean: "BOOLEAN" /* BOOLEAN */,
|
|
1400
|
+
dict: "OBJECT" /* OBJECT */
|
|
1401
|
+
};
|
|
1402
|
+
return typeMap[need.type || ""] || "STRING" /* STRING */;
|
|
1403
|
+
}
|
|
1404
|
+
function transformNeedToInputFieldItem(need) {
|
|
1405
|
+
const itemsType = getItemsTypeFromNeed(need);
|
|
1406
|
+
return {
|
|
1407
|
+
...need,
|
|
1408
|
+
// Pass through all original Need fields
|
|
1409
|
+
id: need.key,
|
|
1410
|
+
default_value: need.default || "",
|
|
1411
|
+
depends_on: need.depends_on || [],
|
|
1412
|
+
description: need.help_text || "",
|
|
1413
|
+
invalidates_input_fields: need.alters_custom_fields || false,
|
|
1414
|
+
is_required: need.required || false,
|
|
1415
|
+
placeholder: need.placeholder || "",
|
|
1416
|
+
title: need.label || "",
|
|
1417
|
+
value_type: getInputFieldTypeFromNeed(need),
|
|
1418
|
+
format: getInputFieldFormatFromNeed(need),
|
|
1419
|
+
items: itemsType ? { type: itemsType } : void 0
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
var listInputFieldsPlugin = ({ context }) => {
|
|
1423
|
+
const listInputFields = createPaginatedFunction(
|
|
1424
|
+
async function listInputFieldsPage(options) {
|
|
1425
|
+
const { api, getVersionedImplementationId } = context;
|
|
1426
|
+
const { appKey, actionKey, actionType, authenticationId, inputs } = options;
|
|
1427
|
+
const selectedApi = await getVersionedImplementationId(appKey);
|
|
1428
|
+
if (!selectedApi) {
|
|
1429
|
+
throw new ZapierConfigurationError(
|
|
1430
|
+
"No current_implementation_id found for app",
|
|
1431
|
+
{ configType: "current_implementation_id" }
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
const needsRequest = {
|
|
1435
|
+
selected_api: selectedApi,
|
|
1436
|
+
action: actionKey,
|
|
1437
|
+
type_of: actionType,
|
|
1438
|
+
params: inputs || {}
|
|
1439
|
+
};
|
|
1440
|
+
if (authenticationId !== null) {
|
|
1441
|
+
needsRequest.authentication_id = authenticationId;
|
|
1442
|
+
}
|
|
1443
|
+
const needsData = await api.post(
|
|
1444
|
+
"/api/v4/implementations/needs/",
|
|
1445
|
+
needsRequest
|
|
1446
|
+
);
|
|
1447
|
+
if (!needsData.success) {
|
|
1448
|
+
throw new ZapierApiError(
|
|
1449
|
+
`Failed to get action fields: ${needsData.errors?.join(", ") || "Unknown error"}`
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
const inputFields = (needsData.needs || []).map(
|
|
1453
|
+
transformNeedToInputFieldItem
|
|
1454
|
+
);
|
|
1455
|
+
return {
|
|
1456
|
+
data: inputFields,
|
|
1457
|
+
nextCursor: void 0
|
|
1458
|
+
// No pagination needed since we return all input fields
|
|
1459
|
+
};
|
|
1460
|
+
},
|
|
1461
|
+
ListInputFieldsSchema
|
|
1462
|
+
);
|
|
1463
|
+
return {
|
|
1464
|
+
listInputFields,
|
|
1465
|
+
context: {
|
|
1466
|
+
meta: {
|
|
1467
|
+
listInputFields: {
|
|
1468
|
+
categories: ["action"],
|
|
1469
|
+
inputSchema: ListInputFieldsSchema
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
};
|
|
1475
|
+
var ListAuthenticationsSchema = zod.z.object({
|
|
1476
|
+
appKey: AppKeyPropertySchema.optional().describe(
|
|
1477
|
+
"App key of authentications to list (e.g., 'SlackCLIAPI')"
|
|
1478
|
+
),
|
|
1479
|
+
search: zod.z.string().optional().describe("Search term to filter authentications by title"),
|
|
1480
|
+
title: zod.z.string().optional().describe("Filter authentications by exact title match"),
|
|
1481
|
+
accountId: zod.z.string().optional().describe("Filter by account ID"),
|
|
1482
|
+
owner: zod.z.string().optional().describe("Filter by owner"),
|
|
1483
|
+
pageSize: zod.z.number().min(1).optional().describe("Number of authentications per page"),
|
|
1484
|
+
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
1485
|
+
}).describe("List available authentications with optional filtering");
|
|
1486
|
+
|
|
1487
|
+
// src/plugins/listAuthentications/index.ts
|
|
1488
|
+
var listAuthenticationsPlugin = ({ context }) => {
|
|
1489
|
+
const listAuthentications = createPaginatedFunction(
|
|
1490
|
+
async function listAuthenticationsPage(options) {
|
|
1491
|
+
const { api, getVersionedImplementationId } = context;
|
|
1492
|
+
const searchParams = {};
|
|
1493
|
+
if (options.appKey) {
|
|
1494
|
+
const implementationId = await getVersionedImplementationId(
|
|
1495
|
+
options.appKey
|
|
1496
|
+
);
|
|
1497
|
+
if (implementationId) {
|
|
1498
|
+
const [versionlessSelectedApi] = splitVersionedKey(implementationId);
|
|
1499
|
+
searchParams.versionless_selected_api = versionlessSelectedApi;
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
if (options.search) {
|
|
1503
|
+
searchParams.search = options.search;
|
|
1504
|
+
} else if (options.title) {
|
|
1505
|
+
searchParams.search = options.title;
|
|
1506
|
+
}
|
|
1507
|
+
if (options.accountId) {
|
|
1508
|
+
searchParams.account_id = options.accountId;
|
|
1509
|
+
}
|
|
1510
|
+
if (options.owner) {
|
|
1511
|
+
searchParams.owner = options.owner;
|
|
1512
|
+
}
|
|
1513
|
+
searchParams.limit = options.pageSize.toString();
|
|
1514
|
+
if (options.cursor) {
|
|
1515
|
+
searchParams.offset = options.cursor;
|
|
1516
|
+
}
|
|
1517
|
+
const data = await api.get(
|
|
1518
|
+
"/api/v4/authentications/",
|
|
1519
|
+
{
|
|
1520
|
+
searchParams,
|
|
1521
|
+
customErrorHandler: ({ status }) => {
|
|
1522
|
+
if (status === 401) {
|
|
1523
|
+
return new ZapierAuthenticationError(
|
|
1524
|
+
`Authentication failed. Your token may not have permission to access authentications or may be expired. (HTTP ${status})`,
|
|
1525
|
+
{ statusCode: status }
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
if (status === 403) {
|
|
1529
|
+
return new ZapierAuthenticationError(
|
|
1530
|
+
`Access forbidden. Your token may not have the required scopes to list authentications. (HTTP ${status})`,
|
|
1531
|
+
{ statusCode: status }
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
return void 0;
|
|
1535
|
+
},
|
|
1536
|
+
authRequired: true
|
|
1537
|
+
}
|
|
1538
|
+
);
|
|
1539
|
+
let auths = (data.results || []).map(
|
|
1540
|
+
(auth) => normalizeAuthenticationItem(auth)
|
|
1541
|
+
);
|
|
1542
|
+
if (options.title) {
|
|
1543
|
+
auths = auths.filter((auth) => auth.title === options.title);
|
|
1544
|
+
}
|
|
1545
|
+
return {
|
|
1546
|
+
data: auths,
|
|
1547
|
+
nextCursor: extractCursor(data)
|
|
1548
|
+
};
|
|
1549
|
+
},
|
|
1550
|
+
ListAuthenticationsSchema
|
|
1551
|
+
);
|
|
1552
|
+
return {
|
|
1553
|
+
listAuthentications,
|
|
1554
|
+
context: {
|
|
1555
|
+
meta: {
|
|
1556
|
+
listAuthentications: {
|
|
1557
|
+
categories: ["authentication"],
|
|
1558
|
+
inputSchema: ListAuthenticationsSchema
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
};
|
|
1564
|
+
var GetAppSchema = withOutputSchema(
|
|
1565
|
+
zod.z.object({
|
|
1566
|
+
appKey: AppKeyPropertySchema.describe(
|
|
1567
|
+
"App key of app to fetch (e.g., 'SlackCLIAPI')"
|
|
1568
|
+
)
|
|
1569
|
+
}).describe("Get detailed information about a specific app"),
|
|
1570
|
+
AppItemSchema
|
|
1571
|
+
);
|
|
1572
|
+
|
|
1573
|
+
// src/plugins/getApp/index.ts
|
|
1574
|
+
var getAppPlugin = ({ context }) => {
|
|
1575
|
+
const getApp = createFunction(async function getApp2(options) {
|
|
1576
|
+
const app = await context.getImplementation(options.appKey);
|
|
1577
|
+
if (!app) {
|
|
1578
|
+
throw new ZapierAppNotFoundError("App not found", {
|
|
1579
|
+
appKey: options.appKey
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
return {
|
|
1583
|
+
data: app
|
|
1584
|
+
};
|
|
1585
|
+
}, GetAppSchema);
|
|
1586
|
+
return {
|
|
1587
|
+
getApp,
|
|
1588
|
+
context: {
|
|
1589
|
+
meta: {
|
|
1590
|
+
getApp: {
|
|
1591
|
+
categories: ["app"],
|
|
1592
|
+
inputSchema: GetAppSchema
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
};
|
|
1598
|
+
var GetActionSchema = zod.z.object({
|
|
1599
|
+
appKey: AppKeyPropertySchema,
|
|
1600
|
+
actionType: ActionTypePropertySchema,
|
|
1601
|
+
actionKey: ActionKeyPropertySchema
|
|
1602
|
+
}).describe("Get detailed information about a specific action");
|
|
1603
|
+
|
|
1604
|
+
// src/plugins/getAction/index.ts
|
|
1605
|
+
var getActionPlugin = ({ sdk }) => {
|
|
1606
|
+
const getAction = createFunction(async function getAction2(options) {
|
|
1607
|
+
const { actionKey, actionType, appKey } = options;
|
|
1608
|
+
const actionsResult = await sdk.listActions({ appKey });
|
|
1609
|
+
for (const action of actionsResult.data) {
|
|
1610
|
+
if (action.key === actionKey && action.action_type === actionType) {
|
|
1611
|
+
return { data: action };
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
throw new ZapierResourceNotFoundError(
|
|
1615
|
+
`Action not found: ${actionKey} with type ${actionType}`,
|
|
1616
|
+
{ resourceType: "Action", resourceId: `${actionKey} (${actionType})` }
|
|
1617
|
+
);
|
|
1618
|
+
}, GetActionSchema);
|
|
1619
|
+
return {
|
|
1620
|
+
getAction,
|
|
1621
|
+
context: {
|
|
1622
|
+
meta: {
|
|
1623
|
+
getAction: {
|
|
1624
|
+
categories: ["action"],
|
|
1625
|
+
inputSchema: GetActionSchema
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
};
|
|
1630
|
+
};
|
|
1631
|
+
var GetAuthenticationSchema = zod.z.object({
|
|
1632
|
+
authenticationId: zod.z.number().int().positive().describe("Authentication ID to retrieve")
|
|
1633
|
+
}).describe("Get a specific authentication by ID");
|
|
1634
|
+
|
|
1635
|
+
// src/plugins/getAuthentication/index.ts
|
|
1636
|
+
var getAuthenticationPlugin = ({ context }) => {
|
|
1637
|
+
const getAuthentication = createFunction(async function getAuthentication2(options) {
|
|
1638
|
+
const { api } = context;
|
|
1639
|
+
const { authenticationId } = options;
|
|
1640
|
+
const data = await api.get(
|
|
1641
|
+
`/api/v4/authentications/${authenticationId}/`,
|
|
1642
|
+
{
|
|
1643
|
+
customErrorHandler: ({ status }) => {
|
|
1644
|
+
if (status === 401) {
|
|
1645
|
+
return new ZapierAuthenticationError(
|
|
1646
|
+
`Authentication failed. Your token may not have permission to access authentications or may be expired. (HTTP ${status})`,
|
|
1647
|
+
{ statusCode: status }
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
if (status === 403) {
|
|
1651
|
+
return new ZapierAuthenticationError(
|
|
1652
|
+
`Access forbidden. Your token may not have the required scopes to get authentication ${authenticationId}. (HTTP ${status})`,
|
|
1653
|
+
{ statusCode: status }
|
|
1654
|
+
);
|
|
1655
|
+
}
|
|
1656
|
+
if (status === 404) {
|
|
1657
|
+
return new ZapierResourceNotFoundError(
|
|
1658
|
+
`Authentication ${authenticationId} not found. It may not exist or you may not have access to it. (HTTP ${status})`,
|
|
1659
|
+
{
|
|
1660
|
+
resourceType: "Authentication",
|
|
1661
|
+
resourceId: String(authenticationId)
|
|
1662
|
+
}
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
return void 0;
|
|
1666
|
+
},
|
|
1667
|
+
authRequired: true
|
|
1668
|
+
}
|
|
1669
|
+
);
|
|
1670
|
+
return {
|
|
1671
|
+
data: normalizeAuthenticationItem(data)
|
|
1672
|
+
};
|
|
1673
|
+
}, GetAuthenticationSchema);
|
|
1674
|
+
return {
|
|
1675
|
+
getAuthentication,
|
|
1676
|
+
context: {
|
|
1677
|
+
meta: {
|
|
1678
|
+
getAuthentication: {
|
|
1679
|
+
categories: ["authentication"],
|
|
1680
|
+
inputSchema: GetAuthenticationSchema
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
};
|
|
1686
|
+
var FindFirstAuthenticationSchema = zod.z.object({
|
|
1687
|
+
appKey: AppKeyPropertySchema.optional().describe(
|
|
1688
|
+
"App key of authentication to find (e.g., 'SlackCLIAPI')"
|
|
1689
|
+
),
|
|
1690
|
+
search: zod.z.string().optional().describe("Search term to filter authentications by title"),
|
|
1691
|
+
title: zod.z.string().optional().describe("Filter authentications by exact title match"),
|
|
1692
|
+
accountId: zod.z.string().optional().describe("Filter by account ID"),
|
|
1693
|
+
owner: zod.z.string().optional().describe("Filter by owner")
|
|
1694
|
+
}).describe("Find the first authentication matching the criteria");
|
|
1695
|
+
|
|
1696
|
+
// src/plugins/findFirstAuthentication/index.ts
|
|
1697
|
+
var findFirstAuthenticationPlugin = ({ sdk }) => {
|
|
1698
|
+
const findFirstAuthentication = createFunction(
|
|
1699
|
+
async function findFirstAuthentication2(options = {}) {
|
|
1700
|
+
const authsResponse = await sdk.listAuthentications({
|
|
1701
|
+
...options,
|
|
1702
|
+
maxItems: 1
|
|
1703
|
+
});
|
|
1704
|
+
return {
|
|
1705
|
+
data: authsResponse.data.length > 0 ? authsResponse.data[0] : null
|
|
1706
|
+
};
|
|
1707
|
+
},
|
|
1708
|
+
FindFirstAuthenticationSchema
|
|
1709
|
+
);
|
|
1710
|
+
return {
|
|
1711
|
+
findFirstAuthentication,
|
|
1712
|
+
context: {
|
|
1713
|
+
meta: {
|
|
1714
|
+
findFirstAuthentication: {
|
|
1715
|
+
categories: ["authentication"],
|
|
1716
|
+
inputSchema: FindFirstAuthenticationSchema
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
};
|
|
1722
|
+
var FindUniqueAuthenticationSchema = zod.z.object({
|
|
1723
|
+
appKey: AppKeyPropertySchema.optional().describe(
|
|
1724
|
+
"App key of authentication to find (e.g., 'SlackCLIAPI')"
|
|
1725
|
+
),
|
|
1726
|
+
search: zod.z.string().optional().describe("Search term to filter authentications by title"),
|
|
1727
|
+
title: zod.z.string().optional().describe("Filter authentications by exact title match"),
|
|
1728
|
+
accountId: zod.z.string().optional().describe("Filter by account ID"),
|
|
1729
|
+
owner: zod.z.string().optional().describe("Filter by owner")
|
|
1730
|
+
}).describe("Find a unique authentication matching the criteria");
|
|
1731
|
+
|
|
1732
|
+
// src/plugins/findUniqueAuthentication/index.ts
|
|
1733
|
+
var findUniqueAuthenticationPlugin = ({ sdk }) => {
|
|
1734
|
+
const findUniqueAuthentication = createFunction(
|
|
1735
|
+
async function findUniqueAuthentication2(options = {}) {
|
|
1736
|
+
const authsResponse = await sdk.listAuthentications({
|
|
1737
|
+
...options,
|
|
1738
|
+
maxItems: 2
|
|
1739
|
+
// Get up to 2 to check for uniqueness
|
|
1740
|
+
});
|
|
1741
|
+
if (authsResponse.data.length === 0) {
|
|
1742
|
+
throw new ZapierResourceNotFoundError(
|
|
1743
|
+
"No authentication found matching the specified criteria",
|
|
1744
|
+
{ resourceType: "Authentication" }
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
if (authsResponse.data.length > 1) {
|
|
1748
|
+
throw new ZapierValidationError(
|
|
1749
|
+
"Multiple authentications found matching the specified criteria. Expected exactly one."
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
return {
|
|
1753
|
+
data: authsResponse.data[0]
|
|
1754
|
+
};
|
|
1755
|
+
},
|
|
1756
|
+
FindUniqueAuthenticationSchema
|
|
1757
|
+
);
|
|
1758
|
+
return {
|
|
1759
|
+
findUniqueAuthentication,
|
|
1760
|
+
context: {
|
|
1761
|
+
meta: {
|
|
1762
|
+
findUniqueAuthentication: {
|
|
1763
|
+
categories: ["authentication"],
|
|
1764
|
+
inputSchema: FindUniqueAuthenticationSchema
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
};
|
|
1770
|
+
var RunActionSchema = zod.z.object({
|
|
1771
|
+
appKey: AppKeyPropertySchema,
|
|
1772
|
+
actionType: ActionTypePropertySchema,
|
|
1773
|
+
actionKey: ActionKeyPropertySchema,
|
|
1774
|
+
authenticationId: AuthenticationIdPropertySchema.nullable().optional(),
|
|
1775
|
+
inputs: InputsPropertySchema.optional().describe(
|
|
1776
|
+
"Input parameters for the action"
|
|
1777
|
+
),
|
|
1778
|
+
pageSize: zod.z.number().min(1).optional().describe("Number of results per page"),
|
|
1779
|
+
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
1780
|
+
}).describe("Execute an action with the given inputs");
|
|
1781
|
+
|
|
1782
|
+
// src/plugins/runAction/index.ts
|
|
1783
|
+
async function executeAction(actionOptions) {
|
|
1784
|
+
const {
|
|
1785
|
+
api,
|
|
1786
|
+
context,
|
|
1787
|
+
appKey,
|
|
1788
|
+
actionKey,
|
|
1789
|
+
actionType,
|
|
1790
|
+
executionOptions,
|
|
1791
|
+
authenticationId
|
|
1792
|
+
} = actionOptions;
|
|
1793
|
+
const selectedApi = await context.getVersionedImplementationId(appKey);
|
|
1794
|
+
if (!selectedApi) {
|
|
1795
|
+
throw new ZapierConfigurationError(
|
|
1796
|
+
"No current_implementation_id found for app",
|
|
1797
|
+
{ configType: "current_implementation_id" }
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
const runRequestData = {
|
|
1801
|
+
selected_api: selectedApi,
|
|
1802
|
+
action_key: actionKey,
|
|
1803
|
+
action_type: actionType,
|
|
1804
|
+
inputs: executionOptions.inputs || {}
|
|
1805
|
+
};
|
|
1806
|
+
if (authenticationId !== null && authenticationId !== void 0) {
|
|
1807
|
+
runRequestData.authentication_id = authenticationId;
|
|
1808
|
+
}
|
|
1809
|
+
const runRequest = {
|
|
1810
|
+
data: runRequestData
|
|
1811
|
+
};
|
|
1812
|
+
const runData = await api.post(
|
|
1813
|
+
"/api/actions/v1/runs",
|
|
1814
|
+
runRequest
|
|
1815
|
+
);
|
|
1816
|
+
const runId = runData.data.id;
|
|
1817
|
+
return await api.poll(`/api/actions/v1/runs/${runId}`, {
|
|
1818
|
+
successStatus: 200,
|
|
1819
|
+
pendingStatus: 202,
|
|
1820
|
+
resultExtractor: (result) => result.data
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
var runActionPlugin = ({ sdk, context }) => {
|
|
1824
|
+
const runAction = createPaginatedFunction(async function runActionPage(options) {
|
|
1825
|
+
const { api } = context;
|
|
1826
|
+
const {
|
|
1827
|
+
appKey,
|
|
1828
|
+
actionKey,
|
|
1829
|
+
actionType,
|
|
1830
|
+
authenticationId,
|
|
1831
|
+
inputs = {}
|
|
1832
|
+
} = options;
|
|
1833
|
+
const actionData = await sdk.getAction({
|
|
1834
|
+
appKey,
|
|
1835
|
+
actionKey,
|
|
1836
|
+
actionType
|
|
1837
|
+
});
|
|
1838
|
+
if (actionData.data.action_type !== actionType) {
|
|
1839
|
+
throw new ZapierValidationError(
|
|
1840
|
+
`Action type mismatch: expected ${actionType}, got ${actionData.data.action_type}`
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
const result = await executeAction({
|
|
1844
|
+
api,
|
|
1845
|
+
context,
|
|
1846
|
+
appKey,
|
|
1847
|
+
actionKey,
|
|
1848
|
+
actionType,
|
|
1849
|
+
executionOptions: { inputs },
|
|
1850
|
+
authenticationId});
|
|
1851
|
+
if (result.errors && result.errors.length > 0) {
|
|
1852
|
+
const errorMessage = result.errors.map(
|
|
1853
|
+
(error) => error.detail || error.title || "Unknown error"
|
|
1854
|
+
).join("; ");
|
|
1855
|
+
throw new ZapierActionError(`Action execution failed: ${errorMessage}`, {
|
|
1856
|
+
appKey,
|
|
1857
|
+
actionKey
|
|
1674
1858
|
});
|
|
1675
|
-
return {
|
|
1676
|
-
title: item.full_name,
|
|
1677
|
-
subtitle: `@${item.username}`,
|
|
1678
|
-
details
|
|
1679
|
-
};
|
|
1680
1859
|
}
|
|
1681
|
-
}
|
|
1682
|
-
);
|
|
1683
|
-
|
|
1684
|
-
// src/plugins/getProfile/schemas.ts
|
|
1685
|
-
var GetProfileSchema = withOutputSchema(
|
|
1686
|
-
zod.z.object({}).optional().describe("Get current user's profile information"),
|
|
1687
|
-
UserProfileItemSchema
|
|
1688
|
-
);
|
|
1689
|
-
|
|
1690
|
-
// src/plugins/getProfile/index.ts
|
|
1691
|
-
var getProfilePlugin = ({ context }) => {
|
|
1692
|
-
const getProfile = createFunction(async function getProfile2() {
|
|
1693
|
-
const profile = await context.api.get("/api/v4/profile/", {
|
|
1694
|
-
authRequired: true
|
|
1695
|
-
});
|
|
1696
|
-
const { user_id: _unusedUserId, ...data } = profile;
|
|
1697
1860
|
return {
|
|
1698
|
-
data:
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
full_name: `${profile.first_name} ${profile.last_name}`
|
|
1702
|
-
// Computed field
|
|
1703
|
-
}
|
|
1861
|
+
data: result.results || [],
|
|
1862
|
+
nextCursor: void 0
|
|
1863
|
+
// No pagination implemented yet
|
|
1704
1864
|
};
|
|
1705
|
-
},
|
|
1865
|
+
}, RunActionSchema);
|
|
1706
1866
|
return {
|
|
1707
|
-
|
|
1867
|
+
runAction,
|
|
1708
1868
|
context: {
|
|
1709
1869
|
meta: {
|
|
1710
|
-
|
|
1711
|
-
categories: ["
|
|
1712
|
-
inputSchema:
|
|
1870
|
+
runAction: {
|
|
1871
|
+
categories: ["action"],
|
|
1872
|
+
inputSchema: RunActionSchema
|
|
1713
1873
|
}
|
|
1714
1874
|
}
|
|
1715
1875
|
}
|
|
1716
1876
|
};
|
|
1717
1877
|
};
|
|
1718
|
-
var
|
|
1719
|
-
zod.z.
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
subtitle: `(${item.key})`,
|
|
1737
|
-
details: []
|
|
1738
|
-
};
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
);
|
|
1742
|
-
|
|
1743
|
-
// src/plugins/listApps/schemas.ts
|
|
1744
|
-
var ListAppsSchema = withOutputSchema(
|
|
1745
|
-
zod.z.object({
|
|
1746
|
-
appKeys: zod.z.array(zod.z.string()).optional().describe(
|
|
1747
|
-
"Filter apps by app keys (e.g., 'SlackCLIAPI' or slug like 'github')"
|
|
1748
|
-
),
|
|
1749
|
-
search: zod.z.string().optional().describe("Search for apps by name"),
|
|
1750
|
-
pageSize: zod.z.number().min(1).optional().describe("Number of apps per page"),
|
|
1751
|
-
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
1752
|
-
}).describe("List all available apps with optional filtering"),
|
|
1753
|
-
AppItemSchema
|
|
1754
|
-
);
|
|
1878
|
+
var RelayRequestSchema = zod.z.object({
|
|
1879
|
+
url: zod.z.string().url().describe("The URL to request (will be proxied through Relay)"),
|
|
1880
|
+
method: zod.z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]).optional().describe("HTTP method"),
|
|
1881
|
+
body: zod.z.any().optional().describe("Request body as a string"),
|
|
1882
|
+
authenticationId: zod.z.number().int().optional().describe("Zapier authentication ID to use for the request"),
|
|
1883
|
+
callbackUrl: zod.z.string().url().optional().describe("URL to send async response to (makes request async)"),
|
|
1884
|
+
authenticationTemplate: zod.z.string().optional().describe(
|
|
1885
|
+
"Optional JSON string authentication template to bypass Notary lookup"
|
|
1886
|
+
),
|
|
1887
|
+
headers: zod.z.union([
|
|
1888
|
+
zod.z.record(zod.z.string()),
|
|
1889
|
+
zod.z.instanceof(Headers),
|
|
1890
|
+
zod.z.array(zod.z.tuple([zod.z.string(), zod.z.string()]))
|
|
1891
|
+
]).optional().describe("Request headers")
|
|
1892
|
+
}).extend({
|
|
1893
|
+
relayBaseUrl: zod.z.string().optional().describe("Base URL for Relay service")
|
|
1894
|
+
}).describe("Make authenticated HTTP requests through Zapier's Relay service");
|
|
1895
|
+
var RelayFetchSchema = RelayRequestSchema;
|
|
1755
1896
|
|
|
1756
|
-
// src/
|
|
1757
|
-
function
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
const version = parts.slice(1).join("@");
|
|
1762
|
-
return [baseKey, version];
|
|
1763
|
-
}
|
|
1764
|
-
return [versionedKey, void 0];
|
|
1765
|
-
}
|
|
1766
|
-
function normalizeImplementationToAppItem(implementation) {
|
|
1767
|
-
const [selectedApi, appVersion] = implementation.selected_api ? splitVersionedKey(implementation.selected_api) : [implementation.selected_api || "", void 0];
|
|
1768
|
-
return {
|
|
1769
|
-
title: implementation.name || selectedApi,
|
|
1770
|
-
key: selectedApi,
|
|
1771
|
-
current_implementation_id: implementation.selected_api || "",
|
|
1772
|
-
version: appVersion
|
|
1773
|
-
// Extract version separately
|
|
1774
|
-
};
|
|
1775
|
-
}
|
|
1776
|
-
function normalizeImplementationMetaToAppItem(implementationMeta) {
|
|
1777
|
-
const [selectedApi, appVersion] = splitVersionedKey(implementationMeta.id);
|
|
1778
|
-
return {
|
|
1779
|
-
title: implementationMeta.name,
|
|
1780
|
-
key: selectedApi,
|
|
1781
|
-
current_implementation_id: implementationMeta.id,
|
|
1782
|
-
// Keep the full versioned ID
|
|
1783
|
-
version: appVersion,
|
|
1784
|
-
// Extract version separately
|
|
1785
|
-
slug: implementationMeta.slug
|
|
1786
|
-
};
|
|
1897
|
+
// src/plugins/request/index.ts
|
|
1898
|
+
function transformUrlToRelayPath(url) {
|
|
1899
|
+
const targetUrl = new URL(url);
|
|
1900
|
+
const relayPath = `/relay/${targetUrl.host}${targetUrl.pathname}${targetUrl.search}${targetUrl.hash}`;
|
|
1901
|
+
return relayPath;
|
|
1787
1902
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1903
|
+
var requestPlugin = ({ context }) => {
|
|
1904
|
+
const request = createFunction(async function request2(options) {
|
|
1905
|
+
const { api } = context;
|
|
1906
|
+
const {
|
|
1907
|
+
url,
|
|
1908
|
+
method = "GET",
|
|
1909
|
+
body,
|
|
1910
|
+
headers: optionsHeaders,
|
|
1911
|
+
authenticationId,
|
|
1912
|
+
callbackUrl,
|
|
1913
|
+
authenticationTemplate
|
|
1914
|
+
} = options;
|
|
1915
|
+
const relayPath = transformUrlToRelayPath(url);
|
|
1916
|
+
const headers = {};
|
|
1917
|
+
if (optionsHeaders) {
|
|
1918
|
+
const headerEntries = optionsHeaders instanceof Headers ? Array.from(optionsHeaders.entries()) : Array.isArray(optionsHeaders) ? optionsHeaders : Object.entries(optionsHeaders);
|
|
1919
|
+
for (const [key, value] of headerEntries) {
|
|
1920
|
+
headers[key] = value;
|
|
1921
|
+
}
|
|
1797
1922
|
}
|
|
1798
|
-
if (
|
|
1799
|
-
|
|
1923
|
+
if (authenticationId) {
|
|
1924
|
+
headers["X-Relay-Authentication-Id"] = authenticationId.toString();
|
|
1800
1925
|
}
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
selected_api: selectedApi,
|
|
1804
|
-
customuser_id: userId,
|
|
1805
|
-
...restOfAuth
|
|
1806
|
-
} = auth;
|
|
1807
|
-
return {
|
|
1808
|
-
...restOfAuth,
|
|
1809
|
-
// Pass through all other API response fields except selected_api
|
|
1810
|
-
implementation_id: selectedApi,
|
|
1811
|
-
// Rename selected_api to implementation_id
|
|
1812
|
-
title: auth.title || auth.label || void 0,
|
|
1813
|
-
// Coerce title from label if missing
|
|
1814
|
-
is_expired: auth.is_stale,
|
|
1815
|
-
// Map is_stale to is_expired
|
|
1816
|
-
expired_at: auth.marked_stale_at,
|
|
1817
|
-
// Map marked_stale_at to expired_at
|
|
1818
|
-
app_key: appKey,
|
|
1819
|
-
// App key from implementations endpoint or parsed from selected_api
|
|
1820
|
-
version,
|
|
1821
|
-
// Version from selected_api or provided
|
|
1822
|
-
user_id: userId
|
|
1823
|
-
// Map customuser_id to user_id
|
|
1824
|
-
};
|
|
1825
|
-
}
|
|
1826
|
-
function normalizeActionItem(action) {
|
|
1827
|
-
const { name, type, selected_api: appKey, ...restOfAction } = action;
|
|
1828
|
-
return {
|
|
1829
|
-
...restOfAction,
|
|
1830
|
-
app_key: appKey || "",
|
|
1831
|
-
action_type: type,
|
|
1832
|
-
title: name,
|
|
1833
|
-
// Map name to title
|
|
1834
|
-
type: "action"
|
|
1835
|
-
};
|
|
1836
|
-
}
|
|
1837
|
-
function groupVersionedAppKeysByType(appKeys) {
|
|
1838
|
-
const result = {
|
|
1839
|
-
selectedApi: [],
|
|
1840
|
-
slug: []
|
|
1841
|
-
};
|
|
1842
|
-
const seenSelectedApi = /* @__PURE__ */ new Set();
|
|
1843
|
-
const seenSlugs = /* @__PURE__ */ new Set();
|
|
1844
|
-
for (const key of appKeys) {
|
|
1845
|
-
const [keyWithoutVersion, version] = splitVersionedKey(key);
|
|
1846
|
-
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1847
|
-
if (uuidRegex.test(keyWithoutVersion)) {
|
|
1848
|
-
throw new Error(
|
|
1849
|
-
`UUID app keys are not supported. Use app slug or implementation ID instead of: ${key}`
|
|
1850
|
-
);
|
|
1926
|
+
if (callbackUrl) {
|
|
1927
|
+
headers["X-Relay-Callback-Url"] = callbackUrl;
|
|
1851
1928
|
}
|
|
1852
|
-
if (
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1929
|
+
if (authenticationTemplate) {
|
|
1930
|
+
headers["X-Authentication-Template"] = authenticationTemplate;
|
|
1931
|
+
}
|
|
1932
|
+
return await api.fetch(relayPath, {
|
|
1933
|
+
method,
|
|
1934
|
+
body,
|
|
1935
|
+
headers
|
|
1936
|
+
});
|
|
1937
|
+
}, RelayRequestSchema);
|
|
1938
|
+
return {
|
|
1939
|
+
request,
|
|
1940
|
+
context: {
|
|
1941
|
+
meta: {
|
|
1942
|
+
request: {
|
|
1943
|
+
categories: ["http"],
|
|
1944
|
+
inputSchema: RelayRequestSchema
|
|
1945
|
+
}
|
|
1858
1946
|
}
|
|
1859
|
-
continue;
|
|
1860
|
-
}
|
|
1861
|
-
if (keyWithoutVersion.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)) {
|
|
1862
|
-
seenSlugs.add(key);
|
|
1863
|
-
result.slug.push(key);
|
|
1864
|
-
continue;
|
|
1865
1947
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1948
|
+
};
|
|
1949
|
+
};
|
|
1950
|
+
var ManifestSchema = zod.z.object({
|
|
1951
|
+
apps: zod.z.record(
|
|
1952
|
+
zod.z.string(),
|
|
1953
|
+
zod.z.object({
|
|
1954
|
+
implementationName: zod.z.string().describe(
|
|
1955
|
+
"Base implementation name without version (e.g., 'SlackCLIAPI')"
|
|
1956
|
+
),
|
|
1957
|
+
version: zod.z.string().describe("Version string (e.g., '1.21.1')")
|
|
1958
|
+
})
|
|
1959
|
+
)
|
|
1960
|
+
}).describe("Manifest mapping app keys to version information");
|
|
1961
|
+
zod.z.object({
|
|
1962
|
+
manifestPath: zod.z.string().optional().describe("Path to manifest file"),
|
|
1963
|
+
manifest: zod.z.record(
|
|
1964
|
+
zod.z.string(),
|
|
1965
|
+
zod.z.object({
|
|
1966
|
+
implementationName: zod.z.string(),
|
|
1967
|
+
version: zod.z.string().optional()
|
|
1968
|
+
})
|
|
1969
|
+
).optional().describe("Direct manifest object")
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
// src/plugins/manifest/index.ts
|
|
1973
|
+
function parseManifestContent(content, source) {
|
|
1974
|
+
try {
|
|
1975
|
+
const parsed = JSON.parse(content);
|
|
1976
|
+
if (parsed?.apps && typeof parsed?.apps === "object") {
|
|
1977
|
+
const result = ManifestSchema.safeParse(parsed);
|
|
1978
|
+
if (result.success) {
|
|
1979
|
+
return result.data;
|
|
1980
|
+
}
|
|
1981
|
+
console.warn(`\u26A0\uFE0F Invalid manifest format in ${source}: ${result.error}`);
|
|
1869
1982
|
}
|
|
1983
|
+
return null;
|
|
1984
|
+
} catch (error) {
|
|
1985
|
+
console.warn(`\u26A0\uFE0F Failed to parse manifest from ${source}:`, error);
|
|
1986
|
+
return null;
|
|
1870
1987
|
}
|
|
1871
|
-
return result;
|
|
1872
|
-
}
|
|
1873
|
-
function groupAppKeysByType(appKeys) {
|
|
1874
|
-
const grouped = groupVersionedAppKeysByType(appKeys);
|
|
1875
|
-
return {
|
|
1876
|
-
selectedApi: [
|
|
1877
|
-
...new Set(grouped.selectedApi.map((key) => key.split("@")[0]))
|
|
1878
|
-
],
|
|
1879
|
-
slug: [...new Set(grouped.slug.map((key) => key.split("@")[0]))]
|
|
1880
|
-
};
|
|
1881
1988
|
}
|
|
1882
|
-
function
|
|
1883
|
-
|
|
1884
|
-
|
|
1989
|
+
function loadManifestFromFile(filePath) {
|
|
1990
|
+
try {
|
|
1991
|
+
const resolvedPath = path.resolve(filePath);
|
|
1992
|
+
const content = fs.readFileSync(resolvedPath, "utf8");
|
|
1993
|
+
return parseManifestContent(content, resolvedPath);
|
|
1994
|
+
} catch {
|
|
1995
|
+
console.warn(`\u26A0\uFE0F Failed to load manifest from ${filePath}`);
|
|
1996
|
+
return null;
|
|
1885
1997
|
}
|
|
1886
|
-
return !!slug.match(/^[a-z0-9]+(?:_[a-z0-9]+)*$/);
|
|
1887
1998
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1999
|
+
var emitWarning = (appKey) => {
|
|
2000
|
+
console.warn(
|
|
2001
|
+
`
|
|
2002
|
+
${"\u26A0\uFE0F".padEnd(3)} ${"WARNING".padEnd(8)} No manifest version found for '${appKey}'`
|
|
2003
|
+
);
|
|
2004
|
+
console.warn(
|
|
2005
|
+
` ${"\u21B3".padEnd(3)} Using a manifest ensures version locking and prevents unexpected behavior due to version changes.`
|
|
2006
|
+
);
|
|
2007
|
+
console.warn(
|
|
2008
|
+
` ${"\u21B3".padEnd(3)} Generate/update the manifest with: \`zapier-sdk lock-version ${appKey}\`
|
|
2009
|
+
`
|
|
2010
|
+
);
|
|
2011
|
+
};
|
|
2012
|
+
var manifestPlugin = (params) => {
|
|
2013
|
+
const { sdk, context } = params;
|
|
2014
|
+
const { api, options } = context;
|
|
2015
|
+
const { manifestPath = ".zapierrc", manifest } = options || {};
|
|
2016
|
+
let resolvedManifest;
|
|
2017
|
+
function resolveManifest() {
|
|
2018
|
+
if (manifest) {
|
|
2019
|
+
return manifest;
|
|
2020
|
+
}
|
|
2021
|
+
if (manifestPath) {
|
|
2022
|
+
return loadManifestFromFile(manifestPath);
|
|
2023
|
+
}
|
|
2024
|
+
return null;
|
|
1894
2025
|
}
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
2026
|
+
const getResolvedManifest = () => {
|
|
2027
|
+
if (typeof resolvedManifest === "undefined") {
|
|
2028
|
+
resolvedManifest = resolveManifest() ?? null;
|
|
2029
|
+
}
|
|
2030
|
+
return resolvedManifest;
|
|
2031
|
+
};
|
|
2032
|
+
const getManifestEntry = (appKey) => {
|
|
2033
|
+
return getResolvedManifest()?.apps?.[appKey] || null;
|
|
2034
|
+
};
|
|
2035
|
+
const getImplementation = async (appKey) => {
|
|
2036
|
+
let selectedApi = null;
|
|
2037
|
+
const manifestImplementation = getResolvedManifest()?.apps?.[appKey];
|
|
2038
|
+
const [versionlessAppKey, version] = splitVersionedKey(appKey);
|
|
2039
|
+
if (version) {
|
|
2040
|
+
selectedApi = `${versionlessAppKey}@${version}`;
|
|
2041
|
+
} else if (manifestImplementation) {
|
|
2042
|
+
selectedApi = `${manifestImplementation.implementationName}@${manifestImplementation.version || "latest"}`;
|
|
2043
|
+
}
|
|
2044
|
+
if (selectedApi) {
|
|
2045
|
+
const searchParams = {
|
|
2046
|
+
selected_apis: selectedApi
|
|
2047
|
+
};
|
|
2048
|
+
const implementationData = await api.get(
|
|
2049
|
+
"/api/v4/implementations/",
|
|
1911
2050
|
{
|
|
1912
|
-
searchParams
|
|
2051
|
+
searchParams
|
|
1913
2052
|
}
|
|
1914
2053
|
);
|
|
1915
|
-
const
|
|
1916
|
-
|
|
1917
|
-
);
|
|
1918
|
-
const appKeysSet = new Set(appKeys);
|
|
1919
|
-
for (const implementation of implementations) {
|
|
1920
|
-
if (!appKeysSet.has(implementation.key)) {
|
|
1921
|
-
appKeysSet.add(implementation.key);
|
|
1922
|
-
appKeys.push(implementation.key);
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
2054
|
+
const implementationResults = implementationData.results[0];
|
|
2055
|
+
if (!implementationResults) return null;
|
|
2056
|
+
return normalizeImplementationToAppItem(implementationResults);
|
|
1925
2057
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
2058
|
+
emitWarning(appKey);
|
|
2059
|
+
const appsIterator = sdk.listApps({ appKeys: [appKey] }).items();
|
|
2060
|
+
const apps = [];
|
|
2061
|
+
for await (const app2 of appsIterator) {
|
|
2062
|
+
apps.push(app2);
|
|
2063
|
+
break;
|
|
1929
2064
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
searchParams.offset = opts.cursor;
|
|
2065
|
+
if (apps.length === 0) {
|
|
2066
|
+
return null;
|
|
1933
2067
|
}
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
}
|
|
2068
|
+
const app = apps[0];
|
|
2069
|
+
return app;
|
|
2070
|
+
};
|
|
2071
|
+
const getVersionedImplementationId = async (appKey) => {
|
|
2072
|
+
const manifestEntry = getManifestEntry(appKey);
|
|
2073
|
+
if (manifestEntry) {
|
|
2074
|
+
return `${manifestEntry.implementationName}@${manifestEntry.version || "latest"}`;
|
|
1942
2075
|
}
|
|
1943
|
-
const
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
}
|
|
1948
|
-
);
|
|
1949
|
-
return {
|
|
1950
|
-
data: implementationsEnvelope.results.map(
|
|
1951
|
-
normalizeImplementationMetaToAppItem
|
|
1952
|
-
),
|
|
1953
|
-
nextCursor: extractCursor(implementationsEnvelope)
|
|
1954
|
-
};
|
|
1955
|
-
}, ListAppsSchema);
|
|
2076
|
+
const implementation = await getImplementation(appKey);
|
|
2077
|
+
if (!implementation) return null;
|
|
2078
|
+
return implementation.current_implementation_id;
|
|
2079
|
+
};
|
|
1956
2080
|
return {
|
|
1957
|
-
listApps,
|
|
1958
2081
|
context: {
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
inputSchema: ListAppsSchema
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
2082
|
+
getVersionedImplementationId,
|
|
2083
|
+
getManifestEntry,
|
|
2084
|
+
getImplementation
|
|
1965
2085
|
}
|
|
1966
2086
|
};
|
|
1967
2087
|
};
|
|
1968
|
-
var
|
|
1969
|
-
zod.z.
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
);
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2088
|
+
var LockVersionSchema = zod.z.object({
|
|
2089
|
+
appKey: zod.z.string().describe("The app key to lock version for (e.g., 'slack', 'gmail')")
|
|
2090
|
+
});
|
|
2091
|
+
var lockVersionPlugin = ({ sdk }) => {
|
|
2092
|
+
const lockVersion = createFunction(
|
|
2093
|
+
async function lockVersion2(options) {
|
|
2094
|
+
const { appKey, configPath = ".zapierrc" } = options;
|
|
2095
|
+
const resolvedPath = path.resolve(configPath);
|
|
2096
|
+
const appsIterator = sdk.listApps({ appKeys: [appKey] }).items();
|
|
2097
|
+
const apps = [];
|
|
2098
|
+
for await (const app2 of appsIterator) {
|
|
2099
|
+
apps.push(app2);
|
|
2100
|
+
break;
|
|
2101
|
+
}
|
|
2102
|
+
const app = apps[0];
|
|
2103
|
+
const currentImplementationId = app.current_implementation_id;
|
|
2104
|
+
const [implementationName, version] = currentImplementationId.split("@");
|
|
2105
|
+
if (!implementationName || !version) {
|
|
2106
|
+
throw new Error(
|
|
2107
|
+
`Invalid implementation ID format: ${currentImplementationId}. Expected format: <implementationName>@<version>`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
let config = { apps: {} };
|
|
2111
|
+
if (fs.existsSync(resolvedPath)) {
|
|
2112
|
+
try {
|
|
2113
|
+
const configContent = fs.readFileSync(resolvedPath, "utf8");
|
|
2114
|
+
config = JSON.parse(configContent);
|
|
2115
|
+
if (!config.apps) {
|
|
2116
|
+
config.apps = {};
|
|
2117
|
+
}
|
|
2118
|
+
} catch (error) {
|
|
2119
|
+
console.warn(
|
|
2120
|
+
`\u26A0\uFE0F Failed to parse existing config file, creating new one: ${error}`
|
|
2121
|
+
);
|
|
2122
|
+
config = { apps: {} };
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
config.apps[appKey] = {
|
|
2126
|
+
implementationName,
|
|
2127
|
+
version
|
|
2128
|
+
};
|
|
2129
|
+
fs.writeFileSync(resolvedPath, JSON.stringify(config, null, 2));
|
|
2130
|
+
return {
|
|
2131
|
+
data: {
|
|
2132
|
+
...app,
|
|
2133
|
+
implementationName,
|
|
2134
|
+
version
|
|
2135
|
+
},
|
|
2136
|
+
configPath: resolvedPath
|
|
2137
|
+
};
|
|
2138
|
+
},
|
|
2139
|
+
LockVersionSchema.extend({
|
|
2140
|
+
configPath: zod.z.string().optional().describe("Path to .zapierrc file (defaults to '.zapierrc')")
|
|
2141
|
+
})
|
|
2142
|
+
);
|
|
1990
2143
|
return {
|
|
1991
|
-
|
|
2144
|
+
lockVersion,
|
|
1992
2145
|
context: {
|
|
1993
2146
|
meta: {
|
|
1994
|
-
|
|
1995
|
-
categories: ["
|
|
1996
|
-
inputSchema:
|
|
2147
|
+
lockVersion: {
|
|
2148
|
+
categories: ["utility"],
|
|
2149
|
+
inputSchema: LockVersionSchema
|
|
1997
2150
|
}
|
|
1998
2151
|
}
|
|
1999
2152
|
}
|
|
2000
2153
|
};
|
|
2001
2154
|
};
|
|
2002
|
-
var
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
//
|
|
2006
|
-
action_type: ActionSchema.shape.type,
|
|
2007
|
-
// Mapped from original 'type' field
|
|
2008
|
-
title: zod.z.string(),
|
|
2009
|
-
// Mapped from original 'name' field
|
|
2010
|
-
type: zod.z.literal("action")
|
|
2011
|
-
// Fixed type identifier
|
|
2155
|
+
var UserProfileItemSchema = withFormatter(
|
|
2156
|
+
UserProfileSchema.omit({ user_id: true }).extend({
|
|
2157
|
+
full_name: zod.z.string()
|
|
2158
|
+
// Computed field: first_name + " " + last_name
|
|
2012
2159
|
}),
|
|
2013
2160
|
{
|
|
2014
2161
|
format: (item) => {
|
|
2015
2162
|
const details = [];
|
|
2016
|
-
details.push({
|
|
2017
|
-
|
|
2018
|
-
style: "accent"
|
|
2019
|
-
});
|
|
2020
|
-
if (item.app_key) {
|
|
2163
|
+
details.push({ text: item.email, style: "dim" });
|
|
2164
|
+
if (item.timezone) {
|
|
2021
2165
|
details.push({
|
|
2022
|
-
text: `
|
|
2023
|
-
style: "
|
|
2166
|
+
text: `Timezone: ${item.timezone}`,
|
|
2167
|
+
style: "accent"
|
|
2024
2168
|
});
|
|
2025
2169
|
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2170
|
+
details.push({
|
|
2171
|
+
text: `Member since: ${item.since_signup}`,
|
|
2172
|
+
style: "dim"
|
|
2173
|
+
});
|
|
2029
2174
|
return {
|
|
2030
|
-
title: item.
|
|
2031
|
-
subtitle:
|
|
2175
|
+
title: item.full_name,
|
|
2176
|
+
subtitle: `@${item.username}`,
|
|
2032
2177
|
details
|
|
2033
2178
|
};
|
|
2034
2179
|
}
|
|
2035
2180
|
}
|
|
2036
2181
|
);
|
|
2037
2182
|
|
|
2038
|
-
// src/plugins/
|
|
2039
|
-
var
|
|
2040
|
-
zod.z.object({
|
|
2041
|
-
|
|
2042
|
-
"App key of actions to list (e.g., 'SlackCLIAPI')"
|
|
2043
|
-
),
|
|
2044
|
-
actionType: ActionTypePropertySchema.optional().describe(
|
|
2045
|
-
"Filter actions by type"
|
|
2046
|
-
),
|
|
2047
|
-
pageSize: zod.z.number().min(1).optional().describe("Number of actions per page"),
|
|
2048
|
-
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
2049
|
-
}).describe("List all actions for a specific app"),
|
|
2050
|
-
ActionItemSchema
|
|
2183
|
+
// src/plugins/getProfile/schemas.ts
|
|
2184
|
+
var GetProfileSchema = withOutputSchema(
|
|
2185
|
+
zod.z.object({}).optional().describe("Get current user's profile information"),
|
|
2186
|
+
UserProfileItemSchema
|
|
2051
2187
|
);
|
|
2052
2188
|
|
|
2053
|
-
// src/plugins/
|
|
2054
|
-
var
|
|
2055
|
-
const
|
|
2056
|
-
const
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
"No current_implementation_id found for app",
|
|
2061
|
-
{ configType: "current_implementation_id" }
|
|
2062
|
-
);
|
|
2063
|
-
}
|
|
2064
|
-
const searchParams = {
|
|
2065
|
-
global: "true",
|
|
2066
|
-
public_only: "true",
|
|
2067
|
-
selected_apis: selectedApi
|
|
2068
|
-
};
|
|
2069
|
-
const data = await api.get(
|
|
2070
|
-
"/api/v4/implementations/",
|
|
2071
|
-
{
|
|
2072
|
-
searchParams,
|
|
2073
|
-
customErrorHandler: ({ status }) => {
|
|
2074
|
-
if (status === 401) {
|
|
2075
|
-
return new ZapierAuthenticationError(
|
|
2076
|
-
`Authentication failed. Your token may not have permission to access implementations or may be expired. (HTTP ${status})`,
|
|
2077
|
-
{ statusCode: status }
|
|
2078
|
-
);
|
|
2079
|
-
}
|
|
2080
|
-
if (status === 403) {
|
|
2081
|
-
return new ZapierAuthenticationError(
|
|
2082
|
-
`Access forbidden. Your token may not have the required scopes to list implementations. (HTTP ${status})`,
|
|
2083
|
-
{ statusCode: status }
|
|
2084
|
-
);
|
|
2085
|
-
}
|
|
2086
|
-
return void 0;
|
|
2087
|
-
}
|
|
2088
|
-
}
|
|
2089
|
-
);
|
|
2090
|
-
let allActions = [];
|
|
2091
|
-
for (const implementation of data.results || []) {
|
|
2092
|
-
if (implementation.actions) {
|
|
2093
|
-
for (const action of implementation.actions) {
|
|
2094
|
-
const actionWithContext = {
|
|
2095
|
-
...action,
|
|
2096
|
-
selected_api: action.selected_api || implementation.selected_api
|
|
2097
|
-
};
|
|
2098
|
-
allActions.push(normalizeActionItem(actionWithContext));
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
if (options.actionType) {
|
|
2103
|
-
allActions = allActions.filter(
|
|
2104
|
-
(action) => action.action_type === options.actionType
|
|
2105
|
-
);
|
|
2106
|
-
}
|
|
2189
|
+
// src/plugins/getProfile/index.ts
|
|
2190
|
+
var getProfilePlugin = ({ context }) => {
|
|
2191
|
+
const getProfile = createFunction(async function getProfile2() {
|
|
2192
|
+
const profile = await context.api.get("/api/v4/profile/", {
|
|
2193
|
+
authRequired: true
|
|
2194
|
+
});
|
|
2195
|
+
const { user_id: _unusedUserId, ...data } = profile;
|
|
2107
2196
|
return {
|
|
2108
|
-
data:
|
|
2109
|
-
|
|
2197
|
+
data: {
|
|
2198
|
+
...data,
|
|
2199
|
+
// Pass through all API response fields
|
|
2200
|
+
full_name: `${profile.first_name} ${profile.last_name}`
|
|
2201
|
+
// Computed field
|
|
2202
|
+
}
|
|
2110
2203
|
};
|
|
2111
|
-
},
|
|
2204
|
+
}, GetProfileSchema);
|
|
2112
2205
|
return {
|
|
2113
|
-
|
|
2206
|
+
getProfile,
|
|
2114
2207
|
context: {
|
|
2115
2208
|
meta: {
|
|
2116
|
-
|
|
2117
|
-
categories: ["
|
|
2118
|
-
inputSchema:
|
|
2209
|
+
getProfile: {
|
|
2210
|
+
categories: ["account"],
|
|
2211
|
+
inputSchema: GetProfileSchema
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
};
|
|
2217
|
+
|
|
2218
|
+
// src/api/auth.ts
|
|
2219
|
+
function isJwt(token) {
|
|
2220
|
+
const parts = token.split(".");
|
|
2221
|
+
if (parts.length !== 3) {
|
|
2222
|
+
return false;
|
|
2223
|
+
}
|
|
2224
|
+
const base64UrlPattern = /^[A-Za-z0-9_-]+$/;
|
|
2225
|
+
return parts.every((part) => part.length > 0 && base64UrlPattern.test(part));
|
|
2226
|
+
}
|
|
2227
|
+
function getAuthorizationHeader(token) {
|
|
2228
|
+
if (isJwt(token)) {
|
|
2229
|
+
return `JWT ${token}`;
|
|
2230
|
+
}
|
|
2231
|
+
return `Bearer ${token}`;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
// src/api/debug.ts
|
|
2235
|
+
function createDebugLogger(enabled) {
|
|
2236
|
+
if (!enabled) {
|
|
2237
|
+
return () => {
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
return (message, data) => {
|
|
2241
|
+
console.log(`[Zapier SDK] ${message}`, data || "");
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
function createDebugFetch(options) {
|
|
2245
|
+
const { originalFetch, debugLog } = options;
|
|
2246
|
+
return async (input, options2) => {
|
|
2247
|
+
const startTime = Date.now();
|
|
2248
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
2249
|
+
const method = options2?.method || "GET";
|
|
2250
|
+
debugLog(`\u2192 ${method} ${url}`, {
|
|
2251
|
+
headers: options2?.headers,
|
|
2252
|
+
body: options2?.body && typeof options2.body === "string" ? (() => {
|
|
2253
|
+
try {
|
|
2254
|
+
return JSON.parse(options2.body);
|
|
2255
|
+
} catch {
|
|
2256
|
+
return options2.body;
|
|
2119
2257
|
}
|
|
2258
|
+
})() : options2?.body
|
|
2259
|
+
});
|
|
2260
|
+
try {
|
|
2261
|
+
const response = await originalFetch(input, options2);
|
|
2262
|
+
const duration = Date.now() - startTime;
|
|
2263
|
+
debugLog(`\u2190 ${response.status} ${response.statusText} (${duration}ms)`, {
|
|
2264
|
+
url,
|
|
2265
|
+
method,
|
|
2266
|
+
status: response.status
|
|
2267
|
+
});
|
|
2268
|
+
return response;
|
|
2269
|
+
} catch (error) {
|
|
2270
|
+
const duration = Date.now() - startTime;
|
|
2271
|
+
debugLog(`\u2716 Request failed (${duration}ms)`, {
|
|
2272
|
+
url,
|
|
2273
|
+
method,
|
|
2274
|
+
error: error instanceof Error ? error.message : error
|
|
2275
|
+
});
|
|
2276
|
+
throw error;
|
|
2277
|
+
}
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
// src/api/polling.ts
|
|
2282
|
+
async function pollUntilComplete(options) {
|
|
2283
|
+
const {
|
|
2284
|
+
fetchPoll,
|
|
2285
|
+
maxAttempts = 30,
|
|
2286
|
+
initialDelay = 50,
|
|
2287
|
+
maxDelay = 1e3,
|
|
2288
|
+
successStatus = 200,
|
|
2289
|
+
pendingStatus = 202,
|
|
2290
|
+
resultExtractor = (response) => response
|
|
2291
|
+
} = options;
|
|
2292
|
+
let delay = initialDelay;
|
|
2293
|
+
let errorCount = 0;
|
|
2294
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2295
|
+
const response = await fetchPoll();
|
|
2296
|
+
if (response.status === successStatus) {
|
|
2297
|
+
errorCount = 0;
|
|
2298
|
+
const result = await response.json();
|
|
2299
|
+
return resultExtractor(result);
|
|
2300
|
+
} else if (response.status === pendingStatus) {
|
|
2301
|
+
errorCount = 0;
|
|
2302
|
+
if (attempt < maxAttempts - 1) {
|
|
2303
|
+
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
2304
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
2305
|
+
continue;
|
|
2120
2306
|
}
|
|
2121
|
-
}
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
}).describe("Get detailed information about a specific action");
|
|
2129
|
-
|
|
2130
|
-
// src/plugins/getAction/index.ts
|
|
2131
|
-
var getActionPlugin = ({ sdk }) => {
|
|
2132
|
-
const getAction = createFunction(async function getAction2(options) {
|
|
2133
|
-
const { actionKey, actionType, appKey } = options;
|
|
2134
|
-
const actionsResult = await sdk.listActions({ appKey });
|
|
2135
|
-
for (const action of actionsResult.data) {
|
|
2136
|
-
if (action.key === actionKey && action.action_type === actionType) {
|
|
2137
|
-
return { data: action };
|
|
2307
|
+
} else {
|
|
2308
|
+
errorCount++;
|
|
2309
|
+
if (errorCount >= 3) {
|
|
2310
|
+
throw new ZapierApiError(
|
|
2311
|
+
`Poll request failed: ${response.status} ${response.statusText}`,
|
|
2312
|
+
{ statusCode: response.status }
|
|
2313
|
+
);
|
|
2138
2314
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
);
|
|
2144
|
-
}, GetActionSchema);
|
|
2145
|
-
return {
|
|
2146
|
-
getAction,
|
|
2147
|
-
context: {
|
|
2148
|
-
meta: {
|
|
2149
|
-
getAction: {
|
|
2150
|
-
categories: ["action"],
|
|
2151
|
-
inputSchema: GetActionSchema
|
|
2152
|
-
}
|
|
2315
|
+
if (attempt < maxAttempts - 1) {
|
|
2316
|
+
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
2317
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
2318
|
+
continue;
|
|
2153
2319
|
}
|
|
2154
2320
|
}
|
|
2155
|
-
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
authenticationId: AuthenticationIdPropertySchema.nullable().optional(),
|
|
2162
|
-
inputs: InputsPropertySchema.optional().describe(
|
|
2163
|
-
"Input parameters for the action"
|
|
2164
|
-
),
|
|
2165
|
-
pageSize: zod.z.number().min(1).optional().describe("Number of results per page"),
|
|
2166
|
-
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
2167
|
-
}).describe("Execute an action with the given inputs");
|
|
2321
|
+
}
|
|
2322
|
+
throw new ZapierTimeoutError(
|
|
2323
|
+
`Operation timed out after ${maxAttempts} attempts`,
|
|
2324
|
+
{ attempts: maxAttempts, maxAttempts }
|
|
2325
|
+
);
|
|
2326
|
+
}
|
|
2168
2327
|
|
|
2169
|
-
// src/
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
} = actionOptions;
|
|
2180
|
-
const selectedApi = await context.getVersionedImplementationId(appKey);
|
|
2181
|
-
if (!selectedApi) {
|
|
2182
|
-
throw new ZapierConfigurationError(
|
|
2183
|
-
"No current_implementation_id found for app",
|
|
2184
|
-
{ configType: "current_implementation_id" }
|
|
2185
|
-
);
|
|
2328
|
+
// src/auth.ts
|
|
2329
|
+
function getTokenFromEnv() {
|
|
2330
|
+
return process.env.ZAPIER_TOKEN;
|
|
2331
|
+
}
|
|
2332
|
+
async function getTokenFromCliLogin(options = {}) {
|
|
2333
|
+
try {
|
|
2334
|
+
const { getToken } = await import('@zapier/zapier-sdk-cli-login');
|
|
2335
|
+
return await getToken(options);
|
|
2336
|
+
} catch {
|
|
2337
|
+
return void 0;
|
|
2186
2338
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
};
|
|
2193
|
-
if (authenticationId !== null && authenticationId !== void 0) {
|
|
2194
|
-
runRequestData.authentication_id = authenticationId;
|
|
2339
|
+
}
|
|
2340
|
+
async function getTokenFromEnvOrConfig(options = {}) {
|
|
2341
|
+
const envToken = getTokenFromEnv();
|
|
2342
|
+
if (envToken) {
|
|
2343
|
+
return envToken;
|
|
2195
2344
|
}
|
|
2196
|
-
|
|
2197
|
-
data: runRequestData
|
|
2198
|
-
};
|
|
2199
|
-
const runData = await api.post(
|
|
2200
|
-
"/api/actions/v1/runs",
|
|
2201
|
-
runRequest
|
|
2202
|
-
);
|
|
2203
|
-
const runId = runData.data.id;
|
|
2204
|
-
return await api.poll(`/api/actions/v1/runs/${runId}`, {
|
|
2205
|
-
successStatus: 200,
|
|
2206
|
-
pendingStatus: 202,
|
|
2207
|
-
resultExtractor: (result) => result.data
|
|
2208
|
-
});
|
|
2345
|
+
return getTokenFromCliLogin(options);
|
|
2209
2346
|
}
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
})
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
);
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2347
|
+
|
|
2348
|
+
// src/api/client.ts
|
|
2349
|
+
var SubdomainConfigMap = {
|
|
2350
|
+
// e.g. https://relay.zapier.com
|
|
2351
|
+
relay: {
|
|
2352
|
+
authHeader: "X-Relay-Authorization"
|
|
2353
|
+
}
|
|
2354
|
+
};
|
|
2355
|
+
var ZapierApiClient = class {
|
|
2356
|
+
constructor(options) {
|
|
2357
|
+
this.options = options;
|
|
2358
|
+
this.fetch = async (path, init) => {
|
|
2359
|
+
return this.plainFetch(path, init);
|
|
2360
|
+
};
|
|
2361
|
+
this.get = async (path, options = {}) => {
|
|
2362
|
+
return this.fetchJson("GET", path, void 0, options);
|
|
2363
|
+
};
|
|
2364
|
+
this.post = async (path, data, options = {}) => {
|
|
2365
|
+
return this.fetchJson("POST", path, data, options);
|
|
2366
|
+
};
|
|
2367
|
+
this.put = async (path, data, options = {}) => {
|
|
2368
|
+
return this.fetchJson("PUT", path, data, options);
|
|
2369
|
+
};
|
|
2370
|
+
this.delete = async (path, options = {}) => {
|
|
2371
|
+
return this.fetchJson("DELETE", path, void 0, options);
|
|
2372
|
+
};
|
|
2373
|
+
this.poll = async (path, options = {}) => {
|
|
2374
|
+
return pollUntilComplete({
|
|
2375
|
+
fetchPoll: () => this.plainFetch(path, {
|
|
2376
|
+
method: "GET",
|
|
2377
|
+
searchParams: options.searchParams,
|
|
2378
|
+
authRequired: options.authRequired
|
|
2379
|
+
}),
|
|
2380
|
+
maxAttempts: options.maxAttempts,
|
|
2381
|
+
initialDelay: options.initialDelay,
|
|
2382
|
+
maxDelay: options.maxDelay,
|
|
2383
|
+
successStatus: options.successStatus,
|
|
2384
|
+
pendingStatus: options.pendingStatus,
|
|
2385
|
+
resultExtractor: options.resultExtractor
|
|
2245
2386
|
});
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
// Helper to parse response data
|
|
2390
|
+
async parseResult(response) {
|
|
2391
|
+
try {
|
|
2392
|
+
return { type: "json", data: await response.json() };
|
|
2393
|
+
} catch {
|
|
2394
|
+
return { type: "text", data: await response.text() };
|
|
2246
2395
|
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2396
|
+
}
|
|
2397
|
+
// Helper to get a token from the different places it could be gotten
|
|
2398
|
+
async getAuthToken() {
|
|
2399
|
+
if (this.options.token) {
|
|
2400
|
+
return this.options.token;
|
|
2401
|
+
}
|
|
2402
|
+
if (this.options.getToken) {
|
|
2403
|
+
const token = await this.options.getToken();
|
|
2404
|
+
if (token) {
|
|
2405
|
+
return token;
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
return getTokenFromEnvOrConfig({
|
|
2409
|
+
onEvent: this.options.onEvent,
|
|
2410
|
+
fetch: this.options.fetch
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2413
|
+
// Helper to handle responses
|
|
2414
|
+
async handleResponse(params) {
|
|
2415
|
+
const { response, customErrorHandler, wasMissingAuthToken } = params;
|
|
2416
|
+
const { data: responseData } = await this.parseResult(response);
|
|
2417
|
+
if (response.ok) {
|
|
2418
|
+
return responseData;
|
|
2419
|
+
}
|
|
2420
|
+
const errorInfo = {
|
|
2421
|
+
status: response.status,
|
|
2422
|
+
statusText: response.statusText,
|
|
2423
|
+
data: responseData
|
|
2251
2424
|
};
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2425
|
+
if (customErrorHandler) {
|
|
2426
|
+
const customError = customErrorHandler(errorInfo);
|
|
2427
|
+
if (customError) {
|
|
2428
|
+
if (customError instanceof Error) {
|
|
2429
|
+
throw customError;
|
|
2430
|
+
} else {
|
|
2431
|
+
throw new Error(
|
|
2432
|
+
`customErrorHandler returned a non-Error: ${JSON.stringify(customError)}`
|
|
2433
|
+
);
|
|
2260
2434
|
}
|
|
2261
2435
|
}
|
|
2262
2436
|
}
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
// src/plugins/listAuthentications/index.ts
|
|
2278
|
-
var listAuthenticationsPlugin = ({ context }) => {
|
|
2279
|
-
const listAuthentications = createPaginatedFunction(
|
|
2280
|
-
async function listAuthenticationsPage(options) {
|
|
2281
|
-
const { api, getVersionedImplementationId } = context;
|
|
2282
|
-
const searchParams = {};
|
|
2283
|
-
if (options.appKey) {
|
|
2284
|
-
const implementationId = await getVersionedImplementationId(
|
|
2285
|
-
options.appKey
|
|
2437
|
+
const { message, errors } = this.parseErrorResponse(errorInfo);
|
|
2438
|
+
const errorOptions = {
|
|
2439
|
+
statusCode: response.status,
|
|
2440
|
+
errors
|
|
2441
|
+
};
|
|
2442
|
+
if (response.status === 404) {
|
|
2443
|
+
throw new ZapierNotFoundError(message, errorOptions);
|
|
2444
|
+
}
|
|
2445
|
+
if (response.status === 401 || response.status === 403) {
|
|
2446
|
+
if (wasMissingAuthToken) {
|
|
2447
|
+
throw new ZapierAuthenticationError(
|
|
2448
|
+
`Authentication required (HTTP ${response.status}). Please provide a token in options or set ZAPIER_TOKEN environment variable.`,
|
|
2449
|
+
errorOptions
|
|
2286
2450
|
);
|
|
2287
|
-
if (implementationId) {
|
|
2288
|
-
const [versionlessSelectedApi] = splitVersionedKey(implementationId);
|
|
2289
|
-
searchParams.versionless_selected_api = versionlessSelectedApi;
|
|
2290
|
-
}
|
|
2291
2451
|
}
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2452
|
+
throw new ZapierAuthenticationError(message, errorOptions);
|
|
2453
|
+
}
|
|
2454
|
+
if (response.status === 400) {
|
|
2455
|
+
throw new ZapierValidationError(message, errorOptions);
|
|
2456
|
+
}
|
|
2457
|
+
throw new ZapierApiError(message, errorOptions);
|
|
2458
|
+
}
|
|
2459
|
+
hasErrorArray(data) {
|
|
2460
|
+
return typeof data === "object" && data !== null && "errors" in data && Array.isArray(data.errors);
|
|
2461
|
+
}
|
|
2462
|
+
// Helper to check if data has API errors
|
|
2463
|
+
isApiErrorArray(dataArray) {
|
|
2464
|
+
const data = dataArray[0];
|
|
2465
|
+
return typeof data === "object" && data !== null && "message" in data && "code" in data && "title" in data && "detail" in data;
|
|
2466
|
+
}
|
|
2467
|
+
// Do our best to extract an error message from the response data
|
|
2468
|
+
extractErrorMessage(data) {
|
|
2469
|
+
if (typeof data === "string") {
|
|
2470
|
+
return data;
|
|
2471
|
+
}
|
|
2472
|
+
if (typeof data === "object" && data !== null) {
|
|
2473
|
+
if ("message" in data && typeof data.message === "string") {
|
|
2474
|
+
return data.message;
|
|
2296
2475
|
}
|
|
2297
|
-
if (
|
|
2298
|
-
|
|
2476
|
+
if ("error" in data) {
|
|
2477
|
+
if (typeof data.error === "string") {
|
|
2478
|
+
return data.error;
|
|
2479
|
+
}
|
|
2480
|
+
if (typeof data.error === "object" && data.error !== null) {
|
|
2481
|
+
if ("message" in data.error && typeof data.error.message === "string") {
|
|
2482
|
+
return data.error.message;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
try {
|
|
2486
|
+
return JSON.stringify(data.error);
|
|
2487
|
+
} catch {
|
|
2488
|
+
}
|
|
2299
2489
|
}
|
|
2300
|
-
if (
|
|
2301
|
-
|
|
2490
|
+
if ("errors" in data && Array.isArray(data.errors)) {
|
|
2491
|
+
if (this.isApiErrorArray(data.errors)) {
|
|
2492
|
+
return data.errors[0].detail || data.errors[0].title;
|
|
2493
|
+
}
|
|
2302
2494
|
}
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2495
|
+
}
|
|
2496
|
+
return void 0;
|
|
2497
|
+
}
|
|
2498
|
+
// Helper to parse API error response
|
|
2499
|
+
parseErrorResponse(errorInfo) {
|
|
2500
|
+
const fallbackMessage = `HTTP ${errorInfo.status}: ${errorInfo.statusText}`;
|
|
2501
|
+
try {
|
|
2502
|
+
if (typeof errorInfo.data === "string") {
|
|
2503
|
+
return { message: `${fallbackMessage}: ${errorInfo.data}` };
|
|
2306
2504
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
}
|
|
2325
|
-
return void 0;
|
|
2326
|
-
},
|
|
2327
|
-
authRequired: true
|
|
2505
|
+
const errorMessage = this.extractErrorMessage(errorInfo.data) || fallbackMessage;
|
|
2506
|
+
if (this.hasErrorArray(errorInfo.data)) {
|
|
2507
|
+
if (this.isApiErrorArray(errorInfo.data.errors)) {
|
|
2508
|
+
return {
|
|
2509
|
+
message: errorMessage,
|
|
2510
|
+
errors: errorInfo.data.errors
|
|
2511
|
+
};
|
|
2512
|
+
} else {
|
|
2513
|
+
return {
|
|
2514
|
+
message: errorMessage,
|
|
2515
|
+
errors: errorInfo.data.errors.map((e) => ({
|
|
2516
|
+
status: errorInfo.status,
|
|
2517
|
+
code: String(errorInfo.status),
|
|
2518
|
+
title: errorInfo.statusText,
|
|
2519
|
+
detail: JSON.stringify(e)
|
|
2520
|
+
}))
|
|
2521
|
+
};
|
|
2328
2522
|
}
|
|
2329
|
-
);
|
|
2330
|
-
let auths = (data.results || []).map(
|
|
2331
|
-
(auth) => normalizeAuthenticationItem(auth)
|
|
2332
|
-
);
|
|
2333
|
-
if (options.title) {
|
|
2334
|
-
auths = auths.filter((auth) => auth.title === options.title);
|
|
2335
2523
|
}
|
|
2336
|
-
return {
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2524
|
+
return { message: errorMessage };
|
|
2525
|
+
} catch {
|
|
2526
|
+
return { message: fallbackMessage };
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
// Check if this is a path that needs subdomain routing
|
|
2530
|
+
// e.g. /relay/workflows -> relay.zapier.com/workflows
|
|
2531
|
+
applySubdomainBehavior(path) {
|
|
2532
|
+
const pathSegments = path.split("/").filter(Boolean);
|
|
2533
|
+
if (pathSegments.length > 0 && pathSegments[0] in SubdomainConfigMap) {
|
|
2534
|
+
const domainPrefix = pathSegments[0];
|
|
2535
|
+
const subdomainConfig = SubdomainConfigMap[domainPrefix];
|
|
2536
|
+
const originalBaseUrl = new URL(this.options.baseUrl);
|
|
2537
|
+
const finalBaseUrl = `https://${domainPrefix}.${originalBaseUrl.hostname}`;
|
|
2538
|
+
const pathWithoutPrefix = "/" + pathSegments.slice(1).join("/");
|
|
2539
|
+
return { url: new URL(pathWithoutPrefix, finalBaseUrl), subdomainConfig };
|
|
2540
|
+
}
|
|
2541
|
+
return {
|
|
2542
|
+
url: new URL(path, this.options.baseUrl),
|
|
2543
|
+
subdomainConfig: void 0
|
|
2544
|
+
};
|
|
2545
|
+
}
|
|
2546
|
+
// Helper to build full URLs and return routing info
|
|
2547
|
+
buildUrl(path, searchParams) {
|
|
2548
|
+
const { url, subdomainConfig } = this.applySubdomainBehavior(path);
|
|
2549
|
+
if (searchParams) {
|
|
2550
|
+
Object.entries(searchParams).forEach(([key, value]) => {
|
|
2551
|
+
url.searchParams.set(key, value);
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
return { url: url.toString(), subdomainConfig };
|
|
2555
|
+
}
|
|
2556
|
+
// Helper to build headers
|
|
2557
|
+
async buildHeaders(options = {}, subdomainConfig) {
|
|
2558
|
+
const headers = new Headers(options.headers ?? {});
|
|
2559
|
+
const authToken = await this.getAuthToken();
|
|
2560
|
+
if (authToken) {
|
|
2561
|
+
const authHeaderName = subdomainConfig?.authHeader || "Authorization";
|
|
2562
|
+
headers.set(authHeaderName, getAuthorizationHeader(authToken));
|
|
2563
|
+
}
|
|
2564
|
+
if (options.authRequired) {
|
|
2565
|
+
if (headers.get("Authorization") == null && authToken == null) {
|
|
2566
|
+
throw new ZapierAuthenticationError(
|
|
2567
|
+
`Authentication required but no token available. Please set ZAPIER_TOKEN, or run the 'login' command with the CLI.`
|
|
2568
|
+
);
|
|
2351
2569
|
}
|
|
2352
2570
|
}
|
|
2353
|
-
|
|
2571
|
+
return headers;
|
|
2572
|
+
}
|
|
2573
|
+
// Helper to perform HTTP requests with JSON handling
|
|
2574
|
+
async fetchJson(method, path, data, options = {}) {
|
|
2575
|
+
const headers = { ...options.headers };
|
|
2576
|
+
if (data && typeof data === "object") {
|
|
2577
|
+
headers["Content-Type"] = "application/json";
|
|
2578
|
+
}
|
|
2579
|
+
const wasMissingAuthToken = options.authRequired && await this.getAuthToken() == null;
|
|
2580
|
+
const response = await this.plainFetch(path, {
|
|
2581
|
+
...options,
|
|
2582
|
+
method,
|
|
2583
|
+
body: data != null ? JSON.stringify(data) : void 0,
|
|
2584
|
+
headers
|
|
2585
|
+
});
|
|
2586
|
+
const result = await this.handleResponse({
|
|
2587
|
+
response,
|
|
2588
|
+
customErrorHandler: options.customErrorHandler,
|
|
2589
|
+
wasMissingAuthToken
|
|
2590
|
+
});
|
|
2591
|
+
if (typeof result === "string") {
|
|
2592
|
+
throw new ZapierValidationError(
|
|
2593
|
+
`Response could not be parsed as JSON: ${result}`
|
|
2594
|
+
);
|
|
2595
|
+
}
|
|
2596
|
+
return result;
|
|
2597
|
+
}
|
|
2598
|
+
// Plain fetch method for API paths (must start with /)
|
|
2599
|
+
async plainFetch(path, fetchOptions) {
|
|
2600
|
+
if (!path.startsWith("/")) {
|
|
2601
|
+
throw new ZapierValidationError(
|
|
2602
|
+
`plainFetch expects a path starting with '/', got: ${path}`
|
|
2603
|
+
);
|
|
2604
|
+
}
|
|
2605
|
+
if (fetchOptions?.body && typeof fetchOptions.body === "object") {
|
|
2606
|
+
fetchOptions.body = JSON.stringify(fetchOptions.body);
|
|
2607
|
+
}
|
|
2608
|
+
const { url, subdomainConfig } = this.buildUrl(
|
|
2609
|
+
path,
|
|
2610
|
+
fetchOptions?.searchParams
|
|
2611
|
+
);
|
|
2612
|
+
const builtHeaders = await this.buildHeaders(
|
|
2613
|
+
fetchOptions,
|
|
2614
|
+
subdomainConfig
|
|
2615
|
+
);
|
|
2616
|
+
const inputHeaders = new Headers(fetchOptions?.headers ?? {});
|
|
2617
|
+
const mergedHeaders = new Headers();
|
|
2618
|
+
builtHeaders.forEach((value, key) => {
|
|
2619
|
+
mergedHeaders.set(key, value);
|
|
2620
|
+
});
|
|
2621
|
+
inputHeaders.forEach((value, key) => {
|
|
2622
|
+
mergedHeaders.set(key, value);
|
|
2623
|
+
});
|
|
2624
|
+
return await this.options.fetch(url, {
|
|
2625
|
+
...fetchOptions,
|
|
2626
|
+
headers: mergedHeaders
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
};
|
|
2630
|
+
var createZapierApi = (options) => {
|
|
2631
|
+
const {
|
|
2632
|
+
baseUrl,
|
|
2633
|
+
token,
|
|
2634
|
+
getToken,
|
|
2635
|
+
debug = false,
|
|
2636
|
+
fetch: originalFetch = globalThis.fetch,
|
|
2637
|
+
onEvent
|
|
2638
|
+
} = options;
|
|
2639
|
+
const debugLog = createDebugLogger(debug);
|
|
2640
|
+
const debugFetch = createDebugFetch({ originalFetch, debugLog });
|
|
2641
|
+
return new ZapierApiClient({
|
|
2642
|
+
baseUrl,
|
|
2643
|
+
token,
|
|
2644
|
+
getToken,
|
|
2645
|
+
debug,
|
|
2646
|
+
fetch: debugFetch,
|
|
2647
|
+
onEvent
|
|
2648
|
+
});
|
|
2354
2649
|
};
|
|
2355
|
-
var GetAuthenticationSchema = zod.z.object({
|
|
2356
|
-
authenticationId: zod.z.number().int().positive().describe("Authentication ID to retrieve")
|
|
2357
|
-
}).describe("Get a specific authentication by ID");
|
|
2358
2650
|
|
|
2359
|
-
// src/plugins/
|
|
2360
|
-
var
|
|
2361
|
-
const
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
{ statusCode: status }
|
|
2378
|
-
);
|
|
2379
|
-
}
|
|
2380
|
-
if (status === 404) {
|
|
2381
|
-
return new ZapierResourceNotFoundError(
|
|
2382
|
-
`Authentication ${authenticationId} not found. It may not exist or you may not have access to it. (HTTP ${status})`,
|
|
2383
|
-
{
|
|
2384
|
-
resourceType: "Authentication",
|
|
2385
|
-
resourceId: String(authenticationId)
|
|
2386
|
-
}
|
|
2387
|
-
);
|
|
2388
|
-
}
|
|
2389
|
-
return void 0;
|
|
2390
|
-
},
|
|
2391
|
-
authRequired: true
|
|
2392
|
-
}
|
|
2393
|
-
);
|
|
2394
|
-
return {
|
|
2395
|
-
data: normalizeAuthenticationItem(data)
|
|
2396
|
-
};
|
|
2397
|
-
}, GetAuthenticationSchema);
|
|
2651
|
+
// src/plugins/api/index.ts
|
|
2652
|
+
var apiPlugin = (params) => {
|
|
2653
|
+
const {
|
|
2654
|
+
fetch: customFetch = globalThis.fetch,
|
|
2655
|
+
baseUrl = "https://zapier.com",
|
|
2656
|
+
token,
|
|
2657
|
+
getToken,
|
|
2658
|
+
onEvent,
|
|
2659
|
+
debug = false
|
|
2660
|
+
} = params.context.options;
|
|
2661
|
+
const api = createZapierApi({
|
|
2662
|
+
baseUrl,
|
|
2663
|
+
token,
|
|
2664
|
+
getToken,
|
|
2665
|
+
debug,
|
|
2666
|
+
fetch: customFetch,
|
|
2667
|
+
onEvent
|
|
2668
|
+
});
|
|
2398
2669
|
return {
|
|
2399
|
-
getAuthentication,
|
|
2400
2670
|
context: {
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
categories: ["authentication"],
|
|
2404
|
-
inputSchema: GetAuthenticationSchema
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2671
|
+
api
|
|
2672
|
+
// Provide API client in context for other plugins to use
|
|
2407
2673
|
}
|
|
2408
2674
|
};
|
|
2409
2675
|
};
|
|
2410
|
-
var FindFirstAuthenticationSchema = zod.z.object({
|
|
2411
|
-
appKey: AppKeyPropertySchema.optional().describe(
|
|
2412
|
-
"App key of authentication to find (e.g., 'SlackCLIAPI')"
|
|
2413
|
-
),
|
|
2414
|
-
search: zod.z.string().optional().describe("Search term to filter authentications by title"),
|
|
2415
|
-
title: zod.z.string().optional().describe("Filter authentications by exact title match"),
|
|
2416
|
-
account_id: zod.z.string().optional().describe("Filter by account ID"),
|
|
2417
|
-
owner: zod.z.string().optional().describe("Filter by owner")
|
|
2418
|
-
}).describe("Find the first authentication matching the criteria");
|
|
2419
2676
|
|
|
2420
|
-
// src/
|
|
2421
|
-
var
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
...options,
|
|
2426
|
-
maxItems: 1
|
|
2427
|
-
});
|
|
2428
|
-
return {
|
|
2429
|
-
data: authsResponse.data.length > 0 ? authsResponse.data[0] : null
|
|
2430
|
-
};
|
|
2431
|
-
},
|
|
2432
|
-
FindFirstAuthenticationSchema
|
|
2433
|
-
);
|
|
2434
|
-
return {
|
|
2435
|
-
findFirstAuthentication,
|
|
2436
|
-
context: {
|
|
2437
|
-
meta: {
|
|
2438
|
-
findFirstAuthentication: {
|
|
2439
|
-
categories: ["authentication"],
|
|
2440
|
-
inputSchema: FindFirstAuthenticationSchema
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
};
|
|
2677
|
+
// src/resolvers/appKey.ts
|
|
2678
|
+
var appKeyResolver = {
|
|
2679
|
+
type: "static",
|
|
2680
|
+
inputType: "text",
|
|
2681
|
+
placeholder: "Enter app key (e.g., 'SlackCLIAPI' or slug like 'github')"
|
|
2445
2682
|
};
|
|
2446
|
-
var FindUniqueAuthenticationSchema = zod.z.object({
|
|
2447
|
-
appKey: AppKeyPropertySchema.optional().describe(
|
|
2448
|
-
"App key of authentication to find (e.g., 'SlackCLIAPI')"
|
|
2449
|
-
),
|
|
2450
|
-
search: zod.z.string().optional().describe("Search term to filter authentications by title"),
|
|
2451
|
-
title: zod.z.string().optional().describe("Filter authentications by exact title match"),
|
|
2452
|
-
account_id: zod.z.string().optional().describe("Filter by account ID"),
|
|
2453
|
-
owner: zod.z.string().optional().describe("Filter by owner")
|
|
2454
|
-
}).describe("Find a unique authentication matching the criteria");
|
|
2455
2683
|
|
|
2456
|
-
// src/
|
|
2457
|
-
var
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2684
|
+
// src/resolvers/actionType.ts
|
|
2685
|
+
var actionTypeResolver = {
|
|
2686
|
+
type: "dynamic",
|
|
2687
|
+
depends: ["appKey"],
|
|
2688
|
+
fetch: async (sdk, resolvedParams) => {
|
|
2689
|
+
const actionsResponse = await sdk.listActions({
|
|
2690
|
+
appKey: resolvedParams.appKey
|
|
2691
|
+
});
|
|
2692
|
+
const types = [
|
|
2693
|
+
...new Set(actionsResponse.data.map((action) => action.action_type))
|
|
2694
|
+
];
|
|
2695
|
+
return types.map((type) => ({ key: type, name: type }));
|
|
2696
|
+
},
|
|
2697
|
+
prompt: (types) => ({
|
|
2698
|
+
type: "list",
|
|
2699
|
+
name: "actionType",
|
|
2700
|
+
message: "Select action type:",
|
|
2701
|
+
choices: types.map((type) => ({
|
|
2702
|
+
name: type.name,
|
|
2703
|
+
value: type.key
|
|
2704
|
+
}))
|
|
2705
|
+
})
|
|
2706
|
+
};
|
|
2707
|
+
|
|
2708
|
+
// src/resolvers/actionKey.ts
|
|
2709
|
+
var actionKeyResolver = {
|
|
2710
|
+
type: "dynamic",
|
|
2711
|
+
depends: ["appKey", "actionType"],
|
|
2712
|
+
fetch: async (sdk, resolvedParams) => {
|
|
2713
|
+
const actionsResponse = await sdk.listActions({
|
|
2714
|
+
appKey: resolvedParams.appKey
|
|
2715
|
+
});
|
|
2716
|
+
return actionsResponse.data.filter(
|
|
2717
|
+
(action) => action.action_type === resolvedParams.actionType
|
|
2718
|
+
);
|
|
2719
|
+
},
|
|
2720
|
+
prompt: (actions) => ({
|
|
2721
|
+
type: "list",
|
|
2722
|
+
name: "actionKey",
|
|
2723
|
+
message: "Select action:",
|
|
2724
|
+
choices: actions.map((action) => ({
|
|
2725
|
+
name: `${action.title || action.name || action.key} - ${action.description || "No description"}`,
|
|
2726
|
+
value: action.key
|
|
2727
|
+
}))
|
|
2728
|
+
})
|
|
2729
|
+
};
|
|
2730
|
+
|
|
2731
|
+
// src/resolvers/authenticationId.ts
|
|
2732
|
+
var authenticationIdResolver = {
|
|
2733
|
+
type: "dynamic",
|
|
2734
|
+
depends: ["appKey"],
|
|
2735
|
+
fetch: async (sdk, resolvedParams) => {
|
|
2736
|
+
const myAuths = await sdk.listAuthentications({
|
|
2737
|
+
appKey: resolvedParams.appKey,
|
|
2738
|
+
maxItems: 1e3,
|
|
2739
|
+
owner: "me"
|
|
2740
|
+
});
|
|
2741
|
+
const allAuths = await sdk.listAuthentications({
|
|
2742
|
+
appKey: resolvedParams.appKey,
|
|
2743
|
+
maxItems: 1e3
|
|
2744
|
+
});
|
|
2745
|
+
const otherAuths = allAuths.data.filter(
|
|
2746
|
+
(auth) => !myAuths.data.some((myAuth) => myAuth.id === auth.id)
|
|
2747
|
+
);
|
|
2748
|
+
return [...myAuths.data, ...otherAuths];
|
|
2749
|
+
},
|
|
2750
|
+
prompt: (auths, params) => ({
|
|
2751
|
+
type: "list",
|
|
2752
|
+
name: "authenticationId",
|
|
2753
|
+
message: `Select authentication for ${params.appKey}:`,
|
|
2754
|
+
choices: [
|
|
2755
|
+
...auths.map((auth) => ({
|
|
2756
|
+
name: `${auth.title || auth.label || "Authentication"} (ID: ${auth.id})`,
|
|
2757
|
+
value: auth.id
|
|
2758
|
+
})),
|
|
2759
|
+
{
|
|
2760
|
+
name: "\u2197 Skip authentication (may fail)",
|
|
2761
|
+
value: null
|
|
2490
2762
|
}
|
|
2491
|
-
|
|
2492
|
-
}
|
|
2763
|
+
]
|
|
2764
|
+
})
|
|
2493
2765
|
};
|
|
2494
|
-
var ListInputFieldsSchema = zod.z.object({
|
|
2495
|
-
appKey: AppKeyPropertySchema,
|
|
2496
|
-
actionType: ActionTypePropertySchema,
|
|
2497
|
-
actionKey: ActionKeyPropertySchema,
|
|
2498
|
-
authenticationId: AuthenticationIdPropertySchema.nullable().optional(),
|
|
2499
|
-
inputs: InputsPropertySchema.optional().describe(
|
|
2500
|
-
"Current input values that may affect available fields"
|
|
2501
|
-
),
|
|
2502
|
-
pageSize: zod.z.number().min(1).optional().describe("Number of input fields per page"),
|
|
2503
|
-
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
2504
|
-
}).describe("Get the input fields required for a specific action");
|
|
2505
2766
|
|
|
2506
|
-
// src/
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2767
|
+
// src/resolvers/inputs.ts
|
|
2768
|
+
var inputsResolver = {
|
|
2769
|
+
type: "fields",
|
|
2770
|
+
depends: ["appKey", "actionKey", "actionType", "authenticationId"],
|
|
2771
|
+
fetch: async (sdk, resolvedParams) => {
|
|
2772
|
+
const fieldsResponse = await sdk.listInputFields({
|
|
2773
|
+
appKey: resolvedParams.appKey,
|
|
2774
|
+
actionKey: resolvedParams.actionKey,
|
|
2775
|
+
actionType: resolvedParams.actionType,
|
|
2776
|
+
authenticationId: resolvedParams.authenticationId,
|
|
2777
|
+
inputs: resolvedParams.inputs
|
|
2778
|
+
// Pass along currently resolved inputs
|
|
2779
|
+
});
|
|
2780
|
+
return fieldsResponse.data;
|
|
2510
2781
|
}
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2782
|
+
};
|
|
2783
|
+
|
|
2784
|
+
// src/resolvers/index.ts
|
|
2785
|
+
var resolverRegistry = {
|
|
2786
|
+
appKey: appKeyResolver,
|
|
2787
|
+
actionType: actionTypeResolver,
|
|
2788
|
+
actionKey: actionKeyResolver,
|
|
2789
|
+
authenticationId: authenticationIdResolver,
|
|
2790
|
+
inputs: inputsResolver
|
|
2791
|
+
};
|
|
2792
|
+
function getResolver(name) {
|
|
2793
|
+
return resolverRegistry[name];
|
|
2519
2794
|
}
|
|
2520
|
-
function
|
|
2521
|
-
|
|
2522
|
-
|
|
2795
|
+
function getResolversForMissingParams(missingParams) {
|
|
2796
|
+
const resolvers = {};
|
|
2797
|
+
for (const param of missingParams) {
|
|
2798
|
+
const resolver = resolverRegistry[param];
|
|
2799
|
+
if (resolver) {
|
|
2800
|
+
resolvers[param] = resolver;
|
|
2801
|
+
}
|
|
2523
2802
|
}
|
|
2524
|
-
|
|
2525
|
-
text: "MULTILINE" /* MULTILINE */,
|
|
2526
|
-
datetime: "DATETIME" /* DATETIME */,
|
|
2527
|
-
file: "FILE" /* FILE */,
|
|
2528
|
-
password: "PASSWORD" /* PASSWORD */,
|
|
2529
|
-
code: "CODE" /* CODE */
|
|
2530
|
-
};
|
|
2531
|
-
return formatMap[need.type || ""];
|
|
2803
|
+
return resolvers;
|
|
2532
2804
|
}
|
|
2533
|
-
function
|
|
2534
|
-
|
|
2535
|
-
return void 0;
|
|
2536
|
-
}
|
|
2537
|
-
const typeMap = {
|
|
2538
|
-
string: "STRING" /* STRING */,
|
|
2539
|
-
decimal: "NUMBER" /* NUMBER */,
|
|
2540
|
-
integer: "INTEGER" /* INTEGER */,
|
|
2541
|
-
boolean: "BOOLEAN" /* BOOLEAN */,
|
|
2542
|
-
dict: "OBJECT" /* OBJECT */
|
|
2543
|
-
};
|
|
2544
|
-
return typeMap[need.type || ""] || "STRING" /* STRING */;
|
|
2805
|
+
function hasResolver(paramName) {
|
|
2806
|
+
return paramName in resolverRegistry;
|
|
2545
2807
|
}
|
|
2546
|
-
function
|
|
2547
|
-
|
|
2548
|
-
return {
|
|
2549
|
-
...need,
|
|
2550
|
-
// Pass through all original Need fields
|
|
2551
|
-
id: need.key,
|
|
2552
|
-
default_value: need.default || "",
|
|
2553
|
-
depends_on: need.depends_on || [],
|
|
2554
|
-
description: need.help_text || "",
|
|
2555
|
-
invalidates_input_fields: need.alters_custom_fields || false,
|
|
2556
|
-
is_required: need.required || false,
|
|
2557
|
-
placeholder: need.placeholder || "",
|
|
2558
|
-
title: need.label || "",
|
|
2559
|
-
value_type: getInputFieldTypeFromNeed(need),
|
|
2560
|
-
format: getInputFieldFormatFromNeed(need),
|
|
2561
|
-
items: itemsType ? { type: itemsType } : void 0
|
|
2562
|
-
};
|
|
2808
|
+
function getResolvableParams() {
|
|
2809
|
+
return Object.keys(resolverRegistry);
|
|
2563
2810
|
}
|
|
2564
|
-
|
|
2565
|
-
const
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
);
|
|
2575
|
-
}
|
|
2576
|
-
const needsRequest = {
|
|
2577
|
-
selected_api: selectedApi,
|
|
2578
|
-
action: actionKey,
|
|
2579
|
-
type_of: actionType,
|
|
2580
|
-
params: inputs || {}
|
|
2581
|
-
};
|
|
2582
|
-
if (authenticationId !== null) {
|
|
2583
|
-
needsRequest.authentication_id = authenticationId;
|
|
2584
|
-
}
|
|
2585
|
-
const needsData = await api.post(
|
|
2586
|
-
"/api/v4/implementations/needs/",
|
|
2587
|
-
needsRequest
|
|
2588
|
-
);
|
|
2589
|
-
if (!needsData.success) {
|
|
2590
|
-
throw new ZapierApiError(
|
|
2591
|
-
`Failed to get action fields: ${needsData.errors?.join(", ") || "Unknown error"}`
|
|
2592
|
-
);
|
|
2593
|
-
}
|
|
2594
|
-
const inputFields = (needsData.needs || []).map(
|
|
2595
|
-
transformNeedToInputFieldItem
|
|
2596
|
-
);
|
|
2597
|
-
return {
|
|
2598
|
-
data: inputFields,
|
|
2599
|
-
nextCursor: void 0
|
|
2600
|
-
// No pagination needed since we return all input fields
|
|
2601
|
-
};
|
|
2602
|
-
},
|
|
2603
|
-
ListInputFieldsSchema
|
|
2604
|
-
);
|
|
2605
|
-
return {
|
|
2606
|
-
listInputFields,
|
|
2607
|
-
context: {
|
|
2608
|
-
meta: {
|
|
2609
|
-
listInputFields: {
|
|
2610
|
-
categories: ["action"],
|
|
2611
|
-
inputSchema: ListInputFieldsSchema
|
|
2612
|
-
}
|
|
2811
|
+
function getResolutionOrder(paramName, resolved = /* @__PURE__ */ new Set()) {
|
|
2812
|
+
const resolver = getResolver(paramName);
|
|
2813
|
+
if (!resolver || resolver.type === "static") {
|
|
2814
|
+
return [paramName];
|
|
2815
|
+
}
|
|
2816
|
+
const order = [];
|
|
2817
|
+
if ("depends" in resolver && resolver.depends) {
|
|
2818
|
+
for (const dependency of resolver.depends) {
|
|
2819
|
+
if (!resolved.has(dependency)) {
|
|
2820
|
+
order.push(...getResolutionOrder(dependency, resolved));
|
|
2821
|
+
resolved.add(dependency);
|
|
2613
2822
|
}
|
|
2614
2823
|
}
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
const relayPath = `/relay/${targetUrl.host}${targetUrl.pathname}${targetUrl.search}${targetUrl.hash}`;
|
|
2622
|
-
return relayPath;
|
|
2824
|
+
}
|
|
2825
|
+
if (!resolved.has(paramName)) {
|
|
2826
|
+
order.push(paramName);
|
|
2827
|
+
resolved.add(paramName);
|
|
2828
|
+
}
|
|
2829
|
+
return order;
|
|
2623
2830
|
}
|
|
2624
|
-
|
|
2625
|
-
const
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
authenticationId,
|
|
2633
|
-
callbackUrl,
|
|
2634
|
-
authenticationTemplate
|
|
2635
|
-
} = options;
|
|
2636
|
-
const relayPath = transformUrlToRelayPath(url);
|
|
2637
|
-
const headers = {};
|
|
2638
|
-
if (optionsHeaders) {
|
|
2639
|
-
const headerEntries = optionsHeaders instanceof Headers ? Array.from(optionsHeaders.entries()) : Array.isArray(optionsHeaders) ? optionsHeaders : Object.entries(optionsHeaders);
|
|
2640
|
-
for (const [key, value] of headerEntries) {
|
|
2641
|
-
headers[key] = value;
|
|
2642
|
-
}
|
|
2643
|
-
}
|
|
2644
|
-
if (authenticationId) {
|
|
2645
|
-
headers["X-Relay-Authentication-Id"] = authenticationId.toString();
|
|
2646
|
-
}
|
|
2647
|
-
if (callbackUrl) {
|
|
2648
|
-
headers["X-Relay-Callback-Url"] = callbackUrl;
|
|
2649
|
-
}
|
|
2650
|
-
if (authenticationTemplate) {
|
|
2651
|
-
headers["X-Authentication-Template"] = authenticationTemplate;
|
|
2652
|
-
}
|
|
2653
|
-
return await api.fetch(relayPath, {
|
|
2654
|
-
method,
|
|
2655
|
-
body,
|
|
2656
|
-
headers
|
|
2657
|
-
});
|
|
2658
|
-
}, RelayRequestSchema);
|
|
2659
|
-
return {
|
|
2660
|
-
request,
|
|
2661
|
-
context: {
|
|
2662
|
-
meta: {
|
|
2663
|
-
request: {
|
|
2664
|
-
categories: ["http"],
|
|
2665
|
-
inputSchema: RelayRequestSchema
|
|
2666
|
-
}
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
};
|
|
2670
|
-
};
|
|
2671
|
-
var ManifestSchema = zod.z.object({
|
|
2672
|
-
apps: zod.z.record(
|
|
2673
|
-
zod.z.string(),
|
|
2674
|
-
zod.z.object({
|
|
2675
|
-
implementationName: zod.z.string().describe(
|
|
2676
|
-
"Base implementation name without version (e.g., 'SlackCLIAPI')"
|
|
2677
|
-
),
|
|
2678
|
-
version: zod.z.string().describe("Version string (e.g., '1.21.1')")
|
|
2679
|
-
})
|
|
2680
|
-
)
|
|
2681
|
-
}).describe("Manifest mapping app keys to version information");
|
|
2682
|
-
zod.z.object({
|
|
2683
|
-
manifestPath: zod.z.string().optional().describe("Path to manifest file"),
|
|
2684
|
-
manifest: zod.z.record(
|
|
2685
|
-
zod.z.string(),
|
|
2686
|
-
zod.z.object({
|
|
2687
|
-
implementationName: zod.z.string(),
|
|
2688
|
-
version: zod.z.string().optional()
|
|
2689
|
-
})
|
|
2690
|
-
).optional().describe("Direct manifest object")
|
|
2691
|
-
});
|
|
2692
|
-
|
|
2693
|
-
// src/plugins/manifest/index.ts
|
|
2694
|
-
function parseManifestContent(content, source) {
|
|
2695
|
-
try {
|
|
2696
|
-
const parsed = JSON.parse(content);
|
|
2697
|
-
if (parsed?.apps && typeof parsed?.apps === "object") {
|
|
2698
|
-
const result = ManifestSchema.safeParse(parsed);
|
|
2699
|
-
if (result.success) {
|
|
2700
|
-
return result.data;
|
|
2831
|
+
function getResolutionOrderForParams(paramNames) {
|
|
2832
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
2833
|
+
const order = [];
|
|
2834
|
+
for (const paramName of paramNames) {
|
|
2835
|
+
const paramOrder = getResolutionOrder(paramName, resolved);
|
|
2836
|
+
for (const param of paramOrder) {
|
|
2837
|
+
if (!order.includes(param)) {
|
|
2838
|
+
order.push(param);
|
|
2701
2839
|
}
|
|
2702
|
-
console.warn(`\u26A0\uFE0F Invalid manifest format in ${source}: ${result.error}`);
|
|
2703
2840
|
}
|
|
2704
|
-
return null;
|
|
2705
|
-
} catch (error) {
|
|
2706
|
-
console.warn(`\u26A0\uFE0F Failed to parse manifest from ${source}:`, error);
|
|
2707
|
-
return null;
|
|
2708
2841
|
}
|
|
2842
|
+
return order;
|
|
2709
2843
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2844
|
+
|
|
2845
|
+
// src/plugins/registry/index.ts
|
|
2846
|
+
var registryPlugin = ({ sdk, context }) => {
|
|
2847
|
+
const metaKeys = Object.keys(context.meta || {});
|
|
2848
|
+
const categoryDefinitions = {
|
|
2849
|
+
account: {
|
|
2850
|
+
title: "Account"
|
|
2851
|
+
},
|
|
2852
|
+
app: {
|
|
2853
|
+
title: "App",
|
|
2854
|
+
titlePlural: "Apps"
|
|
2855
|
+
},
|
|
2856
|
+
authentication: {
|
|
2857
|
+
title: "Authentication"
|
|
2858
|
+
},
|
|
2859
|
+
action: {
|
|
2860
|
+
title: "Action"
|
|
2861
|
+
},
|
|
2862
|
+
http: {
|
|
2863
|
+
title: "HTTP Request"
|
|
2864
|
+
},
|
|
2865
|
+
utility: {
|
|
2866
|
+
title: "Utility",
|
|
2867
|
+
titlePlural: "Utilities"
|
|
2868
|
+
},
|
|
2869
|
+
other: {
|
|
2870
|
+
title: "Other"
|
|
2871
|
+
}
|
|
2872
|
+
};
|
|
2873
|
+
const functions = metaKeys.filter((key) => typeof sdk[key] === "function").map((key) => {
|
|
2874
|
+
return {
|
|
2875
|
+
...context.meta[key],
|
|
2876
|
+
categories: context.meta[key].categories || [],
|
|
2877
|
+
name: key
|
|
2878
|
+
};
|
|
2879
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
2880
|
+
const knownCategories = Object.keys(categoryDefinitions);
|
|
2881
|
+
const categories = knownCategories.sort((a, b) => {
|
|
2882
|
+
if (a === "other") return 1;
|
|
2883
|
+
if (b === "other") return -1;
|
|
2884
|
+
const titleA = categoryDefinitions[a].title;
|
|
2885
|
+
const titleB = categoryDefinitions[b].title;
|
|
2886
|
+
return titleA.localeCompare(titleB);
|
|
2887
|
+
}).map((categoryKey) => {
|
|
2888
|
+
const categoryFunctions = functions.filter(
|
|
2889
|
+
(f) => f.categories.includes(categoryKey) || // If the category is "other" and the function is not in any other category, include it
|
|
2890
|
+
categoryKey === "other" && !f.categories.some((c) => knownCategories.includes(c))
|
|
2891
|
+
).map((f) => f.name).sort();
|
|
2892
|
+
const definition = categoryDefinitions[categoryKey];
|
|
2893
|
+
const title = definition.title;
|
|
2894
|
+
return {
|
|
2895
|
+
key: categoryKey,
|
|
2896
|
+
title,
|
|
2897
|
+
titlePlural: definition.titlePlural ?? `${title}s`,
|
|
2898
|
+
functions: categoryFunctions
|
|
2899
|
+
};
|
|
2900
|
+
});
|
|
2901
|
+
function getRegistry() {
|
|
2902
|
+
return {
|
|
2903
|
+
functions,
|
|
2904
|
+
categories
|
|
2905
|
+
};
|
|
2718
2906
|
}
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
`
|
|
2723
|
-
${"\u26A0\uFE0F".padEnd(3)} ${"WARNING".padEnd(8)} No manifest version found for '${appKey}'`
|
|
2724
|
-
);
|
|
2725
|
-
console.warn(
|
|
2726
|
-
` ${"\u21B3".padEnd(3)} Using a manifest ensures version locking and prevents unexpected behavior due to version changes.`
|
|
2727
|
-
);
|
|
2728
|
-
console.warn(
|
|
2729
|
-
` ${"\u21B3".padEnd(3)} Generate/update the manifest with: \`zapier-sdk lock-version ${appKey}\`
|
|
2730
|
-
`
|
|
2731
|
-
);
|
|
2907
|
+
return {
|
|
2908
|
+
getRegistry
|
|
2909
|
+
};
|
|
2732
2910
|
};
|
|
2733
|
-
var
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2911
|
+
var InputFieldChoiceItemSchema = withFormatter(
|
|
2912
|
+
zod.z.object({
|
|
2913
|
+
key: zod.z.string().optional().describe("Unique key/value for the choice"),
|
|
2914
|
+
label: zod.z.string().optional().describe("Human readable label for the choice"),
|
|
2915
|
+
sample: zod.z.string().optional().describe("Sample value for the choice"),
|
|
2916
|
+
value: zod.z.string().optional().describe("Value to be submitted when selected")
|
|
2917
|
+
}),
|
|
2918
|
+
{
|
|
2919
|
+
format: (item) => {
|
|
2920
|
+
const title = item.label || item.key || "Choice";
|
|
2921
|
+
const subtitle = item.label && item.key && item.label !== item.key ? `(${item.key})` : void 0;
|
|
2922
|
+
const details = [];
|
|
2923
|
+
if (item.sample && item.sample !== item.key) {
|
|
2924
|
+
details.push({ text: `Sample: ${item.sample}`, style: "dim" });
|
|
2925
|
+
}
|
|
2926
|
+
if (item.value && item.value !== item.key) {
|
|
2927
|
+
details.push({ text: `Value: ${item.value}`, style: "normal" });
|
|
2928
|
+
}
|
|
2929
|
+
return {
|
|
2930
|
+
title,
|
|
2931
|
+
subtitle,
|
|
2932
|
+
details
|
|
2933
|
+
};
|
|
2744
2934
|
}
|
|
2745
|
-
return null;
|
|
2746
2935
|
}
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2936
|
+
);
|
|
2937
|
+
var ListInputFieldChoicesSchema = withOutputSchema(
|
|
2938
|
+
zod.z.object({
|
|
2939
|
+
// Required action identification
|
|
2940
|
+
appKey: AppKeyPropertySchema,
|
|
2941
|
+
actionType: ActionTypePropertySchema,
|
|
2942
|
+
actionKey: ActionKeyPropertySchema,
|
|
2943
|
+
// Input field specification
|
|
2944
|
+
inputFieldKey: zod.z.string().min(1).describe("Input field key to get choices for."),
|
|
2945
|
+
// Common parameters
|
|
2946
|
+
authenticationId: AuthenticationIdPropertySchema.nullable().optional(),
|
|
2947
|
+
inputs: InputsPropertySchema.optional().describe(
|
|
2948
|
+
"Current input values that may affect available choices"
|
|
2949
|
+
),
|
|
2950
|
+
page: zod.z.number().int().min(0).optional().describe("Page number for paginated results"),
|
|
2951
|
+
// Pagination options (SDK-level)
|
|
2952
|
+
pageSize: zod.z.number().min(1).optional().describe("Number of choices per page"),
|
|
2953
|
+
maxItems: zod.z.number().min(1).optional().describe("Maximum total items to return across all pages")
|
|
2954
|
+
}).describe("Get the available choices for a dynamic dropdown input field"),
|
|
2955
|
+
InputFieldChoiceItemSchema
|
|
2956
|
+
);
|
|
2957
|
+
|
|
2958
|
+
// src/plugins/listInputFieldChoices/index.ts
|
|
2959
|
+
function transformNeedChoicesToInputFieldChoiceItem(choice) {
|
|
2960
|
+
return {
|
|
2961
|
+
key: choice.key,
|
|
2962
|
+
label: choice.label,
|
|
2963
|
+
sample: choice.sample,
|
|
2964
|
+
value: choice.value
|
|
2755
2965
|
};
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
const
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2966
|
+
}
|
|
2967
|
+
var listInputFieldChoicesPlugin = ({ context, sdk }) => {
|
|
2968
|
+
const listInputFieldChoices = createPaginatedFunction(async function listInputFieldChoicesPage(options) {
|
|
2969
|
+
const { api } = context;
|
|
2970
|
+
const {
|
|
2971
|
+
appKey,
|
|
2972
|
+
actionType,
|
|
2973
|
+
actionKey,
|
|
2974
|
+
inputFieldKey,
|
|
2975
|
+
authenticationId,
|
|
2976
|
+
inputs,
|
|
2977
|
+
page,
|
|
2978
|
+
cursor
|
|
2979
|
+
} = options;
|
|
2980
|
+
const actionResult = await sdk.getAction({ appKey, actionType, actionKey });
|
|
2981
|
+
const actionId = actionResult.data.id;
|
|
2982
|
+
if (!actionId) {
|
|
2983
|
+
throw new ZapierApiError(
|
|
2984
|
+
`Action ${actionKey} does not have an ID - cannot retrieve input field choices`
|
|
2774
2985
|
);
|
|
2775
|
-
const implementationResults = implementationData.results[0];
|
|
2776
|
-
if (!implementationResults) return null;
|
|
2777
|
-
return normalizeImplementationToAppItem(implementationResults);
|
|
2778
|
-
}
|
|
2779
|
-
emitWarning(appKey);
|
|
2780
|
-
const appsIterator = sdk.listApps({ appKeys: [appKey] }).items();
|
|
2781
|
-
const apps = [];
|
|
2782
|
-
for await (const app2 of appsIterator) {
|
|
2783
|
-
apps.push(app2);
|
|
2784
|
-
break;
|
|
2785
2986
|
}
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
return `${manifestEntry.implementationName}@${manifestEntry.version || "latest"}`;
|
|
2987
|
+
const requestPage = cursor ? parseInt(cursor, 10) : page ?? 0;
|
|
2988
|
+
const choicesRequest = {
|
|
2989
|
+
action_id: actionId,
|
|
2990
|
+
input_field_id: inputFieldKey,
|
|
2991
|
+
page: requestPage,
|
|
2992
|
+
params: inputs || {}
|
|
2993
|
+
};
|
|
2994
|
+
if (authenticationId !== null) {
|
|
2995
|
+
choicesRequest.authentication_id = authenticationId;
|
|
2796
2996
|
}
|
|
2797
|
-
const
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
getImplementation
|
|
2997
|
+
const choicesData = await api.post(
|
|
2998
|
+
"/api/v4/implementations/choices/",
|
|
2999
|
+
choicesRequest
|
|
3000
|
+
);
|
|
3001
|
+
if (!choicesData.success) {
|
|
3002
|
+
throw new ZapierApiError(
|
|
3003
|
+
`Failed to get input field choices: ${choicesData.errors?.join(", ") || "Unknown error"}`
|
|
3004
|
+
);
|
|
2806
3005
|
}
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
for await (const app2 of appsIterator) {
|
|
2820
|
-
apps.push(app2);
|
|
2821
|
-
break;
|
|
2822
|
-
}
|
|
2823
|
-
const app = apps[0];
|
|
2824
|
-
const currentImplementationId = app.current_implementation_id;
|
|
2825
|
-
const [implementationName, version] = currentImplementationId.split("@");
|
|
2826
|
-
if (!implementationName || !version) {
|
|
2827
|
-
throw new Error(
|
|
2828
|
-
`Invalid implementation ID format: ${currentImplementationId}. Expected format: <implementationName>@<version>`
|
|
2829
|
-
);
|
|
2830
|
-
}
|
|
2831
|
-
let config = { apps: {} };
|
|
2832
|
-
if (fs.existsSync(resolvedPath)) {
|
|
2833
|
-
try {
|
|
2834
|
-
const configContent = fs.readFileSync(resolvedPath, "utf8");
|
|
2835
|
-
config = JSON.parse(configContent);
|
|
2836
|
-
if (!config.apps) {
|
|
2837
|
-
config.apps = {};
|
|
2838
|
-
}
|
|
2839
|
-
} catch (error) {
|
|
2840
|
-
console.warn(
|
|
2841
|
-
`\u26A0\uFE0F Failed to parse existing config file, creating new one: ${error}`
|
|
2842
|
-
);
|
|
2843
|
-
config = { apps: {} };
|
|
3006
|
+
const choices = (choicesData.choices || []).map(
|
|
3007
|
+
transformNeedChoicesToInputFieldChoiceItem
|
|
3008
|
+
);
|
|
3009
|
+
let nextCursor;
|
|
3010
|
+
if (choicesData.next_page !== void 0) {
|
|
3011
|
+
nextCursor = choicesData.next_page.toString();
|
|
3012
|
+
} else if (choicesData.links?.next) {
|
|
3013
|
+
try {
|
|
3014
|
+
const nextUrl = new URL(choicesData.links.next);
|
|
3015
|
+
const nextPage = nextUrl.searchParams.get("page");
|
|
3016
|
+
if (nextPage) {
|
|
3017
|
+
nextCursor = nextPage;
|
|
2844
3018
|
}
|
|
3019
|
+
} catch {
|
|
3020
|
+
nextCursor = void 0;
|
|
2845
3021
|
}
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
data: {
|
|
2853
|
-
...app,
|
|
2854
|
-
implementationName,
|
|
2855
|
-
version
|
|
2856
|
-
},
|
|
2857
|
-
configPath: resolvedPath
|
|
2858
|
-
};
|
|
2859
|
-
},
|
|
2860
|
-
LockVersionSchema.extend({
|
|
2861
|
-
configPath: zod.z.string().optional().describe("Path to .zapierrc file (defaults to '.zapierrc')")
|
|
2862
|
-
})
|
|
2863
|
-
);
|
|
3022
|
+
}
|
|
3023
|
+
return {
|
|
3024
|
+
data: choices,
|
|
3025
|
+
nextCursor
|
|
3026
|
+
};
|
|
3027
|
+
}, ListInputFieldChoicesSchema);
|
|
2864
3028
|
return {
|
|
2865
|
-
|
|
3029
|
+
listInputFieldChoices,
|
|
2866
3030
|
context: {
|
|
2867
3031
|
meta: {
|
|
2868
|
-
|
|
2869
|
-
categories: ["
|
|
2870
|
-
inputSchema:
|
|
3032
|
+
listInputFieldChoices: {
|
|
3033
|
+
categories: ["action"],
|
|
3034
|
+
inputSchema: ListInputFieldChoicesSchema
|
|
2871
3035
|
}
|
|
2872
3036
|
}
|
|
2873
3037
|
}
|
|
@@ -2922,8 +3086,11 @@ function createSdk(options = {}, initialSdk = {}, initialContext = { meta: {} })
|
|
|
2922
3086
|
}
|
|
2923
3087
|
};
|
|
2924
3088
|
}
|
|
3089
|
+
function createZapierSdkWithoutRegistry(options = {}) {
|
|
3090
|
+
return createSdk(options).addPlugin(apiPlugin).addPlugin(listAppsPlugin).addPlugin(manifestPlugin).addPlugin(getAppPlugin).addPlugin(listActionsPlugin).addPlugin(getActionPlugin).addPlugin(listInputFieldsPlugin).addPlugin(listInputFieldChoicesPlugin).addPlugin(runActionPlugin).addPlugin(lockVersionPlugin).addPlugin(listAuthenticationsPlugin).addPlugin(getAuthenticationPlugin).addPlugin(findFirstAuthenticationPlugin).addPlugin(findUniqueAuthenticationPlugin).addPlugin(requestPlugin).addPlugin(fetchPlugin).addPlugin(appsPlugin).addPlugin(getProfilePlugin);
|
|
3091
|
+
}
|
|
2925
3092
|
function createZapierSdk(options = {}) {
|
|
2926
|
-
return
|
|
3093
|
+
return createZapierSdkWithoutRegistry(options).addPlugin(registryPlugin);
|
|
2927
3094
|
}
|
|
2928
3095
|
|
|
2929
3096
|
exports.ActionKeyPropertySchema = ActionKeyPropertySchema;
|
|
@@ -2952,13 +3119,22 @@ exports.ZapierUnknownError = ZapierUnknownError;
|
|
|
2952
3119
|
exports.ZapierValidationError = ZapierValidationError;
|
|
2953
3120
|
exports.actionKeyResolver = actionKeyResolver;
|
|
2954
3121
|
exports.actionTypeResolver = actionTypeResolver;
|
|
3122
|
+
exports.apiPlugin = apiPlugin;
|
|
2955
3123
|
exports.appKeyResolver = appKeyResolver;
|
|
2956
3124
|
exports.appsPlugin = appsPlugin;
|
|
2957
3125
|
exports.authenticationIdResolver = authenticationIdResolver;
|
|
3126
|
+
exports.createFunction = createFunction;
|
|
2958
3127
|
exports.createSdk = createSdk;
|
|
2959
3128
|
exports.createZapierSdk = createZapierSdk;
|
|
3129
|
+
exports.createZapierSdkWithoutRegistry = createZapierSdkWithoutRegistry;
|
|
2960
3130
|
exports.fetchPlugin = fetchPlugin;
|
|
3131
|
+
exports.findFirstAuthenticationPlugin = findFirstAuthenticationPlugin;
|
|
3132
|
+
exports.findUniqueAuthenticationPlugin = findUniqueAuthenticationPlugin;
|
|
2961
3133
|
exports.formatErrorMessage = formatErrorMessage;
|
|
3134
|
+
exports.getActionPlugin = getActionPlugin;
|
|
3135
|
+
exports.getAppPlugin = getAppPlugin;
|
|
3136
|
+
exports.getAuthenticationPlugin = getAuthenticationPlugin;
|
|
3137
|
+
exports.getProfilePlugin = getProfilePlugin;
|
|
2962
3138
|
exports.getResolutionOrder = getResolutionOrder;
|
|
2963
3139
|
exports.getResolutionOrderForParams = getResolutionOrderForParams;
|
|
2964
3140
|
exports.getResolvableParams = getResolvableParams;
|
|
@@ -2970,4 +3146,14 @@ exports.getTokenFromEnvOrConfig = getTokenFromEnvOrConfig;
|
|
|
2970
3146
|
exports.hasResolver = hasResolver;
|
|
2971
3147
|
exports.inputsResolver = inputsResolver;
|
|
2972
3148
|
exports.isPositional = isPositional;
|
|
3149
|
+
exports.listActionsPlugin = listActionsPlugin;
|
|
3150
|
+
exports.listAppsPlugin = listAppsPlugin;
|
|
3151
|
+
exports.listAuthenticationsPlugin = listAuthenticationsPlugin;
|
|
3152
|
+
exports.listInputFieldsPlugin = listInputFieldsPlugin;
|
|
3153
|
+
exports.loadManifestFromFile = loadManifestFromFile;
|
|
3154
|
+
exports.lockVersionPlugin = lockVersionPlugin;
|
|
3155
|
+
exports.manifestPlugin = manifestPlugin;
|
|
3156
|
+
exports.registryPlugin = registryPlugin;
|
|
3157
|
+
exports.requestPlugin = requestPlugin;
|
|
2973
3158
|
exports.resolverRegistry = resolverRegistry;
|
|
3159
|
+
exports.runActionPlugin = runActionPlugin;
|