@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,76 @@
|
|
|
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 { notionDefinition } from "../shared/tokens.js";
|
|
6
|
+
import { jsonOperation, validatedOperation } from "./jsonOperation.js";
|
|
7
|
+
|
|
8
|
+
const identifier = { type: "string", required: true, pattern: "^(?:[a-fA-F0-9]{32}|[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$" };
|
|
9
|
+
const document = { type: "object", additionalProperties: true,
|
|
10
|
+
validator: value => Buffer.byteLength(JSON.stringify(value)) <= 65536 || "Keep each document below 64 KiB." };
|
|
11
|
+
const pagination = { page_size: { type: "integer", min: 1, max: 100, defaultTo: 100 },
|
|
12
|
+
start_cursor: { type: "string", minLength: 1, maxLength: 4096 } };
|
|
13
|
+
const listResult = result => result?.object === "list" && Array.isArray(result.results) && typeof result.has_more === "boolean";
|
|
14
|
+
const objectResult = object => result => result?.object === object && typeof result.id === "string" && result.id.length > 0;
|
|
15
|
+
const children = { type: "array", required: true, minLength: 1, maxLength: 100,
|
|
16
|
+
items: { ...document, required: true }, validator: value => value.length >= 1 && value.length <= 100 && Buffer.byteLength(JSON.stringify(value)) <= 262144 || "Keep blocks below 256 KiB." };
|
|
17
|
+
// Notion owns its property/block grammar. Bound documents without inventing a second content schema.
|
|
18
|
+
function readObject(collection, object) {
|
|
19
|
+
return validatedOperation({ id: identifier }, ({ id }) => ({ method: "GET", url: `https://api.notion.com/v1/${collection}/${id}` }), objectResult(object));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const mcpMetadata = Object.freeze({ issuer: "https://mcp.notion.com",
|
|
23
|
+
authorization_endpoint: "https://mcp.notion.com/authorize", token_endpoint: "https://mcp.notion.com/token",
|
|
24
|
+
registration_endpoint: "https://mcp.notion.com/register" });
|
|
25
|
+
const mcpRuntime = Object.freeze({ oauth: mcpMetadata, oauthPkce: true,
|
|
26
|
+
oauthBasicEncoding: undefined, tokenRequestEncoding: undefined, authorizationParameters: {}, oauthHeaders: undefined,
|
|
27
|
+
apiOrigins: ["https://mcp.notion.com"], checkOperation: "tools.list", exchange: exchangeMcp,
|
|
28
|
+
operations: mcpOperations(() => "https://mcp.notion.com/mcp") });
|
|
29
|
+
const notionProvider = Object.freeze({
|
|
30
|
+
...notionDefinition,
|
|
31
|
+
runtimeForSettings: (settings) => settings.connectionType === "mcp" ? mcpRuntime : {},
|
|
32
|
+
oauth: {
|
|
33
|
+
issuer: "https://api.notion.com",
|
|
34
|
+
authorization_endpoint: "https://api.notion.com/v1/oauth/authorize",
|
|
35
|
+
token_endpoint: "https://api.notion.com/v1/oauth/token"
|
|
36
|
+
},
|
|
37
|
+
oauthPkce: false,
|
|
38
|
+
oauthBasicEncoding: "raw",
|
|
39
|
+
tokenRequestEncoding: "json",
|
|
40
|
+
authorizationParameters: { owner: "user" },
|
|
41
|
+
oauthHeaders: () => ({ "Notion-Version": "2026-03-11" }),
|
|
42
|
+
apiOrigins: ["https://api.notion.com"],
|
|
43
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}`, "Notion-Version": "2026-03-11" }) },
|
|
44
|
+
checkOperation: "content.search",
|
|
45
|
+
operations: {
|
|
46
|
+
"pages.get": readObject("pages", "page"),
|
|
47
|
+
"databases.get": readObject("databases", "database"),
|
|
48
|
+
"dataSources.get": readObject("data_sources", "data_source"),
|
|
49
|
+
"blocks.list": validatedOperation({ id: identifier, ...pagination }, ({ id, ...query }) => ({ method: "GET",
|
|
50
|
+
url: `https://api.notion.com/v1/blocks/${id}/children?${new URLSearchParams(query)}` }), listResult),
|
|
51
|
+
"dataSources.query": validatedOperation({ id: identifier, ...pagination, filter: document,
|
|
52
|
+
sorts: { type: "array", validator: value => value.length <= 20 || "Use at most 20 sorts.", items: document } }, ({ id, ...body }) => ({ method: "POST",
|
|
53
|
+
url: `https://api.notion.com/v1/data_sources/${id}/query`, body }), listResult),
|
|
54
|
+
"pages.create": validatedOperation({ parentId: identifier, parentType: { type: "string", required: true, enum: ["page_id", "data_source_id"] },
|
|
55
|
+
properties: { ...document, required: true }, children: { ...children, required: false } },
|
|
56
|
+
({ parentId, parentType, ...body }) => ({ method: "POST", url: "https://api.notion.com/v1/pages",
|
|
57
|
+
body: { parent: { type: parentType, [parentType]: parentId }, ...body } }), objectResult("page")),
|
|
58
|
+
"pages.update": validatedOperation({ id: identifier, properties: { ...document, required: true } },
|
|
59
|
+
({ id, ...body }) => ({ method: "PATCH", url: `https://api.notion.com/v1/pages/${id}`, body }), objectResult("page")),
|
|
60
|
+
"blocks.append": validatedOperation({ id: identifier, children }, ({ id, ...body }) => ({ method: "PATCH",
|
|
61
|
+
url: `https://api.notion.com/v1/blocks/${id}/children`, body }), listResult),
|
|
62
|
+
"content.search": jsonOperation("https://api.notion.com/v1/search", {
|
|
63
|
+
query: { type: "string", maxLength: 4096 },
|
|
64
|
+
page_size: { type: "integer", min: 1, max: 100, defaultTo: 100 },
|
|
65
|
+
start_cursor: { type: "string", minLength: 1, maxLength: 4096 }
|
|
66
|
+
}, (result) => result?.object === "list" && Array.isArray(result.results) && typeof result.has_more === "boolean", "POST"),
|
|
67
|
+
"identity.read": jsonOperation("https://api.notion.com/v1/users/me", {},
|
|
68
|
+
(result) => result?.object === "user" && typeof result.id === "string" && ["bot", "person"].includes(result.type))
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
const registrationSchema = createSchema(mcpRegistrationFields);
|
|
72
|
+
async function registerNotionMcpClient(input, options) {
|
|
73
|
+
const values = validateSchemaPayload({ schema: registrationSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
74
|
+
return registerMcpClient("Notion MCP", mcpMetadata, { ...values, scopes: ["default"] }, options);
|
|
75
|
+
}
|
|
76
|
+
export { notionProvider, registerNotionMcpClient };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ConnectorError } from "@jskit-ai/connectors-core/server";
|
|
2
|
+
import { ouraDefinition } from "../shared/oura.js";
|
|
3
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
4
|
+
|
|
5
|
+
const date = {
|
|
6
|
+
type: "string",
|
|
7
|
+
validator: (value) => (/^\d{4}-\d{2}-\d{2}$/u.test(value) &&
|
|
8
|
+
Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value) || "Enter a valid date as YYYY-MM-DD."
|
|
9
|
+
};
|
|
10
|
+
const datetime = { type: "string", validator: value =>
|
|
11
|
+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/.test(value) && Number.isFinite(Date.parse(value)) && date.validator(value.slice(0, 10)) === true || "Enter an ISO timestamp with timezone." };
|
|
12
|
+
function collection(endpoint, scope = "daily", timed = false) {
|
|
13
|
+
const start = timed ? "start_datetime" : "start_date", end = timed ? "end_datetime" : "end_date";
|
|
14
|
+
const operation = jsonOperation(`https://api.ouraring.com/v2/usercollection/${endpoint}`, {
|
|
15
|
+
[start]: timed ? datetime : date, [end]: timed ? datetime : date,
|
|
16
|
+
next_token: { type: "string", minLength: 1, maxLength: 4096 }
|
|
17
|
+
}, result => Array.isArray(result?.data) && (result.next_token == null || typeof result.next_token === "string"));
|
|
18
|
+
return { ...operation, scopes: [scope], request(input, settings) {
|
|
19
|
+
const request = operation.request(input, settings);
|
|
20
|
+
const query = new URL(request.url).searchParams;
|
|
21
|
+
if (query.has(start) && query.has(end) && Date.parse(query.get(start)) > Date.parse(query.get(end)))
|
|
22
|
+
throw new ConnectorError("connector_input_invalid", "Start must be before or equal to end.", { statusCode: 422 });
|
|
23
|
+
return request;
|
|
24
|
+
} };
|
|
25
|
+
}
|
|
26
|
+
const ouraProvider = Object.freeze({
|
|
27
|
+
...ouraDefinition,
|
|
28
|
+
oauth: {
|
|
29
|
+
issuer: "https://cloud.ouraring.com",
|
|
30
|
+
authorization_endpoint: "https://cloud.ouraring.com/oauth/authorize",
|
|
31
|
+
token_endpoint: "https://api.ouraring.com/oauth/token"
|
|
32
|
+
},
|
|
33
|
+
scopesInAuthorizationResponse: true,
|
|
34
|
+
apiOrigins: ["https://api.ouraring.com"], checkOperation: "dailySleep.list",
|
|
35
|
+
operations: {
|
|
36
|
+
"dailySleep.list": collection("daily_sleep"),
|
|
37
|
+
"sleep.list": collection("sleep"),
|
|
38
|
+
"dailyReadiness.list": collection("daily_readiness"),
|
|
39
|
+
"dailyActivity.list": collection("daily_activity"),
|
|
40
|
+
"heartRate.list": collection("heartrate", "heartrate", true),
|
|
41
|
+
"personalInfo.read": {
|
|
42
|
+
...jsonOperation("https://api.ouraring.com/v2/usercollection/personal_info", {},
|
|
43
|
+
(result) => typeof result?.id === "string" && result.id.length > 0),
|
|
44
|
+
scopes: ["personal", "email"]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
export { ouraProvider };
|
|
@@ -0,0 +1,83 @@
|
|
|
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 { paddleDefinition } from "../shared/tokens.js";
|
|
5
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
6
|
+
|
|
7
|
+
const origins = { sandbox: "https://sandbox-api.paddle.com", live: "https://api.paddle.com" };
|
|
8
|
+
const webhookUpdateSchema = createSchema({
|
|
9
|
+
notification_setting_id: { type: "string", required: true,
|
|
10
|
+
validator: (value) => /^ntfset_[a-z0-9]{26}$/.test(value) || "Enter a Paddle notification setting ID." },
|
|
11
|
+
description: { type: "string", minLength: 1, maxLength: 500 },
|
|
12
|
+
destination: { type: "string", minLength: 1, maxLength: 2048 },
|
|
13
|
+
active: { type: "boolean" },
|
|
14
|
+
api_version: { type: "integer", min: 1 },
|
|
15
|
+
include_sensitive_fields: { type: "boolean" },
|
|
16
|
+
subscribed_events: { type: "array", items: { type: "string", minLength: 3, maxLength: 100,
|
|
17
|
+
validator: (value) => /^[a-z_]+\.[a-z_]+$/.test(value) || "Use a Paddle event type name." } },
|
|
18
|
+
traffic_source: { type: "string", enum: ["platform", "simulation", "all"] }
|
|
19
|
+
});
|
|
20
|
+
const paddleProvider = Object.freeze({
|
|
21
|
+
...paddleDefinition, apiOrigins: Object.values(origins),
|
|
22
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}`, "Paddle-Version": "1" }) },
|
|
23
|
+
checkOperation: "products.list",
|
|
24
|
+
async exchange(address, options, { request }) {
|
|
25
|
+
const result = await request(address, options);
|
|
26
|
+
if (new URL(address).pathname.startsWith("/notification-settings/") && result?.data) {
|
|
27
|
+
// Editing a destination must not expose its signing secret to assistant tools.
|
|
28
|
+
const { endpoint_secret_key, ...data } = result.data;
|
|
29
|
+
return { ...result, data };
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
},
|
|
33
|
+
operations: {
|
|
34
|
+
"checkoutDomains.get": {
|
|
35
|
+
scopes: [], assistantAction: "goLive.check",
|
|
36
|
+
request(input, settings) {
|
|
37
|
+
const { domain_id } = validateSchemaPayload({ schema: createSchema({
|
|
38
|
+
domain_id: { type: "string", required: true,
|
|
39
|
+
validator: (value) => /^chedom_[a-z0-9]{26}$/.test(value) || "Enter a Paddle checkout domain ID." }
|
|
40
|
+
}), mode: "replace" }, input, { statusCode: 422 });
|
|
41
|
+
return { method: "GET", url: `${origins[settings.environment]}/checkout-domains/${domain_id}` };
|
|
42
|
+
},
|
|
43
|
+
validateResult: (result) => /^chedom_[a-z0-9]{26}$/.test(result?.data?.id || "") &&
|
|
44
|
+
typeof result.data.domain === "string" && result.data.domain.length > 0 &&
|
|
45
|
+
["pending_review", "approved", "rejected", "in_review", "action_required"].includes(result.data.status)
|
|
46
|
+
},
|
|
47
|
+
"webhooks.update": {
|
|
48
|
+
scopes: [],
|
|
49
|
+
request(input, settings) {
|
|
50
|
+
const { notification_setting_id, ...body } = validateSchemaPayload(
|
|
51
|
+
{ schema: webhookUpdateSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
52
|
+
if (!Object.keys(body).length) throw new ConnectorError("connector_input_invalid", "Choose at least one notification setting to change.", { statusCode: 422 });
|
|
53
|
+
return { method: "PATCH", url: `${origins[settings.environment]}/notification-settings/${notification_setting_id}`, body };
|
|
54
|
+
},
|
|
55
|
+
validateResult: (result) => /^ntfset_[a-z0-9]{26}$/.test(result?.data?.id || "") && typeof result.data.active === "boolean"
|
|
56
|
+
},
|
|
57
|
+
"products.create": jsonOperation((settings) => `${origins[settings.environment]}/products`, {
|
|
58
|
+
name: { type: "string", required: true, minLength: 1, maxLength: 200 },
|
|
59
|
+
description: { type: "string", maxLength: 2048 },
|
|
60
|
+
tax_category: { type: "string", required: true, enum: ["digital-goods", "ebooks", "implementation-services", "professional-services", "saas", "software-programming-services", "standard", "training-services", "website-hosting"] }
|
|
61
|
+
}, (result) => /^pro_[a-z0-9]{26}$/.test(result?.data?.id || ""), "POST"),
|
|
62
|
+
"prices.create": jsonOperation((settings) => `${origins[settings.environment]}/prices`, {
|
|
63
|
+
product_id: { type: "string", required: true, validator: (value) => /^pro_[a-z0-9]{26}$/.test(value) || "Enter a Paddle product ID." },
|
|
64
|
+
description: { type: "string", required: true, minLength: 2, maxLength: 500 },
|
|
65
|
+
unit_price: { type: "object", required: true, schema: createSchema({
|
|
66
|
+
amount: { type: "string", required: true, validator: (value) => /^\d+$/.test(value) || "Enter an integer amount in the currency's smallest unit." },
|
|
67
|
+
currency_code: { type: "string", required: true, validator: (value) => /^[A-Z]{3}$/.test(value) || "Enter a three-letter currency code supported by Paddle." }
|
|
68
|
+
}) },
|
|
69
|
+
billing_cycle: { type: "object", schema: createSchema({
|
|
70
|
+
interval: { type: "string", required: true, enum: ["day", "week", "month", "year"] },
|
|
71
|
+
frequency: { type: "integer", required: true, min: 1 }
|
|
72
|
+
}) }
|
|
73
|
+
}, (result) => /^pri_[a-z0-9]{26}$/.test(result?.data?.id || ""), "POST"),
|
|
74
|
+
|
|
75
|
+
"products.list": { assistantAction: "api.read", ...jsonOperation((settings) => `${origins[settings.environment]}/products`, {
|
|
76
|
+
per_page: { type: "integer", min: 1, max: 200, defaultTo: 50 },
|
|
77
|
+
after: { type: "string", maxLength: 200 },
|
|
78
|
+
status: { type: "string", enum: ["active", "archived"], defaultTo: "active" }
|
|
79
|
+
}, (result) => Array.isArray(result?.data) && Boolean(result.meta?.pagination)
|
|
80
|
+
&& typeof result.meta.pagination.has_more === "boolean") }
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
export { paddleProvider };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { perplexityDefinition } from "../shared/tokens.js";
|
|
2
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
3
|
+
|
|
4
|
+
const perplexityProvider = Object.freeze({
|
|
5
|
+
...perplexityDefinition, apiOrigins: ["https://api.perplexity.ai"],
|
|
6
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
|
|
7
|
+
checkOperation: "requests.list",
|
|
8
|
+
operations: {
|
|
9
|
+
"requests.list": jsonOperation("https://api.perplexity.ai/v1/async/sonar", {},
|
|
10
|
+
(result) => Array.isArray(result?.requests) && (result.next_token === undefined || result.next_token === null || typeof result.next_token === "string")),
|
|
11
|
+
"models.list": jsonOperation("https://api.perplexity.ai/v1/models", {},
|
|
12
|
+
(result) => result?.object === "list" && Array.isArray(result.data))
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
export { perplexityProvider };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { pipedriveDefinition } from "../shared/tokens.js";
|
|
4
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
5
|
+
import { ConnectorError } from "@jskit-ai/connectors-core/server";
|
|
6
|
+
|
|
7
|
+
function companyApiDomain(value) {
|
|
8
|
+
if (typeof value !== "string" || !/^https:\/\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.pipedrive\.com$/.test(value)) {
|
|
9
|
+
throw new ConnectorError("connector_response_invalid", "Pipedrive returned an invalid company API address.", { statusCode: 502 });
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const numberId = { type: "integer", min: 1 };
|
|
15
|
+
const text = { type: "string", minLength: 1, maxLength: 1000 };
|
|
16
|
+
const contactValues = { type: "array", validator: values => values.length <= 20 || "Use at most 20 contact values.",
|
|
17
|
+
items: { type: "object", schema: createSchema({ value: { ...text, required: true }, primary: { type: "boolean" }, label: text }) } };
|
|
18
|
+
const recordFields = {
|
|
19
|
+
deals: { title: text, person_id: numberId, org_id: numberId, owner_id: numberId, stage_id: numberId,
|
|
20
|
+
value: { type: "number", min: 0 }, currency: { type: "string", pattern: "^[A-Z]{3}$" }, status: { type: "string", enum: ["open", "won", "lost"] } },
|
|
21
|
+
persons: { name: text, org_id: numberId, owner_id: numberId, emails: contactValues, phones: contactValues },
|
|
22
|
+
organizations: { name: text, owner_id: numberId },
|
|
23
|
+
activities: { subject: text, type: text, person_id: numberId, org_id: numberId, deal_id: numberId,
|
|
24
|
+
done: { type: "boolean" }, due_date: { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}$" }, due_time: { type: "string", pattern: "^\\d{2}:\\d{2}$" } },
|
|
25
|
+
leads: { title: text, person_id: numberId, organization_id: numberId, owner_id: numberId }
|
|
26
|
+
};
|
|
27
|
+
function crmOperation(collection, action) {
|
|
28
|
+
const lead = collection === "leads", write = ["create", "update"].includes(action);
|
|
29
|
+
const id = lead ? { type: "string", pattern: "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" } : numberId;
|
|
30
|
+
const primary = ["deals", "leads"].includes(collection) ? "title" : collection === "activities" ? "subject" : "name";
|
|
31
|
+
const fields = write ? { ...recordFields[collection], ...(action === "create" ? { [primary]: { ...text, required: true } } : {}) } : action === "list"
|
|
32
|
+
? { limit: { type: "integer", min: 1, max: 500, defaultTo: 100 }, ...(lead ? { start: { type: "integer", min: 0 } } : { cursor: { type: "string", minLength: 1, maxLength: 4096 } }) } : {};
|
|
33
|
+
const schema = createSchema({ ...fields, ...(["get", "update"].includes(action) ? { id: { ...id, required: true } } : {}) });
|
|
34
|
+
return { scopes: [], request(input, settings = {}, data) {
|
|
35
|
+
const { id, ...values } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
36
|
+
if (action === "update" && !Object.keys(values).length || lead && action === "create" && !values.person_id && !values.organization_id)
|
|
37
|
+
throw new ConnectorError("connector_input_invalid", "Supply changed fields; a new lead also needs a person or organization.", { statusCode: 422 });
|
|
38
|
+
if (!data && !settings.companyDomain) throw new ConnectorError("connector_binding_missing", "Set the Pipedrive Company domain for API-token CRM operations.", { statusCode: 422 });
|
|
39
|
+
const origin = data ? companyApiDomain(data.apiDomain) : companyApiDomain(`https://${settings.companyDomain}.pipedrive.com`);
|
|
40
|
+
const url = new URL(`${origin}/api/${lead ? "v1" : "v2"}/${collection}${id === undefined ? "" : `/${id}`}`);
|
|
41
|
+
if (!write) for (const [name, value] of Object.entries(values)) url.searchParams.set(name, String(value));
|
|
42
|
+
return { method: action === "create" ? "POST" : action === "update" ? "PATCH" : "GET", url: url.href, ...(write ? { body: values } : {}) };
|
|
43
|
+
}, validateResult: result => result?.success === true && (action === "list" ? Array.isArray(result.data)
|
|
44
|
+
: lead ? typeof result.data?.id === "string" : Number.isInteger(result.data?.id)) };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const profile = jsonOperation("https://api.pipedrive.com/v1/users/me", {},
|
|
48
|
+
(result) => result?.success === true && Number.isInteger(result.data?.id));
|
|
49
|
+
|
|
50
|
+
const pipedriveProvider = Object.freeze({
|
|
51
|
+
...pipedriveDefinition,
|
|
52
|
+
oauth: { issuer: "https://oauth.pipedrive.com", authorization_endpoint: "https://oauth.pipedrive.com/oauth/authorize",
|
|
53
|
+
token_endpoint: "https://oauth.pipedrive.com/oauth/token" },
|
|
54
|
+
oauthPkce: false,
|
|
55
|
+
oauthBasicEncoding: "raw",
|
|
56
|
+
dataFromTokenResponse(response, previous) {
|
|
57
|
+
const apiDomain = companyApiDomain(response.api_domain);
|
|
58
|
+
if (previous && apiDomain !== previous.apiDomain) {
|
|
59
|
+
throw new ConnectorError("connector_reconnect_required", "The Pipedrive company address changed. Connect this account again.", { statusCode: 401 });
|
|
60
|
+
}
|
|
61
|
+
return { apiDomain };
|
|
62
|
+
},
|
|
63
|
+
apiOrigins: (settings, data) => data ? [companyApiDomain(data.apiDomain)] : ["https://api.pipedrive.com", ...(settings.companyDomain ? [companyApiDomain(`https://${settings.companyDomain}.pipedrive.com`)] : [])],
|
|
64
|
+
apiKey: { headers: (key) => ({ "x-api-token": key }) },
|
|
65
|
+
checkOperation: "profile.read",
|
|
66
|
+
operations: {
|
|
67
|
+
"deals.list": crmOperation("deals", "list"),
|
|
68
|
+
"deals.get": crmOperation("deals", "get"),
|
|
69
|
+
"deals.create": crmOperation("deals", "create"),
|
|
70
|
+
"deals.update": crmOperation("deals", "update"),
|
|
71
|
+
"persons.list": crmOperation("persons", "list"),
|
|
72
|
+
"persons.get": crmOperation("persons", "get"),
|
|
73
|
+
"persons.create": crmOperation("persons", "create"),
|
|
74
|
+
"persons.update": crmOperation("persons", "update"),
|
|
75
|
+
"organizations.list": crmOperation("organizations", "list"),
|
|
76
|
+
"organizations.get": crmOperation("organizations", "get"),
|
|
77
|
+
"organizations.create": crmOperation("organizations", "create"),
|
|
78
|
+
"organizations.update": crmOperation("organizations", "update"),
|
|
79
|
+
"activities.list": crmOperation("activities", "list"),
|
|
80
|
+
"activities.get": crmOperation("activities", "get"),
|
|
81
|
+
"activities.create": crmOperation("activities", "create"),
|
|
82
|
+
"activities.update": crmOperation("activities", "update"),
|
|
83
|
+
"leads.list": crmOperation("leads", "list"),
|
|
84
|
+
"leads.get": crmOperation("leads", "get"),
|
|
85
|
+
"leads.create": crmOperation("leads", "create"),
|
|
86
|
+
"leads.update": crmOperation("leads", "update"),
|
|
87
|
+
"pipelines.list": crmOperation("pipelines", "list"),
|
|
88
|
+
"pipelines.get": crmOperation("pipelines", "get"),
|
|
89
|
+
"profile.read": { ...profile, request(input, settings, data) {
|
|
90
|
+
const request = profile.request(input, settings);
|
|
91
|
+
return { ...request, url: data ? `${companyApiDomain(data.apiDomain)}/api/v1/users/me` : request.url };
|
|
92
|
+
} }
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
export { pipedriveProvider };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { polarDefinition } from "../shared/tokens.js";
|
|
2
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
3
|
+
|
|
4
|
+
const origins = { sandbox: "https://sandbox-api.polar.sh", production: "https://api.polar.sh" };
|
|
5
|
+
const polarProvider = Object.freeze({
|
|
6
|
+
...polarDefinition, apiOrigins: Object.values(origins),
|
|
7
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
|
|
8
|
+
checkOperation: "products.list",
|
|
9
|
+
operations: {
|
|
10
|
+
"products.list": jsonOperation((settings) => `${origins[settings.environment]}/v1/products/`, {
|
|
11
|
+
page: { type: "integer", min: 1, max: 2147483647, defaultTo: 1 },
|
|
12
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 10 },
|
|
13
|
+
query: { type: "string", maxLength: 1024 },
|
|
14
|
+
is_archived: { type: "boolean" },
|
|
15
|
+
is_recurring: { type: "boolean" }
|
|
16
|
+
}, (result) => Array.isArray(result?.items) && Number.isInteger(result.pagination?.total_count) &&
|
|
17
|
+
result.pagination.total_count >= 0 && Number.isInteger(result.pagination.max_page) && result.pagination.max_page >= 0)
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
export { polarProvider };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ConnectorError } from "@jskit-ai/connectors-core/server";
|
|
2
|
+
import { posthogDefinition } from "../shared/posthog.js";
|
|
3
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
4
|
+
|
|
5
|
+
const origin = (settings) => `https://${settings.region}.i.posthog.com`;
|
|
6
|
+
const distinctId = { type: "string", required: true, minLength: 1, maxLength: 200 };
|
|
7
|
+
const posthogProvider = Object.freeze({
|
|
8
|
+
...posthogDefinition,
|
|
9
|
+
apiOrigins: (settings) => [origin(settings)],
|
|
10
|
+
apiKey: { bodyParameter: "api_key", headers: (token) => {
|
|
11
|
+
if (!/^phc_[a-zA-Z0-9_-]+$/u.test(token)) {
|
|
12
|
+
throw new ConnectorError("connector_binding_missing", "Use the public PostHog project token starting with phc_.");
|
|
13
|
+
}
|
|
14
|
+
return { "User-Agent": "jskit-connectors/0.1.0 (posthog-node/compatible)" };
|
|
15
|
+
} },
|
|
16
|
+
checkOperation: "flags.evaluate",
|
|
17
|
+
operations: {
|
|
18
|
+
"flags.evaluate": jsonOperation((settings) => `${origin(settings)}/flags?v=2`, {
|
|
19
|
+
distinct_id: distinctId,
|
|
20
|
+
groups: { type: "object", additionalProperties: true },
|
|
21
|
+
person_properties: { type: "object", additionalProperties: true }
|
|
22
|
+
}, (result) => {
|
|
23
|
+
if (!result?.flags || typeof result.flags !== "object" || Array.isArray(result.flags) ||
|
|
24
|
+
typeof result.errorsWhileComputingFlags !== "boolean" ||
|
|
25
|
+
(result.quotaLimited !== undefined && (!Array.isArray(result.quotaLimited) || !result.quotaLimited.every((item) => typeof item === "string")))) return false;
|
|
26
|
+
if (result.quotaLimited?.includes("feature_flags")) {
|
|
27
|
+
throw new ConnectorError("connector_quota_limited", "PostHog's feature flag quota has been reached.", { statusCode: 429 });
|
|
28
|
+
}
|
|
29
|
+
if (result.errorsWhileComputingFlags) {
|
|
30
|
+
throw new ConnectorError("connector_provider_failed", "PostHog could not evaluate all requested flags.", { statusCode: 502 });
|
|
31
|
+
}
|
|
32
|
+
return Object.values(result.flags).every((flag) => flag && typeof flag.enabled === "boolean" &&
|
|
33
|
+
(flag.variant === undefined || typeof flag.variant === "string"));
|
|
34
|
+
}, "POST"),
|
|
35
|
+
"events.capture": jsonOperation((settings) => `${origin(settings)}/i/v0/e/`, {
|
|
36
|
+
event: { type: "string", required: true, minLength: 1, maxLength: 200 },
|
|
37
|
+
distinct_id: distinctId,
|
|
38
|
+
properties: { type: "object", additionalProperties: true, defaultTo: {},
|
|
39
|
+
validator: (value) => !Object.hasOwn(value, "token") && !Object.hasOwn(value, "distinct_id") || "Set the event identity through distinct_id; properties cannot override its token or identity." }
|
|
40
|
+
}, (result) => result?.status === "Ok" &&
|
|
41
|
+
(result.quota_limited === undefined || Array.isArray(result.quota_limited) && result.quota_limited.every((item) => typeof item === "string")), "POST")
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
export { posthogProvider };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { prestashopDefinition } from "../shared/prestashop.js";
|
|
4
|
+
|
|
5
|
+
const pageSchema = createSchema({
|
|
6
|
+
offset: { type: "integer", min: 0, max: 1000000, defaultTo: 0 },
|
|
7
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
8
|
+
language: { type: "integer", min: 1, max: 2147483647 }
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function listOperation(resource) {
|
|
12
|
+
return {
|
|
13
|
+
scopes: [],
|
|
14
|
+
request(input, settings) {
|
|
15
|
+
const values = validateSchemaPayload({ schema: pageSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
16
|
+
const url = new URL(`${settings.siteUrl.replace(/\/+$/u, "")}/api/${resource}`);
|
|
17
|
+
url.searchParams.set("output_format", "JSON");
|
|
18
|
+
url.searchParams.set("display", "full");
|
|
19
|
+
url.searchParams.set("sort", "[id_ASC]");
|
|
20
|
+
url.searchParams.set("limit", `${values.offset},${values.limit}`);
|
|
21
|
+
if (values.language !== undefined) url.searchParams.set("language", String(values.language));
|
|
22
|
+
return { method: "GET", url: url.href };
|
|
23
|
+
},
|
|
24
|
+
validateResult(result) {
|
|
25
|
+
// PrestaShop's JSON writer emits [] when no resource nodes were rendered.
|
|
26
|
+
if (Array.isArray(result)) return result.length === 0;
|
|
27
|
+
return Boolean(result && !result.errors && Array.isArray(result[resource]) && result[resource].every((record) =>
|
|
28
|
+
(typeof record?.id === "string" && /^[1-9]\d*$/u.test(record.id)) ||
|
|
29
|
+
(Number.isSafeInteger(record?.id) && record.id > 0)));
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const prestashopProvider = Object.freeze({
|
|
35
|
+
...prestashopDefinition,
|
|
36
|
+
apiOrigins: (settings) => [new URL(settings.siteUrl).origin],
|
|
37
|
+
apiKey: { headers: (key) => ({ Authorization: `Basic ${Buffer.from(`${key}:`).toString("base64")}` }) },
|
|
38
|
+
checkOperation: "products.list",
|
|
39
|
+
operations: { "products.list": listOperation("products"), "orders.list": listOperation("orders") }
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export { prestashopProvider };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { replicateDefinition } from "../shared/tokens.js";
|
|
4
|
+
import { jsonOperation } from "./jsonOperation.js";
|
|
5
|
+
|
|
6
|
+
const identifier = { type: "string", required: true, minLength: 1, maxLength: 200, pattern: "^[A-Za-z0-9_-]+$" };
|
|
7
|
+
const modelFields = { owner: identifier, name: identifier };
|
|
8
|
+
const modelInput = { type: "object", required: true, additionalProperties: true,
|
|
9
|
+
validator: (value) => Buffer.byteLength(JSON.stringify(value)) <= 262144 || "Model input must fit within 256 KiB; use file URLs for larger inputs." };
|
|
10
|
+
const predictionResult = (result) => typeof result?.id === "string" &&
|
|
11
|
+
["starting", "processing", "succeeded", "failed", "canceled"].includes(result.status);
|
|
12
|
+
function predictionOperation(fields, method, destination, validateResult = predictionResult) {
|
|
13
|
+
const schema = createSchema(fields);
|
|
14
|
+
return { scopes: [], request(input) {
|
|
15
|
+
const values = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
16
|
+
const { owner, name, id, ...body } = values;
|
|
17
|
+
return { method, url: `https://api.replicate.com/v1/${destination(values)}`,
|
|
18
|
+
...(method === "POST" ? { body } : {}) };
|
|
19
|
+
}, validateResult };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const replicateProvider = Object.freeze({
|
|
23
|
+
...replicateDefinition, apiOrigins: ["https://api.replicate.com"],
|
|
24
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
|
|
25
|
+
checkOperation: "account.read",
|
|
26
|
+
operations: {
|
|
27
|
+
"account.read": jsonOperation("https://api.replicate.com/v1/account", {},
|
|
28
|
+
(result) => ["user", "organization"].includes(result?.type) && typeof result.username === "string"),
|
|
29
|
+
"models.get": predictionOperation(modelFields, "GET", ({ owner, name }) => `models/${owner}/${name}`,
|
|
30
|
+
(result) => typeof result?.name === "string" && typeof result.owner === "string"),
|
|
31
|
+
"predictions.create": predictionOperation({ version: { type: "string", required: true,
|
|
32
|
+
pattern: "^(?:[A-Za-z0-9_-]+/[A-Za-z0-9_-]+:)?[a-f0-9]{64}$" }, input: modelInput },
|
|
33
|
+
"POST", () => "predictions"),
|
|
34
|
+
"models.predict": predictionOperation({ ...modelFields, input: modelInput }, "POST",
|
|
35
|
+
({ owner, name }) => `models/${owner}/${name}/predictions`),
|
|
36
|
+
"predictions.get": predictionOperation({ id: identifier }, "GET", ({ id }) => `predictions/${id}`),
|
|
37
|
+
"predictions.cancel": predictionOperation({ id: identifier }, "POST", ({ id }) => `predictions/${id}/cancel`),
|
|
38
|
+
"hardware.list": jsonOperation("https://api.replicate.com/v1/hardware", {},
|
|
39
|
+
(result) => Array.isArray(result) && result.every((item) => typeof item?.name === "string" && typeof item.sku === "string"))
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
export { replicateProvider };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
3
|
+
import { resendDefinition } from "../shared/definitions.js";
|
|
4
|
+
|
|
5
|
+
const pagination = createSchema({
|
|
6
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
7
|
+
after: { type: "string", minLength: 1, maxLength: 200 }
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const email = createSchema({
|
|
11
|
+
from: { type: "string", required: true, minLength: 1, maxLength: 320, pattern: "^[^\\r\\n]+$" },
|
|
12
|
+
to: { type: "array", required: true,
|
|
13
|
+
validator: (value) => (value.length >= 1 && value.length <= 50) || "Provide between 1 and 50 recipients.",
|
|
14
|
+
items: { type: "string", minLength: 1, maxLength: 320, pattern: "^[^\\s@]+@[^\\s@]+$" } },
|
|
15
|
+
subject: { type: "string", required: true, minLength: 1, maxLength: 998, pattern: "^[^\\r\\n]+$" },
|
|
16
|
+
text: { type: "string", required: true, noTrim: true, minLength: 1, maxLength: 1000000 },
|
|
17
|
+
idempotencyKey: { type: "string", required: true, minLength: 1, maxLength: 256, pattern: "^[A-Za-z0-9_./:-]+$" }
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const resourceId = { type: "string", required: true, minLength: 1, maxLength: 200, pattern: "^[A-Za-z0-9_-]+$" };
|
|
21
|
+
const shortText = { type: "string", minLength: 1, maxLength: 1000, pattern: "^[^\\r\\n]+$" };
|
|
22
|
+
const emailAddress = { type: "string", required: true, maxLength: 320, pattern: "^[^\\s@]+@[^\\s@]+$" };
|
|
23
|
+
const listFields = { limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
24
|
+
after: { type: "string", minLength: 1, maxLength: 200 } };
|
|
25
|
+
const broadcastFields = { segment_id: resourceId, from: { ...shortText, required: true, maxLength: 320 },
|
|
26
|
+
subject: { ...shortText, required: true, maxLength: 998 }, name: shortText,
|
|
27
|
+
html: { type: "string", required: true, noTrim: true, minLength: 1, maxLength: 1000000,
|
|
28
|
+
validator: (value) => value.includes("{{{RESEND_UNSUBSCRIBE_URL}}}") || "Include {{{RESEND_UNSUBSCRIBE_URL}}} in the broadcast HTML." },
|
|
29
|
+
text: { type: "string", noTrim: true, maxLength: 1000000 }, preview_text: shortText };
|
|
30
|
+
const hasId = (result) => typeof result?.id === "string" && result.id.length > 0;
|
|
31
|
+
const membershipResult = (result) => result?.object === "contact_segment" && typeof result.contact_id === "string" && typeof result.segment_id === "string";
|
|
32
|
+
const listResult = (result) => result?.object === "list" && Array.isArray(result.data);
|
|
33
|
+
// Fixed provider routes; the application authorizes each named operation before HTTP.
|
|
34
|
+
function marketingOperation(fields, method, destination, validateResult = hasId, draft = false) {
|
|
35
|
+
const schema = createSchema(fields);
|
|
36
|
+
return { scopes: [], request(input) {
|
|
37
|
+
const values = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
38
|
+
const { id, segmentId, ...body } = values;
|
|
39
|
+
const url = new URL(`https://api.resend.com/${destination(values)}`);
|
|
40
|
+
if (method === "GET") {
|
|
41
|
+
for (const [key, value] of Object.entries(body)) url.searchParams.set(key, String(value));
|
|
42
|
+
return { method, url: url.href };
|
|
43
|
+
}
|
|
44
|
+
return { method, url: url.href, ...(method === "DELETE" ? {} : { body: draft ? { ...body, send: false } : body }) };
|
|
45
|
+
}, validateResult };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const resendProvider = Object.freeze({
|
|
49
|
+
...resendDefinition,
|
|
50
|
+
apiOrigins: ["https://api.resend.com"],
|
|
51
|
+
apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
|
|
52
|
+
checkOperation: "domains.list",
|
|
53
|
+
operations: {
|
|
54
|
+
"segments.list": marketingOperation(listFields, "GET", () => "segments", listResult),
|
|
55
|
+
"segments.create": marketingOperation({ name: { ...shortText, required: true } }, "POST", () => "segments"),
|
|
56
|
+
"contacts.list": marketingOperation({ ...listFields, segment_id: { ...resourceId, required: false } }, "GET", () => "contacts", listResult),
|
|
57
|
+
"contacts.get": marketingOperation({ id: resourceId }, "GET", ({ id }) => `contacts/${id}`),
|
|
58
|
+
"contacts.create": marketingOperation({ email: emailAddress, first_name: shortText, last_name: shortText,
|
|
59
|
+
unsubscribed: { type: "boolean", required: true },
|
|
60
|
+
segments: { type: "array", validator: (value) => value.length <= 100 || "Provide at most 100 segments.",
|
|
61
|
+
items: { type: "object", properties: { id: resourceId } } } }, "POST", () => "contacts"),
|
|
62
|
+
"contacts.update": marketingOperation({ id: resourceId, unsubscribed: { type: "boolean", required: true }, first_name: shortText, last_name: shortText },
|
|
63
|
+
"PATCH", ({ id }) => `contacts/${id}`),
|
|
64
|
+
"contacts.addSegment": marketingOperation({ id: resourceId, segmentId: resourceId }, "POST", ({ id, segmentId }) => `contacts/${id}/segments/${segmentId}`, membershipResult),
|
|
65
|
+
"contacts.removeSegment": marketingOperation({ id: resourceId, segmentId: resourceId }, "DELETE", ({ id, segmentId }) => `contacts/${id}/segments/${segmentId}`, (result) => membershipResult(result) && result.deleted === true),
|
|
66
|
+
"broadcasts.create": marketingOperation(broadcastFields, "POST", () => "broadcasts", hasId, true),
|
|
67
|
+
"broadcasts.get": marketingOperation({ id: resourceId }, "GET", ({ id }) => `broadcasts/${id}`),
|
|
68
|
+
"broadcasts.list": marketingOperation(listFields, "GET", () => "broadcasts", listResult),
|
|
69
|
+
"broadcasts.update": marketingOperation({ id: resourceId, ...broadcastFields }, "PATCH", ({ id }) => `broadcasts/${id}`),
|
|
70
|
+
"broadcasts.send": marketingOperation({ id: resourceId }, "POST", ({ id }) => `broadcasts/${id}/send`),
|
|
71
|
+
"domains.get": marketingOperation({ id: resourceId }, "GET", ({ id }) => `domains/${id}`),
|
|
72
|
+
"emails.send": {
|
|
73
|
+
scopes: [],
|
|
74
|
+
request(input) {
|
|
75
|
+
const { idempotencyKey, ...body } = validateSchemaPayload({ schema: email, mode: "replace" }, input, { statusCode: 422 });
|
|
76
|
+
return { method: "POST", url: "https://api.resend.com/emails",
|
|
77
|
+
headers: { "Idempotency-Key": idempotencyKey }, body };
|
|
78
|
+
},
|
|
79
|
+
validateResult: (result) => typeof result?.id === "string" && result.id.length > 0
|
|
80
|
+
},
|
|
81
|
+
"domains.list": {
|
|
82
|
+
scopes: [],
|
|
83
|
+
request(input) {
|
|
84
|
+
const query = validateSchemaPayload({ schema: pagination, mode: "replace" }, input, { statusCode: 422 });
|
|
85
|
+
const url = new URL("https://api.resend.com/domains");
|
|
86
|
+
for (const [name, value] of Object.entries(query)) url.searchParams.set(name, String(value));
|
|
87
|
+
return { method: "GET", url: url.href };
|
|
88
|
+
},
|
|
89
|
+
validateResult: (result) => result?.object === "list" && Array.isArray(result.data) && typeof result.has_more === "boolean"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
export { resendProvider };
|