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