@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,212 @@
1
+ import { optionalString } from "../../core/cast.js";
2
+ import { randomUUIDv7 } from "../../core/uuid-v7.js";
3
+ import { IntegrationConnectionError, PermanentIntegrationError, TransientIntegrationError, } from "../../triggers/common/integration.js";
4
+ import { bytes, verifyHexHmac } from "../../triggers/signature.js";
5
+ import { snapshot } from "./trigger-on-event.definition.js";
6
+ const signatureToleranceSeconds = 5 * 60;
7
+ const endpoints = "/v1/webhook_endpoints";
8
+ const formHeaders = { "Content-Type": "application/x-www-form-urlencoded" };
9
+ export const stripeEvent = {
10
+ initialState: { checkpoint: null, subscription: {} },
11
+ snapshot,
12
+ async receive(context) {
13
+ const signingSecret = subscriptionSecret(context.state);
14
+ if (signingSecret == null ||
15
+ !(await validSignature(context.header("stripe-signature"), signingSecret, context.rawBody, context.now))) {
16
+ return { body: "", contentType: "text/plain", outcome: "respond", status: 404 };
17
+ }
18
+ if (context.payload == null || typeof context.payload != "object" || Array.isArray(context.payload)) {
19
+ return { outcome: "ignored", reason: "Stripe event body is missing." };
20
+ }
21
+ const payload = context.payload;
22
+ const event = payload.type;
23
+ const eventId = payload.id;
24
+ if (typeof event != "string" || typeof eventId != "string")
25
+ return { outcome: "ignored", reason: "Stripe event identity is missing." };
26
+ const config = resolveConfig(context.config);
27
+ if (!config.events.includes("*") && !config.events.includes(event))
28
+ return { outcome: "ignored", reason: "Stripe event is not subscribed." };
29
+ return {
30
+ dedupeKey: eventId,
31
+ outcome: "event",
32
+ outputs: { body: payload, event, eventId, livemode: payload.livemode === true },
33
+ };
34
+ },
35
+ async reconcile(context) {
36
+ const state = requireState(context.state);
37
+ const config = resolveConfig(context.config);
38
+ let endpointId = subscriptionId(state);
39
+ let signingSecret = subscriptionSecret(state);
40
+ let createKey = optionalString(state.subscription.createKey);
41
+ if (!context.active) {
42
+ endpointId ??= await findByUrl(context);
43
+ if (endpointId != null) {
44
+ const result = await request(context, "endpoint delete", {
45
+ endpoint: `${endpoints}/${endpointId}`,
46
+ method: "DELETE",
47
+ });
48
+ if (result.status != 404)
49
+ success(result, "endpoint delete");
50
+ }
51
+ await state.saveSubscription({}, later(context.now));
52
+ return { outcome: "ready" };
53
+ }
54
+ if (endpointId == null && createKey == null)
55
+ endpointId = await findByUrl(context);
56
+ if (endpointId != null && signingSecret == null) {
57
+ createKey = randomUUIDv7();
58
+ await state.saveSubscription({ endpointId, createKey }, context.now);
59
+ const removed = await request(context, "endpoint delete", {
60
+ endpoint: `${endpoints}/${endpointId}`,
61
+ method: "DELETE",
62
+ });
63
+ if (removed.status != 404)
64
+ success(removed, "endpoint delete");
65
+ endpointId = null;
66
+ }
67
+ if (endpointId == null) {
68
+ createKey ??= context.idempotencyKey;
69
+ await state.saveSubscription({ createKey }, context.now);
70
+ const created = await request(context, "endpoint create", {
71
+ body: createForm(context.endpointUrl, config),
72
+ endpoint: endpoints,
73
+ headers: { ...formHeaders, "Idempotency-Key": createKey },
74
+ method: "POST",
75
+ });
76
+ success(created, "endpoint create");
77
+ const endpoint = createdEndpoint(created.data);
78
+ endpointId = endpoint.id;
79
+ signingSecret = endpoint.secret;
80
+ }
81
+ await state.saveSubscription({ endpointId, signingSecret }, context.now);
82
+ const aligned = await request(context, "endpoint update", {
83
+ body: form([
84
+ ...config.events.map((event, index) => [`enabled_events[${index}]`, event]),
85
+ ["disabled", "false"],
86
+ ["description", "Managed by Open Flow."],
87
+ ]),
88
+ endpoint: `${endpoints}/${endpointId}`,
89
+ headers: formHeaders,
90
+ method: "POST",
91
+ });
92
+ if (aligned.status == 404) {
93
+ await state.saveSubscription({ createKey: randomUUIDv7() }, context.now);
94
+ return { outcome: "pending" };
95
+ }
96
+ success(aligned, "endpoint update");
97
+ await state.saveSubscription({ endpointId, signingSecret }, later(context.now));
98
+ return { outcome: "ready" };
99
+ },
100
+ };
101
+ function resolveConfig(value) {
102
+ return {
103
+ apiVersion: value.apiVersion,
104
+ events: [...new Set(value.events)],
105
+ includeConnectedAccounts: value.includeConnectedAccounts,
106
+ };
107
+ }
108
+ function createForm(url, config) {
109
+ const fields = [
110
+ ["url", url],
111
+ ...config.events.map((event, index) => [`enabled_events[${index}]`, event]),
112
+ ["description", "Managed by Open Flow."],
113
+ ];
114
+ if (config.includeConnectedAccounts)
115
+ fields.push(["connect", "true"]);
116
+ if (config.apiVersion.length > 0)
117
+ fields.push(["api_version", config.apiVersion]);
118
+ return form(fields);
119
+ }
120
+ function form(fields) {
121
+ const parameters = new URLSearchParams();
122
+ for (const [name, value] of fields)
123
+ parameters.append(name, value);
124
+ return parameters.toString();
125
+ }
126
+ async function findByUrl(context) {
127
+ let startingAfter;
128
+ for (let page = 0; page < 10; page += 1) {
129
+ const result = await request(context, "endpoint list", {
130
+ endpoint: endpoints,
131
+ method: "GET",
132
+ query: { limit: 100, ...(startingAfter == null ? {} : { starting_after: startingAfter }) },
133
+ });
134
+ success(result, "endpoint list");
135
+ const listing = record(result.data);
136
+ const values = Array.isArray(listing?.data) ? listing.data : [];
137
+ for (const value of values) {
138
+ const endpoint = record(value);
139
+ if (endpoint?.url === context.endpointUrl && typeof endpoint.id == "string")
140
+ return endpoint.id;
141
+ }
142
+ const last = record(values.at(-1))?.id;
143
+ if (listing?.has_more !== true || typeof last != "string")
144
+ return null;
145
+ startingAfter = last;
146
+ }
147
+ return null;
148
+ }
149
+ async function request(context, operation, value) {
150
+ try {
151
+ return await context.connector.execute(value, context.signal);
152
+ }
153
+ catch (cause) {
154
+ if (cause instanceof IntegrationConnectionError)
155
+ throw cause;
156
+ throw new TransientIntegrationError(`Stripe ${operation} request failed.`, { cause });
157
+ }
158
+ }
159
+ function success(result, operation) {
160
+ if (result.status >= 200 && result.status < 300)
161
+ return;
162
+ if (result.status == 401 || result.status == 403)
163
+ throw new IntegrationConnectionError(`Stripe ${operation} rejected the Connection.`);
164
+ if (result.status == 400 || result.status == 404) {
165
+ const message = record(record(result.data)?.error)?.message;
166
+ throw new PermanentIntegrationError(`Stripe ${operation} rejected the subscription${typeof message == "string" ? `: ${message}` : "."}`);
167
+ }
168
+ throw new TransientIntegrationError(`Stripe ${operation} failed with status ${result.status}.`);
169
+ }
170
+ function createdEndpoint(data) {
171
+ const value = record(data);
172
+ if (typeof value?.id != "string" || typeof value.secret != "string" || value.secret.length == 0) {
173
+ throw new TransientIntegrationError("Stripe endpoint response is missing its ID or signing secret.");
174
+ }
175
+ return { id: value.id, secret: value.secret };
176
+ }
177
+ function requireState(value) {
178
+ if (value == null)
179
+ throw new PermanentIntegrationError("Stripe Integration state is missing.");
180
+ return value;
181
+ }
182
+ function subscriptionId(state) {
183
+ const value = state.subscription.endpointId;
184
+ return typeof value == "string" && value.length > 0 ? value : null;
185
+ }
186
+ function subscriptionSecret(state) {
187
+ const value = state?.subscription.signingSecret;
188
+ return typeof value == "string" && value.length > 0 ? value : null;
189
+ }
190
+ async function validSignature(header, secret, body, now) {
191
+ if (header == null)
192
+ return false;
193
+ const values = header.split(",").map((value) => value.trim().split("=", 2));
194
+ const timestamp = values.find(([name]) => name == "t")?.[1];
195
+ if (timestamp == null || !/^\d+$/.test(timestamp))
196
+ return false;
197
+ const seconds = Number(timestamp);
198
+ if (!Number.isSafeInteger(seconds) ||
199
+ Math.abs(Math.floor(now.getTime() / 1_000) - seconds) > signatureToleranceSeconds)
200
+ return false;
201
+ const signatures = values.filter(([name, value]) => name == "v1" && value != null).map(([, value]) => value);
202
+ const source = [bytes(`${timestamp}.`), body];
203
+ return (await Promise.all(signatures.map((signature) => verifyHexHmac(secret, source, signature)))).some(Boolean);
204
+ }
205
+ function later(now) {
206
+ return new Date(now.getTime() + 30 * 24 * 60 * 60 * 1_000);
207
+ }
208
+ function record(value) {
209
+ return value != null && typeof value == "object" && !Array.isArray(value)
210
+ ? value
211
+ : undefined;
212
+ }
@@ -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 TelegramActionHandler = (input: Record<string, unknown>, context: ApiKeyProviderContext) => Promise<unknown>;
@@ -6,4 +8,5 @@ export declare const telegramActionHandlers: ProviderActionHandlers<"telegram",
6
8
  export declare const executors: ProviderExecutors;
7
9
  export declare const proxy: ProviderProxyExecutor;
8
10
  export declare const credentialValidators: CredentialValidators;
11
+ export declare const triggers: readonly (IntegrationDefinition | PollDefinition)[];
9
12
  export {};
@@ -1,6 +1,7 @@
1
1
  import { optionalBoolean, optionalNumber, optionalRecord, optionalString } from "../../core/cast.js";
2
2
  import { assertPublicHttpUrl } from "../../core/request.js";
3
3
  import { defineApiKeyProviderExecutors, defineProviderProxy, isAbortLikeError, ProviderRequestError, requireApiKeyCredential, } from "../provider-runtime.js";
4
+ import { telegramUpdate } from "./trigger-on-update.js";
4
5
  const service = "telegram";
5
6
  const telegramApiBaseUrl = "https://api.telegram.org";
6
7
  const telegramDefaultRequestTimeoutMs = 30_000;
@@ -1165,3 +1166,4 @@ function assertValidTelegramBotToken(botToken) {
1165
1166
  throw new ProviderRequestError(400, "telegram bot token is malformed");
1166
1167
  }
1167
1168
  }
1169
+ export const triggers = [telegramUpdate];
@@ -0,0 +1,2 @@
1
+ import type { IntegrationDefinition } from "../../triggers/common/integration.ts";
2
+ export declare const telegramUpdate: IntegrationDefinition;
@@ -0,0 +1,5 @@
1
+ import type { TriggerKeySnapshot } from "../../triggers/common/types.ts";
2
+ export declare const updateTypes: readonly string[];
3
+ export declare const snapshot: TriggerKeySnapshot & {
4
+ readonly type: "integration";
5
+ };
@@ -0,0 +1,78 @@
1
+ import { jsonSchema as s } from "../../core/json-schema.js";
2
+ export const updateTypes = [
3
+ "*",
4
+ "message",
5
+ "edited_message",
6
+ "channel_post",
7
+ "edited_channel_post",
8
+ "business_connection",
9
+ "business_message",
10
+ "edited_business_message",
11
+ "deleted_business_messages",
12
+ "guest_message",
13
+ "message_reaction",
14
+ "message_reaction_count",
15
+ "inline_query",
16
+ "chosen_inline_result",
17
+ "callback_query",
18
+ "shipping_query",
19
+ "pre_checkout_query",
20
+ "purchased_paid_media",
21
+ "poll",
22
+ "poll_answer",
23
+ "my_chat_member",
24
+ "chat_member",
25
+ "chat_join_request",
26
+ "chat_boost",
27
+ "removed_chat_boost",
28
+ "managed_bot",
29
+ "subscription",
30
+ ];
31
+ export const snapshot = {
32
+ configInputs: [
33
+ {
34
+ handle: "chatIds",
35
+ jsonSchema: s.array(s.string({ pattern: "^-?[0-9]{1,20}$" }), { maxItems: 100, uniqueItems: true }),
36
+ nullable: false,
37
+ value: [],
38
+ description: "Only accept updates from these numeric Telegram chat IDs. Empty means every chat.",
39
+ },
40
+ {
41
+ handle: "dropPendingUpdates",
42
+ jsonSchema: s.boolean(),
43
+ nullable: false,
44
+ value: true,
45
+ description: "Discard updates queued before the webhook is created or resumed.",
46
+ },
47
+ {
48
+ handle: "updates",
49
+ jsonSchema: s.array({ enum: updateTypes }, { minItems: 1, uniqueItems: true }),
50
+ nullable: false,
51
+ description: "Telegram update types that trigger a Run. Use * for the Telegram default set.",
52
+ },
53
+ {
54
+ handle: "userIds",
55
+ jsonSchema: s.array(s.string({ pattern: "^[0-9]{1,20}$" }), { maxItems: 100, uniqueItems: true }),
56
+ nullable: false,
57
+ value: [],
58
+ description: "Only accept updates from these numeric Telegram user IDs. Empty means every user.",
59
+ },
60
+ ],
61
+ definitionVersion: 2,
62
+ description: "Triggers when the connected Telegram bot receives a selected update.",
63
+ displayName: "Bot Update",
64
+ endpoint: {
65
+ body: { allowArray: false, allowEmpty: false, formats: ["json"] },
66
+ methods: ["POST"],
67
+ successStatus: 200,
68
+ },
69
+ key: "telegram.on_update",
70
+ name: "on_update",
71
+ outputs: [
72
+ { handle: "body", jsonSchema: { type: "object" }, nullable: false },
73
+ { handle: "deliveryId", jsonSchema: s.string(), nullable: false },
74
+ { handle: "event", jsonSchema: s.string(), nullable: false },
75
+ ],
76
+ provider: "telegram",
77
+ type: "integration",
78
+ };
@@ -0,0 +1,141 @@
1
+ import { IntegrationConnectionError, PermanentIntegrationError, TransientIntegrationError, } from "../../triggers/common/integration.js";
2
+ import { snapshot, updateTypes } from "./trigger-on-update.definition.js";
3
+ const optInUpdates = new Set(["chat_member", "message_reaction", "message_reaction_count"]);
4
+ const defaultUpdates = updateTypes.filter((value) => value != "*" && !optInUpdates.has(value));
5
+ export const telegramUpdate = {
6
+ snapshot,
7
+ receive(context) {
8
+ if (!sameSecret(context.header("x-telegram-bot-api-secret-token"), context.callbackSecret)) {
9
+ return { body: "", contentType: "text/plain", outcome: "respond", status: 404 };
10
+ }
11
+ if (!isRecord(context.payload) || !Number.isSafeInteger(context.payload.update_id)) {
12
+ return { outcome: "ignored", reason: "Telegram update_id is missing." };
13
+ }
14
+ const event = Object.keys(context.payload).find((key) => key != "update_id");
15
+ if (event == null)
16
+ return { outcome: "ignored", reason: "Telegram update body is missing." };
17
+ const config = resolveConfig(context.config);
18
+ const subscribed = subscribedUpdates(config) ?? defaultUpdates;
19
+ if (!subscribed.includes(event))
20
+ return { outcome: "ignored", reason: "Telegram update type is not subscribed." };
21
+ const update = context.payload[event];
22
+ if (config.chatIds.length > 0) {
23
+ const chatId = resolveChatId(update);
24
+ if (chatId == null || !config.chatIds.includes(chatId))
25
+ return { outcome: "ignored", reason: "Telegram chat does not match." };
26
+ }
27
+ if (config.userIds.length > 0) {
28
+ const userId = resolveUserId(update);
29
+ if (userId == null || !config.userIds.includes(userId))
30
+ return { outcome: "ignored", reason: "Telegram user does not match." };
31
+ }
32
+ const deliveryId = String(context.payload.update_id);
33
+ return {
34
+ dedupeKey: deliveryId,
35
+ outcome: "event",
36
+ outputs: { body: context.payload, deliveryId, event },
37
+ };
38
+ },
39
+ async reconcile(context) {
40
+ const current = await webhookUrl(context);
41
+ if (!context.active) {
42
+ if (current == context.endpointUrl)
43
+ await deleteWebhook(context);
44
+ return { outcome: "ready" };
45
+ }
46
+ if (current != "" && current != context.endpointUrl) {
47
+ throw new PermanentIntegrationError(`The Telegram bot already sends updates to ${host(current)}.`);
48
+ }
49
+ const config = resolveConfig(context.config);
50
+ await request(context, "setWebhook", {
51
+ allowed_updates: subscribedUpdates(config) ?? [],
52
+ drop_pending_updates: current == "" && config.dropPendingUpdates,
53
+ secret_token: context.callbackSecret,
54
+ url: context.endpointUrl,
55
+ });
56
+ return { outcome: "ready" };
57
+ },
58
+ };
59
+ function resolveConfig(value) {
60
+ return {
61
+ chatIds: value.chatIds,
62
+ dropPendingUpdates: value.dropPendingUpdates,
63
+ updates: value.updates,
64
+ userIds: value.userIds,
65
+ };
66
+ }
67
+ function subscribedUpdates(config) {
68
+ if (!config.updates.includes("*"))
69
+ return config.updates;
70
+ const named = config.updates.filter((value) => value != "*");
71
+ return named.length == 0 ? null : [...new Set([...defaultUpdates, ...named])];
72
+ }
73
+ function sameSecret(candidate, expected) {
74
+ if (candidate == null || candidate.length != expected.length)
75
+ return false;
76
+ let difference = 0;
77
+ for (let index = 0; index < expected.length; index += 1)
78
+ difference |= candidate.charCodeAt(index) ^ expected.charCodeAt(index);
79
+ return difference == 0;
80
+ }
81
+ function isRecord(value) {
82
+ return value != null && typeof value == "object" && !Array.isArray(value);
83
+ }
84
+ function id(value) {
85
+ if (!isRecord(value))
86
+ return;
87
+ return typeof value.id == "string" || typeof value.id == "number" ? String(value.id) : undefined;
88
+ }
89
+ function resolveChatId(value) {
90
+ if (!isRecord(value))
91
+ return;
92
+ return id(value.chat) ?? (isRecord(value.message) ? id(value.message.chat) : undefined);
93
+ }
94
+ function resolveUserId(value) {
95
+ if (!isRecord(value))
96
+ return;
97
+ return id(value.from) ?? id(value.user);
98
+ }
99
+ async function webhookUrl(context) {
100
+ const envelope = await request(context, "getWebhookInfo");
101
+ const url = isRecord(envelope.result) ? envelope.result.url : undefined;
102
+ if (typeof url != "string")
103
+ throw new TransientIntegrationError("Telegram getWebhookInfo response is missing result.url.");
104
+ return url;
105
+ }
106
+ async function deleteWebhook(context) {
107
+ await request(context, "deleteWebhook", { drop_pending_updates: false });
108
+ }
109
+ async function request(context, endpoint, body) {
110
+ let result;
111
+ try {
112
+ result = await context.connector.execute({
113
+ ...(body == null ? {} : { body }),
114
+ endpoint: `/${endpoint}`,
115
+ method: body == null ? "GET" : "POST",
116
+ }, context.signal);
117
+ }
118
+ catch (cause) {
119
+ if (cause instanceof IntegrationConnectionError)
120
+ throw cause;
121
+ throw new TransientIntegrationError(`Telegram ${endpoint} request failed.`, { cause });
122
+ }
123
+ const envelope = isRecord(result.data) ? result.data : {};
124
+ if (result.status >= 200 && result.status < 300 && envelope.ok === true)
125
+ return envelope;
126
+ const message = typeof envelope.description == "string" ? envelope.description : `HTTP ${result.status}`;
127
+ if (result.status == 401 || result.status == 403)
128
+ throw new IntegrationConnectionError(`Telegram ${endpoint} rejected the Connection.`);
129
+ if (result.status >= 400 && result.status < 500 && result.status != 429) {
130
+ throw new PermanentIntegrationError(`Telegram ${endpoint} rejected the subscription: ${message}`);
131
+ }
132
+ throw new TransientIntegrationError(`Telegram ${endpoint} failed: ${message}`);
133
+ }
134
+ function host(value) {
135
+ try {
136
+ return new URL(value).host;
137
+ }
138
+ catch {
139
+ return "an invalid URL";
140
+ }
141
+ }
@@ -45,7 +45,8 @@ export declare function isWechatTokenError(errcode: number | null): boolean;
45
45
  /**
46
46
  * Map a failed WeChat API call to the shared provider error. Rate limits
47
47
  * (45009/45011) become 429, the missing-permission 48001 becomes 403, the
48
- * 4xxxx client errcodes become 400, and everything else becomes a 502.
48
+ * 4xxxx client errcodes and the documented invalid-input store errcodes become
49
+ * 400, and everything else becomes a 502.
49
50
  */
50
51
  export declare function normalizeWechatApiError(result: WechatApiResult): ProviderRequestError;
51
52
  export {};
@@ -1,9 +1,19 @@
1
1
  import { createHash } from "node:crypto";
2
- import { optionalInteger, optionalNumber, optionalRecord, optionalString } from "../../core/cast.js";
2
+ import { optionalIntegerOrNull, optionalNumber, optionalRecord, optionalString } from "../../core/cast.js";
3
3
  import { ProviderRequestError, providerResponseError, providerUserAgent, requiredInputString, runProviderRequest, } from "../provider-runtime.js";
4
4
  const wechatStableTokenUrl = "https://api.weixin.qq.com/cgi-bin/stable_token";
5
5
  const defaultAccessTokenTtlSeconds = 7200;
6
6
  const accessTokenRefreshSkewMs = 120_000;
7
+ /**
8
+ * WeChat Store errcodes outside the 4xxxx range that the category and
9
+ * qualification docs describe as bad parameters or materials, such as an
10
+ * invalid or non-leaf category id, a mismatched certificate group, or a file id
11
+ * that did not come from the qualification upload. The caller has to fix the
12
+ * input, so they map to 400 instead of an upstream failure.
13
+ */
14
+ const wechatInvalidInputErrcodes = new Set([
15
+ 9401020, 9700234, 10020017, 10020059, 10020060, 10020062, 10020063, 10020087, 10020094, 10020127, 10020504,
16
+ ]);
7
17
  const accessTokenCache = new Map();
8
18
  const accessTokenInFlight = new Map();
9
19
  const accessTokenRefreshInFlight = new Map();
@@ -115,7 +125,7 @@ export function parseWechatJson(rawText) {
115
125
  }
116
126
  /** Read the numeric errcode of a WeChat envelope; absent or non-numeric reads as null. */
117
127
  export function readWechatErrcode(record) {
118
- return optionalInteger(record?.errcode) ?? null;
128
+ return optionalIntegerOrNull(record?.errcode);
119
129
  }
120
130
  /** Whether the errcode marks the access token as invalid, so the caller can refresh and retry once. */
121
131
  export function isWechatTokenError(errcode) {
@@ -124,7 +134,8 @@ export function isWechatTokenError(errcode) {
124
134
  /**
125
135
  * Map a failed WeChat API call to the shared provider error. Rate limits
126
136
  * (45009/45011) become 429, the missing-permission 48001 becomes 403, the
127
- * 4xxxx client errcodes become 400, and everything else becomes a 502.
137
+ * 4xxxx client errcodes and the documented invalid-input store errcodes become
138
+ * 400, and everything else becomes a 502.
128
139
  */
129
140
  export function normalizeWechatApiError(result) {
130
141
  const errcode = readWechatErrcode(result.record);
@@ -142,7 +153,7 @@ export function normalizeWechatApiError(result) {
142
153
  if (errcode === 48001) {
143
154
  return new ProviderRequestError(403, message, result.record);
144
155
  }
145
- if (errcode !== null && errcode >= 40000 && errcode < 50000) {
156
+ if (errcode !== null && ((errcode >= 40000 && errcode < 50000) || wechatInvalidInputErrcodes.has(errcode))) {
146
157
  return new ProviderRequestError(400, message, result.record);
147
158
  }
148
159
  if (result.status >= 400 && result.status < 500) {