@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,186 @@
1
+ import { IntegrationConnectionError, PermanentIntegrationError, TransientIntegrationError, } from "../../triggers/common/integration.js";
2
+ import { sameSecret } from "../../triggers/signature.js";
3
+ import { snapshot, events } from "./trigger-on-project-event.definition.js";
4
+ const eventNames = new Map([
5
+ ["Push Hook", "push"],
6
+ ["Tag Push Hook", "tag_push"],
7
+ ["Issue Hook", "issues"],
8
+ ["Confidential Issue Hook", "confidential_issues"],
9
+ ["Note Hook", "note"],
10
+ ["Confidential Note Hook", "confidential_note"],
11
+ ["Merge Request Hook", "merge_requests"],
12
+ ["Job Hook", "job"],
13
+ ["Pipeline Hook", "pipeline"],
14
+ ["Wiki Page Hook", "wiki_page"],
15
+ ["Deployment Hook", "deployment"],
16
+ ["Feature Flag Hook", "feature_flag"],
17
+ ["Release Hook", "releases"],
18
+ ["Milestone Hook", "milestone"],
19
+ ["Emoji Hook", "emoji"],
20
+ ["Resource Access Token Hook", "resource_access_token"],
21
+ ["Resource Deploy Token Hook", "resource_deploy_token"],
22
+ ]);
23
+ export const gitlabProjectEvent = {
24
+ initialState: { checkpoint: null, subscription: {} },
25
+ snapshot,
26
+ receive(context) {
27
+ if (!sameSecret(context.header("x-gitlab-token"), context.callbackSecret)) {
28
+ return { body: "", contentType: "text/plain", outcome: "respond", status: 404 };
29
+ }
30
+ const gitlabEvent = context.header("x-gitlab-event")?.trim();
31
+ if (!gitlabEvent)
32
+ return { outcome: "ignored", reason: "GitLab event header is missing." };
33
+ const event = eventNames.get(gitlabEvent);
34
+ if (event == null)
35
+ return { outcome: "ignored", reason: "GitLab event header is unknown." };
36
+ if (!resolveConfig(context.config).events.includes(event))
37
+ return { outcome: "ignored", reason: "GitLab event is not subscribed." };
38
+ const deliveryId = context.header("idempotency-key") ?? "";
39
+ return {
40
+ dedupeKey: deliveryId.length == 0 ? undefined : deliveryId,
41
+ outcome: "event",
42
+ outputs: {
43
+ body: context.payload,
44
+ deliveryId,
45
+ event,
46
+ gitlabEvent,
47
+ },
48
+ };
49
+ },
50
+ async reconcile(context) {
51
+ const state = requireState(context.state);
52
+ const config = resolveConfig(context.config);
53
+ const known = subscriptionId(state);
54
+ if (!context.active) {
55
+ const ids = known == null ? await findByUrl(context, config) : [known];
56
+ for (const id of ids ?? [])
57
+ await remove(context, config, id);
58
+ await state.saveSubscription({}, later(context.now));
59
+ return { outcome: "ready" };
60
+ }
61
+ const matches = await findByUrl(context, config);
62
+ if (matches == null)
63
+ throw new PermanentIntegrationError("The GitLab project does not exist or is not visible.");
64
+ let hookId = known ?? matches[0];
65
+ if (hookId == null) {
66
+ const created = await request(context, "hook create", {
67
+ body: desired(context.endpointUrl, context.callbackSecret, config),
68
+ endpoint: endpoint(config),
69
+ method: "POST",
70
+ });
71
+ success(created, "hook create");
72
+ hookId = readId(created.data);
73
+ }
74
+ else {
75
+ const aligned = await request(context, "hook update", {
76
+ body: desired(context.endpointUrl, context.callbackSecret, config),
77
+ endpoint: `${endpoint(config)}/${hookId}`,
78
+ method: "PUT",
79
+ });
80
+ if (aligned.status == 404) {
81
+ await state.saveSubscription({}, context.now);
82
+ return { outcome: "pending" };
83
+ }
84
+ success(aligned, "hook update");
85
+ }
86
+ for (const duplicate of matches.filter((value) => value != hookId))
87
+ await remove(context, config, duplicate);
88
+ await state.saveSubscription({ hookId }, later(context.now));
89
+ return { outcome: "ready" };
90
+ },
91
+ };
92
+ function resolveConfig(value) {
93
+ return {
94
+ events: value.events,
95
+ insecureSsl: value.insecureSsl,
96
+ project: value.project,
97
+ pushBranchFilter: value.pushBranchFilter,
98
+ };
99
+ }
100
+ function endpoint(config) {
101
+ return `/projects/${encodeURIComponent(config.project)}/hooks`;
102
+ }
103
+ function desired(url, token, config) {
104
+ return {
105
+ branch_filter_strategy: "wildcard",
106
+ description: "Managed by Open Flow.",
107
+ enable_ssl_verification: !config.insecureSsl,
108
+ name: `Open Flow ${url.slice(url.lastIndexOf("/") + 1)}`,
109
+ push_events_branch_filter: config.pushBranchFilter,
110
+ url,
111
+ token,
112
+ ...Object.fromEntries(events.map((event) => [`${event}_events`, config.events.includes(event)])),
113
+ };
114
+ }
115
+ async function findByUrl(context, config) {
116
+ const matches = [];
117
+ for (let page = 1; page <= 10; page += 1) {
118
+ const result = await request(context, "hook list", {
119
+ endpoint: endpoint(config),
120
+ method: "GET",
121
+ query: { page, per_page: 100 },
122
+ });
123
+ if (result.status == 404)
124
+ return null;
125
+ success(result, "hook list");
126
+ const hooks = Array.isArray(result.data) ? result.data : [];
127
+ for (const hook of hooks) {
128
+ const value = record(hook);
129
+ if (value?.url === context.endpointUrl && (typeof value.id == "number" || typeof value.id == "string"))
130
+ matches.push(String(value.id));
131
+ }
132
+ if (hooks.length < 100)
133
+ break;
134
+ }
135
+ return matches;
136
+ }
137
+ async function remove(context, config, hookId) {
138
+ const result = await request(context, "hook delete", {
139
+ endpoint: `${endpoint(config)}/${hookId}`,
140
+ method: "DELETE",
141
+ });
142
+ if (result.status != 404)
143
+ success(result, "hook delete");
144
+ }
145
+ async function request(context, operation, value) {
146
+ try {
147
+ return await context.connector.execute(value, context.signal);
148
+ }
149
+ catch (cause) {
150
+ if (cause instanceof IntegrationConnectionError)
151
+ throw cause;
152
+ throw new TransientIntegrationError(`GitLab ${operation} request failed.`, { cause });
153
+ }
154
+ }
155
+ function success(result, operation) {
156
+ if (result.status >= 200 && result.status < 300)
157
+ return;
158
+ if (result.status == 401 || result.status == 403)
159
+ throw new IntegrationConnectionError(`GitLab ${operation} rejected the Connection.`);
160
+ if ([400, 404, 422].includes(result.status))
161
+ throw new PermanentIntegrationError(`GitLab ${operation} rejected the subscription.`);
162
+ throw new TransientIntegrationError(`GitLab ${operation} failed with status ${result.status}.`);
163
+ }
164
+ function readId(data) {
165
+ const value = record(data)?.id;
166
+ if (typeof value != "number" && typeof value != "string")
167
+ throw new TransientIntegrationError("GitLab hook response is missing its ID.");
168
+ return String(value);
169
+ }
170
+ function requireState(value) {
171
+ if (value == null)
172
+ throw new PermanentIntegrationError("GitLab Integration state is missing.");
173
+ return value;
174
+ }
175
+ function subscriptionId(state) {
176
+ const value = state.subscription.hookId;
177
+ return typeof value == "string" && value.length > 0 ? value : null;
178
+ }
179
+ function later(now) {
180
+ return new Date(now.getTime() + 30 * 24 * 60 * 60 * 1_000);
181
+ }
182
+ function record(value) {
183
+ return value != null && typeof value == "object" && !Array.isArray(value)
184
+ ? value
185
+ : undefined;
186
+ }
@@ -1,4 +1,6 @@
1
1
  import type { CredentialValidators, ExecutionContext, 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
  declare const service = "gmail";
4
6
  interface ActionContext {
@@ -13,4 +15,5 @@ export declare const gmailActionHandlers: ProviderActionHandlers<typeof service,
13
15
  export declare const executors: ProviderExecutors;
14
16
  export declare const proxy: ProviderProxyExecutor;
15
17
  export declare const credentialValidators: CredentialValidators;
18
+ export declare const triggers: readonly (IntegrationDefinition | PollDefinition)[];
16
19
  export {};
@@ -5,6 +5,7 @@ import { defineProviderExecutors, defineProviderProxy, ProviderRequestError, rea
5
5
  import { decodeGmailAttachment } from "./attachment-stream.js";
6
6
  import { buildRecipients, encodeMimeMessage, extractBodyContent, firstAddress, normalizeGmailMessage, normalizeMessageId, normalizeThreadId, parseAddressList, readHeader, resolveReplyHeaders, summarizeGmailMessage, } from "./message.js";
7
7
  import { gmailOAuthScopes } from "./scopes.js";
8
+ import { gmailMessageReceived } from "./trigger-on-message-received.js";
8
9
  const service = "gmail";
9
10
  const gmailApiBaseUrl = "https://gmail.googleapis.com/gmail/v1";
10
11
  const detailHydrationBatchSize = 10;
@@ -789,3 +790,4 @@ async function readGmailError(response) {
789
790
  looseArray(error?.errors).some((entry) => gmailQuotaReasons.has(optionalString(optionalRecord(entry)?.reason) ?? ""));
790
791
  return new ProviderRequestError(response.status, optionalString(error?.message) ?? `gmail request failed with ${response.status}`, withRetryAfterSeconds(response), rateLimited ? "rate_limited" : undefined);
791
792
  }
793
+ export const triggers = [gmailMessageReceived];
@@ -0,0 +1,2 @@
1
+ import type { PollDefinition } from "../../triggers/common/poll.ts";
2
+ export declare const gmailMessageReceived: 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,74 @@
1
+ import { jsonSchema as s } from "../../core/json-schema.js";
2
+ const defaultPageSize = 25;
3
+ const eventSchema = s.object({
4
+ historyId: { type: ["string", "null"] },
5
+ labelIds: s.array(s.string()),
6
+ messageId: s.string(),
7
+ messageTimestamp: s.string(),
8
+ sender: s.string(),
9
+ subject: s.string(),
10
+ threadId: s.string(),
11
+ to: s.string(),
12
+ }, {
13
+ additionalProperties: false,
14
+ required: ["messageId", "threadId", "historyId", "labelIds", "subject", "sender", "to", "messageTimestamp"],
15
+ });
16
+ export const snapshot = {
17
+ configInputs: [
18
+ {
19
+ handle: "includeDrafts",
20
+ jsonSchema: s.boolean(),
21
+ nullable: false,
22
+ value: false,
23
+ description: "Also trigger on draft messages.",
24
+ },
25
+ {
26
+ handle: "includeSpamAndTrash",
27
+ jsonSchema: s.boolean(),
28
+ nullable: false,
29
+ value: false,
30
+ description: "Also trigger on messages in SPAM and TRASH.",
31
+ },
32
+ {
33
+ handle: "labelNamesOrIds",
34
+ jsonSchema: s.array(s.string({ minLength: 1 })),
35
+ nullable: false,
36
+ description: "Only trigger on messages carrying all of these labels, referenced by name or ID.",
37
+ value: [],
38
+ },
39
+ {
40
+ handle: "maxMessagesPerPoll",
41
+ jsonSchema: s.integer({ maximum: 100, minimum: 1 }),
42
+ nullable: false,
43
+ value: defaultPageSize,
44
+ },
45
+ {
46
+ handle: "readStatus",
47
+ jsonSchema: s.stringEnum(["Read", "Unread", "All"]),
48
+ nullable: false,
49
+ value: "All",
50
+ },
51
+ {
52
+ handle: "search",
53
+ jsonSchema: s.string(),
54
+ nullable: false,
55
+ description: "Gmail search-syntax query the message must match.",
56
+ value: "",
57
+ },
58
+ {
59
+ handle: "sender",
60
+ jsonSchema: s.string(),
61
+ nullable: false,
62
+ description: "Only trigger on messages whose sender matches this address or name.",
63
+ value: "",
64
+ },
65
+ ],
66
+ definitionVersion: 2,
67
+ description: "Polls the Gmail mailbox and triggers when a new message is received.",
68
+ displayName: "New Message Received",
69
+ key: "gmail.on_message_received",
70
+ name: "on_message_received",
71
+ outputs: [{ handle: "events", jsonSchema: s.array(eventSchema), nullable: false }],
72
+ provider: "gmail",
73
+ type: "poll",
74
+ };
@@ -0,0 +1,232 @@
1
+ import { eventsPollOutputs, maximumPollEventsPerPage, PermanentPollError, PollConnectionError, TransientPollError, } from "../../triggers/common/poll.js";
2
+ import { snapshot } from "./trigger-on-message-received.definition.js";
3
+ export const gmailMessageReceived = {
4
+ buildOutputs: eventsPollOutputs,
5
+ snapshot,
6
+ async poll(context) {
7
+ const config = resolveConfig(context.config);
8
+ if (context.checkpoint === null)
9
+ return { checkpoint: { historyId: await profileHistoryId(context) }, events: [] };
10
+ const checkpoint = readCheckpoint(context.checkpoint);
11
+ const labels = await labelIds(context, config.labelNamesOrIds);
12
+ const budget = Math.min(config.maxMessagesPerPoll, maximumPollEventsPerPage);
13
+ const { candidates, lastPage } = await listMessages(context, checkpoint, labels, budget);
14
+ const offset = checkpoint.messageOffset ?? 0;
15
+ const end = Math.min(offset + budget, candidates.length);
16
+ // History pages count records, so consume all messages before advancing the page token.
17
+ const next = end < candidates.length
18
+ ? {
19
+ checkpoint: { historyId: checkpoint.historyId, pageToken: checkpoint.pageToken, messageOffset: end },
20
+ hasMore: true,
21
+ }
22
+ : nextCheckpoint(checkpoint.historyId, lastPage);
23
+ const events = [];
24
+ let filtered = 0;
25
+ for (const candidate of candidates.slice(offset, end)) {
26
+ const message = await messageMetadata(context, candidate.id);
27
+ if (message == null)
28
+ continue;
29
+ if (!(await matches(context, message, config, labels))) {
30
+ filtered += 1;
31
+ continue;
32
+ }
33
+ events.push(event(message));
34
+ }
35
+ return {
36
+ checkpoint: next.checkpoint,
37
+ events,
38
+ filtered,
39
+ ...(next.hasMore ? { hasMore: true } : {}),
40
+ };
41
+ },
42
+ };
43
+ function resolveConfig(value) {
44
+ return {
45
+ includeDrafts: value.includeDrafts,
46
+ includeSpamAndTrash: value.includeSpamAndTrash,
47
+ labelNamesOrIds: value.labelNamesOrIds,
48
+ maxMessagesPerPoll: value.maxMessagesPerPoll,
49
+ readStatus: value.readStatus,
50
+ search: value.search.trim(),
51
+ sender: value.sender.trim(),
52
+ };
53
+ }
54
+ async function profileHistoryId(context) {
55
+ const result = await get(context, "/users/me/profile");
56
+ success(result, "profile fetch");
57
+ const historyId = record(result.data)?.historyId;
58
+ if (typeof historyId != "string" || historyId.length == 0)
59
+ throw new TransientPollError("Gmail profile historyId is missing.");
60
+ return historyId;
61
+ }
62
+ function readCheckpoint(value) {
63
+ const checkpoint = record(value);
64
+ if (typeof checkpoint?.historyId != "string" || checkpoint.historyId.length == 0) {
65
+ throw new PermanentPollError("Gmail checkpoint historyId is missing; recreate the Trigger.");
66
+ }
67
+ if (checkpoint.messageOffset !== undefined &&
68
+ (!Number.isInteger(checkpoint.messageOffset) || checkpoint.messageOffset < 0)) {
69
+ throw new PermanentPollError("Gmail checkpoint messageOffset is invalid; recreate the Trigger.");
70
+ }
71
+ return {
72
+ historyId: checkpoint.historyId,
73
+ ...(typeof checkpoint.pageToken == "string" ? { pageToken: checkpoint.pageToken } : {}),
74
+ messageOffset: checkpoint.messageOffset,
75
+ };
76
+ }
77
+ async function labelIds(context, configured) {
78
+ if (configured.length == 0)
79
+ return [];
80
+ const result = await get(context, "/users/me/labels");
81
+ success(result, "labels list");
82
+ const labels = record(result.data)?.labels;
83
+ const byId = new Set();
84
+ const byName = new Map();
85
+ if (Array.isArray(labels)) {
86
+ for (const value of labels) {
87
+ const label = record(value);
88
+ if (typeof label?.id != "string" || label.id.length == 0)
89
+ continue;
90
+ byId.add(label.id);
91
+ if (typeof label.name == "string" && label.name.length > 0)
92
+ byName.set(label.name.toLowerCase(), label.id);
93
+ }
94
+ }
95
+ return [
96
+ ...new Set(configured.map((value) => {
97
+ const id = byId.has(value) ? value : byName.get(value.toLowerCase());
98
+ if (id == null)
99
+ throw new PermanentPollError(`Gmail label "${value}" does not exist.`);
100
+ return id;
101
+ })),
102
+ ];
103
+ }
104
+ async function listMessages(context, checkpoint, labels, maxMessages) {
105
+ const candidates = new Map();
106
+ const result = await get(context, "/users/me/history", {
107
+ historyTypes: "messageAdded",
108
+ labelId: labels[0],
109
+ maxResults: maxMessages,
110
+ pageToken: checkpoint.pageToken,
111
+ startHistoryId: checkpoint.historyId,
112
+ });
113
+ if (result.status == 404)
114
+ throw new PermanentPollError("Gmail history checkpoint expired; recreate the Trigger.");
115
+ success(result, "history list");
116
+ const lastPage = (record(result.data) ?? {});
117
+ for (const history of lastPage.history ?? []) {
118
+ for (const added of history.messagesAdded ?? []) {
119
+ const id = added.message?.id;
120
+ if (id != null && id.length > 0 && !candidates.has(id))
121
+ candidates.set(id, { id });
122
+ }
123
+ }
124
+ return { candidates: [...candidates.values()], lastPage };
125
+ }
126
+ function nextCheckpoint(historyId, page) {
127
+ if (page.nextPageToken)
128
+ return { checkpoint: { historyId, pageToken: page.nextPageToken }, hasMore: true };
129
+ if (!page.historyId)
130
+ throw new TransientPollError("Gmail history response historyId is missing.");
131
+ return { checkpoint: { historyId: page.historyId }, hasMore: false };
132
+ }
133
+ async function messageMetadata(context, id) {
134
+ const result = await get(context, `/users/me/messages/${encodeURIComponent(id)}`, {
135
+ format: "metadata",
136
+ });
137
+ if (result.status == 404)
138
+ return null;
139
+ success(result, "message metadata fetch");
140
+ return record(result.data) ?? null;
141
+ }
142
+ async function matches(context, message, config, requiredLabels) {
143
+ const labels = message.labelIds ?? [];
144
+ if (labels.includes("SENT") && !labels.includes("INBOX"))
145
+ return false;
146
+ if (!config.includeSpamAndTrash && (labels.includes("SPAM") || labels.includes("TRASH")))
147
+ return false;
148
+ if (!config.includeDrafts && labels.includes("DRAFT"))
149
+ return false;
150
+ if (config.readStatus == "Unread" && !labels.includes("UNREAD"))
151
+ return false;
152
+ if (config.readStatus == "Read" && labels.includes("UNREAD"))
153
+ return false;
154
+ if (!requiredLabels.every((label) => labels.includes(label)))
155
+ return false;
156
+ const query = searchQuery(config);
157
+ return query.length == 0 || (await matchesQuery(context, message, query, config.includeSpamAndTrash));
158
+ }
159
+ function searchQuery(config) {
160
+ const query = config.search.length == 0 ? [] : [config.search];
161
+ const sender = config.sender.replaceAll('"', "").trim();
162
+ if (sender.length > 0)
163
+ query.push(`from:${/\s/.test(sender) ? `"${sender}"` : sender}`);
164
+ return query.join(" ");
165
+ }
166
+ async function matchesQuery(context, message, query, includeSpamAndTrash) {
167
+ const messageId = header(message.payload?.headers ?? [], "Message-ID")
168
+ .replace(/^</, "")
169
+ .replace(/>$/, "");
170
+ if (messageId.length == 0)
171
+ return false;
172
+ const result = await get(context, "/users/me/messages", {
173
+ includeSpamTrash: includeSpamAndTrash ? "true" : undefined,
174
+ q: `${query} rfc822msgid:${messageId}`,
175
+ });
176
+ success(result, "query match");
177
+ const messages = record(result.data)?.messages;
178
+ return Array.isArray(messages) && messages.some((value) => record(value)?.id === message.id);
179
+ }
180
+ function event(message) {
181
+ const headers = message.payload?.headers ?? [];
182
+ return {
183
+ dedupeKey: message.id,
184
+ payload: {
185
+ historyId: message.historyId ?? null,
186
+ labelIds: message.labelIds ?? [],
187
+ messageId: message.id,
188
+ messageTimestamp: timestamp(message.internalDate, header(headers, "Date")),
189
+ sender: header(headers, "From"),
190
+ subject: header(headers, "Subject"),
191
+ threadId: message.threadId ?? "",
192
+ to: header(headers, "To"),
193
+ },
194
+ };
195
+ }
196
+ async function get(context, endpoint, query = {}) {
197
+ try {
198
+ return await context.connector.execute({
199
+ endpoint,
200
+ method: "GET",
201
+ query: Object.fromEntries(Object.entries(query).filter((entry) => entry[1] != null)),
202
+ }, context.signal);
203
+ }
204
+ catch (cause) {
205
+ throw new TransientPollError(`Gmail proxy request to ${endpoint} failed.`, { cause });
206
+ }
207
+ }
208
+ function success(result, operation) {
209
+ if (result.status >= 200 && result.status < 300)
210
+ return;
211
+ if (result.status == 401 || result.status == 403)
212
+ throw new PollConnectionError(`Gmail ${operation} rejected the Connection.`);
213
+ throw new TransientPollError(`Gmail ${operation} failed with status ${result.status}.`);
214
+ }
215
+ function header(headers, name) {
216
+ return headers.find((value) => value.name.toLowerCase() == name.toLowerCase())?.value ?? "";
217
+ }
218
+ function timestamp(internalDate, fallback) {
219
+ const epoch = internalDate == null ? Number.NaN : Number(internalDate);
220
+ if (Number.isFinite(epoch)) {
221
+ const date = new Date(epoch);
222
+ if (!Number.isNaN(date.getTime()))
223
+ return date.toISOString();
224
+ }
225
+ const parsed = Date.parse(fallback);
226
+ return Number.isNaN(parsed) ? "" : new Date(parsed).toISOString();
227
+ }
228
+ function record(value) {
229
+ return value != null && typeof value == "object" && !Array.isArray(value)
230
+ ? value
231
+ : undefined;
232
+ }
@@ -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 { OAuthProviderContext } from "../provider-runtime.ts";
4
6
  type GooglecalendarRuntimeDeps = OAuthProviderContext;
@@ -7,4 +9,5 @@ export declare const googlecalendarActionHandlers: ProviderActionHandlers<"googl
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 {};
@@ -4,6 +4,7 @@ import { combineProviderActionHandlers, defineProviderProxy, ProviderRequestErro
4
4
  import { googlecalendarEventActionHandlers } from "./runtime-events.js";
5
5
  import { googlecalendarApiBaseUrl, googlecalendarJsonRequest, googlecalendarRequest, resolveCalendarId, resolveRuleId, resolveSettingId, } from "./runtime-shared.js";
6
6
  import { googlecalendarOAuthScopes } from "./scopes.js";
7
+ import { googleCalendarEventChanged } from "./trigger-on-event-changed.js";
7
8
  function asObject(value) {
8
9
  return requiredRecord(value, "object input", (message) => new ProviderRequestError(400, message));
9
10
  }
@@ -566,3 +567,4 @@ function asRecord(value) {
566
567
  }
567
568
  return value;
568
569
  }
570
+ export const triggers = [googleCalendarEventChanged];
@@ -0,0 +1,2 @@
1
+ import type { PollDefinition } from "../../triggers/common/poll.ts";
2
+ export declare const googleCalendarEventChanged: 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,74 @@
1
+ import { jsonSchema as s } from "../../core/json-schema.js";
2
+ const changes = ["created", "updated", "cancelled"];
3
+ const eventSchema = s.object({
4
+ attendeeEmails: s.array(s.string()),
5
+ calendarId: s.string(),
6
+ changeType: s.stringEnum(changes),
7
+ created: { type: ["string", "null"] },
8
+ creatorEmail: { type: ["string", "null"] },
9
+ description: { type: ["string", "null"] },
10
+ end: { type: ["object", "null"] },
11
+ eventId: s.string(),
12
+ eventType: { type: ["string", "null"] },
13
+ htmlLink: { type: ["string", "null"] },
14
+ isAllDay: s.boolean(),
15
+ location: { type: ["string", "null"] },
16
+ organizerEmail: { type: ["string", "null"] },
17
+ recurringEventId: { type: ["string", "null"] },
18
+ start: { type: ["object", "null"] },
19
+ status: s.string(),
20
+ summary: { type: ["string", "null"] },
21
+ updated: { type: ["string", "null"] },
22
+ }, {
23
+ additionalProperties: false,
24
+ required: [
25
+ "changeType",
26
+ "calendarId",
27
+ "eventId",
28
+ "status",
29
+ "summary",
30
+ "description",
31
+ "location",
32
+ "htmlLink",
33
+ "start",
34
+ "end",
35
+ "isAllDay",
36
+ "organizerEmail",
37
+ "creatorEmail",
38
+ "attendeeEmails",
39
+ "recurringEventId",
40
+ "eventType",
41
+ "created",
42
+ "updated",
43
+ ],
44
+ });
45
+ export const snapshot = {
46
+ configInputs: [
47
+ {
48
+ handle: "calendarId",
49
+ jsonSchema: s.string({ maxLength: 1024, minLength: 1 }),
50
+ nullable: false,
51
+ },
52
+ {
53
+ handle: "changes",
54
+ jsonSchema: s.array(s.stringEnum(changes), { minItems: 1, uniqueItems: true }),
55
+ nullable: false,
56
+ value: changes,
57
+ },
58
+ { handle: "matchTerm", jsonSchema: s.string(), nullable: false, value: "" },
59
+ {
60
+ handle: "maxEventsPerPoll",
61
+ jsonSchema: s.integer({ maximum: 500, minimum: 1 }),
62
+ nullable: false,
63
+ value: 100,
64
+ },
65
+ ],
66
+ definitionVersion: 2,
67
+ description: "Polls a Google Calendar and triggers when an event is created, updated or cancelled.",
68
+ displayName: "Event Changed",
69
+ key: "googlecalendar.on_event_changed",
70
+ name: "on_event_changed",
71
+ outputs: [{ handle: "events", jsonSchema: s.array(eventSchema), nullable: false }],
72
+ provider: "googlecalendar",
73
+ type: "poll",
74
+ };