@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,188 @@
1
+ import { IntegrationConnectionError, PermanentIntegrationError, TransientIntegrationError, } from "../../triggers/common/integration.js";
2
+ import { bytes, verifyBase64Hmac } from "../../triggers/signature.js";
3
+ import { snapshot, endpoint } from "./trigger-on-event.definition.js";
4
+ const signatureToleranceMs = 5 * 60 * 1_000;
5
+ export const zendeskEvent = {
6
+ initialState: { checkpoint: null, subscription: {} },
7
+ snapshot,
8
+ async receive(context) {
9
+ const signingSecret = subscriptionSecret(context.state);
10
+ const timestamp = context.header("x-zendesk-webhook-signature-timestamp");
11
+ const signedAt = timestamp == null ? Number.NaN : Date.parse(timestamp);
12
+ if (signingSecret == null ||
13
+ timestamp == null ||
14
+ !Number.isFinite(signedAt) ||
15
+ Math.abs(context.now.getTime() - signedAt) > signatureToleranceMs ||
16
+ !(await verifyBase64Hmac(signingSecret, [bytes(timestamp), context.rawBody], context.header("x-zendesk-webhook-signature")))) {
17
+ return { body: "", contentType: "text/plain", outcome: "respond", status: 404 };
18
+ }
19
+ if (context.payload == null || typeof context.payload != "object" || Array.isArray(context.payload)) {
20
+ return { outcome: "ignored", reason: "Zendesk event body is missing." };
21
+ }
22
+ const payload = context.payload;
23
+ const event = payload.type;
24
+ if (typeof event != "string" || event.length == 0)
25
+ return { outcome: "ignored", reason: "Zendesk event type is missing." };
26
+ if (!context.config.events.includes(event))
27
+ return { outcome: "ignored", reason: "Zendesk event is not subscribed." };
28
+ const deliveryId = typeof payload.id == "string" ? payload.id : "";
29
+ const subject = payload.subject;
30
+ return {
31
+ dedupeKey: deliveryId.length == 0 ? undefined : deliveryId,
32
+ outcome: "event",
33
+ outputs: {
34
+ body: payload,
35
+ deliveryId,
36
+ event,
37
+ ...(typeof subject == "string" ? { subject } : {}),
38
+ },
39
+ };
40
+ },
41
+ async reconcile(context) {
42
+ const state = requireState(context.state);
43
+ const listed = await findByEndpoint(context);
44
+ const known = subscriptionId(state);
45
+ if (!context.active) {
46
+ for (const webhookId of known == null ? listed : [...new Set([known, ...listed])])
47
+ await remove(context, webhookId);
48
+ await state.saveSubscription({}, later(context.now));
49
+ return { outcome: "ready" };
50
+ }
51
+ let webhookId = known ?? listed[0];
52
+ if (webhookId == null) {
53
+ const created = await request(context, "webhook create", {
54
+ body: desired(context),
55
+ endpoint,
56
+ method: "POST",
57
+ });
58
+ success(created, "webhook create");
59
+ webhookId = readId(record(created.data)?.webhook);
60
+ }
61
+ else {
62
+ const aligned = await request(context, "webhook update", {
63
+ body: desired(context),
64
+ endpoint: `${endpoint}/${webhookId}`,
65
+ method: "PUT",
66
+ });
67
+ if (aligned.status == 404) {
68
+ await state.saveSubscription({}, context.now);
69
+ return { outcome: "pending" };
70
+ }
71
+ success(aligned, "webhook update");
72
+ }
73
+ await state.saveSubscription({ ...state.subscription, webhookId }, context.now);
74
+ for (const duplicate of listed.filter((value) => value != webhookId))
75
+ await remove(context, duplicate);
76
+ await state.saveSubscription({ signingSecret: await readSigningSecret(context, webhookId), webhookId }, later(context.now));
77
+ return { outcome: "ready" };
78
+ },
79
+ };
80
+ function desired(context) {
81
+ return {
82
+ webhook: {
83
+ description: "Managed by Open Flow. Do not edit.",
84
+ endpoint: context.endpointUrl,
85
+ http_method: "POST",
86
+ name: name(context.endpointUrl),
87
+ request_format: "json",
88
+ status: "active",
89
+ subscriptions: [...new Set(context.config.events)],
90
+ },
91
+ };
92
+ }
93
+ async function findByEndpoint(context) {
94
+ const matches = [];
95
+ let after;
96
+ for (let page = 0; page < 10; page += 1) {
97
+ const result = await request(context, "webhook list", {
98
+ endpoint,
99
+ method: "GET",
100
+ query: {
101
+ "filter[name_contains]": name(context.endpointUrl),
102
+ "page[size]": "100",
103
+ ...(after == null ? {} : { "page[after]": after }),
104
+ },
105
+ });
106
+ success(result, "webhook list");
107
+ const listing = record(result.data);
108
+ const webhooks = Array.isArray(listing?.webhooks) ? listing.webhooks : [];
109
+ for (const item of webhooks) {
110
+ const value = record(item);
111
+ if (value?.endpoint === context.endpointUrl && typeof value.id == "string")
112
+ matches.push(value.id);
113
+ }
114
+ const meta = record(listing?.meta);
115
+ if (meta?.has_more !== true || typeof meta.after_cursor != "string")
116
+ return matches;
117
+ after = meta.after_cursor;
118
+ }
119
+ return matches;
120
+ }
121
+ async function remove(context, webhookId) {
122
+ const result = await request(context, "webhook delete", {
123
+ endpoint: `${endpoint}/${webhookId}`,
124
+ method: "DELETE",
125
+ });
126
+ if (result.status != 404)
127
+ success(result, "webhook 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(`Zendesk ${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(`Zendesk ${operation} rejected the Connection.`);
144
+ if ([400, 404, 422].includes(result.status))
145
+ throw new PermanentIntegrationError(`Zendesk ${operation} rejected the subscription.`);
146
+ throw new TransientIntegrationError(`Zendesk ${operation} failed with status ${result.status}.`);
147
+ }
148
+ function name(url) {
149
+ return `open-flow-${url.slice(url.lastIndexOf("/") + 1)}`;
150
+ }
151
+ function readId(value) {
152
+ const webhookId = record(value)?.id;
153
+ if (typeof webhookId != "string")
154
+ throw new TransientIntegrationError("Zendesk webhook response is missing its ID.");
155
+ return webhookId;
156
+ }
157
+ function requireState(value) {
158
+ if (value == null)
159
+ throw new PermanentIntegrationError("Zendesk Integration state is missing.");
160
+ return value;
161
+ }
162
+ function subscriptionId(state) {
163
+ const value = state.subscription.webhookId;
164
+ return typeof value == "string" && value.length > 0 ? value : null;
165
+ }
166
+ function subscriptionSecret(state) {
167
+ const value = state?.subscription.signingSecret;
168
+ return typeof value == "string" && value.length > 0 ? value : null;
169
+ }
170
+ async function readSigningSecret(context, webhookId) {
171
+ const result = await request(context, "webhook signing secret", {
172
+ endpoint: `${endpoint}/${webhookId}/signing_secret`,
173
+ method: "GET",
174
+ });
175
+ success(result, "webhook signing secret");
176
+ const secret = record(record(result.data)?.signing_secret)?.secret;
177
+ if (typeof secret != "string" || secret.length == 0)
178
+ throw new TransientIntegrationError("Zendesk webhook signing secret response is invalid.");
179
+ return secret;
180
+ }
181
+ function later(now) {
182
+ return new Date(now.getTime() + 30 * 24 * 60 * 60 * 1_000);
183
+ }
184
+ function record(value) {
185
+ return value != null && typeof value == "object" && !Array.isArray(value)
186
+ ? value
187
+ : undefined;
188
+ }
@@ -0,0 +1,26 @@
1
+ import type { RuntimeLogger } from "../core/types.ts";
2
+ import type { ConnectionRequestStore } from "../server/storage/connection-request-store.ts";
3
+ import type { SaasProjectStore } from "../server/storage/saas-project-store.ts";
4
+ import { SaasClient } from "./saas-client.ts";
5
+ export interface SaasCleanupServiceOptions {
6
+ store: SaasProjectStore;
7
+ requests: ConnectionRequestStore;
8
+ client?: SaasClient;
9
+ logger?: RuntimeLogger;
10
+ }
11
+ /** Resumable deletion of ordinary SaaS accounts; never creates or reconnects an account. */
12
+ export declare class SaasCleanupService {
13
+ private readonly options;
14
+ private readonly client;
15
+ private readonly shutdown;
16
+ private pending?;
17
+ private timer?;
18
+ private started;
19
+ constructor(options: SaasCleanupServiceOptions);
20
+ run(): Promise<void>;
21
+ /** Node owns this timer; Workers invoke run from their scheduled entry point instead. */
22
+ start(): void;
23
+ close(): Promise<void>;
24
+ private runBatch;
25
+ private nextAttempt;
26
+ }
@@ -0,0 +1,124 @@
1
+ import { SaasClient, SaasError } from "./saas-client.js";
2
+ /** Resumable deletion of ordinary SaaS accounts; never creates or reconnects an account. */
3
+ export class SaasCleanupService {
4
+ options;
5
+ client;
6
+ shutdown = new AbortController();
7
+ pending;
8
+ timer;
9
+ started = false;
10
+ constructor(options) {
11
+ this.options = options;
12
+ this.client = options.client ?? new SaasClient();
13
+ }
14
+ run() {
15
+ if (this.shutdown.signal.aborted)
16
+ return Promise.resolve();
17
+ this.pending ??= this.runBatch()
18
+ .catch(() => {
19
+ this.options.logger?.warn({ errorCode: "saas_cleanup_failed" }, "SaaS cleanup batch failed; pending work is retained");
20
+ })
21
+ .finally(() => {
22
+ this.pending = undefined;
23
+ });
24
+ return this.pending;
25
+ }
26
+ /** Node owns this timer; Workers invoke run from their scheduled entry point instead. */
27
+ start() {
28
+ if (this.started || this.shutdown.signal.aborted)
29
+ return;
30
+ this.started = true;
31
+ const tick = () => {
32
+ void this.run().finally(() => {
33
+ if (this.shutdown.signal.aborted)
34
+ return;
35
+ this.timer = setTimeout(tick, 60_000);
36
+ this.timer.unref?.();
37
+ });
38
+ };
39
+ tick();
40
+ }
41
+ async close() {
42
+ this.shutdown.abort();
43
+ clearTimeout(this.timer);
44
+ await this.pending;
45
+ }
46
+ async runBatch() {
47
+ await this.options.requests.expireSaas();
48
+ const deadline = Date.now() + 50_000;
49
+ for (let count = 0; count < 10 && Date.now() < deadline && !this.shutdown.signal.aborted; count++) {
50
+ const project = await this.options.store.getProject();
51
+ if (!project)
52
+ return;
53
+ const task = await this.options.store.claimCleanup(Date.now());
54
+ if (!task)
55
+ return;
56
+ const signal = AbortSignal.any([
57
+ this.shutdown.signal,
58
+ AbortSignal.timeout(Math.max(1, Math.min(30_000, deadline - Date.now()))),
59
+ ]);
60
+ try {
61
+ if (project.id !== task.managedProjectId)
62
+ throw new SaasError("oauth_source_mismatch", "Cleanup project identity does not match.", 409);
63
+ let accountId = task.connectedAccountId;
64
+ if (!accountId) {
65
+ if (!task.remoteRequestId) {
66
+ await this.options.store.retryCleanup(task, Date.now(), "oauth_source_result_unknown", true);
67
+ continue;
68
+ }
69
+ const result = await this.client.getRequest(project, task.remoteRequestId, signal);
70
+ if (result.providerConfigId !== task.providerConfigId ||
71
+ result.externalUserId !== task.externalUserId ||
72
+ result.service !== task.service ||
73
+ result.alias !== `connect-${task.id.slice("request:".length)}`)
74
+ throw new SaasError("oauth_source_mismatch", "Cleanup request identity does not match.", 409);
75
+ if (result.status === "initiated") {
76
+ const expired = Date.now() >= Date.parse(task.requestExpiresAt ?? result.expiresAt) + 86_400_000;
77
+ await this.options.store.retryCleanup(task, Date.now() + 60_000, expired ? "oauth_source_result_unknown" : "authorization_pending", expired);
78
+ continue;
79
+ }
80
+ if (result.status !== "connected") {
81
+ await this.options.store.finishCleanup(task);
82
+ continue;
83
+ }
84
+ accountId = result.connectedAccountId;
85
+ if (!(await this.options.store.saveCleanupAccount(task, accountId)))
86
+ continue;
87
+ task.connectedAccountId = accountId;
88
+ }
89
+ signal.throwIfAborted();
90
+ await this.client.deleteAccount(project, {
91
+ providerConfigId: task.providerConfigId,
92
+ externalUserId: task.externalUserId,
93
+ connectedAccountId: accountId,
94
+ }, signal);
95
+ await this.options.store.finishCleanup(task);
96
+ }
97
+ catch (error) {
98
+ const code = error instanceof SaasError ? error.code : "saas_cleanup_failed";
99
+ if (code === "oauth_source_unauthorized")
100
+ await this.options.store.pauseCleanup(task, project.apiKey);
101
+ const manual = code === "oauth_source_mismatch" ||
102
+ code === "oauth_source_protocol_error" ||
103
+ (code === "oauth_source_not_found" && !task.connectedAccountId);
104
+ await this.options.store.retryCleanup(task, this.nextAttempt(task, error), code === "oauth_source_not_found" && !task.connectedAccountId ? "oauth_source_result_unknown" : code, manual);
105
+ if (!this.shutdown.signal.aborted)
106
+ this.options.logger?.warn({ taskId: task.id, errorCode: code, manual }, code === "oauth_source_unauthorized"
107
+ ? "SaaS cleanup paused; update the project key to resume"
108
+ : "SaaS cleanup deferred");
109
+ if (code === "oauth_source_unauthorized" || this.shutdown.signal.aborted)
110
+ return;
111
+ }
112
+ }
113
+ }
114
+ nextAttempt(task, error) {
115
+ let delay = Math.min(300_000, 60_000 * 2 ** Math.min(task.attempts, 3));
116
+ if (error instanceof SaasError && error.retryAfter) {
117
+ const seconds = Number(error.retryAfter);
118
+ const until = Number.isFinite(seconds) ? Date.now() + seconds * 1_000 : Date.parse(error.retryAfter);
119
+ if (Number.isFinite(until))
120
+ delay = Math.max(delay, until - Date.now());
121
+ }
122
+ return Date.now() + delay;
123
+ }
124
+ }
@@ -0,0 +1,86 @@
1
+ import type { ProxyRequestInput, ProxyResponse } from "../core/types.ts";
2
+ import type { ManagedProject } from "../server/storage/saas-project-store.ts";
3
+ export interface SaasProviderConfig {
4
+ id: string;
5
+ service: string;
6
+ displayName: string;
7
+ callbackUrl: string;
8
+ effectiveScopes: string[];
9
+ actionIds: string[];
10
+ proxyAvailable: boolean;
11
+ }
12
+ export interface SaasDiscovery {
13
+ projectId: string;
14
+ providerConfigs: SaasProviderConfig[];
15
+ }
16
+ export interface SaasAccount extends SaasAccountSelector {
17
+ projectId: string;
18
+ service: string;
19
+ alias: string | null;
20
+ status: "active" | "reauth_required" | "error" | "disconnected";
21
+ providerAccountId: string | null;
22
+ accountLabel: string | null;
23
+ scopes: string[];
24
+ }
25
+ export interface SaasConnectionRequest {
26
+ id: string;
27
+ status: "initiated" | "connected" | "failed" | "expired";
28
+ projectId: string;
29
+ providerConfigId: string;
30
+ externalUserId: string;
31
+ service: string;
32
+ alias: string | null;
33
+ authorizationUrl: string;
34
+ connectedAccountId: string | null;
35
+ errorCode: string | null;
36
+ errorMessage: string | null;
37
+ expiresAt: string;
38
+ createdAt: number;
39
+ updatedAt: number;
40
+ }
41
+ export declare class SaasError extends Error {
42
+ readonly code: string;
43
+ readonly status: 400 | 402 | 403 | 404 | 409 | 413 | 429 | 500 | 501 | 502 | 503 | 504;
44
+ readonly retryAfter?: string;
45
+ readonly reason?: string;
46
+ connectionRequestId?: string;
47
+ remoteExecutionId?: string;
48
+ constructor(code: string, message: string, status?: SaasError["status"], retryAfter?: string, reason?: string);
49
+ }
50
+ export interface SaasAccountSelector {
51
+ providerConfigId: string;
52
+ externalUserId: string;
53
+ connectedAccountId: string;
54
+ }
55
+ export interface SaasLinkInput {
56
+ providerConfigId: string;
57
+ externalUserId: string;
58
+ alias: string;
59
+ returnUri: string;
60
+ }
61
+ export interface SaasActionResult {
62
+ executionId: string;
63
+ actionId: string;
64
+ output: unknown;
65
+ }
66
+ export interface SaasProxyResult {
67
+ executionId: string;
68
+ response: ProxyResponse;
69
+ }
70
+ /** Reject values that the project proxy protocol cannot represent before any remote request. */
71
+ export declare function parseSaasProxyRequest(input: unknown): Promise<ProxyRequestInput>;
72
+ /** The project API never supplies credentials to the local provider runtime. */
73
+ export declare class SaasClient {
74
+ private readonly fetcher?;
75
+ constructor(fetcher?: typeof fetch);
76
+ discover(project: Pick<ManagedProject, "baseUrl" | "apiKey">, signal?: AbortSignal): Promise<SaasDiscovery>;
77
+ getAccount(project: ManagedProject, selector: SaasAccountSelector, service: string, signal?: AbortSignal): Promise<SaasAccount>;
78
+ getRequest(project: ManagedProject, id: string, signal?: AbortSignal): Promise<SaasConnectionRequest>;
79
+ createLink(project: ManagedProject, input: SaasLinkInput, signal?: AbortSignal): Promise<SaasConnectionRequest>;
80
+ deleteAccount(project: ManagedProject, selector: SaasAccountSelector, signal?: AbortSignal): Promise<void>;
81
+ executeAction(project: ManagedProject, selector: SaasAccountSelector, actionId: string, input: unknown, signal?: AbortSignal): Promise<SaasActionResult>;
82
+ executeProxy(project: ManagedProject, selector: SaasAccountSelector, service: string, request: ProxyRequestInput, signal?: AbortSignal): Promise<SaasProxyResult>;
83
+ private request;
84
+ private requestEnvelope;
85
+ }
86
+ export declare function normalizeSaasBaseUrl(value: string): string;