@jskit-ai/connectors-catalog 0.1.1
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/README.md +192 -0
- package/docs/ai-upstream-notices.md +51 -0
- package/docs/ai.md +191 -0
- package/docs/airtable.md +130 -0
- package/docs/algolia.md +190 -0
- package/docs/amazon-redshift.md +229 -0
- package/docs/amplitude.md +210 -0
- package/docs/apify.md +162 -0
- package/docs/apollo-io.md +200 -0
- package/docs/asana.md +126 -0
- package/docs/ashby.md +188 -0
- package/docs/atlassian.md +224 -0
- package/docs/attention.md +162 -0
- package/docs/aws-athena.md +150 -0
- package/docs/aws-credentials.md +75 -0
- package/docs/aws-s3.md +193 -0
- package/docs/bigquery.md +146 -0
- package/docs/brevo.md +122 -0
- package/docs/calendly.md +158 -0
- package/docs/canva.md +191 -0
- package/docs/chargebee.md +192 -0
- package/docs/clay.md +194 -0
- package/docs/clickhouse.md +242 -0
- package/docs/confidence-exp.md +74 -0
- package/docs/confidence-flags.md +75 -0
- package/docs/confidence-oauth.md +137 -0
- package/docs/contentful.md +123 -0
- package/docs/databricks.md +180 -0
- package/docs/dbt-semantic-layer.md +175 -0
- package/docs/elevenlabs.md +102 -0
- package/docs/figma.md +132 -0
- package/docs/firebase-cloud-messaging.md +262 -0
- package/docs/firecrawl.md +137 -0
- package/docs/fireflies.md +95 -0
- package/docs/fireworks-ai.md +53 -0
- package/docs/gatewayapi.md +113 -0
- package/docs/gemini-enterprise.md +224 -0
- package/docs/github-api.md +133 -0
- package/docs/gitlab-api.md +138 -0
- package/docs/gmail.md +133 -0
- package/docs/gong.md +205 -0
- package/docs/google-ads.md +156 -0
- package/docs/google-analytics.md +202 -0
- package/docs/google-docs.md +105 -0
- package/docs/google-drive.md +108 -0
- package/docs/google-maps-platform.md +208 -0
- package/docs/google-oauth.md +123 -0
- package/docs/google-search-console.md +124 -0
- package/docs/google-sheets.md +94 -0
- package/docs/google-slides.md +95 -0
- package/docs/granola.md +178 -0
- package/docs/hex.md +161 -0
- package/docs/heygen.md +124 -0
- package/docs/hubspot.md +142 -0
- package/docs/incident-io.md +118 -0
- package/docs/inngest.md +178 -0
- package/docs/klipy.md +149 -0
- package/docs/lexware.md +144 -0
- package/docs/lightspeed.md +193 -0
- package/docs/linear.md +137 -0
- package/docs/linkedin.md +196 -0
- package/docs/logo-dev.md +152 -0
- package/docs/mailgun.md +153 -0
- package/docs/mapbox.md +174 -0
- package/docs/microsoft-excel.md +97 -0
- package/docs/microsoft-fabric.md +249 -0
- package/docs/microsoft-oauth.md +143 -0
- package/docs/microsoft-onedrive.md +76 -0
- package/docs/microsoft-onenote.md +75 -0
- package/docs/microsoft-outlook.md +90 -0
- package/docs/microsoft-powerpoint.md +119 -0
- package/docs/microsoft-sharepoint.md +75 -0
- package/docs/microsoft-teams.md +72 -0
- package/docs/microsoft-word.md +119 -0
- package/docs/miro.md +117 -0
- package/docs/n8n.md +169 -0
- package/docs/notion.md +137 -0
- package/docs/oura.md +161 -0
- package/docs/paddle.md +168 -0
- package/docs/perplexity.md +85 -0
- package/docs/pipedrive.md +120 -0
- package/docs/polar.md +63 -0
- package/docs/posthog.md +227 -0
- package/docs/prestashop.md +151 -0
- package/docs/replicate.md +60 -0
- package/docs/resend.md +153 -0
- package/docs/salesforce.md +234 -0
- package/docs/sanity.md +122 -0
- package/docs/semrush.md +276 -0
- package/docs/sentry.md +112 -0
- package/docs/sevdesk.md +107 -0
- package/docs/shopify.md +206 -0
- package/docs/slack.md +257 -0
- package/docs/snowflake.md +271 -0
- package/docs/storyblok.md +130 -0
- package/docs/stripe.md +77 -0
- package/docs/supabase.md +51 -0
- package/docs/tally.md +105 -0
- package/docs/telegram.md +165 -0
- package/docs/tiktok.md +201 -0
- package/docs/twilio.md +284 -0
- package/docs/twitch.md +428 -0
- package/docs/wave.md +440 -0
- package/docs/wix.md +1249 -0
- package/docs/wiz.md +177 -0
- package/docs/woocommerce.md +400 -0
- package/docs/wordpress-com.md +358 -0
- package/docs/wordpress-self-hosted.md +252 -0
- package/docs/workday.md +292 -0
- package/docs/x-twitter.md +188 -0
- package/docs/xero.md +429 -0
- package/docs/zoho-books.md +214 -0
- package/docs/zoho-crm.md +192 -0
- package/package.json +153 -0
- package/patterns/ai-connections/PATTERN.md +84 -0
- package/patterns/ai-connections/example/ai-model.js +11 -0
- package/patterns/ai-connections/example/perplexity-answer.js +29 -0
- package/patterns/api-key-connection/PATTERN.md +368 -0
- package/patterns/api-key-connection/example/connections.js +18 -0
- package/patterns/api-key-connection/example/integrations.json +19 -0
- package/patterns/assistant-mcp/PATTERN.md +110 -0
- package/patterns/assistant-mcp/example/integrations.json +13 -0
- package/patterns/assistant-mcp-oauth/PATTERN.md +235 -0
- package/patterns/assistant-mcp-oauth/example/integrations.json +102 -0
- package/patterns/aws-storage-queries/PATTERN.md +169 -0
- package/patterns/aws-storage-queries/example/formats/data-formats.js +35 -0
- package/patterns/aws-storage-queries/example/formats/package-lock.json +49 -0
- package/patterns/aws-storage-queries/example/formats/package.json +12 -0
- package/patterns/aws-storage-queries/example/formats/verify-formats.mjs +37 -0
- package/patterns/aws-storage-queries/example/integrations.json +38 -0
- package/patterns/aws-storage-queries/example/s3-transfer.js +34 -0
- package/patterns/event-delivery/PATTERN.md +151 -0
- package/patterns/event-delivery/example/integrations.json +17 -0
- package/patterns/firebase-messaging/PATTERN.md +156 -0
- package/patterns/firebase-messaging/example/server/notifications.js +23 -0
- package/patterns/google-ads-search/PATTERN.md +92 -0
- package/patterns/google-ads-search/example/ads-setup.js +25 -0
- package/patterns/oauth-connection/PATTERN.md +724 -0
- package/patterns/oauth-connection/example/integrations.json +20 -0
- package/patterns/paddle-catalogue/PATTERN.md +78 -0
- package/patterns/paddle-catalogue/example/create-products.js +38 -0
- package/patterns/public-image/PATTERN.md +84 -0
- package/patterns/public-image/example/integrations.json +13 -0
- package/patterns/public-image/example/logo-url.js +16 -0
- package/patterns/redshift-queries/PATTERN.md +150 -0
- package/patterns/redshift-queries/example/integrations.json +33 -0
- package/patterns/source-scanning/PATTERN.md +83 -0
- package/patterns/source-scanning/example/source-scanner.js +13 -0
- package/src/client/firebase-cloud-messaging.js +10 -0
- package/src/client/google-analytics.js +72 -0
- package/src/client/google-maps-platform.js +39 -0
- package/src/client/logo-dev.js +36 -0
- package/src/server/ai.js +46 -0
- package/src/server/airtable.js +67 -0
- package/src/server/algolia.js +63 -0
- package/src/server/amazon-redshift.js +96 -0
- package/src/server/amplitude.js +31 -0
- package/src/server/apify.js +68 -0
- package/src/server/apollo-io.js +85 -0
- package/src/server/asana.js +64 -0
- package/src/server/ashby.js +61 -0
- package/src/server/atlassian.js +32 -0
- package/src/server/attention.js +93 -0
- package/src/server/aws-athena.js +82 -0
- package/src/server/aws-s3.js +49 -0
- package/src/server/awsTransport.js +75 -0
- package/src/server/bigquery.js +97 -0
- package/src/server/brevo.js +97 -0
- package/src/server/calendly.js +77 -0
- package/src/server/canva.js +13 -0
- package/src/server/chargebee.js +88 -0
- package/src/server/clay.js +78 -0
- package/src/server/clickhouse.js +66 -0
- package/src/server/confidence.js +29 -0
- package/src/server/contentful.js +40 -0
- package/src/server/databricks.js +55 -0
- package/src/server/dbt-semantic-layer.js +95 -0
- package/src/server/elevenlabs.js +81 -0
- package/src/server/figma.js +24 -0
- package/src/server/firebase-cloud-messaging.js +111 -0
- package/src/server/firecrawl.js +70 -0
- package/src/server/fireflies.js +31 -0
- package/src/server/fireworks-ai.js +16 -0
- package/src/server/gatewayapi.js +48 -0
- package/src/server/gemini-enterprise.js +47 -0
- package/src/server/github-api.js +90 -0
- package/src/server/gitlab-api.js +67 -0
- package/src/server/gmail.js +60 -0
- package/src/server/gong.js +52 -0
- package/src/server/google-ads-search.js +134 -0
- package/src/server/google-ads.js +80 -0
- package/src/server/google-docs.js +36 -0
- package/src/server/google-drive.js +71 -0
- package/src/server/google-maps-platform.js +90 -0
- package/src/server/google-search-console.js +31 -0
- package/src/server/google-sheets.js +60 -0
- package/src/server/google-slides.js +36 -0
- package/src/server/google.js +61 -0
- package/src/server/granola.js +115 -0
- package/src/server/graphqlOperation.js +15 -0
- package/src/server/hex.js +29 -0
- package/src/server/heygen.js +54 -0
- package/src/server/hubspot.js +85 -0
- package/src/server/incident-io.js +68 -0
- package/src/server/inngest.js +86 -0
- package/src/server/jsonOperation.js +34 -0
- package/src/server/klipy.js +34 -0
- package/src/server/lexware.js +59 -0
- package/src/server/lightspeed.js +82 -0
- package/src/server/linear.js +52 -0
- package/src/server/linkedin.js +74 -0
- package/src/server/mailgun.js +76 -0
- package/src/server/mapbox.js +46 -0
- package/src/server/mcpRegistration.js +41 -0
- package/src/server/mcpTransport.js +60 -0
- package/src/server/microsoft-excel.js +67 -0
- package/src/server/microsoft-fabric.js +81 -0
- package/src/server/microsoft-onedrive.js +58 -0
- package/src/server/microsoft-onenote.js +52 -0
- package/src/server/microsoft-outlook.js +58 -0
- package/src/server/microsoft-powerpoint.js +5 -0
- package/src/server/microsoft-sharepoint.js +46 -0
- package/src/server/microsoft-teams.js +29 -0
- package/src/server/microsoft-word.js +5 -0
- package/src/server/microsoft.js +88 -0
- package/src/server/miro.js +29 -0
- package/src/server/n8n.js +78 -0
- package/src/server/notion.js +76 -0
- package/src/server/oura.js +48 -0
- package/src/server/paddle.js +83 -0
- package/src/server/perplexity.js +15 -0
- package/src/server/pipedrive.js +95 -0
- package/src/server/polar.js +20 -0
- package/src/server/posthog.js +44 -0
- package/src/server/prestashop.js +42 -0
- package/src/server/replicate.js +42 -0
- package/src/server/resend.js +93 -0
- package/src/server/salesforce.js +84 -0
- package/src/server/sanity.js +31 -0
- package/src/server/semrush.js +359 -0
- package/src/server/sentry.js +30 -0
- package/src/server/sevdesk.js +20 -0
- package/src/server/shopify.js +99 -0
- package/src/server/slack.js +143 -0
- package/src/server/snowflake.js +139 -0
- package/src/server/storyblok.js +49 -0
- package/src/server/stripe.js +13 -0
- package/src/server/supabase.js +13 -0
- package/src/server/tally.js +78 -0
- package/src/server/telegram.js +55 -0
- package/src/server/tiktok.js +86 -0
- package/src/server/twilio.js +90 -0
- package/src/server/twitch.js +409 -0
- package/src/server/wave.js +520 -0
- package/src/server/wix.js +1048 -0
- package/src/server/wiz.js +117 -0
- package/src/server/woocommerce.js +412 -0
- package/src/server/wordpress-com.js +236 -0
- package/src/server/wordpress-self-hosted.js +134 -0
- package/src/server/workday.js +136 -0
- package/src/server/x-twitter.js +110 -0
- package/src/server/xero.js +397 -0
- package/src/server/zoho-books.js +90 -0
- package/src/server/zoho-crm.js +89 -0
- package/src/server/zohoOAuth.js +21 -0
- package/src/shared/ai-models.json +8049 -0
- package/src/shared/ai.js +63 -0
- package/src/shared/algolia.js +29 -0
- package/src/shared/amazon-redshift.js +51 -0
- package/src/shared/amplitude.js +28 -0
- package/src/shared/atlassian.js +43 -0
- package/src/shared/aws.js +72 -0
- package/src/shared/canva.js +66 -0
- package/src/shared/chargebee.js +26 -0
- package/src/shared/clickhouse.js +38 -0
- package/src/shared/confidence.js +28 -0
- package/src/shared/databricks.js +33 -0
- package/src/shared/dbt-semantic-layer.js +32 -0
- package/src/shared/definitions.js +140 -0
- package/src/shared/figma.js +21 -0
- package/src/shared/firebase-cloud-messaging.js +45 -0
- package/src/shared/gemini-enterprise.js +33 -0
- package/src/shared/gong.js +33 -0
- package/src/shared/google-ads-search.js +31 -0
- package/src/shared/google-ads.js +28 -0
- package/src/shared/google-maps-platform.js +28 -0
- package/src/shared/google.js +133 -0
- package/src/shared/granola.js +25 -0
- package/src/shared/hex.js +28 -0
- package/src/shared/inngest.js +35 -0
- package/src/shared/lightspeed.js +75 -0
- package/src/shared/linkedin.js +27 -0
- package/src/shared/logo-dev.js +21 -0
- package/src/shared/mapbox.js +38 -0
- package/src/shared/mcp.js +92 -0
- package/src/shared/microsoft-fabric.js +47 -0
- package/src/shared/microsoft.js +231 -0
- package/src/shared/miro.js +27 -0
- package/src/shared/oura.js +23 -0
- package/src/shared/posthog.js +33 -0
- package/src/shared/prestashop.js +23 -0
- package/src/shared/salesforce.js +36 -0
- package/src/shared/semrush.js +24 -0
- package/src/shared/shopify.js +51 -0
- package/src/shared/siteUrl.js +12 -0
- package/src/shared/slack.js +88 -0
- package/src/shared/snowflake.js +55 -0
- package/src/shared/tiktok.js +22 -0
- package/src/shared/tokens.js +747 -0
- package/src/shared/twilio.js +41 -0
- package/src/shared/twitch.js +49 -0
- package/src/shared/wave.js +52 -0
- package/src/shared/wix.js +40 -0
- package/src/shared/wiz.js +41 -0
- package/src/shared/wordpress-com.js +30 -0
- package/src/shared/wordpress.js +69 -0
- package/src/shared/workday.js +50 -0
- package/src/shared/x-twitter.js +19 -0
- package/src/shared/xero.js +46 -0
- package/src/shared/zoho-books.js +39 -0
- package/src/shared/zoho-crm.js +43 -0
- package/src/shared/zoho-regions.js +12 -0
- package/test/ai.test.js +136 -0
- package/test/airtable.test.js +125 -0
- package/test/algolia.test.js +207 -0
- package/test/amazon-redshift.test.js +272 -0
- package/test/amplitude.test.js +239 -0
- package/test/apify.test.js +93 -0
- package/test/apollo-io.test.js +125 -0
- package/test/asana.test.js +99 -0
- package/test/ashby.test.js +97 -0
- package/test/atlassian.test.js +249 -0
- package/test/attention.test.js +79 -0
- package/test/aws.test.js +405 -0
- package/test/brevo.test.js +71 -0
- package/test/calendly.test.js +136 -0
- package/test/canva.test.js +258 -0
- package/test/chargebee.test.js +195 -0
- package/test/clickhouse.test.js +278 -0
- package/test/confidence.test.js +205 -0
- package/test/databricks.test.js +253 -0
- package/test/dbt-semantic-layer.test.js +221 -0
- package/test/designMcp.test.js +336 -0
- package/test/elevenlabs.test.js +52 -0
- package/test/firebase-cloud-messaging.test.js +242 -0
- package/test/firecrawl.test.js +66 -0
- package/test/gemini-enterprise.test.js +218 -0
- package/test/github-api.test.js +167 -0
- package/test/gitlab-api.test.js +160 -0
- package/test/gong.test.js +192 -0
- package/test/google-ads.test.js +350 -0
- package/test/google-maps-platform.test.js +191 -0
- package/test/google.test.js +710 -0
- package/test/granola.test.js +240 -0
- package/test/hex.test.js +253 -0
- package/test/heygen.test.js +121 -0
- package/test/hubspot.test.js +103 -0
- package/test/inngest.test.js +219 -0
- package/test/lightspeed.test.js +250 -0
- package/test/linear.test.js +146 -0
- package/test/linkedin.test.js +243 -0
- package/test/logo-dev.test.js +79 -0
- package/test/mailgun.test.js +107 -0
- package/test/mapbox.test.js +181 -0
- package/test/mcp.test.js +227 -0
- package/test/microsoft-fabric.test.js +258 -0
- package/test/microsoft.test.js +554 -0
- package/test/n8nOAuth.test.js +188 -0
- package/test/notionOAuth.test.js +183 -0
- package/test/oura.test.js +211 -0
- package/test/paddleCatalogue.test.js +51 -0
- package/test/paddlePermissions.test.js +101 -0
- package/test/perplexityComposition.test.js +39 -0
- package/test/pipedriveOAuth.test.js +140 -0
- package/test/posthog.test.js +200 -0
- package/test/prestashop.test.js +207 -0
- package/test/resend.test.js +163 -0
- package/test/salesforce.test.js +276 -0
- package/test/semrush.test.js +479 -0
- package/test/sentry.test.js +35 -0
- package/test/shopify.test.js +247 -0
- package/test/slack.test.js +320 -0
- package/test/snowflake.test.js +391 -0
- package/test/tally.test.js +75 -0
- package/test/telegram.test.js +58 -0
- package/test/tiktok.test.js +243 -0
- package/test/tokens.test.js +1079 -0
- package/test/twilio.test.js +221 -0
- package/test/twitch.test.js +573 -0
- package/test/wave.test.js +678 -0
- package/test/wix.test.js +1099 -0
- package/test/wiz.test.js +185 -0
- package/test/wordpress-com.test.js +469 -0
- package/test/wordpress.test.js +660 -0
- package/test/workday.test.js +314 -0
- package/test/x-twitter.test.js +256 -0
- package/test/xero.test.js +700 -0
- package/test/zoho-books.test.js +253 -0
- package/test/zoho-crm.test.js +223 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { googleSheetsDefinition } from "../shared/google.js";
|
|
4
|
+
import { googleRead, googleProvider, googleFileVerification, documentId } from "./google.js";
|
|
5
|
+
const origin = "https://sheets.googleapis.com";
|
|
6
|
+
const scopes = ["spreadsheets.readonly", "spreadsheets", "drive.readonly", "drive.file", "drive"];
|
|
7
|
+
const writable = ["spreadsheets", "drive.file", "drive"].map(scope => `https://www.googleapis.com/auth/${scope}`);
|
|
8
|
+
const range = { type: "string", required: true, minLength: 1, maxLength: 2048 };
|
|
9
|
+
const inputOption = { type: "string", enum: ["RAW", "USER_ENTERED"], default: "RAW" };
|
|
10
|
+
const values = { type: "array", required: true };
|
|
11
|
+
const validSheet = value => typeof value?.spreadsheetId === "string" && typeof value.properties?.title === "string";
|
|
12
|
+
const sheetUrl = id => `${origin}/v4/spreadsheets/${encodeURIComponent(id)}`;
|
|
13
|
+
const get = googleRead(scopes, { spreadsheetId: documentId }, ({ spreadsheetId }) => ({ url: sheetUrl(spreadsheetId), query: { includeGridData: false } }), validSheet);
|
|
14
|
+
function write(fields, destination, validateResult) {
|
|
15
|
+
const schema = createSchema(fields);
|
|
16
|
+
return { scopes: writable, validateResult, request(input) {
|
|
17
|
+
return destination(validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 }));
|
|
18
|
+
} };
|
|
19
|
+
}
|
|
20
|
+
function checkValues(rows) {
|
|
21
|
+
if (!rows.length || rows.length > 10000 || rows.some(row => !Array.isArray(row) || row.some(cell => cell !== null && typeof cell !== "string" && typeof cell !== "boolean" && !(typeof cell === "number" && Number.isFinite(cell)))) || rows.reduce((count, row) => count + row.length, 0) > 10000 || Buffer.byteLength(JSON.stringify(rows)) > 1048576) {
|
|
22
|
+
throw Object.assign(new Error("Supply a matrix of at most 10,000 cells and 1 MiB, containing strings, finite numbers, booleans or null."), { statusCode: 422 });
|
|
23
|
+
}
|
|
24
|
+
return rows;
|
|
25
|
+
}
|
|
26
|
+
const validUpdate = value => typeof value?.spreadsheetId === "string" && typeof value.updatedRange === "string";
|
|
27
|
+
const base = googleProvider(googleSheetsDefinition, origin, "connection.verify", {
|
|
28
|
+
"connection.verify": googleFileVerification(get, "spreadsheetId"),
|
|
29
|
+
"spreadsheets.get": get,
|
|
30
|
+
"spreadsheets.create": write({ title: { type: "string", required: true, minLength: 1, maxLength: 1024 } }, ({ title }) => ({ method: "POST", url: `${origin}/v4/spreadsheets`, body: { properties: { title } } }), validSheet),
|
|
31
|
+
"values.get": googleRead(scopes, {
|
|
32
|
+
spreadsheetId: documentId, range,
|
|
33
|
+
valueRenderOption: { type: "string", enum: ["FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA"] },
|
|
34
|
+
dateTimeRenderOption: { type: "string", enum: ["SERIAL_NUMBER", "FORMATTED_STRING"] }
|
|
35
|
+
}, ({ spreadsheetId, range, ...query }) => ({ url: `${sheetUrl(spreadsheetId)}/values/${encodeURIComponent(range)}`, query }), value => typeof value?.range === "string" && (value.values === undefined || Array.isArray(value.values))),
|
|
36
|
+
"values.update": write({ spreadsheetId: documentId, range, values, valueInputOption: inputOption }, ({ spreadsheetId, range, values, valueInputOption = "RAW" }) => ({
|
|
37
|
+
method: "PUT", url: `${sheetUrl(spreadsheetId)}/values/${encodeURIComponent(range)}?valueInputOption=${valueInputOption}`,
|
|
38
|
+
body: { range, majorDimension: "ROWS", values: checkValues(values) }
|
|
39
|
+
}), validUpdate),
|
|
40
|
+
"values.append": write({ spreadsheetId: documentId, range, values, valueInputOption: inputOption, insertDataOption: { type: "string", enum: ["INSERT_ROWS", "OVERWRITE"], default: "INSERT_ROWS" } }, ({ spreadsheetId, range, values, valueInputOption = "RAW", insertDataOption = "INSERT_ROWS" }) => ({
|
|
41
|
+
method: "POST", url: `${sheetUrl(spreadsheetId)}/values/${encodeURIComponent(range)}:append?valueInputOption=${valueInputOption}&insertDataOption=${insertDataOption}`,
|
|
42
|
+
body: { range, majorDimension: "ROWS", values: checkValues(values) }
|
|
43
|
+
}), value => typeof value?.spreadsheetId === "string" && validUpdate(value.updates)),
|
|
44
|
+
"values.clear": write({ spreadsheetId: documentId, range }, ({ spreadsheetId, range }) => ({ method: "POST", url: `${sheetUrl(spreadsheetId)}/values/${encodeURIComponent(range)}:clear`, body: {} }), value => typeof value?.spreadsheetId === "string" && typeof value.clearedRange === "string"),
|
|
45
|
+
"values.batchUpdate": write({ spreadsheetId: documentId, data: { type: "array", required: true }, valueInputOption: inputOption }, ({ spreadsheetId, data, valueInputOption = "RAW" }) => {
|
|
46
|
+
const schema = createSchema({ range, values });
|
|
47
|
+
if (!data.length || data.length > 100 || Buffer.byteLength(JSON.stringify(data)) > 1048576) throw Object.assign(new Error("Supply 1–100 ranges within 1 MiB."), { statusCode: 422 });
|
|
48
|
+
const ranges = data.map(value => {
|
|
49
|
+
const parsed = validateSchemaPayload({ schema, mode: "replace" }, value, { statusCode: 422 });
|
|
50
|
+
return { ...parsed, majorDimension: "ROWS", values: checkValues(parsed.values) };
|
|
51
|
+
});
|
|
52
|
+
return { method: "POST", url: `${sheetUrl(spreadsheetId)}/values:batchUpdate`, body: { data: ranges, valueInputOption } };
|
|
53
|
+
}, value => typeof value?.spreadsheetId === "string" && Array.isArray(value.responses)),
|
|
54
|
+
"spreadsheets.batchUpdate": write({ spreadsheetId: documentId, requests: { type: "array", required: true } }, ({ spreadsheetId, requests }) => {
|
|
55
|
+
if (!requests.length || requests.length > 100 || Buffer.byteLength(JSON.stringify(requests)) > 1048576 || requests.some(request => !request || typeof request !== "object" || Array.isArray(request) || Object.keys(request).length !== 1 || !/^[a-zA-Z][a-zA-Z0-9]+$/.test(Object.keys(request)[0]) || !Object.values(request)[0] || typeof Object.values(request)[0] !== "object" || Array.isArray(Object.values(request)[0]))) throw Object.assign(new Error("Supply 1–100 native Sheets request objects, one operation per object, within 1 MiB."), { statusCode: 422 });
|
|
56
|
+
return { method: "POST", url: `${sheetUrl(spreadsheetId)}:batchUpdate`, body: { requests } };
|
|
57
|
+
}, value => typeof value?.spreadsheetId === "string" && (value.replies === undefined || Array.isArray(value.replies)))
|
|
58
|
+
});
|
|
59
|
+
const googleSheetsProvider = Object.freeze({ ...base, apiOrigins: [origin, "https://www.googleapis.com"] });
|
|
60
|
+
export { googleSheetsProvider };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { googleSlidesDefinition } from "../shared/google.js";
|
|
4
|
+
import { googleRead, googleProvider, googleFileVerification, documentId } from "./google.js";
|
|
5
|
+
const origin = "https://slides.googleapis.com";
|
|
6
|
+
const readScopes = ["presentations.readonly", "presentations", "drive.readonly", "drive.file", "drive"];
|
|
7
|
+
const writable = ["presentations", "drive.file", "drive"].map(scope => `https://www.googleapis.com/auth/${scope}`);
|
|
8
|
+
const validPresentation = value => typeof value?.presentationId === "string" && typeof value.title === "string";
|
|
9
|
+
const get = googleRead(readScopes, { presentationId: documentId }, ({ presentationId }) => ({
|
|
10
|
+
url: `${origin}/v1/presentations/${encodeURIComponent(presentationId)}`
|
|
11
|
+
}), validPresentation);
|
|
12
|
+
const pageId = { ...documentId, validator: value => /^[a-zA-Z0-9_][a-zA-Z0-9_:-]*$/.test(value) || "Enter a slide object ID, not its URL." };
|
|
13
|
+
const create = createSchema({ title: { type: "string", required: true, minLength: 1, maxLength: 1024 } });
|
|
14
|
+
const batch = createSchema({ presentationId: documentId, requests: { type: "array", required: true }, requiredRevisionId: { type: "string", minLength: 1, maxLength: 1024 } });
|
|
15
|
+
const base = googleProvider(googleSlidesDefinition, origin, "connection.verify", {
|
|
16
|
+
"connection.verify": googleFileVerification(get, "presentationId"),
|
|
17
|
+
"presentations.get": get,
|
|
18
|
+
"presentations.create": { scopes: writable, validateResult: validPresentation, request(input) {
|
|
19
|
+
return { method: "POST", url: `${origin}/v1/presentations`, body: validateSchemaPayload({ schema: create, mode: "replace" }, input, { statusCode: 422 }) };
|
|
20
|
+
} },
|
|
21
|
+
"presentations.batchUpdate": { scopes: writable,
|
|
22
|
+
validateResult: value => typeof value?.presentationId === "string" && (value.replies === undefined || Array.isArray(value.replies)),
|
|
23
|
+
request(input) {
|
|
24
|
+
const { presentationId, requests, requiredRevisionId } = validateSchemaPayload({ schema: batch, mode: "replace" }, input, { statusCode: 422 });
|
|
25
|
+
if (!requests.length || requests.length > 100 || Buffer.byteLength(JSON.stringify(requests)) > 1048576 || requests.some(request => !request || typeof request !== "object" || Array.isArray(request) || Object.keys(request).length !== 1 || !/^[a-zA-Z][a-zA-Z0-9]+$/.test(Object.keys(request)[0]) || !Object.values(request)[0] || typeof Object.values(request)[0] !== "object" || Array.isArray(Object.values(request)[0]))) throw Object.assign(new Error("Supply 1–100 native Slides request objects, one operation per object, within 1 MiB."), { statusCode: 422 });
|
|
26
|
+
return { method: "POST", url: `${origin}/v1/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, body: { requests, ...(requiredRevisionId ? { writeControl: { requiredRevisionId } } : {}) } };
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"pages.get": googleRead(readScopes, { presentationId: documentId, pageObjectId: pageId }, ({ presentationId, pageObjectId }) => ({ url: `${origin}/v1/presentations/${encodeURIComponent(presentationId)}/pages/${encodeURIComponent(pageObjectId)}` }), value => typeof value?.objectId === "string"),
|
|
30
|
+
"pages.getThumbnail": googleRead(readScopes, { presentationId: documentId, pageObjectId: pageId, size: { type: "string", enum: ["SMALL", "MEDIUM", "LARGE"] } }, ({ presentationId, pageObjectId, size }) => ({ url: `${origin}/v1/presentations/${encodeURIComponent(presentationId)}/pages/${encodeURIComponent(pageObjectId)}/thumbnail`, query: { "thumbnailProperties.mimeType": "PNG", "thumbnailProperties.thumbnailSize": size } }), value => {
|
|
31
|
+
if (typeof value?.contentUrl !== "string" || !Number.isFinite(value.width) || !Number.isFinite(value.height) || value.width <= 0 || value.height <= 0) return false;
|
|
32
|
+
try { const url = new URL(value.contentUrl); return url.protocol === "https:" && !url.username && !url.password; } catch { return false; }
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
const googleSlidesProvider = Object.freeze({ ...base, apiOrigins: [origin, "https://www.googleapis.com"] });
|
|
36
|
+
export { googleSlidesProvider };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
|
|
4
|
+
const oauth = Object.freeze({
|
|
5
|
+
issuer: "https://accounts.google.com",
|
|
6
|
+
authorization_endpoint: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
7
|
+
token_endpoint: "https://oauth2.googleapis.com/token"
|
|
8
|
+
});
|
|
9
|
+
const googleScope = (name) => name.startsWith("https:") ? name : `https://www.googleapis.com/auth/${name}`;
|
|
10
|
+
const documentId = { type: "string", required: true, minLength: 1, maxLength: 1024, validator: (value) => /^[a-zA-Z0-9_-]+$/u.test(value) || "Enter the document ID from its URL." };
|
|
11
|
+
const pageToken = { type: "string", minLength: 1, maxLength: 4096 };
|
|
12
|
+
|
|
13
|
+
function googleOperation(scopes, fields, request, validateResult) {
|
|
14
|
+
const schema = createSchema(fields);
|
|
15
|
+
return {
|
|
16
|
+
scopes: scopes.map(scope => `https://www.googleapis.com/auth/${scope}`),
|
|
17
|
+
validateResult,
|
|
18
|
+
request(input) {
|
|
19
|
+
return request(validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 }));
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function googleRead(scopes, fields, destination, validateResult) {
|
|
25
|
+
const schema = createSchema(fields);
|
|
26
|
+
return {
|
|
27
|
+
scopes: scopes.map(googleScope), validateResult,
|
|
28
|
+
request(input) {
|
|
29
|
+
const params = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
30
|
+
const { url: address, query = {} } = destination(params);
|
|
31
|
+
const url = new URL(address);
|
|
32
|
+
for (const [key, value] of Object.entries(query)) {
|
|
33
|
+
if (value !== undefined) {
|
|
34
|
+
for (const item of Array.isArray(value) ? value : [value]) url.searchParams.append(key, String(item));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { method: "GET", url: url.href };
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function googleProvider(definition, origin, checkOperation, operations) {
|
|
43
|
+
return Object.freeze({
|
|
44
|
+
...definition, oauth, apiOrigins: [origin],
|
|
45
|
+
authorizationParameters: { access_type: "offline", prompt: "consent" },
|
|
46
|
+
checkOperation, operations
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function googleFileVerification(get, field) {
|
|
50
|
+
const schema = createSchema({ [field]: { ...documentId, required: false } });
|
|
51
|
+
return {
|
|
52
|
+
scopes: get.scopes,
|
|
53
|
+
request(input = {}) {
|
|
54
|
+
const params = validateSchemaPayload({ schema, mode: "replace" }, { ...input, ...(input[field] === "" ? { [field]: undefined } : {}) }, { statusCode: 422 });
|
|
55
|
+
if (params[field]) return get.request(params);
|
|
56
|
+
return { method: "GET", url: "https://www.googleapis.com/drive/v3/files?pageSize=1&fields=files(id)&q=trashed%3Dfalse" };
|
|
57
|
+
},
|
|
58
|
+
validateResult: value => get.validateResult(value) || Array.isArray(value?.files)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export { googleOperation, googleRead, googleProvider, googleFileVerification, documentId, pageToken };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { ConnectorError } from "@jskit-ai/connectors-core/server";
|
|
4
|
+
import { granolaDefinition } from "../shared/granola.js";
|
|
5
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
6
|
+
import { mcpOperations, exchangeMcp } from "./mcpTransport.js";
|
|
7
|
+
import { mcpRegistrationFields, registerMcpClient } from "./mcpRegistration.js";
|
|
8
|
+
|
|
9
|
+
const apiOrigin = "https://public-api.granola.ai";
|
|
10
|
+
const noteId = (value) => typeof value === "string" && /^not_[A-Za-z0-9]{14}$/u.test(value);
|
|
11
|
+
const folderId = (value) => typeof value === "string" && /^fol_[A-Za-z0-9]{14}$/u.test(value);
|
|
12
|
+
const cursorValid = (value) => typeof value === "string" && value.length > 0 && value.length <= 8192 && !/[\s\p{Cc}]/u.test(value);
|
|
13
|
+
const cursor = { type: "string", noTrim: true, validator: (value) => cursorValid(value) || "Use the cursor returned by the previous page." };
|
|
14
|
+
const date = { type: "string", noTrim: true, validator: (value) => {
|
|
15
|
+
const match = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z)?$/u.test(value);
|
|
16
|
+
return match && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().startsWith(value.slice(0, 10)) || "Use a valid YYYY-MM-DD date or UTC timestamp.";
|
|
17
|
+
} };
|
|
18
|
+
const noteSummary = (value) => noteId(value?.id) && value.object === "note" && (value.title === null || typeof value.title === "string") &&
|
|
19
|
+
typeof value.owner?.email === "string" && typeof value.created_at === "string" && typeof value.updated_at === "string";
|
|
20
|
+
const transcriptItem = (value) => typeof value?.text === "string" && typeof value.speaker?.source === "string" &&
|
|
21
|
+
typeof value.start_time === "string" && typeof value.end_time === "string";
|
|
22
|
+
const validFolder = (value) => folderId(value?.id) && value.object === "folder" && typeof value.name === "string" &&
|
|
23
|
+
(value.parent_folder_id === null || folderId(value.parent_folder_id));
|
|
24
|
+
const validPage = (value, field, itemValid) => typeof value?.hasMore === "boolean" &&
|
|
25
|
+
(value.cursor === null || cursorValid(value.cursor)) && (!value.hasMore || cursorValid(value.cursor)) &&
|
|
26
|
+
Array.isArray(value[field]) && value[field].every(itemValid);
|
|
27
|
+
|
|
28
|
+
function noteRead(suffix, fields, validateResult) {
|
|
29
|
+
const schema = createSchema({ note_id: { type: "string", required: true, validator: (value) => noteId(value) || "Use the not_ ID returned by notes.list." }, ...fields });
|
|
30
|
+
return {
|
|
31
|
+
scopes: [], validateResult,
|
|
32
|
+
request(input) {
|
|
33
|
+
const { note_id, ...values } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
34
|
+
const url = new URL(`${apiOrigin}/v1/notes/${note_id}${suffix}`);
|
|
35
|
+
for (const [name, value] of Object.entries(values)) url.searchParams.set(name, String(value));
|
|
36
|
+
return { method: "GET", url: url.href };
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const granolaApiProvider = Object.freeze({
|
|
42
|
+
...granolaDefinition, apiOrigins: [apiOrigin], checkOperation: "notes.list",
|
|
43
|
+
apiKey: { headers: (key) => {
|
|
44
|
+
if (!key.startsWith("grn_") || key.length <= 4 || /[\s\p{Cc}]/u.test(key)) {
|
|
45
|
+
throw new ConnectorError("connector_binding_missing", "Use the Granola grn_ API key without whitespace or control characters.");
|
|
46
|
+
}
|
|
47
|
+
return { Authorization: `Bearer ${key}` };
|
|
48
|
+
} },
|
|
49
|
+
operations: {
|
|
50
|
+
"notes.list": jsonOperation(`${apiOrigin}/v1/notes`, {
|
|
51
|
+
created_before: date, created_after: date, updated_after: date,
|
|
52
|
+
folder_id: { type: "string", validator: (value) => folderId(value) || "Use a fol_ ID returned by folders.list." },
|
|
53
|
+
cursor, page_size: { type: "integer", min: 1, max: 30, defaultTo: 10 }
|
|
54
|
+
}, (value) => validPage(value, "notes", noteSummary)),
|
|
55
|
+
"folders.list": jsonOperation(`${apiOrigin}/v1/folders`, {
|
|
56
|
+
cursor, page_size: { type: "integer", min: 1, max: 30, defaultTo: 10 }
|
|
57
|
+
}, (value) => validPage(value, "folders", validFolder)),
|
|
58
|
+
"notes.get": noteRead("", { include: { type: "string", enum: ["transcript"] } }, (value) => noteSummary(value) &&
|
|
59
|
+
typeof value.summary_text === "string" && (value.summary_markdown === null || typeof value.summary_markdown === "string") &&
|
|
60
|
+
(value.private_notes_text === null || typeof value.private_notes_text === "string") &&
|
|
61
|
+
(value.private_notes_markdown === null || typeof value.private_notes_markdown === "string") &&
|
|
62
|
+
(value.transcript === null || Array.isArray(value.transcript) && value.transcript.every(transcriptItem))),
|
|
63
|
+
"transcripts.list": noteRead("/transcript", {
|
|
64
|
+
cursor, page_size: { type: "integer", min: 1, max: 100, defaultTo: 50 }
|
|
65
|
+
}, (value) => validPage(value, "transcript", transcriptItem))
|
|
66
|
+
},
|
|
67
|
+
async exchange(address, options, { fetchImpl }) {
|
|
68
|
+
const response = await fetchImpl(address, { ...options, credentials: "omit" });
|
|
69
|
+
if (response.status === 404) throw new ConnectorError("connector_resource_not_found", "The Granola note is unavailable, inaccessible or still being processed.", { statusCode: 404 });
|
|
70
|
+
if (response.status === 413) throw new ConnectorError("connector_response_too_large", "Read the transcript in pages with transcripts.list.", { statusCode: 413 });
|
|
71
|
+
if (!response.ok) throw Object.assign(new Error("Granola request failed."), { status: response.status });
|
|
72
|
+
let value;
|
|
73
|
+
try { value = await response.json(); } catch { /* checked below */ }
|
|
74
|
+
if (response.status !== 200 || !value || typeof value !== "object" || value.error) {
|
|
75
|
+
throw new ConnectorError("connector_response_invalid", "Granola returned an unexpected response.", { statusCode: 502 });
|
|
76
|
+
}
|
|
77
|
+
const url = new URL(address);
|
|
78
|
+
const list = url.pathname === "/v1/notes" ? "notes" : url.pathname === "/v1/folders" ? "folders" : url.pathname.endsWith("/transcript") ? "transcript" : null;
|
|
79
|
+
if (list ? value[list]?.length > Number(url.searchParams.get("page_size")) : value.id !== url.pathname.split("/").at(-1)) {
|
|
80
|
+
throw new ConnectorError("connector_response_invalid", "Granola returned data that does not match this request.", { statusCode: 502 });
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const mcpResource = "https://mcp.granola.ai/mcp";
|
|
87
|
+
const oauth = Object.freeze({
|
|
88
|
+
issuer: "https://mcp-auth.granola.ai",
|
|
89
|
+
authorization_endpoint: "https://mcp-auth.granola.ai/oauth2/authorize",
|
|
90
|
+
token_endpoint: "https://mcp-auth.granola.ai/oauth2/token",
|
|
91
|
+
registration_endpoint: "https://mcp-auth.granola.ai/oauth2/register",
|
|
92
|
+
response_types_supported: ["code"], code_challenge_methods_supported: ["S256"]
|
|
93
|
+
});
|
|
94
|
+
const granolaProvider = Object.freeze({
|
|
95
|
+
...granolaApiProvider, oauth, oauthResource: mcpResource,
|
|
96
|
+
apiOrigins: [apiOrigin, "https://mcp.granola.ai"],
|
|
97
|
+
checkOperation: (method) => method === "oauth2" ? "tools.list" : "notes.list",
|
|
98
|
+
operations: {
|
|
99
|
+
...Object.fromEntries(Object.entries(granolaApiProvider.operations).map(([name, operation]) =>
|
|
100
|
+
[name, { ...operation, authenticationMethods: ["api-key"] }])),
|
|
101
|
+
...Object.fromEntries(Object.entries(mcpOperations(() => mcpResource)).map(([name, operation]) =>
|
|
102
|
+
[name, { ...operation, authenticationMethods: ["oauth2"], scopes: ["openid"] }]))
|
|
103
|
+
},
|
|
104
|
+
exchange(address, options, context) {
|
|
105
|
+
return address === mcpResource ? exchangeMcp(address, options, context) : granolaApiProvider.exchange(address, options, context);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const registrationSchema = createSchema(mcpRegistrationFields);
|
|
109
|
+
|
|
110
|
+
async function registerGranolaClient(input, options) {
|
|
111
|
+
const values = validateSchemaPayload({ schema: registrationSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
112
|
+
return registerMcpClient("Granola", oauth, { ...values, scopes: granolaDefinition.scopes.map((scope) => scope.value) }, options);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { granolaProvider, registerGranolaClient };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
2
|
+
|
|
3
|
+
function graphqlOperation(url, query, fields, validateData) {
|
|
4
|
+
const operation = jsonOperation(url, fields,
|
|
5
|
+
(result) => (result?.errors === undefined || (Array.isArray(result.errors) && result.errors.length === 0)) && validateData(result?.data), "POST");
|
|
6
|
+
return {
|
|
7
|
+
...operation,
|
|
8
|
+
request(input, settings) {
|
|
9
|
+
const request = operation.request(input, settings);
|
|
10
|
+
return { ...request, body: { query, variables: request.body } };
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { graphqlOperation };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { hexDefinition, hexHosts, hexEndpointField } from "../shared/hex.js";
|
|
4
|
+
import { mcpOperations, exchangeMcp } from "./mcpTransport.js";
|
|
5
|
+
import { mcpRegistrationFields, registerMcpClient } from "./mcpRegistration.js";
|
|
6
|
+
|
|
7
|
+
const origin = (settings) => `https://${hexHosts[settings.endpoint]}`;
|
|
8
|
+
const resource = (settings) => `${origin(settings)}/mcp`;
|
|
9
|
+
const oauth = (settings) => {
|
|
10
|
+
const issuer = `https://auth.${hexHosts[settings.endpoint]}`;
|
|
11
|
+
return { issuer, authorization_endpoint: `${issuer}/oauth2/authorize`, token_endpoint: `${issuer}/oauth2/token`,
|
|
12
|
+
registration_endpoint: `${issuer}/oauth2/register`, response_types_supported: ["code"], code_challenge_methods_supported: ["S256"] };
|
|
13
|
+
};
|
|
14
|
+
const hexProvider = Object.freeze({
|
|
15
|
+
...hexDefinition, oauth, oauthResource: resource, apiOrigins: (settings) => [origin(settings)],
|
|
16
|
+
checkOperation: "tools.list", exchange: exchangeMcp,
|
|
17
|
+
operations: Object.fromEntries(Object.entries(mcpOperations(resource)).map(([name, operation]) => [name, { ...operation, scopes: ["openid"] }]))
|
|
18
|
+
});
|
|
19
|
+
const registrationSchema = createSchema({ endpoint: hexEndpointField, ...mcpRegistrationFields,
|
|
20
|
+
scopes: { type: "array", required: true, minLength: 1, items: { type: "string", enum: hexDefinition.scopes.map((scope) => scope.value) },
|
|
21
|
+
validator: (scopes) => scopes.includes("openid") && new Set(scopes).size === scopes.length || "Select account identity and include each permission once." }
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
async function registerHexClient(input, options) {
|
|
25
|
+
const values = validateSchemaPayload({ schema: registrationSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
26
|
+
return registerMcpClient("Hex", oauth(values), values, options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { hexProvider, registerHexClient };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { mcpOperations, exchangeMcp } from "./mcpTransport.js";
|
|
4
|
+
import { mcpRegistrationFields, registerMcpClient } from "./mcpRegistration.js";
|
|
5
|
+
import { heygenDefinition } from "../shared/tokens.js";
|
|
6
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
7
|
+
|
|
8
|
+
const heygenApiProvider = Object.freeze({
|
|
9
|
+
...heygenDefinition, apiOrigins: ["https://api.heygen.com"],
|
|
10
|
+
apiKey: { headers: (key) => ({ "x-api-key": key }) },
|
|
11
|
+
checkOperation: "profile.read",
|
|
12
|
+
operations: {
|
|
13
|
+
"profile.read": jsonOperation("https://api.heygen.com/v3/users/me", {},
|
|
14
|
+
(result) => typeof result?.data?.username === "string"),
|
|
15
|
+
"voices.list": jsonOperation("https://api.heygen.com/v3/voices", {
|
|
16
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
17
|
+
token: { type: "string", maxLength: 4096 },
|
|
18
|
+
type: { type: "string", enum: ["public", "private"], defaultTo: "public" },
|
|
19
|
+
engine: { type: "string", maxLength: 100 },
|
|
20
|
+
language: { type: "string", maxLength: 100 },
|
|
21
|
+
gender: { type: "string", enum: ["male", "female"] }
|
|
22
|
+
}, (result) => Array.isArray(result?.data) && typeof result.has_more === "boolean")
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const mcpEndpoint = "https://mcp.heygen.com/mcp/v1/";
|
|
26
|
+
const oauth = Object.freeze({
|
|
27
|
+
issuer: "https://api2.heygen.com",
|
|
28
|
+
authorization_endpoint: "https://api2.heygen.com/v1/oauth/authorize",
|
|
29
|
+
token_endpoint: "https://api2.heygen.com/v1/oauth/token",
|
|
30
|
+
registration_endpoint: "https://api2.heygen.com/v1/oauth/register",
|
|
31
|
+
response_types_supported: ["code"], code_challenge_methods_supported: ["S256"]
|
|
32
|
+
});
|
|
33
|
+
const heygenProvider = Object.freeze({
|
|
34
|
+
...heygenApiProvider, oauth, oauthResource: "https://mcp.heygen.com",
|
|
35
|
+
apiOrigins: ["https://api.heygen.com", "https://mcp.heygen.com"],
|
|
36
|
+
checkOperation: (method) => method === "oauth2" ? "tools.list" : "profile.read",
|
|
37
|
+
operations: {
|
|
38
|
+
...Object.fromEntries(Object.entries(heygenApiProvider.operations).map(([name, operation]) =>
|
|
39
|
+
[name, { ...operation, authenticationMethods: ["api-key"] }])),
|
|
40
|
+
...Object.fromEntries(Object.entries(mcpOperations(() => mcpEndpoint)).map(([name, operation]) =>
|
|
41
|
+
[name, { ...operation, authenticationMethods: ["oauth2"], scopes: ["openid"] }]))
|
|
42
|
+
},
|
|
43
|
+
exchange(address, options, context) {
|
|
44
|
+
return address === mcpEndpoint ? exchangeMcp(address, options, context) : context.request(address, options);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const registrationSchema = createSchema(mcpRegistrationFields);
|
|
48
|
+
|
|
49
|
+
async function registerHeyGenClient(input, options) {
|
|
50
|
+
const values = validateSchemaPayload({ schema: registrationSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
51
|
+
return registerMcpClient("HeyGen", oauth, { ...values, scopes: heygenDefinition.scopes.map((scope) => scope.value) }, options);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { heygenProvider, registerHeyGenClient };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { ConnectorError } from "@jskit-ai/connectors-core/server";
|
|
4
|
+
import { hubspotDefinition } from "../shared/tokens.js";
|
|
5
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
6
|
+
|
|
7
|
+
const base = "https://api.hubapi.com/crm/objects/2026-09";
|
|
8
|
+
const recordId = { type: "string", required: true, minLength: 1, maxLength: 100,
|
|
9
|
+
validator: value => /^[0-9]+$/u.test(value) || "Use the HubSpot numeric record ID." };
|
|
10
|
+
const properties = { type: "object", required: true, validator: value => {
|
|
11
|
+
const entries = Object.entries(value);
|
|
12
|
+
return entries.length > 0 && entries.length <= 100 && entries.every(([key, item]) =>
|
|
13
|
+
/^[a-zA-Z_][a-zA-Z0-9_]*$/u.test(key) && typeof item === "string") &&
|
|
14
|
+
Buffer.byteLength(JSON.stringify(value)) <= 100000 || "Provide 1–100 named string properties, within 100KB.";
|
|
15
|
+
} };
|
|
16
|
+
const readFields = {
|
|
17
|
+
properties: { type: "string", maxLength: 4096, validator: value => /^[a-zA-Z0-9_,]+$/u.test(value) || "Use comma-separated property names." },
|
|
18
|
+
associations: { type: "string", enum: ["contacts", "deals", "contacts,deals"] },
|
|
19
|
+
archived: { type: "boolean", defaultTo: false }
|
|
20
|
+
};
|
|
21
|
+
const validRecord = value => typeof value?.id === "string" && value.properties && typeof value.properties === "object" && !Array.isArray(value.properties);
|
|
22
|
+
function objectOperations(object) {
|
|
23
|
+
const readScope = `crm.objects.${object}.read`, writeScope = `crm.objects.${object}.write`;
|
|
24
|
+
const read = createSchema({ id: recordId, ...readFields });
|
|
25
|
+
const write = createSchema({ id: recordId, properties });
|
|
26
|
+
const create = createSchema({ properties });
|
|
27
|
+
return {
|
|
28
|
+
[`${object}.list`]: { ...jsonOperation(`${base}/${object}`, {
|
|
29
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
30
|
+
after: { type: "string", maxLength: 4096 }, ...readFields
|
|
31
|
+
}, value => Array.isArray(value?.results)), scopes: [readScope] },
|
|
32
|
+
[`${object}.get`]: { scopes: [readScope], request(input) {
|
|
33
|
+
const { id, ...query } = validateSchemaPayload({ schema: read, mode: "replace" }, input, { statusCode: 422 });
|
|
34
|
+
const url = new URL(`${base}/${object}/${encodeURIComponent(id)}`);
|
|
35
|
+
for (const [key, value] of Object.entries(query)) url.searchParams.set(key, String(value));
|
|
36
|
+
return { method: "GET", url: url.href };
|
|
37
|
+
}, validateResult: validRecord },
|
|
38
|
+
[`${object}.create`]: { scopes: [writeScope], request(input) {
|
|
39
|
+
const body = validateSchemaPayload({ schema: create, mode: "replace" }, input, { statusCode: 422 });
|
|
40
|
+
if (object === "contacts" ? !["email", "firstname", "lastname"].some(key => body.properties[key]?.trim()) :
|
|
41
|
+
!body.properties.dealname?.trim() || !body.properties.dealstage?.trim()) {
|
|
42
|
+
throw new ConnectorError("connector_input_invalid", object === "contacts" ? "Supply email, firstname or lastname." : "Supply dealname and the internal dealstage ID.", { statusCode: 422 });
|
|
43
|
+
}
|
|
44
|
+
return { method: "POST", url: `${base}/${object}`, body };
|
|
45
|
+
}, validateResult: validRecord },
|
|
46
|
+
[`${object}.update`]: { scopes: [writeScope], request(input) {
|
|
47
|
+
const { id, ...body } = validateSchemaPayload({ schema: write, mode: "replace" }, input, { statusCode: 422 });
|
|
48
|
+
return { method: "PATCH", url: `${base}/${object}/${encodeURIComponent(id)}`, body };
|
|
49
|
+
}, validateResult: validRecord }
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const associationSchema = createSchema({ contactId: recordId, dealId: recordId });
|
|
53
|
+
|
|
54
|
+
const hubspotProvider = Object.freeze({
|
|
55
|
+
...hubspotDefinition, apiOrigins: ["https://api.hubapi.com"],
|
|
56
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
|
|
57
|
+
oauth: {
|
|
58
|
+
issuer: "https://api.hubspot.com",
|
|
59
|
+
authorization_endpoint: "https://app.hubspot.com/oauth/authorize",
|
|
60
|
+
token_endpoint: "https://api.hubspot.com/oauth/2026-09/token"
|
|
61
|
+
},
|
|
62
|
+
// HubSpot documents confidential-client code grants without PKCE.
|
|
63
|
+
oauthPkce: false,
|
|
64
|
+
async normalizeTokenResponse(response) {
|
|
65
|
+
if (!response.ok) return response;
|
|
66
|
+
let value;
|
|
67
|
+
try { value = await response.clone().json(); } catch {
|
|
68
|
+
throw new ConnectorError("connector_response_invalid", "HubSpot returned an invalid token response.", { statusCode: 502 });
|
|
69
|
+
}
|
|
70
|
+
if (!Array.isArray(value?.scopes) || !value.scopes.every((scope) => typeof scope === "string" && /^[^\s]+$/u.test(scope))) {
|
|
71
|
+
throw new ConnectorError("connector_response_invalid", "HubSpot returned an invalid permission grant.", { statusCode: 502 });
|
|
72
|
+
}
|
|
73
|
+
return Response.json({ ...value, scope: value.scopes.join(" ") }, { status: response.status });
|
|
74
|
+
},
|
|
75
|
+
checkOperation: "contacts.list",
|
|
76
|
+
operations: {
|
|
77
|
+
...objectOperations("contacts"), ...objectOperations("deals"),
|
|
78
|
+
"deals.associateContact": { scopes: ["crm.objects.contacts.write", "crm.objects.deals.write"], request(input) {
|
|
79
|
+
const { contactId, dealId } = validateSchemaPayload({ schema: associationSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
80
|
+
return { method: "PUT", url: `${base}/deals/${dealId}/associations/default/contacts/${contactId}` };
|
|
81
|
+
}, validateResult: value => Array.isArray(value?.results) },
|
|
82
|
+
"pipelines.list": { ...jsonOperation("https://api.hubapi.com/crm/pipelines/2026-09/deals", {}, value => Array.isArray(value?.results)), scopes: ["crm.objects.deals.read"] }
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
export { hubspotProvider };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { incidentIoDefinition } from "../shared/tokens.js";
|
|
3
|
+
import { jsonOperation, validatedOperation } from "./jsonOperation.js";
|
|
4
|
+
|
|
5
|
+
const id = { type: "string", required: true, minLength: 1, maxLength: 200,
|
|
6
|
+
validator: value => /^[a-zA-Z0-9_-]+$/u.test(value) || "Use the incident.io resource ID, not a URL." };
|
|
7
|
+
const text = { type: "string", minLength: 1, maxLength: 20000 };
|
|
8
|
+
const optionalId = { ...id, required: false };
|
|
9
|
+
const after = { type: "string", minLength: 1, maxLength: 4096 };
|
|
10
|
+
const page = max => ({ type: "integer", min: 1, max, defaultTo: 25 });
|
|
11
|
+
const result = key => value => typeof value?.[key]?.id === "string";
|
|
12
|
+
const address = path => `https://api.incident.io${path}`;
|
|
13
|
+
const incidentFields = { name: text, summary: text, severity_id: optionalId, incident_status_id: optionalId };
|
|
14
|
+
const followUpFields = { title: { ...text, required: true, maxLength: 1000 }, description: text,
|
|
15
|
+
assignee_id: optionalId, assignee_team_id: optionalId, follow_up_category_id: optionalId, follow_up_priority_option_id: optionalId };
|
|
16
|
+
const literal = value => value && typeof value === "object" && !Array.isArray(value) &&
|
|
17
|
+
Object.keys(value).length === 1 && typeof value.literal === "string" && value.literal.length <= 20000;
|
|
18
|
+
const attributes = { type: "object", required: true, additionalProperties: true,
|
|
19
|
+
validator: value => Object.keys(value).length <= 250 && Buffer.byteLength(JSON.stringify(value)) <= 100000 &&
|
|
20
|
+
Object.entries(value).every(([key, binding]) => /^[a-zA-Z0-9_-]+$/u.test(key) && binding && typeof binding === "object" && !Array.isArray(binding) &&
|
|
21
|
+
Object.keys(binding).every(field => ["value", "array_value"].includes(field)) &&
|
|
22
|
+
(binding.value === undefined || literal(binding.value)) &&
|
|
23
|
+
(binding.array_value === undefined || Array.isArray(binding.array_value) && binding.array_value.length <= 100 && binding.array_value.every(literal))) ||
|
|
24
|
+
"Use at most 250 attributes with literal string value/array_value bindings, within 100KB." };
|
|
25
|
+
|
|
26
|
+
const incidentIoProvider = Object.freeze({
|
|
27
|
+
...incidentIoDefinition, apiOrigins: ["https://api.incident.io"],
|
|
28
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
|
|
29
|
+
checkOperation: "incidents.list",
|
|
30
|
+
operations: {
|
|
31
|
+
...Object.fromEntries([
|
|
32
|
+
["incidents.get", "/v2/incidents", "incident"], ["followUps.get", "/v2/follow_ups", "follow_up"],
|
|
33
|
+
["alerts.get", "/v2/alerts", "alert"], ["schedules.get", "/v2/schedules", "schedule"],
|
|
34
|
+
["catalog.get", "/v3/catalog_entries", "catalog_entry"]
|
|
35
|
+
].map(([name, path, key]) => [name, validatedOperation({ id }, ({ id }) => ({ method: "GET", url: address(`${path}/${id}`) }), result(key))])),
|
|
36
|
+
"incidents.create": validatedOperation({ ...incidentFields, idempotency_key: { ...text, required: true, maxLength: 200 },
|
|
37
|
+
visibility: { type: "string", required: true, enum: ["public", "private"] },
|
|
38
|
+
mode: { type: "string", enum: ["standard", "retrospective", "test", "tutorial"], defaultTo: "standard" }, incident_type_id: optionalId
|
|
39
|
+
}, body => ({ method: "POST", url: address("/v2/incidents"), body }), result("incident")),
|
|
40
|
+
"incidents.update": validatedOperation({ id, incident: { type: "object", required: true, schema: createSchema(incidentFields),
|
|
41
|
+
validator: value => Object.keys(value).length > 0 || "Provide at least one incident field." },
|
|
42
|
+
notify_incident_channel: { type: "boolean", required: true }
|
|
43
|
+
}, ({ id, ...body }) => ({ method: "POST", url: address(`/v2/incidents/${id}/actions/edit`), body }), result("incident")),
|
|
44
|
+
"followUps.list": jsonOperation(address("/v2/follow_ups"), { incident_id: optionalId, assignee_team_id: optionalId,
|
|
45
|
+
incident_mode: { type: "string", enum: ["standard", "retrospective", "test", "tutorial", "stream"] }
|
|
46
|
+
}, value => Array.isArray(value?.follow_ups)),
|
|
47
|
+
"followUps.create": validatedOperation({ ...followUpFields, incident_id: id }, body => ({ method: "POST", url: address("/v2/follow_ups"), body }), result("follow_up")),
|
|
48
|
+
"followUps.update": validatedOperation({ id, ...followUpFields, status: { type: "string", required: true, enum: ["outstanding", "completed", "deleted", "not_doing"] }
|
|
49
|
+
}, ({ id, ...body }) => ({ method: "PUT", url: address(`/v2/follow_ups/${id}`), body }), result("follow_up")),
|
|
50
|
+
"alerts.list": jsonOperation(address("/v2/alerts"), { page_size: page(50), after }, value => Array.isArray(value?.alerts)),
|
|
51
|
+
"alerts.resolve": validatedOperation({ id }, ({ id }) => ({ method: "POST", url: address(`/v2/alerts/${id}/actions/resolve`), body: {} }), result("alert")),
|
|
52
|
+
"schedules.list": jsonOperation(address("/v2/schedules"), { page_size: page(25), after }, value => Array.isArray(value?.schedules)),
|
|
53
|
+
"catalog.types": jsonOperation(address("/v3/catalog_types"), {}, value => Array.isArray(value?.catalog_types)),
|
|
54
|
+
"catalog.list": jsonOperation(address("/v3/catalog_entries"), { catalog_type_id: id, page_size: page(250), after, identifier: text }, value => Array.isArray(value?.catalog_entries)),
|
|
55
|
+
"catalog.create": validatedOperation({ catalog_type_id: id, name: { ...text, required: true, maxLength: 1000 }, external_id: text,
|
|
56
|
+
attribute_values: attributes
|
|
57
|
+
}, body => ({ method: "POST", url: address("/v3/catalog_entries"), body }), result("catalog_entry")),
|
|
58
|
+
"catalog.update": validatedOperation({ id, name: { ...text, required: true, maxLength: 1000 }, external_id: text,
|
|
59
|
+
attribute_values: attributes
|
|
60
|
+
}, ({ id, ...body }) => ({ method: "PUT", url: address(`/v3/catalog_entries/${id}`), body }), result("catalog_entry")),
|
|
61
|
+
"incidents.list": jsonOperation("https://api.incident.io/v2/incidents", {
|
|
62
|
+
page_size: { type: "integer", min: 1, max: 500, defaultTo: 25 },
|
|
63
|
+
after: { type: "string", maxLength: 4096 },
|
|
64
|
+
sort_by: { type: "string", enum: ["created_at_newest_first", "created_at_oldest_first"], defaultTo: "created_at_newest_first" }
|
|
65
|
+
}, (result) => Array.isArray(result?.incidents) && (result.pagination_meta === undefined || Number.isInteger(result.pagination_meta?.page_size)))
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
export { incidentIoProvider };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { ConnectorError } from "@jskit-ai/connectors-core/server";
|
|
4
|
+
import { inngestDefinition } from "../shared/inngest.js";
|
|
5
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
6
|
+
|
|
7
|
+
const api = "https://api.inngest.com/v2";
|
|
8
|
+
const eventEndpoint = "https://inn.gs/e/";
|
|
9
|
+
const pageFields = {
|
|
10
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
11
|
+
cursor: { type: "string", minLength: 1, maxLength: 4096 }
|
|
12
|
+
};
|
|
13
|
+
const functionsSchema = createSchema({
|
|
14
|
+
appId: { type: "string", required: true, minLength: 1, maxLength: 255,
|
|
15
|
+
validator: (value) => ![".", ".."].includes(value) && !/[\p{Cc}]/u.test(value) || "Enter a valid Inngest app ID." },
|
|
16
|
+
...pageFields
|
|
17
|
+
});
|
|
18
|
+
const eventSchema = createSchema({
|
|
19
|
+
name: { type: "string", required: true, minLength: 1, maxLength: 255,
|
|
20
|
+
validator: (value) => Boolean(value.trim()) && !/[\p{Cc}]/u.test(value) || "Enter an event name without control characters." },
|
|
21
|
+
data: { type: "object", required: true },
|
|
22
|
+
id: { type: "string", minLength: 1, maxLength: 512 },
|
|
23
|
+
ts: { type: "integer", min: 315532800000, max: Number.MAX_SAFE_INTEGER },
|
|
24
|
+
v: { type: "string", minLength: 1, maxLength: 255 },
|
|
25
|
+
user: { type: "object" }
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function validPage(result) {
|
|
29
|
+
return Array.isArray(result?.data) && result.data.every((item) => typeof item?.id === "string" && item.id.length > 0) &&
|
|
30
|
+
typeof result.page?.hasMore === "boolean" && Number.isInteger(result.page.limit) && result.page.limit >= 1 &&
|
|
31
|
+
(result.page.cursor == null || typeof result.page.cursor === "string");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const inngestProvider = Object.freeze({
|
|
35
|
+
...inngestDefinition,
|
|
36
|
+
apiOrigins: ["https://api.inngest.com", "https://inn.gs"],
|
|
37
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
|
|
38
|
+
checkOperation: "apps.list",
|
|
39
|
+
async exchange(url, options, { request, resolveReference }) {
|
|
40
|
+
if (url !== eventEndpoint) return request(url, options);
|
|
41
|
+
const { eventKeyRef, branchEnvironment, event } = options.body;
|
|
42
|
+
let key;
|
|
43
|
+
try {
|
|
44
|
+
key = await resolveReference(eventKeyRef);
|
|
45
|
+
if (typeof key !== "string" || !key.trim() || /[\p{Cc}]/u.test(key)) throw new Error("Invalid Event Key.");
|
|
46
|
+
} catch {
|
|
47
|
+
throw new ConnectorError("connector_binding_missing", "The Inngest Event Key binding is missing or invalid.");
|
|
48
|
+
}
|
|
49
|
+
options.signal.throwIfAborted();
|
|
50
|
+
// The Event API uses only the Event Key in its path. Never forward the Signing Key.
|
|
51
|
+
return request(`${eventEndpoint}${encodeURIComponent(key)}`, {
|
|
52
|
+
...options, body: event,
|
|
53
|
+
headers: { Accept: "application/json", ...(branchEnvironment ? { "x-inngest-env": branchEnvironment } : {}) }
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
operations: {
|
|
57
|
+
"apps.list": jsonOperation(`${api}/apps`, { ...pageFields, archived: { type: "boolean", defaultTo: false } }, validPage),
|
|
58
|
+
"functions.list": {
|
|
59
|
+
scopes: [],
|
|
60
|
+
request(input) {
|
|
61
|
+
const { appId, ...query } = validateSchemaPayload({ schema: functionsSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
62
|
+
const url = new URL(`${api}/apps/${encodeURIComponent(appId)}/functions`);
|
|
63
|
+
for (const [name, value] of Object.entries(query)) url.searchParams.set(name, value);
|
|
64
|
+
return { method: "GET", url: url.href };
|
|
65
|
+
},
|
|
66
|
+
validateResult: validPage
|
|
67
|
+
},
|
|
68
|
+
"events.send": {
|
|
69
|
+
scopes: [],
|
|
70
|
+
request(input, settings) {
|
|
71
|
+
const event = validateSchemaPayload({ schema: eventSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
72
|
+
return { method: "POST", url: eventEndpoint,
|
|
73
|
+
body: { event, eventKeyRef: settings.eventKeyRef, branchEnvironment: settings.branchEnvironment } };
|
|
74
|
+
},
|
|
75
|
+
validateResult(result) {
|
|
76
|
+
if (result?.status === 401) throw new ConnectorError("connector_reconnect_required", "Configure a valid Inngest Event Key and reconnect.", { statusCode: 401 });
|
|
77
|
+
if (result?.status === 403) throw new ConnectorError("connector_permission_denied", "Inngest denied this event.", { statusCode: 403 });
|
|
78
|
+
if (result?.status === 429) throw new ConnectorError("connector_rate_limited", "Inngest's event limit was reached.", { statusCode: 429 });
|
|
79
|
+
return result?.status === 200 && !result.error && Array.isArray(result.ids) && result.ids.length === 1 &&
|
|
80
|
+
result.ids.every((id) => typeof id === "string" && id.length > 0);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export { inngestProvider };
|