@oomol-lab/open-connector 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/README.md +4 -0
  2. package/assets/open-connector/catalog/apps/airtable.json +134 -0
  3. package/assets/open-connector/catalog/apps/aisa.json +916 -76
  4. package/assets/open-connector/catalog/apps/cal.json +24 -6
  5. package/assets/open-connector/catalog/apps/discordbot.json +2 -2
  6. package/assets/open-connector/catalog/apps/enormail.json +411 -0
  7. package/assets/open-connector/catalog/apps/feishu_app_bot.json +158 -0
  8. package/assets/open-connector/catalog/apps/github.json +275 -0
  9. package/assets/open-connector/catalog/apps/gitlab.json +125 -0
  10. package/assets/open-connector/catalog/apps/gmail.json +151 -0
  11. package/assets/open-connector/catalog/apps/googlecalendar.json +206 -0
  12. package/assets/open-connector/catalog/apps/googledrive.json +479 -0
  13. package/assets/open-connector/catalog/apps/googlesheets.json +144 -0
  14. package/assets/open-connector/catalog/apps/hithink_finance.json +6 -3
  15. package/assets/open-connector/catalog/apps/indexed.json +1115 -0
  16. package/assets/open-connector/catalog/apps/linear.json +116 -0
  17. package/assets/open-connector/catalog/apps/notion.json +142 -0
  18. package/assets/open-connector/catalog/apps/one_drive.json +185 -0
  19. package/assets/open-connector/catalog/apps/outlook.json +182 -0
  20. package/assets/open-connector/catalog/apps/shopify.json +282 -0
  21. package/assets/open-connector/catalog/apps/slack.json +182 -0
  22. package/assets/open-connector/catalog/apps/spotify.json +46 -43
  23. package/assets/open-connector/catalog/apps/stripe.json +96 -0
  24. package/assets/open-connector/catalog/apps/telegram.json +138 -0
  25. package/assets/open-connector/catalog/apps/weixin_store.json +543 -2
  26. package/assets/open-connector/catalog/apps/woocommerce.json +127 -0
  27. package/assets/open-connector/catalog/apps/zendesk.json +152 -0
  28. package/assets/open-connector/catalog/apps/zerotier.json +9 -9
  29. package/assets/open-connector/catalog/apps-index.json +1 -1
  30. package/assets/open-connector/migrations/0014_saas_project.sql +55 -0
  31. package/assets/open-connector/migrations/0015_saas_cleanup_runtime.sql +8 -0
  32. package/assets/open-connector/migrations/0016_trigger_policy.sql +1 -0
  33. package/assets/open-connector/migrations/0017_trigger_subscriptions.sql +17 -0
  34. package/assets/open-connector/migrations/postgresql/0014_saas_project.sql +55 -0
  35. package/assets/open-connector/migrations/postgresql/0015_saas_cleanup_runtime.sql +8 -0
  36. package/assets/open-connector/migrations/postgresql/0016_trigger_policy.sql +1 -0
  37. package/assets/open-connector/migrations/postgresql/0017_trigger_subscriptions.sql +17 -0
  38. package/package.json +2 -2
  39. package/src/connection-service.d.ts +40 -7
  40. package/src/connection-service.js +60 -18
  41. package/src/core/action-policy.d.ts +8 -1
  42. package/src/core/action-policy.js +51 -0
  43. package/src/core/cast.d.ts +3 -0
  44. package/src/core/cast.js +4 -1
  45. package/src/core/guarded-fetch.d.ts +5 -0
  46. package/src/core/guarded-fetch.js +13 -4
  47. package/src/core/remote-http.d.ts +33 -0
  48. package/src/core/remote-http.js +104 -0
  49. package/src/core/request.d.ts +1 -0
  50. package/src/core/request.js +7 -0
  51. package/src/core/types.d.ts +4 -0
  52. package/src/marketplace/marketplace-service.d.ts +0 -1
  53. package/src/marketplace/marketplace-service.js +80 -92
  54. package/src/mcp.js +19 -5
  55. package/src/oauth/oauth-flow-service.d.ts +8 -1
  56. package/src/oauth/oauth-flow-service.js +10 -4
  57. package/src/providers/action-contracts.generated.d.ts +4 -2
  58. package/src/providers/airtable/executors.d.ts +3 -0
  59. package/src/providers/airtable/executors.js +2 -0
  60. package/src/providers/airtable/trigger-on-record-changed.d.ts +2 -0
  61. package/src/providers/airtable/trigger-on-record-changed.definition.d.ts +4 -0
  62. package/src/providers/airtable/trigger-on-record-changed.definition.js +54 -0
  63. package/src/providers/airtable/trigger-on-record-changed.js +235 -0
  64. package/src/providers/aisa/executors.js +196 -22
  65. package/src/providers/cal/actions.js +5 -1
  66. package/src/providers/enormail/executors.d.ts +4 -0
  67. package/src/providers/enormail/executors.js +97 -0
  68. package/src/providers/feishu_app_bot/executors.d.ts +3 -0
  69. package/src/providers/feishu_app_bot/executors.js +2 -0
  70. package/src/providers/feishu_app_bot/trigger-config.d.ts +1 -0
  71. package/src/providers/feishu_app_bot/trigger-config.js +10 -0
  72. package/src/providers/feishu_app_bot/trigger-on-event.d.ts +2 -0
  73. package/src/providers/feishu_app_bot/trigger-on-event.definition.d.ts +4 -0
  74. package/src/providers/feishu_app_bot/trigger-on-event.definition.js +63 -0
  75. package/src/providers/feishu_app_bot/trigger-on-event.js +29 -0
  76. package/src/providers/feishu_app_bot/trigger-subscriptions.d.ts +5 -0
  77. package/src/providers/feishu_app_bot/trigger-subscriptions.js +70 -0
  78. package/src/providers/github/executors.d.ts +3 -0
  79. package/src/providers/github/executors.js +6 -0
  80. package/src/providers/github/trigger-on-repo-event.d.ts +2 -0
  81. package/src/providers/github/trigger-on-repo-event.definition.d.ts +4 -0
  82. package/src/providers/github/trigger-on-repo-event.definition.js +94 -0
  83. package/src/providers/github/trigger-on-repo-event.js +168 -0
  84. package/src/providers/github/trigger-watch-pull-request.d.ts +2 -0
  85. package/src/providers/github/trigger-watch-pull-request.definition.d.ts +4 -0
  86. package/src/providers/github/trigger-watch-pull-request.definition.js +49 -0
  87. package/src/providers/github/trigger-watch-pull-request.js +106 -0
  88. package/src/providers/gitlab/executors.d.ts +3 -0
  89. package/src/providers/gitlab/executors.js +2 -0
  90. package/src/providers/gitlab/trigger-on-project-event.d.ts +2 -0
  91. package/src/providers/gitlab/trigger-on-project-event.definition.d.ts +5 -0
  92. package/src/providers/gitlab/trigger-on-project-event.definition.js +63 -0
  93. package/src/providers/gitlab/trigger-on-project-event.js +186 -0
  94. package/src/providers/gmail/executors.d.ts +3 -0
  95. package/src/providers/gmail/executors.js +2 -0
  96. package/src/providers/gmail/trigger-on-message-received.d.ts +2 -0
  97. package/src/providers/gmail/trigger-on-message-received.definition.d.ts +4 -0
  98. package/src/providers/gmail/trigger-on-message-received.definition.js +74 -0
  99. package/src/providers/gmail/trigger-on-message-received.js +232 -0
  100. package/src/providers/googlecalendar/executors.d.ts +3 -0
  101. package/src/providers/googlecalendar/executors.js +2 -0
  102. package/src/providers/googlecalendar/trigger-on-event-changed.d.ts +2 -0
  103. package/src/providers/googlecalendar/trigger-on-event-changed.definition.d.ts +4 -0
  104. package/src/providers/googlecalendar/trigger-on-event-changed.definition.js +74 -0
  105. package/src/providers/googlecalendar/trigger-on-event-changed.js +233 -0
  106. package/src/providers/googledrive/executors.d.ts +3 -0
  107. package/src/providers/googledrive/executors.js +7 -0
  108. package/src/providers/googledrive/trigger-changes.d.ts +12 -0
  109. package/src/providers/googledrive/trigger-changes.definition.d.ts +7 -0
  110. package/src/providers/googledrive/trigger-changes.definition.js +101 -0
  111. package/src/providers/googledrive/trigger-changes.js +452 -0
  112. package/src/providers/googledrive/trigger-on-file-change.d.ts +2 -0
  113. package/src/providers/googledrive/trigger-on-file-change.definition.d.ts +4 -0
  114. package/src/providers/googledrive/trigger-on-file-change.definition.js +77 -0
  115. package/src/providers/googledrive/trigger-on-file-change.js +258 -0
  116. package/src/providers/googlesheets/executors.d.ts +3 -0
  117. package/src/providers/googlesheets/executors.js +2 -0
  118. package/src/providers/googlesheets/trigger-on-row-added.d.ts +2 -0
  119. package/src/providers/googlesheets/trigger-on-row-added.definition.d.ts +4 -0
  120. package/src/providers/googlesheets/trigger-on-row-added.definition.js +54 -0
  121. package/src/providers/googlesheets/trigger-on-row-added.js +234 -0
  122. package/src/providers/hithink_finance/runtime.js +1 -1
  123. package/src/providers/indexed/executors.d.ts +4 -0
  124. package/src/providers/indexed/executors.js +16 -0
  125. package/src/providers/indexed/runtime.d.ts +26 -0
  126. package/src/providers/indexed/runtime.js +129 -0
  127. package/src/providers/linear/executors.d.ts +3 -0
  128. package/src/providers/linear/executors.js +2 -0
  129. package/src/providers/linear/trigger-on-issue-changed.d.ts +2 -0
  130. package/src/providers/linear/trigger-on-issue-changed.definition.d.ts +5 -0
  131. package/src/providers/linear/trigger-on-issue-changed.definition.js +43 -0
  132. package/src/providers/linear/trigger-on-issue-changed.js +215 -0
  133. package/src/providers/notion/executors.d.ts +3 -0
  134. package/src/providers/notion/executors.js +2 -0
  135. package/src/providers/notion/trigger-on-database-page-event.d.ts +2 -0
  136. package/src/providers/notion/trigger-on-database-page-event.definition.d.ts +4 -0
  137. package/src/providers/notion/trigger-on-database-page-event.definition.js +63 -0
  138. package/src/providers/notion/trigger-on-database-page-event.js +293 -0
  139. package/src/providers/one_drive/executors.d.ts +3 -0
  140. package/src/providers/one_drive/executors.js +2 -0
  141. package/src/providers/one_drive/trigger-on-item-changed.d.ts +2 -0
  142. package/src/providers/one_drive/trigger-on-item-changed.definition.d.ts +4 -0
  143. package/src/providers/one_drive/trigger-on-item-changed.definition.js +63 -0
  144. package/src/providers/one_drive/trigger-on-item-changed.js +214 -0
  145. package/src/providers/outlook/executors.d.ts +3 -0
  146. package/src/providers/outlook/executors.js +2 -0
  147. package/src/providers/outlook/trigger-on-message-received.d.ts +2 -0
  148. package/src/providers/outlook/trigger-on-message-received.definition.d.ts +4 -0
  149. package/src/providers/outlook/trigger-on-message-received.definition.js +93 -0
  150. package/src/providers/outlook/trigger-on-message-received.js +226 -0
  151. package/src/providers/outlook_calendar/executors.js +6 -0
  152. package/src/providers/provider-loader.d.ts +5 -0
  153. package/src/providers/provider-loader.js +4 -0
  154. package/src/providers/registry.generated.js +2 -0
  155. package/src/providers/shopify/executors.d.ts +3 -0
  156. package/src/providers/shopify/executors.js +2 -0
  157. package/src/providers/shopify/trigger-on-shop-event.d.ts +2 -0
  158. package/src/providers/shopify/trigger-on-shop-event.definition.d.ts +4 -0
  159. package/src/providers/shopify/trigger-on-shop-event.definition.js +214 -0
  160. package/src/providers/shopify/trigger-on-shop-event.js +165 -0
  161. package/src/providers/slack/executors.d.ts +3 -0
  162. package/src/providers/slack/executors.js +2 -0
  163. package/src/providers/slack/trigger-on-message-posted.d.ts +2 -0
  164. package/src/providers/slack/trigger-on-message-posted.definition.d.ts +4 -0
  165. package/src/providers/slack/trigger-on-message-posted.definition.js +80 -0
  166. package/src/providers/slack/trigger-on-message-posted.js +176 -0
  167. package/src/providers/spotify/runtime.js +15 -4
  168. package/src/providers/stripe/executors.d.ts +3 -0
  169. package/src/providers/stripe/executors.js +2 -0
  170. package/src/providers/stripe/trigger-on-event.d.ts +2 -0
  171. package/src/providers/stripe/trigger-on-event.definition.d.ts +4 -0
  172. package/src/providers/stripe/trigger-on-event.definition.js +43 -0
  173. package/src/providers/stripe/trigger-on-event.js +212 -0
  174. package/src/providers/telegram/executors.d.ts +3 -0
  175. package/src/providers/telegram/executors.js +2 -0
  176. package/src/providers/telegram/trigger-on-update.d.ts +2 -0
  177. package/src/providers/telegram/trigger-on-update.definition.d.ts +5 -0
  178. package/src/providers/telegram/trigger-on-update.definition.js +78 -0
  179. package/src/providers/telegram/trigger-on-update.js +141 -0
  180. package/src/providers/weixin_store/access-token.d.ts +2 -1
  181. package/src/providers/weixin_store/access-token.js +15 -4
  182. package/src/providers/weixin_store/executors.js +97 -12
  183. package/src/providers/woocommerce/executors.d.ts +3 -0
  184. package/src/providers/woocommerce/executors.js +2 -0
  185. package/src/providers/woocommerce/trigger-on-store-event.d.ts +2 -0
  186. package/src/providers/woocommerce/trigger-on-store-event.definition.d.ts +4 -0
  187. package/src/providers/woocommerce/trigger-on-store-event.definition.js +55 -0
  188. package/src/providers/woocommerce/trigger-on-store-event.js +179 -0
  189. package/src/providers/zendesk/executors.d.ts +3 -0
  190. package/src/providers/zendesk/executors.js +2 -0
  191. package/src/providers/zendesk/trigger-on-event.d.ts +2 -0
  192. package/src/providers/zendesk/trigger-on-event.definition.d.ts +5 -0
  193. package/src/providers/zendesk/trigger-on-event.definition.js +101 -0
  194. package/src/providers/zendesk/trigger-on-event.js +188 -0
  195. package/src/saas/saas-cleanup-service.d.ts +26 -0
  196. package/src/saas/saas-cleanup-service.js +124 -0
  197. package/src/saas/saas-client.d.ts +86 -0
  198. package/src/saas/saas-client.js +369 -0
  199. package/src/saas/saas-execution-service.d.ts +15 -0
  200. package/src/saas/saas-execution-service.js +21 -0
  201. package/src/saas/saas-oauth-service.d.ts +25 -0
  202. package/src/saas/saas-oauth-service.js +197 -0
  203. package/src/saas/saas-project-service.d.ts +61 -0
  204. package/src/saas/saas-project-service.js +211 -0
  205. package/src/server/actions/action-idempotency.d.ts +1 -0
  206. package/src/server/actions/action-idempotency.js +1 -0
  207. package/src/server/actions/action-runner.d.ts +7 -0
  208. package/src/server/actions/action-runner.js +43 -7
  209. package/src/server/actions/run-log-summary.js +9 -0
  210. package/src/server/api/auth.d.ts +1 -0
  211. package/src/server/api/auth.js +13 -2
  212. package/src/server/api/cache-policy.js +4 -1
  213. package/src/server/api/connection-input.d.ts +7 -1
  214. package/src/server/api/connection-input.js +7 -1
  215. package/src/server/api/connection-routes.d.ts +3 -1
  216. package/src/server/api/connection-routes.js +21 -3
  217. package/src/server/api/http-utils.d.ts +4 -3
  218. package/src/server/api/oauth-completion-page.d.ts +124 -0
  219. package/src/server/api/oauth-completion-page.js +113 -18
  220. package/src/server/api/openapi.js +112 -14
  221. package/src/server/api/policy-input.js +6 -0
  222. package/src/server/api/runtime-api.d.ts +10 -2
  223. package/src/server/api/runtime-api.js +17 -4
  224. package/src/server/api/saas-completion-page.d.ts +2 -0
  225. package/src/server/api/saas-completion-page.js +103 -0
  226. package/src/server/connect-app.d.ts +5 -0
  227. package/src/server/connect-app.js +50 -0
  228. package/src/server/connect-server.d.ts +12 -0
  229. package/src/server/connect-server.js +147 -13
  230. package/src/server/connector-runtime.d.ts +3 -1
  231. package/src/server/connector-runtime.js +5 -2
  232. package/src/server/files/transit-files.js +16 -7
  233. package/src/server/proxy/proxy-runner.d.ts +5 -1
  234. package/src/server/proxy/proxy-runner.js +72 -26
  235. package/src/server/storage/connection-request-store.d.ts +41 -1
  236. package/src/server/storage/connection-request-store.js +245 -20
  237. package/src/server/storage/connection-store.d.ts +16 -0
  238. package/src/server/storage/connection-store.js +119 -0
  239. package/src/server/storage/node-runtime-database.js +3 -3
  240. package/src/server/storage/{postgres-migrations.d.ts → postgres/migrations.d.ts} +2 -2
  241. package/src/server/storage/{postgres-migrations.js → postgres/migrations.js} +1 -1
  242. package/src/server/storage/postgres/runtime-store.d.ts +43 -0
  243. package/src/server/storage/{postgres-runtime-store.js → postgres/runtime-store.js} +59 -94
  244. package/src/server/storage/runtime-database.d.ts +4 -0
  245. package/src/server/storage/runtime-sql.d.ts +1 -1
  246. package/src/server/storage/runtime-sql.js +2 -1
  247. package/src/server/storage/runtime-store.d.ts +1 -0
  248. package/src/server/storage/runtime-token-service.d.ts +2 -0
  249. package/src/server/storage/runtime-token-service.js +3 -0
  250. package/src/server/storage/saas-project-store.d.ts +77 -0
  251. package/src/server/storage/saas-project-store.js +273 -0
  252. package/src/server/storage/{sqlite-runtime-store.d.ts → sqlite/runtime-store.d.ts} +21 -25
  253. package/src/server/storage/{sqlite-runtime-store.js → sqlite/runtime-store.js} +100 -115
  254. package/src/server/storage/trigger-store.d.ts +16 -0
  255. package/src/server/storage/trigger-store.js +96 -0
  256. package/src/triggers/common/config.d.ts +2 -0
  257. package/src/triggers/common/config.js +17 -0
  258. package/src/triggers/common/configOptions.d.ts +13 -0
  259. package/src/triggers/common/configOptions.js +1 -0
  260. package/src/triggers/common/encoding.d.ts +3 -0
  261. package/src/triggers/common/encoding.js +19 -0
  262. package/src/triggers/common/integration.d.ts +115 -0
  263. package/src/triggers/common/integration.js +31 -0
  264. package/src/triggers/common/json.d.ts +6 -0
  265. package/src/triggers/common/json.js +91 -0
  266. package/src/triggers/common/poll.d.ts +41 -0
  267. package/src/triggers/common/poll.js +15 -0
  268. package/src/triggers/common/proxy.d.ts +14 -0
  269. package/src/triggers/common/proxy.js +1 -0
  270. package/src/triggers/common/types.d.ts +42 -0
  271. package/src/triggers/common/types.js +1 -0
  272. package/src/triggers/maintenance.d.ts +40 -0
  273. package/src/triggers/maintenance.js +120 -0
  274. package/src/triggers/metadata.d.ts +8 -0
  275. package/src/triggers/metadata.js +1 -0
  276. package/src/triggers/request.d.ts +33 -0
  277. package/src/triggers/request.js +52 -0
  278. package/src/triggers/resources.d.ts +12 -0
  279. package/src/triggers/resources.js +121 -0
  280. package/src/triggers/signature.d.ts +4 -0
  281. package/src/triggers/signature.js +48 -0
  282. package/src/triggers/store.d.ts +29 -0
  283. package/src/triggers/store.js +1 -0
  284. package/src/triggers/subscriptions.d.ts +22 -0
  285. package/src/triggers/subscriptions.js +220 -0
  286. package/src/triggers/trigger-runner.d.ts +34 -0
  287. package/src/triggers/trigger-runner.js +169 -0
  288. package/src/server/storage/postgres-runtime-store.d.ts +0 -39
@@ -0,0 +1,165 @@
1
+ import { IntegrationConnectionError, PermanentIntegrationError, TransientIntegrationError, } from "../../triggers/common/integration.js";
2
+ import { sameSecret } from "../../triggers/signature.js";
3
+ import { snapshot } from "./trigger-on-shop-event.definition.js";
4
+ export const shopifyShopEvent = {
5
+ initialState: { checkpoint: null, subscription: {} },
6
+ snapshot,
7
+ receive(context) {
8
+ if (!sameSecret(context.query("open_flow_callback"), context.callbackSecret)) {
9
+ return { body: "", contentType: "text/plain", outcome: "respond", status: 404 };
10
+ }
11
+ const topic = context.header("x-shopify-topic")?.trim();
12
+ if (!topic)
13
+ return { outcome: "ignored", reason: "Shopify topic header is missing." };
14
+ if (!configuredTopics(context.config).includes(topic))
15
+ return { outcome: "ignored", reason: "Shopify topic is not subscribed." };
16
+ const webhookId = context.header("x-shopify-webhook-id") ?? "";
17
+ return {
18
+ dedupeKey: webhookId.length == 0 ? undefined : webhookId,
19
+ outcome: "event",
20
+ outputs: {
21
+ apiVersion: context.header("x-shopify-api-version") ?? "",
22
+ body: context.payload,
23
+ eventId: context.header("x-shopify-event-id") ?? "",
24
+ shopDomain: context.header("x-shopify-shop-domain") ?? "",
25
+ topic,
26
+ triggeredAt: context.header("x-shopify-triggered-at") ?? "",
27
+ webhookId,
28
+ },
29
+ };
30
+ },
31
+ async reconcile(context) {
32
+ const state = requireState(context.state);
33
+ const subscriptions = await list(context);
34
+ if (subscriptions == null) {
35
+ if (!context.active)
36
+ return { outcome: "ready" };
37
+ throw new PermanentIntegrationError("The Shopify store or webhook resource is unavailable.");
38
+ }
39
+ if (!context.active) {
40
+ for (const id of subscriptions.values())
41
+ await remove(context, id);
42
+ await state.saveSubscription({}, later(context.now));
43
+ return { outcome: "ready" };
44
+ }
45
+ const wanted = configuredTopics(context.config);
46
+ const ids = [];
47
+ const created = [];
48
+ try {
49
+ for (const topic of wanted) {
50
+ const existing = subscriptions.get(topic);
51
+ if (existing != null)
52
+ ids.push(existing);
53
+ else {
54
+ const subscription = await create(context, topic);
55
+ ids.push(subscription.id);
56
+ if (subscription.created)
57
+ created.push(subscription.id);
58
+ }
59
+ }
60
+ }
61
+ catch (cause) {
62
+ await Promise.allSettled(created.map((id) => remove(context, id)));
63
+ throw cause;
64
+ }
65
+ for (const [topic, id] of subscriptions)
66
+ if (!wanted.includes(topic))
67
+ await remove(context, id);
68
+ await state.saveSubscription({ webhookIds: ids }, later(context.now));
69
+ return { outcome: "ready" };
70
+ },
71
+ };
72
+ function configuredTopics(value) {
73
+ return [...new Set(value.topics)];
74
+ }
75
+ async function create(context, topic) {
76
+ const address = callbackUrl(context);
77
+ const result = await request(context, "subscription create", {
78
+ body: { webhook: { address, format: "json", topic } },
79
+ endpoint: "/webhooks.json",
80
+ method: "POST",
81
+ });
82
+ if (result.status == 422) {
83
+ const subscriptions = await list(context);
84
+ const winner = subscriptions?.get(topic);
85
+ if (winner != null)
86
+ return { created: false, id: winner };
87
+ }
88
+ success(result, "subscription create");
89
+ const id = normalizeId(record(record(result.data)?.webhook)?.id);
90
+ if (id == null)
91
+ throw new TransientIntegrationError("Shopify subscription response is missing its ID.");
92
+ return { created: true, id };
93
+ }
94
+ async function list(context) {
95
+ const address = callbackUrl(context);
96
+ const result = await request(context, "subscription list", {
97
+ endpoint: "/webhooks.json",
98
+ method: "GET",
99
+ query: { address, limit: 250 },
100
+ });
101
+ if (result.status == 404)
102
+ return null;
103
+ success(result, "subscription list");
104
+ const raw = record(result.data)?.webhooks;
105
+ const subscriptions = new Map();
106
+ if (!Array.isArray(raw))
107
+ return subscriptions;
108
+ for (const item of raw) {
109
+ const value = record(item);
110
+ const id = normalizeId(value?.id);
111
+ if (value?.address === address && typeof value.topic == "string" && id != null)
112
+ subscriptions.set(value.topic, id);
113
+ }
114
+ return subscriptions;
115
+ }
116
+ function callbackUrl(context) {
117
+ const url = new URL(context.endpointUrl);
118
+ url.searchParams.set("open_flow_callback", context.callbackSecret);
119
+ return url.href;
120
+ }
121
+ async function remove(context, id) {
122
+ const result = await request(context, "subscription delete", {
123
+ endpoint: `/webhooks/${encodeURIComponent(id)}.json`,
124
+ method: "DELETE",
125
+ });
126
+ if (result.status != 404)
127
+ success(result, "subscription delete");
128
+ }
129
+ async function request(context, operation, value) {
130
+ try {
131
+ return await context.connector.execute(value, context.signal);
132
+ }
133
+ catch (cause) {
134
+ if (cause instanceof IntegrationConnectionError)
135
+ throw cause;
136
+ throw new TransientIntegrationError(`Shopify ${operation} request failed.`, { cause });
137
+ }
138
+ }
139
+ function success(result, operation) {
140
+ if (result.status >= 200 && result.status < 300)
141
+ return;
142
+ if (result.status == 401 || result.status == 403)
143
+ throw new IntegrationConnectionError(`Shopify ${operation} rejected the Connection.`);
144
+ if ([402, 404, 422, 423, 501].includes(result.status))
145
+ throw new PermanentIntegrationError(`Shopify ${operation} rejected the subscription.`);
146
+ throw new TransientIntegrationError(`Shopify ${operation} failed with status ${result.status}.`);
147
+ }
148
+ function normalizeId(value) {
149
+ if (typeof value == "string")
150
+ return value.trim().length == 0 ? null : value;
151
+ return typeof value == "number" && Number.isFinite(value) ? String(value) : null;
152
+ }
153
+ function requireState(value) {
154
+ if (value == null)
155
+ throw new PermanentIntegrationError("Shopify Integration state is missing.");
156
+ return value;
157
+ }
158
+ function later(now) {
159
+ return new Date(now.getTime() + 30 * 24 * 60 * 60 * 1_000);
160
+ }
161
+ function record(value) {
162
+ return value != null && typeof value == "object" && !Array.isArray(value)
163
+ ? value
164
+ : undefined;
165
+ }
@@ -1,4 +1,7 @@
1
1
  import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
2
+ import type { IntegrationDefinition } from "../../triggers/common/integration.ts";
3
+ import type { PollDefinition } from "../../triggers/common/poll.ts";
2
4
  export declare const executors: ProviderExecutors;
3
5
  export declare const proxy: ProviderProxyExecutor;
4
6
  export declare const credentialValidators: CredentialValidators;
7
+ export declare const triggers: readonly (IntegrationDefinition | PollDefinition)[];
@@ -1,5 +1,6 @@
1
1
  import { defineProviderProxy } from "../provider-runtime.js";
2
2
  import { defineSlackProviderExecutors, slackActionHandlers, slackApiBaseUrl, slackCredentialValidators, } from "./runtime.js";
3
+ import { slackMessagePosted } from "./trigger-on-message-posted.js";
3
4
  const service = "slack";
4
5
  export const executors = defineSlackProviderExecutors(service, "user", slackActionHandlers);
5
6
  export const proxy = defineProviderProxy({
@@ -9,3 +10,4 @@ export const proxy = defineProviderProxy({
9
10
  skipDnsValidation: true,
10
11
  });
11
12
  export const credentialValidators = slackCredentialValidators;
13
+ export const triggers = [slackMessagePosted];
@@ -0,0 +1,2 @@
1
+ import type { PollDefinition } from "../../triggers/common/poll.ts";
2
+ export declare const slackMessagePosted: PollDefinition;
@@ -0,0 +1,4 @@
1
+ import type { TriggerKeySnapshot } from "../../triggers/common/types.ts";
2
+ export declare const snapshot: TriggerKeySnapshot & {
3
+ readonly type: "poll";
4
+ };
@@ -0,0 +1,80 @@
1
+ import { jsonSchema as s } from "../../core/json-schema.js";
2
+ const defaultPageSize = 15;
3
+ export const snapshot = {
4
+ configInputs: [
5
+ {
6
+ handle: "channelId",
7
+ jsonSchema: s.string({ pattern: "^[CDG][A-Z0-9]{2,31}$" }),
8
+ nullable: false,
9
+ description: "Slack conversation ID to watch, e.g. C0122KQ70S7E.",
10
+ },
11
+ {
12
+ handle: "fromUserIds",
13
+ jsonSchema: s.array(s.string({ pattern: "^[UW][A-Z0-9]{2,31}$" })),
14
+ nullable: false,
15
+ value: [],
16
+ description: "Only trigger on messages written by these Slack user IDs. Empty means any author.",
17
+ },
18
+ {
19
+ handle: "ignoreUserIds",
20
+ jsonSchema: s.array(s.string({ pattern: "^[UW][A-Z0-9]{2,31}$" })),
21
+ nullable: false,
22
+ value: [],
23
+ description: "Never trigger on messages written by these Slack user IDs.",
24
+ },
25
+ {
26
+ handle: "includeBotMessages",
27
+ jsonSchema: s.boolean(),
28
+ nullable: false,
29
+ value: false,
30
+ description: "Also trigger on messages posted by bots and apps.",
31
+ },
32
+ {
33
+ handle: "includeSystemMessages",
34
+ jsonSchema: s.boolean(),
35
+ nullable: false,
36
+ value: false,
37
+ description: "Also trigger on Slack system notices.",
38
+ },
39
+ {
40
+ handle: "maxMessagesPerPoll",
41
+ jsonSchema: s.integer({ maximum: 100, minimum: 1 }),
42
+ nullable: false,
43
+ value: defaultPageSize,
44
+ description: "Maximum number of messages processed per poll.",
45
+ },
46
+ {
47
+ handle: "textContains",
48
+ jsonSchema: s.string(),
49
+ nullable: false,
50
+ value: "",
51
+ description: "Only trigger when message text contains this string, case-insensitively.",
52
+ },
53
+ ],
54
+ definitionVersion: 2,
55
+ description: "Polls one Slack conversation and triggers when a new message is posted to it.",
56
+ displayName: "New Channel Message",
57
+ key: "slack.on_message_posted",
58
+ name: "on_message_posted",
59
+ outputs: [
60
+ {
61
+ handle: "events",
62
+ jsonSchema: s.array(s.object({
63
+ botId: { type: ["string", "null"] },
64
+ channelId: s.string(),
65
+ files: s.array(s.object({ id: s.string(), mimetype: s.string(), name: s.string() }, { additionalProperties: false, required: ["id", "name", "mimetype"] })),
66
+ messageTs: s.string(),
67
+ subtype: { type: ["string", "null"] },
68
+ text: s.string(),
69
+ threadTs: { type: ["string", "null"] },
70
+ userId: { type: ["string", "null"] },
71
+ }, {
72
+ additionalProperties: false,
73
+ required: ["channelId", "messageTs", "threadTs", "userId", "botId", "subtype", "text", "files"],
74
+ })),
75
+ nullable: false,
76
+ },
77
+ ],
78
+ provider: "slack",
79
+ type: "poll",
80
+ };
@@ -0,0 +1,176 @@
1
+ import { HttpRequestError } from "../../server/api/http-utils.js";
2
+ import { eventsPollOutputs, PermanentPollError, PollConnectionError, TransientPollError, } from "../../triggers/common/poll.js";
3
+ import { snapshot } from "./trigger-on-message-posted.definition.js";
4
+ const seedBackoffMs = 60_000;
5
+ const plainSubtypes = new Set(["file_share", "thread_broadcast", "me_message", "bot_message"]);
6
+ const credentialErrors = new Set([
7
+ "invalid_auth",
8
+ "not_authed",
9
+ "token_revoked",
10
+ "token_expired",
11
+ "account_inactive",
12
+ "two_factor_setup_required",
13
+ ]);
14
+ const permanentErrors = new Set([
15
+ "channel_not_found",
16
+ "not_in_channel",
17
+ "not_allowed_token_type",
18
+ "access_denied",
19
+ "channel_is_limited_access",
20
+ "ekm_access_denied",
21
+ "enterprise_is_restricted",
22
+ "no_permission",
23
+ "team_access_not_granted",
24
+ "invalid_ts_oldest",
25
+ "invalid_ts_latest",
26
+ "method_deprecated",
27
+ "deprecated_endpoint",
28
+ "accesslimited",
29
+ ]);
30
+ export const slackMessagePosted = {
31
+ buildOutputs: eventsPollOutputs,
32
+ snapshot,
33
+ async poll(context) {
34
+ const config = resolveConfig(context.config);
35
+ if (context.checkpoint === null) {
36
+ return { checkpoint: { lastTs: await seed(context, config.channelId) }, events: [] };
37
+ }
38
+ const checkpoint = readCheckpoint(context.checkpoint);
39
+ const history = await fetchHistory(context, config, checkpoint);
40
+ const messages = timedMessages(history);
41
+ const events = [];
42
+ let filtered = 0;
43
+ for (const entry of messages.toReversed()) {
44
+ if (!matches(entry.message, config)) {
45
+ filtered += 1;
46
+ continue;
47
+ }
48
+ events.push(event(entry, config.channelId));
49
+ }
50
+ return { checkpoint: nextCheckpoint(checkpoint, history, messages), events, filtered };
51
+ },
52
+ };
53
+ function resolveConfig(value) {
54
+ return {
55
+ channelId: value.channelId,
56
+ fromUserIds: value.fromUserIds,
57
+ ignoreUserIds: value.ignoreUserIds,
58
+ includeBotMessages: value.includeBotMessages,
59
+ includeSystemMessages: value.includeSystemMessages,
60
+ maxMessagesPerPoll: value.maxMessagesPerPoll,
61
+ textContains: value.textContains,
62
+ };
63
+ }
64
+ async function seed(context, channelId) {
65
+ const result = await slackGet(context, { channel: channelId, limit: 1 });
66
+ success(result, "baseline history fetch");
67
+ const ts = result.data.messages?.[0]?.ts;
68
+ return ts != null && validTs(ts) ? ts : `${Math.floor((context.now.getTime() - seedBackoffMs) / 1000)}.000000`;
69
+ }
70
+ function readCheckpoint(value) {
71
+ const checkpoint = value;
72
+ if (typeof checkpoint.lastTs != "string" || checkpoint.lastTs.length == 0) {
73
+ throw new PermanentPollError("Slack checkpoint lastTs is missing.");
74
+ }
75
+ const drainLatest = checkpoint.drainLatest;
76
+ const pendingHighTs = checkpoint.pendingHighTs;
77
+ if (drainLatest == null && pendingHighTs == null)
78
+ return { lastTs: checkpoint.lastTs };
79
+ if (typeof drainLatest != "string" ||
80
+ drainLatest.length == 0 ||
81
+ typeof pendingHighTs != "string" ||
82
+ pendingHighTs.length == 0) {
83
+ throw new PermanentPollError("Slack checkpoint drain fields are incomplete.");
84
+ }
85
+ return { drainLatest, lastTs: checkpoint.lastTs, pendingHighTs };
86
+ }
87
+ async function fetchHistory(context, config, checkpoint) {
88
+ const result = await slackGet(context, {
89
+ channel: config.channelId,
90
+ inclusive: "false",
91
+ latest: checkpoint.drainLatest,
92
+ limit: config.maxMessagesPerPoll,
93
+ oldest: checkpoint.lastTs,
94
+ });
95
+ success(result, "history list");
96
+ return result.data;
97
+ }
98
+ async function slackGet(context, query) {
99
+ try {
100
+ return await context.connector.execute({
101
+ endpoint: "/conversations.history",
102
+ method: "GET",
103
+ query: Object.fromEntries(Object.entries(query).filter((entry) => entry[1] != null)),
104
+ }, context.signal);
105
+ }
106
+ catch (cause) {
107
+ throw new TransientPollError("Slack proxy request failed.", { cause });
108
+ }
109
+ }
110
+ function success(result, operation) {
111
+ const body = result.data != null && typeof result.data === "object" && !Array.isArray(result.data)
112
+ ? result.data
113
+ : {};
114
+ if (result.status >= 200 && result.status < 300 && body.ok === true)
115
+ return;
116
+ const code = typeof body.error == "string" ? body.error : "";
117
+ if (code === "missing_scope")
118
+ throw new HttpRequestError("scope_missing", "Slack history permission is missing for the selected conversation", 403);
119
+ if (credentialErrors.has(code))
120
+ throw new PollConnectionError(`Slack ${operation} rejected with ${code}.`);
121
+ if (permanentErrors.has(code))
122
+ throw new PermanentPollError(`Slack ${operation} rejected with ${code}.`);
123
+ throw new TransientPollError(`Slack ${operation} failed with ${code || `status ${result.status}`}.`);
124
+ }
125
+ function timedMessages(history) {
126
+ return (history.messages ?? []).flatMap((message) => message.ts != null && validTs(message.ts) ? [{ message, ts: message.ts }] : []);
127
+ }
128
+ function nextCheckpoint(checkpoint, history, messages) {
129
+ const newestTs = messages[0]?.ts;
130
+ const oldestTs = messages.at(-1)?.ts;
131
+ if (history.has_more === true) {
132
+ if (newestTs == null || oldestTs == null)
133
+ throw new TransientPollError("Slack history reports has_more without a usable timestamp.");
134
+ return {
135
+ drainLatest: oldestTs,
136
+ lastTs: checkpoint.lastTs,
137
+ pendingHighTs: checkpoint.pendingHighTs ?? newestTs,
138
+ };
139
+ }
140
+ if (checkpoint.pendingHighTs != null)
141
+ return { lastTs: checkpoint.pendingHighTs };
142
+ return { lastTs: newestTs ?? checkpoint.lastTs };
143
+ }
144
+ function matches(message, config) {
145
+ if (!config.includeBotMessages && (message.bot_id != null || message.subtype == "bot_message"))
146
+ return false;
147
+ if (!config.includeSystemMessages && message.subtype != null && !plainSubtypes.has(message.subtype))
148
+ return false;
149
+ if (config.fromUserIds.length > 0 && (message.user == null || !config.fromUserIds.includes(message.user)))
150
+ return false;
151
+ if (message.user != null && config.ignoreUserIds.includes(message.user))
152
+ return false;
153
+ return (config.textContains.length == 0 || (message.text ?? "").toLowerCase().includes(config.textContains.toLowerCase()));
154
+ }
155
+ function event(entry, channelId) {
156
+ return {
157
+ dedupeKey: `${channelId}:${entry.ts}`,
158
+ payload: {
159
+ botId: entry.message.bot_id ?? null,
160
+ channelId,
161
+ files: (entry.message.files ?? []).map((file) => ({
162
+ id: file.id ?? "",
163
+ mimetype: file.mimetype ?? "",
164
+ name: file.name ?? "",
165
+ })),
166
+ messageTs: entry.ts,
167
+ subtype: entry.message.subtype ?? null,
168
+ text: entry.message.text ?? "",
169
+ threadTs: entry.message.thread_ts ?? null,
170
+ userId: entry.message.user ?? null,
171
+ },
172
+ };
173
+ }
174
+ function validTs(value) {
175
+ return /^\d+(?:\.\d+)?$/.test(value) && !Number.isNaN(new Date(Math.round(Number(value) * 1000)).getTime());
176
+ }
@@ -1,5 +1,5 @@
1
1
  import { compactObject, optionalBoolean, optionalInteger, optionalString } from "../../core/cast.js";
2
- import { providerUserAgent, ProviderRequestError } from "../provider-runtime.js";
2
+ import { providerInputError, providerUserAgent, ProviderRequestError } from "../provider-runtime.js";
3
3
  export const spotifyApiBaseUrl = "https://api.spotify.com/v1/";
4
4
  export const spotifyActionHandlers = {
5
5
  get_current_user_profile(_input, deps) {
@@ -131,9 +131,7 @@ export const spotifyActionHandlers = {
131
131
  query: {
132
132
  market: optionalString(input.market),
133
133
  limit: stringifyOptionalInteger(input.limit),
134
- seed_artists: joinStringArray(input.seedArtists),
135
- seed_tracks: joinStringArray(input.seedTracks),
136
- seed_genres: joinStringArray(input.seedGenres),
134
+ ...buildRecommendationSeedQuery(input),
137
135
  },
138
136
  });
139
137
  },
@@ -761,6 +759,19 @@ function buildPagingQuery(input, extraQuery) {
761
759
  ...(extraQuery ?? {}),
762
760
  };
763
761
  }
762
+ // Spotify takes 1 to 5 seed values in any combination of seed_artists, seed_tracks, and seed_genres.
763
+ function buildRecommendationSeedQuery(input) {
764
+ const query = {
765
+ seed_artists: joinStringArray(input.seedArtists),
766
+ seed_tracks: joinStringArray(input.seedTracks),
767
+ seed_genres: joinStringArray(input.seedGenres),
768
+ };
769
+ const seedCount = [input.seedArtists, input.seedTracks, input.seedGenres].reduce((count, seeds) => count + (Array.isArray(seeds) ? seeds.length : 0), 0);
770
+ if (seedCount < 1 || seedCount > 5) {
771
+ throw providerInputError(`get_recommendations needs 1 to 5 seeds across seedArtists, seedTracks, and seedGenres; received ${seedCount}`);
772
+ }
773
+ return query;
774
+ }
764
775
  function buildPlaybackQuery(input) {
765
776
  return {
766
777
  market: optionalString(input.market),
@@ -1,4 +1,6 @@
1
1
  import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
2
+ import type { IntegrationDefinition } from "../../triggers/common/integration.ts";
3
+ import type { PollDefinition } from "../../triggers/common/poll.ts";
2
4
  import type { ProviderActionHandlers } from "../provider-runtime.ts";
3
5
  import type { ApiKeyProviderContext } from "../provider-runtime.ts";
4
6
  type StripeActionContext = ApiKeyProviderContext;
@@ -7,4 +9,5 @@ export declare const stripeActionHandlers: ProviderActionHandlers<"stripe", Stri
7
9
  export declare const executors: ProviderExecutors;
8
10
  export declare const proxy: ProviderProxyExecutor;
9
11
  export declare const credentialValidators: CredentialValidators;
12
+ export declare const triggers: readonly (IntegrationDefinition | PollDefinition)[];
10
13
  export {};
@@ -1,5 +1,6 @@
1
1
  import { compactObject, optionalRecord, optionalString } from "../../core/cast.js";
2
2
  import { ProviderRequestError, defineApiKeyProviderExecutors, defineProviderProxy, providerUserAgent, requiredResponseRecord, } from "../provider-runtime.js";
3
+ import { stripeEvent } from "./trigger-on-event.js";
3
4
  const service = "stripe";
4
5
  const stripeApiBaseUrl = "https://api.stripe.com";
5
6
  const stripeApiVersion = "2024-06-20";
@@ -289,3 +290,4 @@ function mapStripeError(status, message) {
289
290
  }
290
291
  return new ProviderRequestError(502, message, status);
291
292
  }
293
+ export const triggers = [stripeEvent];
@@ -0,0 +1,2 @@
1
+ import type { IntegrationDefinition } from "../../triggers/common/integration.ts";
2
+ export declare const stripeEvent: IntegrationDefinition;
@@ -0,0 +1,4 @@
1
+ import type { TriggerKeySnapshot } from "../../triggers/common/types.ts";
2
+ export declare const snapshot: TriggerKeySnapshot & {
3
+ readonly type: "integration";
4
+ };
@@ -0,0 +1,43 @@
1
+ import { jsonSchema as s } from "../../core/json-schema.js";
2
+ export const snapshot = {
3
+ configInputs: [
4
+ {
5
+ handle: "apiVersion",
6
+ jsonSchema: s.string({ maxLength: 40 }),
7
+ nullable: false,
8
+ value: "",
9
+ },
10
+ {
11
+ handle: "events",
12
+ jsonSchema: s.array(s.string({ maxLength: 120, pattern: "^(\\*|[a-z0-9_]+(?:\\.[a-z0-9_]+)+)$" }), {
13
+ minItems: 1,
14
+ uniqueItems: true,
15
+ }),
16
+ nullable: false,
17
+ },
18
+ {
19
+ handle: "includeConnectedAccounts",
20
+ jsonSchema: s.boolean(),
21
+ nullable: false,
22
+ value: false,
23
+ },
24
+ ],
25
+ definitionVersion: 2,
26
+ description: "Triggers when selected Stripe events happen on the connected account.",
27
+ displayName: "Account Event",
28
+ endpoint: {
29
+ body: { allowArray: false, allowEmpty: false, formats: ["json"] },
30
+ methods: ["POST"],
31
+ successStatus: 202,
32
+ },
33
+ key: "stripe.on_event",
34
+ name: "on_event",
35
+ outputs: [
36
+ { handle: "body", jsonSchema: { type: "object" }, nullable: false },
37
+ { handle: "event", jsonSchema: s.string(), nullable: false },
38
+ { handle: "eventId", jsonSchema: s.string(), nullable: false },
39
+ { handle: "livemode", jsonSchema: s.boolean(), nullable: false },
40
+ ],
41
+ provider: "stripe",
42
+ type: "integration",
43
+ };