@oomol-lab/open-flow 0.1.0-beta.73 → 0.1.0-beta.74

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 (432) hide show
  1. package/LICENSE +36 -0
  2. package/NOTICE +7 -0
  3. package/README.md +40 -0
  4. package/dist/browser/{createResourceDialog-BWJkjiOO.js → createResourceDialog-DI9pL-Yr.js} +2 -2
  5. package/dist/browser/{dialog-CUy_fwx9.js → dialog-BYJGvItc.js} +54 -19
  6. package/dist/browser/{flowChanges-CgSnSqqP.js → flowChanges-RL-H4CjD.js} +306 -48
  7. package/dist/browser/{flowWorkspace-Bf7H-r0t.js → flowWorkspace-BkDsETj0.js} +20745 -20186
  8. package/dist/browser/preview.css +73 -3
  9. package/dist/browser/preview.js +21410 -20607
  10. package/dist/browser/{typeScriptSession-B0hC-C8y.js → typeScriptSession-CSRwWmZm.js} +1 -1
  11. package/dist/browser/ui.css +72 -2
  12. package/dist/browser/workbench.css +73 -3
  13. package/dist/browser/workbench.js +14 -76
  14. package/dist/browser/{workbenchSelect-C3e4J93X.js → workbenchSelect-BcWPWGzw.js} +1 -1
  15. package/dist/command/index.js +34 -34
  16. package/dist/common/authoring.d.ts +1 -1
  17. package/dist/common/authoringFields.d.ts +1 -1
  18. package/dist/common/authoringSchema.d.ts +28 -4
  19. package/dist/common/connector-proxy.d.ts +1 -1
  20. package/dist/common/control-api-conformance.d.ts +1 -1
  21. package/dist/common/control-api-conformance.js +61 -24
  22. package/dist/common/control-api.d.ts +4 -3
  23. package/dist/common/control-api.js +59 -22
  24. package/dist/common/control-requests.d.ts +2 -1
  25. package/dist/common/control-requests.js +204 -42
  26. package/dist/common/cron-trigger.d.ts +1 -1
  27. package/dist/common/decision.d.ts +1 -1
  28. package/dist/common/draftOperations.d.ts +1 -1
  29. package/dist/common/engine-contract.d.ts +3 -2
  30. package/dist/common/event-source-definition.d.ts +1 -1
  31. package/dist/common/flow-agent.d.ts +1 -1
  32. package/dist/{browser → common}/flow-authoring-node.d.ts +3 -3
  33. package/dist/{browser → common}/flow-authoring.js +75 -23
  34. package/dist/{browser → common}/flow-change-schema.d.ts +177 -6
  35. package/dist/{browser → common}/flow-change.d.ts +35 -15
  36. package/dist/{browser → common}/flow-change.js +77 -25
  37. package/dist/common/flow-connectionUsage.d.ts +1 -1
  38. package/dist/common/flow-encoding.d.ts +1 -1
  39. package/dist/common/flow-encoding.js +34 -16
  40. package/dist/common/flow-errorHandling.d.ts +1 -1
  41. package/dist/common/flow-graph.d.ts +1 -1
  42. package/dist/common/flow-modules.d.ts +1 -1
  43. package/dist/common/flow-promptTemplate.d.ts +1 -1
  44. package/dist/common/flow-retry.d.ts +1 -1
  45. package/dist/common/flow-semantics.d.ts +1 -1
  46. package/dist/common/flow-semantics.js +151 -32
  47. package/dist/common/flow-workflowFile.d.ts +1 -1
  48. package/dist/common/flowInspection.d.ts +3 -3
  49. package/dist/common/integration-trigger.d.ts +1 -1
  50. package/dist/common/licenses.md +118 -1
  51. package/dist/common/liveDetails.d.ts +1 -1
  52. package/dist/common/mcp.d.ts +2 -1
  53. package/dist/common/mcp.js +59 -22
  54. package/dist/common/openapi.d.ts +1 -1
  55. package/dist/common/poll-trigger.d.ts +1 -1
  56. package/dist/common/run-results.d.ts +8 -1
  57. package/dist/common/runtime-contract.d.ts +8 -2
  58. package/dist/common/runtime-contract.js +22 -5
  59. package/dist/common/scheduler-checkpoint.d.ts +1 -1
  60. package/dist/common/scheduler-dependencies.d.ts +1 -1
  61. package/dist/common/scheduler-node-execution.d.ts +1 -1
  62. package/dist/common/scheduler.d.ts +2 -2
  63. package/dist/common/scheduler.js +31 -12
  64. package/dist/common/trigger-catalog.d.ts +1 -1
  65. package/dist/common/trigger-config-options.d.ts +1 -1
  66. package/dist/common/trigger-config.d.ts +1 -1
  67. package/dist/common/trigger-contract.d.ts +2 -2
  68. package/dist/common/trigger-localization.d.ts +1 -1
  69. package/dist/common/webhook-response.d.ts +1 -1
  70. package/dist/common/webhook-trigger.d.ts +1 -1
  71. package/dist/common/workflow.d.ts +17 -1
  72. package/dist/common/workflow.js +68 -22
  73. package/dist/index.d.ts +6 -3
  74. package/package.json +7 -6
  75. package/src/base/common/json.ts +90 -0
  76. package/src/canvas/common/commands.ts +15 -0
  77. package/src/connector/common/actionSchema.ts +168 -0
  78. package/src/connector/common/proxy.ts +44 -0
  79. package/src/control/common/api.ts +1505 -0
  80. package/src/control/common/authoring.ts +912 -0
  81. package/src/control/common/authoringCondition.ts +110 -0
  82. package/src/control/common/authoringDiagnosticFields.ts +136 -0
  83. package/src/control/common/authoringExamples.ts +192 -0
  84. package/src/control/common/authoringFields.ts +53 -0
  85. package/src/control/common/authoringLlm.ts +70 -0
  86. package/src/control/common/authoringOpenApi.ts +129 -0
  87. package/src/control/common/authoringSchema.ts +404 -0
  88. package/src/control/common/authoringTools.ts +123 -0
  89. package/src/control/common/authoringTriggers.ts +26 -0
  90. package/src/control/common/checkDecoders.ts +65 -0
  91. package/src/control/common/conformance.ts +1989 -0
  92. package/src/control/common/connectorDecoders.ts +370 -0
  93. package/src/control/common/connectorQueries.ts +61 -0
  94. package/src/control/common/decoding.ts +60 -0
  95. package/src/control/common/draftOperations.ts +61 -0
  96. package/src/control/common/errors.ts +114 -0
  97. package/src/control/common/eventSourceSchemas.ts +13 -0
  98. package/src/control/common/eventSources.ts +186 -0
  99. package/src/control/common/flowDecoders.ts +153 -0
  100. package/src/control/common/flowInspection.ts +181 -0
  101. package/src/control/common/flowNotifications.ts +37 -0
  102. package/src/control/common/liveDetails.ts +124 -0
  103. package/src/control/common/mcp.ts +241 -0
  104. package/src/control/common/mcpConformance.ts +142 -0
  105. package/src/control/common/providerAccess.ts +28 -0
  106. package/src/control/common/providerIconSprite.ts +42 -0
  107. package/src/control/common/publicationDecoders.ts +98 -0
  108. package/src/control/common/random.ts +16 -0
  109. package/src/control/common/requests.ts +112 -0
  110. package/src/control/common/resultQuery.ts +8 -0
  111. package/src/control/common/results.ts +176 -0
  112. package/src/control/common/revisionDecoders.ts +52 -0
  113. package/src/control/common/runDecoders.ts +240 -0
  114. package/src/control/common/triggerCatalog.ts +89 -0
  115. package/src/control/common/triggerDecoders.ts +175 -0
  116. package/src/decision/common/decision.ts +168 -0
  117. package/src/event-source/common/catalog.generated.json +65 -0
  118. package/src/event-source/common/definition.ts +66 -0
  119. package/src/event-source/common/index.ts +99 -0
  120. package/src/execution/common/checkpoint.ts +311 -0
  121. package/src/execution/common/dependencies.ts +440 -0
  122. package/src/execution/common/engineContract.ts +80 -0
  123. package/src/execution/common/events.ts +328 -0
  124. package/src/execution/common/node-execution.ts +467 -0
  125. package/src/execution/common/runLifecycle.ts +250 -0
  126. package/src/execution/common/runtime.ts +563 -0
  127. package/src/execution/common/scheduler.ts +696 -0
  128. package/src/execution/common/wait.ts +36 -0
  129. package/src/flow/common/agent.ts +305 -0
  130. package/src/flow/common/authoring.ts +30 -0
  131. package/src/flow/common/change.ts +1036 -0
  132. package/src/flow/common/changeSchema.ts +783 -0
  133. package/src/flow/common/codePermissions.ts +2 -0
  134. package/src/flow/common/condition.ts +265 -0
  135. package/src/flow/common/connectionUsage.ts +131 -0
  136. package/src/flow/common/edgeChanges.ts +35 -0
  137. package/src/flow/common/encoding.ts +437 -0
  138. package/src/flow/common/errorHandling.ts +34 -0
  139. package/src/flow/common/graph.ts +892 -0
  140. package/src/flow/common/inputValue.ts +16 -0
  141. package/src/flow/common/json.ts +8 -0
  142. package/src/flow/common/moduleChanges.ts +31 -0
  143. package/src/flow/common/modules.ts +292 -0
  144. package/src/flow/common/nodeChanges.ts +762 -0
  145. package/src/flow/common/portChanges.ts +36 -0
  146. package/src/flow/common/promptTemplate.ts +15 -0
  147. package/src/flow/common/retry.ts +20 -0
  148. package/src/flow/common/schema.ts +162 -0
  149. package/src/flow/common/schemaCompare.ts +176 -0
  150. package/src/flow/common/schemaComparer.ts +178 -0
  151. package/src/flow/common/schemaKeywords.ts +53 -0
  152. package/src/flow/common/semantics.ts +474 -0
  153. package/src/flow/common/sourceField.ts +35 -0
  154. package/src/flow/common/triggerScheduleSchema.ts +8 -0
  155. package/src/flow/common/wait.ts +30 -0
  156. package/src/flow/common/webhookMethod.ts +7 -0
  157. package/src/flow/common/webhookResponse.ts +75 -0
  158. package/src/flow/common/workflowFile.ts +97 -0
  159. package/src/json-schema-subset/checker/array.ts +142 -0
  160. package/src/json-schema-subset/checker/cache.ts +57 -0
  161. package/src/json-schema-subset/checker/checker.ts +230 -0
  162. package/src/json-schema-subset/checker/enumAndConst.ts +113 -0
  163. package/src/json-schema-subset/checker/equals.ts +93 -0
  164. package/src/json-schema-subset/checker/numeric/checker.ts +50 -0
  165. package/src/json-schema-subset/checker/numeric/float.ts +121 -0
  166. package/src/json-schema-subset/checker/numeric/index.ts +1 -0
  167. package/src/json-schema-subset/checker/numeric/integer.ts +119 -0
  168. package/src/json-schema-subset/checker/numeric/integerNormalizer.ts +72 -0
  169. package/src/json-schema-subset/checker/object.ts +354 -0
  170. package/src/json-schema-subset/checker/range.ts +91 -0
  171. package/src/json-schema-subset/checker/string.ts +60 -0
  172. package/src/json-schema-subset/checker/tools/index.ts +3 -0
  173. package/src/json-schema-subset/checker/tools/mark.ts +82 -0
  174. package/src/json-schema-subset/checker/tools/quantum.ts +106 -0
  175. package/src/json-schema-subset/checker/tools/range.ts +210 -0
  176. package/src/json-schema-subset/checker/validator.ts +23 -0
  177. package/src/json-schema-subset/compiler/array.ts +163 -0
  178. package/src/json-schema-subset/compiler/combination.ts +109 -0
  179. package/src/json-schema-subset/compiler/common.ts +99 -0
  180. package/src/json-schema-subset/compiler/compiler.ts +188 -0
  181. package/src/json-schema-subset/compiler/enumAndConst.ts +75 -0
  182. package/src/json-schema-subset/compiler/index.ts +11 -0
  183. package/src/json-schema-subset/compiler/object.ts +411 -0
  184. package/src/json-schema-subset/compiler/primitive.ts +144 -0
  185. package/src/json-schema-subset/compiler/utils.ts +24 -0
  186. package/src/json-schema-subset/compiler/wrapper.ts +192 -0
  187. package/src/json-schema-subset/error.ts +55 -0
  188. package/src/json-schema-subset/expression/calculator.ts +442 -0
  189. package/src/json-schema-subset/expression/combination.ts +49 -0
  190. package/src/json-schema-subset/expression/expression.ts +191 -0
  191. package/src/json-schema-subset/expression/index.ts +25 -0
  192. package/src/json-schema-subset/expression/result.ts +88 -0
  193. package/src/json-schema-subset/expression/types.ts +36 -0
  194. package/src/json-schema-subset/extends/extendsContext.ts +89 -0
  195. package/src/json-schema-subset/extends/index.ts +2 -0
  196. package/src/json-schema-subset/extends/types.ts +12 -0
  197. package/src/json-schema-subset/global.ts +60 -0
  198. package/src/json-schema-subset/index.ts +8 -0
  199. package/src/json-schema-subset/preprocessing.ts +74 -0
  200. package/src/json-schema-subset/types.ts +8 -0
  201. package/src/json-schema-subset/utils.ts +95 -0
  202. package/src/localization/common/languages.ts +118 -0
  203. package/src/localization/common/timeZone.ts +25 -0
  204. package/src/openapi/common/authBindings.ts +12 -0
  205. package/src/openapi/common/openapi.ts +459 -0
  206. package/src/trigger/common/config.ts +56 -0
  207. package/src/trigger/common/configOptions.ts +15 -0
  208. package/src/trigger/common/configSchema.ts +8 -0
  209. package/src/trigger/common/contract.ts +132 -0
  210. package/src/trigger/common/cron.ts +151 -0
  211. package/src/trigger/common/integration.ts +498 -0
  212. package/src/trigger/common/poll.ts +187 -0
  213. package/src/trigger/common/webhook.ts +412 -0
  214. package/src/trigger/providers/airtable/locales/fr.json +18 -0
  215. package/src/trigger/providers/airtable/locales/ja.json +18 -0
  216. package/src/trigger/providers/airtable/locales/ko.json +18 -0
  217. package/src/trigger/providers/airtable/locales/ru.json +18 -0
  218. package/src/trigger/providers/airtable/locales/zh-CN.json +18 -0
  219. package/src/trigger/providers/airtable/locales/zh-TW.json +18 -0
  220. package/src/trigger/providers/asana/locales/fr.json +15 -0
  221. package/src/trigger/providers/asana/locales/ja.json +15 -0
  222. package/src/trigger/providers/asana/locales/ko.json +15 -0
  223. package/src/trigger/providers/asana/locales/ru.json +15 -0
  224. package/src/trigger/providers/asana/locales/zh-CN.json +15 -0
  225. package/src/trigger/providers/asana/locales/zh-TW.json +15 -0
  226. package/src/trigger/providers/calendly/locales/fr.json +16 -0
  227. package/src/trigger/providers/calendly/locales/ja.json +16 -0
  228. package/src/trigger/providers/calendly/locales/ko.json +16 -0
  229. package/src/trigger/providers/calendly/locales/ru.json +16 -0
  230. package/src/trigger/providers/calendly/locales/zh-CN.json +16 -0
  231. package/src/trigger/providers/calendly/locales/zh-TW.json +16 -0
  232. package/src/trigger/providers/catalog.generated.json +4996 -0
  233. package/src/trigger/providers/clickup/locales/fr.json +16 -0
  234. package/src/trigger/providers/clickup/locales/ja.json +16 -0
  235. package/src/trigger/providers/clickup/locales/ko.json +16 -0
  236. package/src/trigger/providers/clickup/locales/ru.json +16 -0
  237. package/src/trigger/providers/clickup/locales/zh-CN.json +16 -0
  238. package/src/trigger/providers/clickup/locales/zh-TW.json +16 -0
  239. package/src/trigger/providers/definitions.ts +19 -0
  240. package/src/trigger/providers/feishu/locales/fr.json +20 -0
  241. package/src/trigger/providers/feishu/locales/ja.json +20 -0
  242. package/src/trigger/providers/feishu/locales/ko.json +20 -0
  243. package/src/trigger/providers/feishu/locales/ru.json +20 -0
  244. package/src/trigger/providers/feishu/locales/zh-CN.json +20 -0
  245. package/src/trigger/providers/feishu/locales/zh-TW.json +20 -0
  246. package/src/trigger/providers/fieldDescriptions.ts +392 -0
  247. package/src/trigger/providers/fieldLabels.ts +256 -0
  248. package/src/trigger/providers/fireflies/locales/fr.json +17 -0
  249. package/src/trigger/providers/fireflies/locales/ja.json +17 -0
  250. package/src/trigger/providers/fireflies/locales/ko.json +17 -0
  251. package/src/trigger/providers/fireflies/locales/ru.json +17 -0
  252. package/src/trigger/providers/fireflies/locales/zh-CN.json +17 -0
  253. package/src/trigger/providers/fireflies/locales/zh-TW.json +17 -0
  254. package/src/trigger/providers/github/locales/fr.json +30 -0
  255. package/src/trigger/providers/github/locales/ja.json +30 -0
  256. package/src/trigger/providers/github/locales/ko.json +30 -0
  257. package/src/trigger/providers/github/locales/ru.json +30 -0
  258. package/src/trigger/providers/github/locales/zh-CN.json +30 -0
  259. package/src/trigger/providers/github/locales/zh-TW.json +30 -0
  260. package/src/trigger/providers/gitlab/locales/fr.json +18 -0
  261. package/src/trigger/providers/gitlab/locales/ja.json +18 -0
  262. package/src/trigger/providers/gitlab/locales/ko.json +18 -0
  263. package/src/trigger/providers/gitlab/locales/ru.json +18 -0
  264. package/src/trigger/providers/gitlab/locales/zh-CN.json +18 -0
  265. package/src/trigger/providers/gitlab/locales/zh-TW.json +18 -0
  266. package/src/trigger/providers/gmail/locales/fr.json +18 -0
  267. package/src/trigger/providers/gmail/locales/ja.json +18 -0
  268. package/src/trigger/providers/gmail/locales/ko.json +18 -0
  269. package/src/trigger/providers/gmail/locales/ru.json +18 -0
  270. package/src/trigger/providers/gmail/locales/zh-CN.json +18 -0
  271. package/src/trigger/providers/gmail/locales/zh-TW.json +18 -0
  272. package/src/trigger/providers/google-calendar/locales/fr.json +15 -0
  273. package/src/trigger/providers/google-calendar/locales/ja.json +15 -0
  274. package/src/trigger/providers/google-calendar/locales/ko.json +15 -0
  275. package/src/trigger/providers/google-calendar/locales/ru.json +15 -0
  276. package/src/trigger/providers/google-calendar/locales/zh-CN.json +15 -0
  277. package/src/trigger/providers/google-calendar/locales/zh-TW.json +15 -0
  278. package/src/trigger/providers/google-drive/locales/fr.json +48 -0
  279. package/src/trigger/providers/google-drive/locales/ja.json +48 -0
  280. package/src/trigger/providers/google-drive/locales/ko.json +48 -0
  281. package/src/trigger/providers/google-drive/locales/ru.json +48 -0
  282. package/src/trigger/providers/google-drive/locales/zh-CN.json +48 -0
  283. package/src/trigger/providers/google-drive/locales/zh-TW.json +48 -0
  284. package/src/trigger/providers/google-sheets/locales/fr.json +19 -0
  285. package/src/trigger/providers/google-sheets/locales/ja.json +19 -0
  286. package/src/trigger/providers/google-sheets/locales/ko.json +19 -0
  287. package/src/trigger/providers/google-sheets/locales/ru.json +19 -0
  288. package/src/trigger/providers/google-sheets/locales/zh-CN.json +19 -0
  289. package/src/trigger/providers/google-sheets/locales/zh-TW.json +19 -0
  290. package/src/trigger/providers/intercom/locales/fr.json +17 -0
  291. package/src/trigger/providers/intercom/locales/ja.json +17 -0
  292. package/src/trigger/providers/intercom/locales/ko.json +17 -0
  293. package/src/trigger/providers/intercom/locales/ru.json +17 -0
  294. package/src/trigger/providers/intercom/locales/zh-CN.json +17 -0
  295. package/src/trigger/providers/intercom/locales/zh-TW.json +17 -0
  296. package/src/trigger/providers/jira/locales/fr.json +17 -0
  297. package/src/trigger/providers/jira/locales/ja.json +17 -0
  298. package/src/trigger/providers/jira/locales/ko.json +17 -0
  299. package/src/trigger/providers/jira/locales/ru.json +17 -0
  300. package/src/trigger/providers/jira/locales/zh-CN.json +17 -0
  301. package/src/trigger/providers/jira/locales/zh-TW.json +17 -0
  302. package/src/trigger/providers/linear/locales/fr.json +13 -0
  303. package/src/trigger/providers/linear/locales/ja.json +13 -0
  304. package/src/trigger/providers/linear/locales/ko.json +13 -0
  305. package/src/trigger/providers/linear/locales/ru.json +13 -0
  306. package/src/trigger/providers/linear/locales/zh-CN.json +13 -0
  307. package/src/trigger/providers/linear/locales/zh-TW.json +13 -0
  308. package/src/trigger/providers/locales/fr.ts +71 -0
  309. package/src/trigger/providers/locales/ja.ts +71 -0
  310. package/src/trigger/providers/locales/ko.ts +71 -0
  311. package/src/trigger/providers/locales/ru.ts +71 -0
  312. package/src/trigger/providers/locales/zh-CN.ts +71 -0
  313. package/src/trigger/providers/locales/zh-TW.ts +71 -0
  314. package/src/trigger/providers/localization.ts +59 -0
  315. package/src/trigger/providers/notion/locales/fr.json +16 -0
  316. package/src/trigger/providers/notion/locales/ja.json +16 -0
  317. package/src/trigger/providers/notion/locales/ko.json +16 -0
  318. package/src/trigger/providers/notion/locales/ru.json +16 -0
  319. package/src/trigger/providers/notion/locales/zh-CN.json +16 -0
  320. package/src/trigger/providers/notion/locales/zh-TW.json +16 -0
  321. package/src/trigger/providers/one-drive/locales/fr.json +16 -0
  322. package/src/trigger/providers/one-drive/locales/ja.json +16 -0
  323. package/src/trigger/providers/one-drive/locales/ko.json +16 -0
  324. package/src/trigger/providers/one-drive/locales/ru.json +16 -0
  325. package/src/trigger/providers/one-drive/locales/zh-CN.json +16 -0
  326. package/src/trigger/providers/one-drive/locales/zh-TW.json +16 -0
  327. package/src/trigger/providers/outlook/locales/fr.json +18 -0
  328. package/src/trigger/providers/outlook/locales/ja.json +18 -0
  329. package/src/trigger/providers/outlook/locales/ko.json +18 -0
  330. package/src/trigger/providers/outlook/locales/ru.json +18 -0
  331. package/src/trigger/providers/outlook/locales/zh-CN.json +18 -0
  332. package/src/trigger/providers/outlook/locales/zh-TW.json +18 -0
  333. package/src/trigger/providers/pandadoc/locales/fr.json +12 -0
  334. package/src/trigger/providers/pandadoc/locales/ja.json +12 -0
  335. package/src/trigger/providers/pandadoc/locales/ko.json +12 -0
  336. package/src/trigger/providers/pandadoc/locales/ru.json +12 -0
  337. package/src/trigger/providers/pandadoc/locales/zh-CN.json +12 -0
  338. package/src/trigger/providers/pandadoc/locales/zh-TW.json +12 -0
  339. package/src/trigger/providers/pipedrive/locales/fr.json +15 -0
  340. package/src/trigger/providers/pipedrive/locales/ja.json +15 -0
  341. package/src/trigger/providers/pipedrive/locales/ko.json +15 -0
  342. package/src/trigger/providers/pipedrive/locales/ru.json +15 -0
  343. package/src/trigger/providers/pipedrive/locales/zh-CN.json +15 -0
  344. package/src/trigger/providers/pipedrive/locales/zh-TW.json +15 -0
  345. package/src/trigger/providers/remote.ts +163 -0
  346. package/src/trigger/providers/resend/locales/fr.json +16 -0
  347. package/src/trigger/providers/resend/locales/ja.json +16 -0
  348. package/src/trigger/providers/resend/locales/ko.json +16 -0
  349. package/src/trigger/providers/resend/locales/ru.json +16 -0
  350. package/src/trigger/providers/resend/locales/zh-CN.json +16 -0
  351. package/src/trigger/providers/resend/locales/zh-TW.json +16 -0
  352. package/src/trigger/providers/sentry/locales/fr.json +19 -0
  353. package/src/trigger/providers/sentry/locales/ja.json +19 -0
  354. package/src/trigger/providers/sentry/locales/ko.json +19 -0
  355. package/src/trigger/providers/sentry/locales/ru.json +19 -0
  356. package/src/trigger/providers/sentry/locales/zh-CN.json +19 -0
  357. package/src/trigger/providers/sentry/locales/zh-TW.json +19 -0
  358. package/src/trigger/providers/shopify/locales/fr.json +18 -0
  359. package/src/trigger/providers/shopify/locales/ja.json +18 -0
  360. package/src/trigger/providers/shopify/locales/ko.json +18 -0
  361. package/src/trigger/providers/shopify/locales/ru.json +18 -0
  362. package/src/trigger/providers/shopify/locales/zh-CN.json +18 -0
  363. package/src/trigger/providers/shopify/locales/zh-TW.json +18 -0
  364. package/src/trigger/providers/slack/locales/fr.json +18 -0
  365. package/src/trigger/providers/slack/locales/ja.json +18 -0
  366. package/src/trigger/providers/slack/locales/ko.json +18 -0
  367. package/src/trigger/providers/slack/locales/ru.json +18 -0
  368. package/src/trigger/providers/slack/locales/zh-CN.json +18 -0
  369. package/src/trigger/providers/slack/locales/zh-TW.json +18 -0
  370. package/src/trigger/providers/slackbot/locales/fr.json +23 -0
  371. package/src/trigger/providers/slackbot/locales/ja.json +23 -0
  372. package/src/trigger/providers/slackbot/locales/ko.json +23 -0
  373. package/src/trigger/providers/slackbot/locales/ru.json +23 -0
  374. package/src/trigger/providers/slackbot/locales/zh-CN.json +23 -0
  375. package/src/trigger/providers/slackbot/locales/zh-TW.json +23 -0
  376. package/src/trigger/providers/source-events.ts +25 -0
  377. package/src/trigger/providers/stripe/locales/fr.json +17 -0
  378. package/src/trigger/providers/stripe/locales/ja.json +17 -0
  379. package/src/trigger/providers/stripe/locales/ko.json +17 -0
  380. package/src/trigger/providers/stripe/locales/ru.json +17 -0
  381. package/src/trigger/providers/stripe/locales/zh-CN.json +17 -0
  382. package/src/trigger/providers/stripe/locales/zh-TW.json +17 -0
  383. package/src/trigger/providers/tally/locales/fr.json +12 -0
  384. package/src/trigger/providers/tally/locales/ja.json +12 -0
  385. package/src/trigger/providers/tally/locales/ko.json +12 -0
  386. package/src/trigger/providers/tally/locales/ru.json +12 -0
  387. package/src/trigger/providers/tally/locales/zh-CN.json +12 -0
  388. package/src/trigger/providers/tally/locales/zh-TW.json +12 -0
  389. package/src/trigger/providers/telegram/locales/fr.json +17 -0
  390. package/src/trigger/providers/telegram/locales/ja.json +17 -0
  391. package/src/trigger/providers/telegram/locales/ko.json +17 -0
  392. package/src/trigger/providers/telegram/locales/ru.json +17 -0
  393. package/src/trigger/providers/telegram/locales/zh-CN.json +17 -0
  394. package/src/trigger/providers/telegram/locales/zh-TW.json +17 -0
  395. package/src/trigger/providers/typeform/locales/fr.json +12 -0
  396. package/src/trigger/providers/typeform/locales/ja.json +12 -0
  397. package/src/trigger/providers/typeform/locales/ko.json +12 -0
  398. package/src/trigger/providers/typeform/locales/ru.json +12 -0
  399. package/src/trigger/providers/typeform/locales/zh-CN.json +12 -0
  400. package/src/trigger/providers/typeform/locales/zh-TW.json +12 -0
  401. package/src/trigger/providers/vercel/locales/fr.json +18 -0
  402. package/src/trigger/providers/vercel/locales/ja.json +18 -0
  403. package/src/trigger/providers/vercel/locales/ko.json +18 -0
  404. package/src/trigger/providers/vercel/locales/ru.json +18 -0
  405. package/src/trigger/providers/vercel/locales/zh-CN.json +18 -0
  406. package/src/trigger/providers/vercel/locales/zh-TW.json +18 -0
  407. package/src/trigger/providers/woocommerce/locales/fr.json +19 -0
  408. package/src/trigger/providers/woocommerce/locales/ja.json +19 -0
  409. package/src/trigger/providers/woocommerce/locales/ko.json +19 -0
  410. package/src/trigger/providers/woocommerce/locales/ru.json +19 -0
  411. package/src/trigger/providers/woocommerce/locales/zh-CN.json +19 -0
  412. package/src/trigger/providers/woocommerce/locales/zh-TW.json +19 -0
  413. package/src/trigger/providers/zendesk/locales/fr.json +15 -0
  414. package/src/trigger/providers/zendesk/locales/ja.json +15 -0
  415. package/src/trigger/providers/zendesk/locales/ko.json +15 -0
  416. package/src/trigger/providers/zendesk/locales/ru.json +15 -0
  417. package/src/trigger/providers/zendesk/locales/zh-CN.json +15 -0
  418. package/src/trigger/providers/zendesk/locales/zh-TW.json +15 -0
  419. package/src/trigger/providers/zoom/locales/fr.json +17 -0
  420. package/src/trigger/providers/zoom/locales/ja.json +17 -0
  421. package/src/trigger/providers/zoom/locales/ko.json +17 -0
  422. package/src/trigger/providers/zoom/locales/ru.json +17 -0
  423. package/src/trigger/providers/zoom/locales/zh-CN.json +17 -0
  424. package/src/trigger/providers/zoom/locales/zh-TW.json +17 -0
  425. package/src/types/index.ts +112 -0
  426. package/src/workflow/common/workflow.ts +273 -0
  427. package/dist/browser/lib-DrKav3HG.js +0 -11403
  428. /package/dist/{browser → common}/flow-authoring-edge.d.ts +0 -0
  429. /package/dist/{browser → common}/flow-authoring-module.d.ts +0 -0
  430. /package/dist/{browser → common}/flow-authoring.d.ts +0 -0
  431. /package/dist/{browser → common}/flow-input-value.d.ts +0 -0
  432. /package/dist/{browser → common}/webhook-method.d.ts +0 -0
@@ -0,0 +1,151 @@
1
+ import type { JsonValue, TriggerSchedule } from '../../flow/common/change.ts'
2
+
3
+ import { Cron } from 'croner'
4
+
5
+ const minuteMs = 60_000
6
+ const hourMs = 60 * minuteMs
7
+ const dayMs = 24 * hourMs
8
+ const weekAnchorMs = 4 * dayMs
9
+ const maximumIntervalMs = 366 * dayMs
10
+ const encoder = new TextEncoder()
11
+
12
+ const unitMs = {
13
+ day: dayMs,
14
+ hour: hourMs,
15
+ minute: minuteMs,
16
+ week: 7 * dayMs,
17
+ } as const
18
+
19
+ export function validateTriggerSchedule(rules: readonly TriggerSchedule[]): void {
20
+ if (rules.length == 0) throw new TypeError('At least one schedule rule is required.')
21
+ for (const rule of rules) {
22
+ if (rule.type == 'every') {
23
+ if (!Number.isSafeInteger(rule.value) || rule.value < 1) throw new TypeError('Every rule value must be a positive integer.')
24
+ if ((rule.unit == 'month' && rule.value > 12) || (rule.unit != 'month' && rule.value * unitMs[rule.unit] > maximumIntervalMs)) {
25
+ throw new TypeError('Every rule interval must not exceed one year.')
26
+ }
27
+ continue
28
+ }
29
+ try {
30
+ Intl.DateTimeFormat('en-US', { timeZone: rule.timezone })
31
+ } catch {
32
+ throw new TypeError(`Invalid IANA timezone: ${rule.timezone}`)
33
+ }
34
+ validateCronExpression(rule.expression, rule.timezone)
35
+ }
36
+ }
37
+
38
+ export function validateCronExpression(expression: string, timezone: string): void {
39
+ if (expression.trim().split(/\s+/).length != 5) throw new TypeError('Cron expression must have exactly five fields.')
40
+ let cron: Cron
41
+ try {
42
+ cron = new Cron(expression, { timezone })
43
+ } catch (error) {
44
+ throw new TypeError(`Invalid cron expression: ${error instanceof Error ? error.message : String(error)}`, { cause: error })
45
+ }
46
+ if (cron.nextRun() == null) throw new TypeError('Cron expression has no future occurrence.')
47
+ }
48
+
49
+ export function nextTriggerScheduledAt(rules: readonly TriggerSchedule[], afterMs: number): number {
50
+ let earliest = Infinity
51
+ for (const rule of rules) earliest = Math.min(earliest, nextForRule(rule, afterMs))
52
+ if (!Number.isFinite(earliest)) throw new Error('Schedule has no future occurrence.')
53
+ return earliest
54
+ }
55
+
56
+ export async function scheduledTriggerOccurrenceId(bindingId: string, runtimeVersion: number, scheduledAt: string): Promise<string> {
57
+ const value = encoder.encode(`${bindingId}\0${runtimeVersion}\0${scheduledAt}`)
58
+ const digest = await crypto.subtle.digest('SHA-256', value)
59
+ return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')}`
60
+ }
61
+
62
+ export interface CronConformanceFixture {
63
+ readonly publishedAt: string
64
+ readonly rules: readonly TriggerSchedule[]
65
+ }
66
+
67
+ export interface CronConformanceHarness {
68
+ dispose(): Promise<void>
69
+ outputs(): Promise<readonly Readonly<Record<string, JsonValue>>[]>
70
+ republish(at: string, rules: readonly TriggerSchedule[]): Promise<void>
71
+ retire(at: string): Promise<void>
72
+ tick(at: string): Promise<void>
73
+ }
74
+
75
+ export interface CronConformanceCase {
76
+ readonly fixture: CronConformanceFixture
77
+ readonly name: string
78
+ verify(harness: CronConformanceHarness): Promise<void>
79
+ }
80
+
81
+ const hourly: readonly TriggerSchedule[] = [{ type: 'every', unit: 'hour', value: 1 }]
82
+ const minutely: readonly TriggerSchedule[] = [{ type: 'every', unit: 'minute', value: 1 }]
83
+
84
+ export const cronConformanceCases: readonly CronConformanceCase[] = [
85
+ {
86
+ fixture: { publishedAt: '2026-08-21T00:30:00.000Z', rules: hourly },
87
+ name: 'admits an exact schedule grid once',
88
+ async verify(harness) {
89
+ await harness.tick('2026-08-21T01:00:00.000Z')
90
+ await harness.tick('2026-08-21T01:00:00.000Z')
91
+ equal(await harness.outputs(), [{ scheduledAt: '2026-08-21T01:00:00.000Z' }], 'Exact schedule outputs')
92
+ },
93
+ },
94
+ {
95
+ fixture: { publishedAt: '2026-08-21T00:00:30.000Z', rules: minutely },
96
+ name: 'coalesces missed grids and preserves the earliest due time',
97
+ async verify(harness) {
98
+ await harness.tick('2026-08-21T00:03:30.000Z')
99
+ equal(await harness.outputs(), [{ scheduledAt: '2026-08-21T00:01:00.000Z' }], 'Delayed schedule outputs')
100
+ await harness.tick('2026-08-21T00:04:00.000Z')
101
+ equal(await harness.outputs(), [{ scheduledAt: '2026-08-21T00:01:00.000Z' }, { scheduledAt: '2026-08-21T00:04:00.000Z' }], 'Advanced schedule outputs')
102
+ },
103
+ },
104
+ {
105
+ fixture: { publishedAt: '2026-08-21T00:00:30.000Z', rules: minutely },
106
+ name: 'resets the schedule when its publication changes',
107
+ async verify(harness) {
108
+ await harness.republish('2026-08-21T00:00:45.000Z', hourly)
109
+ await harness.tick('2026-08-21T00:01:00.000Z')
110
+ equal(await harness.outputs(), [], 'Outputs from the previous publication')
111
+ await harness.tick('2026-08-21T01:00:00.000Z')
112
+ equal(await harness.outputs(), [{ scheduledAt: '2026-08-21T01:00:00.000Z' }], 'Republished schedule outputs')
113
+ },
114
+ },
115
+ {
116
+ fixture: { publishedAt: '2026-08-21T00:30:00.000Z', rules: hourly },
117
+ name: 'stops admitting runs after retirement',
118
+ async verify(harness) {
119
+ await harness.retire('2026-08-21T00:45:00.000Z')
120
+ await harness.tick('2026-08-21T01:00:00.000Z')
121
+ equal(await harness.outputs(), [], 'Retired schedule outputs')
122
+ },
123
+ },
124
+ ]
125
+
126
+ function nextForRule(rule: TriggerSchedule, afterMs: number): number {
127
+ if (rule.type == 'cron') {
128
+ const next = new Cron(rule.expression, { timezone: rule.timezone }).nextRun(new Date(afterMs))
129
+ return next?.getTime() ?? Infinity
130
+ }
131
+ if (rule.unit == 'month') {
132
+ const after = new Date(afterMs)
133
+ const monthIndex = (after.getUTCFullYear() - 1970) * 12 + after.getUTCMonth()
134
+ let gridMonth = Math.floor(monthIndex / rule.value) * rule.value
135
+ let candidate = Date.UTC(1970, gridMonth, 1)
136
+ while (candidate <= afterMs) {
137
+ gridMonth += rule.value
138
+ candidate = Date.UTC(1970, gridMonth, 1)
139
+ }
140
+ return candidate
141
+ }
142
+ const period = rule.value * unitMs[rule.unit]
143
+ const anchor = rule.unit == 'week' ? weekAnchorMs : 0
144
+ return anchor + (Math.floor((afterMs - anchor) / period) + 1) * period
145
+ }
146
+
147
+ function equal(actual: unknown, expected: unknown, message: string): void {
148
+ if (JSON.stringify(actual) != JSON.stringify(expected)) {
149
+ throw new Error(`${message}: expected ${JSON.stringify(expected)}, received ${JSON.stringify(actual)}.`)
150
+ }
151
+ }
@@ -0,0 +1,498 @@
1
+ export { resolveTriggerConfig, triggerConfigValues } from './config.ts'
2
+ import type { ConnectorProxy } from '../../connector/common/proxy.ts'
3
+ import type { IntegrationEndpointMethod, JsonValue, TriggerKeySnapshot } from '../../flow/common/change.ts'
4
+ import type { TriggerConfigOption, TriggerConfigOptionsContext } from './configOptions.ts'
5
+
6
+ import { dequal } from 'dequal/lite'
7
+ import { isJsonObject, isJsonValue } from '../../base/common/json.ts'
8
+
9
+ export const maximumIntegrationBodyBytes = 64 * 1024
10
+ export const maximumIntegrationDeliveryPages = 5
11
+
12
+ const endpointPattern = /^\/v1\/integrations\/(endpoint_[0-9a-f]{32})$/
13
+ const encoder = new TextEncoder()
14
+
15
+ export interface ListenerReadContext {
16
+ readonly checkpoint: JsonValue
17
+ readonly config: Readonly<Record<string, JsonValue>>
18
+ readonly connector: ConnectorProxy
19
+ readonly now: Date
20
+ readonly signal?: AbortSignal
21
+ }
22
+
23
+ export interface ListenerPage {
24
+ readonly checkpoint: JsonValue
25
+ readonly dedupeKey: string
26
+ readonly hasMore: boolean
27
+ readonly outputs: Readonly<Record<string, JsonValue>> | null
28
+ }
29
+
30
+ export interface ListenerSource {
31
+ readonly intervalMs: number
32
+ readonly read: (context: ListenerReadContext) => Promise<ListenerPage>
33
+ }
34
+
35
+ export function validateListenerPage(value: unknown): asserts value is ListenerPage {
36
+ if (!isJsonObject(value) || value.checkpoint == null || !isJsonValue(value.checkpoint) || typeof value.hasMore != 'boolean') {
37
+ throw new TypeError('Listener page must contain a JSON checkpoint and a boolean hasMore.')
38
+ }
39
+ if (typeof value.dedupeKey != 'string' || value.dedupeKey.length == 0 || encoder.encode(value.dedupeKey).byteLength > 1024) {
40
+ throw new TypeError('Listener page identity must contain between 1 and 1024 bytes.')
41
+ }
42
+ if (value.outputs !== null && !isJsonObject(value.outputs)) throw new TypeError('Listener page outputs must be an object or null.')
43
+ if (encoder.encode(JSON.stringify(value.checkpoint)).byteLength > 64 * 1024) {
44
+ throw new TypeError('Listener checkpoint exceeds 64 KiB.')
45
+ }
46
+ }
47
+
48
+ export type IntegrationReceiveResult =
49
+ | { readonly outcome: 'wake' }
50
+ | {
51
+ readonly checkpoint?: JsonValue
52
+ readonly continue?: boolean
53
+ readonly dedupeKey?: string
54
+ readonly outcome: 'event'
55
+ readonly outputs: Readonly<Record<string, JsonValue>>
56
+ }
57
+ | { readonly checkpoint?: JsonValue; readonly continue?: boolean; readonly outcome: 'ignored'; readonly reason: string }
58
+ | {
59
+ readonly body: string
60
+ readonly contentType: string
61
+ readonly headers?: Readonly<Record<string, string>>
62
+ readonly outcome: 'respond'
63
+ readonly status: number
64
+ }
65
+
66
+ export interface IntegrationStateContext {
67
+ readonly checkpoint: JsonValue
68
+ readonly subscription: Readonly<Record<string, JsonValue>>
69
+ readonly saveCheckpoint: (checkpoint: JsonValue) => Promise<void>
70
+ readonly saveSubscription: (subscription: Readonly<Record<string, JsonValue>>, reconcileAt: Date) => Promise<void>
71
+ }
72
+
73
+ export interface IntegrationReceiveContext {
74
+ readonly eventSourceId?: string
75
+ readonly signal?: AbortSignal
76
+ readonly admit: boolean
77
+ readonly allow?: () => Promise<boolean>
78
+ readonly bindingId: string
79
+ readonly callbackSecret: string
80
+ readonly config: Readonly<Record<string, JsonValue>>
81
+ readonly connector: ConnectorProxy
82
+ readonly current: boolean
83
+ readonly header: (name: string) => string | undefined
84
+ readonly method: IntegrationEndpointMethod
85
+ readonly now: Date
86
+ readonly payload: JsonValue
87
+ readonly query: (name: string) => string | undefined
88
+ readonly rawBody: Uint8Array
89
+ readonly state?: IntegrationStateContext
90
+ }
91
+
92
+ export interface IntegrationReconcileContext {
93
+ readonly active: boolean
94
+ readonly callbackSecret: string
95
+ readonly config: Readonly<Record<string, JsonValue>>
96
+ readonly connector: ConnectorProxy
97
+ readonly endpointUrl: string
98
+ readonly idempotencyKey: string
99
+ readonly now: Date
100
+ readonly signal?: AbortSignal
101
+ readonly state?: IntegrationStateContext
102
+ }
103
+
104
+ export interface IntegrationReconcileResult {
105
+ readonly outcome: 'pending' | 'ready'
106
+ }
107
+
108
+ export interface IntegrationDefinition {
109
+ readonly eventSource?: 'feishu' | 'slack' | 'intercom' | 'sentry' | 'zoom' | 'fireflies' | 'jira'
110
+ readonly configOptions?: (context: TriggerConfigOptionsContext) => Promise<readonly TriggerConfigOption[]>
111
+ readonly listener?: ListenerSource
112
+ readonly initialState?: { readonly checkpoint: JsonValue; readonly subscription: Readonly<Record<string, JsonValue>> }
113
+ readonly receive: (context: IntegrationReceiveContext) => IntegrationReceiveResult | Promise<IntegrationReceiveResult>
114
+ readonly reconcile: (context: IntegrationReconcileContext) => Promise<IntegrationReconcileResult>
115
+ readonly snapshot: TriggerKeySnapshot & { readonly type: 'integration' }
116
+ }
117
+
118
+ export class IntegrationConnectionError extends Error {
119
+ override readonly name = 'IntegrationConnectionError'
120
+ }
121
+
122
+ export class PermanentIntegrationError extends Error {
123
+ override readonly name = 'PermanentIntegrationError'
124
+ }
125
+
126
+ export class TransientIntegrationError extends Error {
127
+ override readonly name = 'TransientIntegrationError'
128
+ }
129
+
130
+ export function integrationEndpointId(url: URL): string | undefined {
131
+ return endpointPattern.exec(url.pathname)?.[1]
132
+ }
133
+
134
+ export async function integrationCallbackSecret(callbackKey: string | undefined, endpointId: string): Promise<string> {
135
+ if (callbackKey == null || callbackKey.length == 0) throw new TypeError('Integration callback key is required.')
136
+ const key = await crypto.subtle.importKey('raw', encoder.encode(callbackKey), { hash: 'SHA-256', name: 'HMAC' }, false, ['sign'])
137
+ const signature = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(`open-flow-integration/v1/${endpointId}`)))
138
+ return btoa(String.fromCharCode(...signature))
139
+ .replaceAll('+', '-')
140
+ .replaceAll('/', '_')
141
+ .replaceAll('=', '')
142
+ }
143
+
144
+ export async function integrationOccurrenceId(bindingId: string, runtimeVersion: number, definitionKey: string, dedupeKey: string | null): Promise<string> {
145
+ if (dedupeKey == null) return `integration_${crypto.randomUUID().replaceAll('-', '')}`
146
+ const source = JSON.stringify([1, 'integration-occurrence', bindingId, runtimeVersion, definitionKey, dedupeKey])
147
+ const bytes = new Uint8Array(await crypto.subtle.digest('SHA-256', encoder.encode(source)))
148
+ return [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('')
149
+ }
150
+
151
+ export interface IntegrationConformanceFixture {
152
+ readonly config: Readonly<Record<string, JsonValue>>
153
+ readonly connectionId: string
154
+ readonly definition: Pick<IntegrationDefinition, 'initialState' | 'listener' | 'receive' | 'reconcile'>
155
+ readonly publishedAt: string
156
+ }
157
+
158
+ export interface IntegrationConformanceState {
159
+ readonly checkpoint: JsonValue | null
160
+ readonly health: 'healthy' | 'initializing'
161
+ readonly payloads: readonly JsonValue[]
162
+ readonly receiveCalls: number
163
+ readonly reconcileCalls: number
164
+ readonly runtimeVersion: number
165
+ readonly subscription: Readonly<Record<string, JsonValue>> | null
166
+ }
167
+
168
+ export interface IntegrationConformanceHarness {
169
+ readonly callbackSecret: string
170
+ readonly endpointUrl: string
171
+ dispose(): Promise<void>
172
+ reconcile(at: string): Promise<void>
173
+ republish(at: string): Promise<void>
174
+ request(request: Request): Promise<Response>
175
+ restart(): Promise<void>
176
+ retire(at: string): Promise<void>
177
+ state(): Promise<IntegrationConformanceState>
178
+ }
179
+
180
+ export interface IntegrationConformanceCase {
181
+ readonly fixture: IntegrationConformanceFixture
182
+ readonly name: string
183
+ verify(harness: IntegrationConformanceHarness): Promise<void>
184
+ }
185
+
186
+ const conformanceDefinition: IntegrationConformanceFixture['definition'] = {
187
+ initialState: { checkpoint: null, subscription: {} },
188
+ receive(context): IntegrationReceiveResult {
189
+ if (context.header('x-integration-secret') != context.callbackSecret) {
190
+ return { body: '', contentType: 'text/plain', outcome: 'respond', status: 404 }
191
+ }
192
+ const value = record(context.payload)
193
+ if (value?.action == 'handshake') {
194
+ return {
195
+ body: 'accepted',
196
+ contentType: 'text/plain',
197
+ headers: { 'x-integration-response': 'conformance' },
198
+ outcome: 'respond',
199
+ status: 201,
200
+ }
201
+ }
202
+ if (value?.action == 'ignored') return { outcome: 'ignored', reason: 'Conformance delivery was ignored.' }
203
+ if (value?.action == 'pages') {
204
+ const checkpoint = record(context.state?.checkpoint)
205
+ const page = typeof checkpoint?.page == 'number' ? checkpoint.page + 1 : 1
206
+ const pages = typeof value.pages == 'number' ? value.pages : 1
207
+ const deliveryId = typeof value.deliveryId == 'string' ? value.deliveryId : 'pages'
208
+ return {
209
+ checkpoint: { page },
210
+ continue: page < pages,
211
+ dedupeKey: `${deliveryId}:${page}`,
212
+ outcome: 'event',
213
+ outputs: { payload: { body: { page }, deliveryId, event: 'conformance' } },
214
+ }
215
+ }
216
+ const deliveryId = typeof value?.deliveryId == 'string' ? value.deliveryId : 'delivery'
217
+ const body = record(value?.body) ?? {}
218
+ return {
219
+ checkpoint: { deliveryId },
220
+ dedupeKey: deliveryId,
221
+ outcome: 'event',
222
+ outputs: { payload: { body, deliveryId, event: 'conformance' } },
223
+ }
224
+ },
225
+ async reconcile(context) {
226
+ if (context.state == null) throw new PermanentIntegrationError('Conformance Integration state is missing.')
227
+ await context.state.saveSubscription(context.active ? { endpointUrl: context.endpointUrl } : {}, new Date(context.now.getTime() + 60_000))
228
+ return { outcome: 'ready' }
229
+ },
230
+ }
231
+
232
+ const fixture: IntegrationConformanceFixture = {
233
+ config: { source: 'primary' },
234
+ connectionId: 'connection-primary',
235
+ definition: conformanceDefinition,
236
+ publishedAt: '2026-08-21T00:00:30.000Z',
237
+ }
238
+
239
+ export const integrationConformanceCases: readonly IntegrationConformanceCase[] = [
240
+ {
241
+ fixture,
242
+ name: 'enforces callback routing, method, body, and handshake boundaries',
243
+ async verify(harness) {
244
+ const base = new URL(harness.endpointUrl)
245
+ await response(await call(harness, { url: new URL('/v1/integrations/not-an-endpoint', base).href }), { status: 404 }, 'Malformed endpoint')
246
+ await response(
247
+ await call(harness, { url: new URL('/v1/integrations/endpoint_00000000000000000000000000000000', base).href }),
248
+ { status: 404 },
249
+ 'Unknown endpoint',
250
+ )
251
+ await response(await call(harness, { method: 'GET' }), { headers: { allow: 'POST' }, status: 405 }, 'Method')
252
+ await response(await call(harness, { body: '{' }), { status: 400 }, 'Invalid JSON')
253
+ await response(
254
+ await call(harness, { body: 'a=1', headers: { 'content-type': 'application/x-www-form-urlencoded' } }),
255
+ { status: 400 },
256
+ 'Unsupported body format',
257
+ )
258
+ await response(await call(harness, { body: 'x'.repeat(maximumIntegrationBodyBytes + 1) }), { status: 413 }, 'Oversized body')
259
+ await response(
260
+ await delivery(harness, { action: 'handshake' }),
261
+ { body: 'accepted', headers: { 'content-type': 'text/plain', 'x-integration-response': 'conformance' }, status: 201 },
262
+ 'Handshake',
263
+ )
264
+ },
265
+ },
266
+ {
267
+ fixture,
268
+ name: 'verifies initializing callbacks before activating the subscription',
269
+ async verify(harness) {
270
+ await response(await delivery(harness, { action: 'event', body: { value: 'before' }, deliveryId: 'before' }, false), { status: 404 }, 'Secret')
271
+ await response(await delivery(harness, { action: 'event', body: { value: 'before' }, deliveryId: 'before' }), { status: 202 }, 'Initializing delivery')
272
+ equal(
273
+ await harness.state(),
274
+ {
275
+ checkpoint: null,
276
+ health: 'initializing',
277
+ payloads: [],
278
+ receiveCalls: 2,
279
+ reconcileCalls: 0,
280
+ runtimeVersion: 1,
281
+ subscription: {},
282
+ },
283
+ 'Initializing state',
284
+ )
285
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
286
+ equal(
287
+ await harness.state(),
288
+ {
289
+ checkpoint: null,
290
+ health: 'healthy',
291
+ payloads: [],
292
+ receiveCalls: 2,
293
+ reconcileCalls: 1,
294
+ runtimeVersion: 1,
295
+ subscription: { endpointUrl: harness.endpointUrl },
296
+ },
297
+ 'Active subscription',
298
+ )
299
+ },
300
+ },
301
+ {
302
+ fixture,
303
+ name: 'deduplicates keyed events and rejects conflicting payloads',
304
+ async verify(harness) {
305
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
306
+ const first = { action: 'event', body: { value: 'first' }, deliveryId: 'delivery-1' }
307
+ await response(await delivery(harness, first), { status: 202 }, 'Initial event')
308
+ await response(await delivery(harness, first), { status: 202 }, 'Repeated event')
309
+ await response(await delivery(harness, { ...first, body: { value: 'changed' } }), { status: 409 }, 'Conflicting event')
310
+ const state = await harness.state()
311
+ equal(state.checkpoint, { deliveryId: 'delivery-1' }, 'Event checkpoint')
312
+ equal(state.payloads, [{ body: { value: 'first' }, deliveryId: 'delivery-1', event: 'conformance' }], 'Event payloads')
313
+ },
314
+ },
315
+ {
316
+ fixture,
317
+ name: 'bounds callback pages and resumes from the durable checkpoint',
318
+ async verify(harness) {
319
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
320
+ const pages = { action: 'pages', deliveryId: 'delivery-pages', pages: 7 }
321
+ await response(await delivery(harness, pages), { status: 202 }, 'First page batch')
322
+ let state = await harness.state()
323
+ equal(state.checkpoint, { page: 5 }, 'Bounded checkpoint')
324
+ equal(state.payloads.length, 5, 'Bounded payload count')
325
+ await response(await delivery(harness, pages), { status: 202 }, 'Resumed page batch')
326
+ state = await harness.state()
327
+ equal(state.checkpoint, { page: 7 }, 'Resumed checkpoint')
328
+ equal(state.payloads.length, 7, 'Resumed payload count')
329
+ },
330
+ },
331
+ {
332
+ fixture,
333
+ name: 'restores subscription state and callback admission after restart',
334
+ async verify(harness) {
335
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
336
+ await delivery(harness, { action: 'event', body: { value: 'before' }, deliveryId: 'before' })
337
+ await harness.restart()
338
+ await delivery(harness, { action: 'event', body: { value: 'after' }, deliveryId: 'after' })
339
+ const state = await harness.state()
340
+ equal(state.checkpoint, { deliveryId: 'after' }, 'Restart checkpoint')
341
+ equal(
342
+ state.payloads,
343
+ [
344
+ { body: { value: 'before' }, deliveryId: 'before', event: 'conformance' },
345
+ { body: { value: 'after' }, deliveryId: 'after', event: 'conformance' },
346
+ ],
347
+ 'Restart payloads',
348
+ )
349
+ },
350
+ },
351
+ {
352
+ fixture,
353
+ name: 'retires the old runtime before activating a republished target',
354
+ async verify(harness) {
355
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
356
+ const event = { action: 'event', body: { value: 'event' }, deliveryId: 'same-delivery' }
357
+ await delivery(harness, event)
358
+ const endpointUrl = harness.endpointUrl
359
+ await harness.republish('2026-08-21T00:00:40.000Z')
360
+ equal(harness.endpointUrl, endpointUrl, 'Republished endpoint')
361
+ await response(await delivery(harness, event), { status: 202 }, 'Old runtime delivery')
362
+ equal((await harness.state()).payloads.length, 1, 'Old runtime payload count')
363
+ await harness.reconcile('2026-08-21T00:00:41.000Z')
364
+ await response(await delivery(harness, event), { status: 202 }, 'New runtime delivery')
365
+ const state = await harness.state()
366
+ equal(state.runtimeVersion, 2, 'Republished runtime')
367
+ equal(state.health, 'healthy', 'Republished health')
368
+ equal(state.payloads.length, 2, 'Republished payload count')
369
+ },
370
+ },
371
+ {
372
+ fixture,
373
+ name: 'retires the remote subscription and rejects later callbacks',
374
+ async verify(harness) {
375
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
376
+ await harness.retire('2026-08-21T00:00:40.000Z')
377
+ await response(await delivery(harness, { action: 'event', deliveryId: 'retired' }), { status: 404 }, 'Retired callback')
378
+ await harness.reconcile('2026-08-21T00:00:41.000Z')
379
+ await response(await delivery(harness, { action: 'event', deliveryId: 'retired' }), { status: 404 }, 'Reconciled retirement')
380
+ equal((await harness.state()).payloads, [], 'Retired payloads')
381
+ },
382
+ },
383
+ ]
384
+
385
+ const listenerFixture: IntegrationConformanceFixture = {
386
+ ...fixture,
387
+ definition: {
388
+ initialState: { checkpoint: null, subscription: {} },
389
+ async reconcile({ active, state, now }) {
390
+ if (active && state?.checkpoint == null) await state!.saveCheckpoint(0)
391
+ if (active) await state!.saveSubscription({}, new Date(now.getTime() + 3_600_000))
392
+ return { outcome: 'ready' }
393
+ },
394
+ receive(context) {
395
+ return context.header('x-integration-secret') == context.callbackSecret
396
+ ? { outcome: 'wake' }
397
+ : { outcome: 'respond', status: 404, body: '', contentType: 'text/plain' }
398
+ },
399
+ listener: {
400
+ intervalMs: 60_000,
401
+ async read({ checkpoint, now }) {
402
+ const cursor = Number(checkpoint)
403
+ const available = now.getTime() >= Date.parse('2026-08-21T00:01:31.000Z') ? 8 : 7
404
+ return cursor < available
405
+ ? {
406
+ checkpoint: cursor + 1,
407
+ dedupeKey: String(cursor),
408
+ hasMore: cursor + 1 < available,
409
+ outputs: { payload: { body: {}, deliveryId: String(cursor), event: 'change' } },
410
+ }
411
+ : { checkpoint, dedupeKey: String(cursor), hasMore: false, outputs: null }
412
+ },
413
+ },
414
+ },
415
+ }
416
+
417
+ export const listenerConformanceCases: readonly IntegrationConformanceCase[] = [
418
+ {
419
+ fixture: listenerFixture,
420
+ name: 'drains listener pages without a callback and resumes periodic scanning after restart',
421
+ async verify(harness) {
422
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
423
+ equal((await harness.state()).checkpoint, 7, 'Drained cursor')
424
+ equal((await harness.state()).payloads.length, 7, 'Drained pages')
425
+ await harness.restart()
426
+ await harness.reconcile('2026-08-21T00:01:31.000Z')
427
+ equal((await harness.state()).checkpoint, 8, 'Periodic cursor')
428
+ equal((await harness.state()).payloads.length, 8, 'Periodic pages')
429
+ },
430
+ },
431
+ {
432
+ fixture: listenerFixture,
433
+ name: 'persists verified listener wakes across restart without duplicating accepted pages',
434
+ async verify(harness) {
435
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
436
+ await response(await delivery(harness, {}, false), { status: 404 }, 'Invalid wake')
437
+ await response(await delivery(harness, {}), { status: 202 }, 'First wake')
438
+ await response(await delivery(harness, {}), { status: 202 }, 'Duplicate wake')
439
+ await harness.restart()
440
+ await harness.reconcile('2026-08-21T00:00:32.000Z')
441
+ equal((await harness.state()).checkpoint, 7, 'Retained cursor')
442
+ equal((await harness.state()).payloads.length, 7, 'Deduplicated pages')
443
+ },
444
+ },
445
+ {
446
+ fixture: listenerFixture,
447
+ name: 'preserves a listener cursor on unchanged publication and fences retirement',
448
+ async verify(harness) {
449
+ await harness.reconcile('2026-08-21T00:00:31.000Z')
450
+ await harness.republish('2026-08-21T00:00:40.000Z')
451
+ equal((await harness.state()).checkpoint, 7, 'Republished cursor')
452
+ await harness.reconcile('2026-08-21T00:01:31.000Z')
453
+ equal((await harness.state()).payloads.length, 8, 'Republished pages')
454
+ await harness.retire('2026-08-21T00:01:40.000Z')
455
+ await response(await delivery(harness, {}), { status: 404 }, 'Retired wake')
456
+ await harness.reconcile('2026-08-21T00:02:40.000Z')
457
+ equal((await harness.state()).payloads.length, 8, 'Retired pages')
458
+ },
459
+ },
460
+ ]
461
+
462
+ function call(
463
+ harness: IntegrationConformanceHarness,
464
+ input: { readonly body?: string; readonly headers?: Readonly<Record<string, string>>; readonly method?: string; readonly url?: string } = {},
465
+ ): Promise<Response> {
466
+ return harness.request(
467
+ new Request(input.url ?? harness.endpointUrl, {
468
+ ...(input.body == null ? {} : { body: input.body }),
469
+ headers: { ...(input.body == null ? {} : { 'content-type': 'application/json' }), ...input.headers },
470
+ method: input.method ?? 'POST',
471
+ }),
472
+ )
473
+ }
474
+
475
+ function delivery(harness: IntegrationConformanceHarness, body: Readonly<Record<string, JsonValue>>, authenticated = true): Promise<Response> {
476
+ return call(harness, {
477
+ body: JSON.stringify(body),
478
+ headers: authenticated ? { 'x-integration-secret': harness.callbackSecret } : {},
479
+ })
480
+ }
481
+
482
+ function equal(actual: unknown, expected: unknown, message: string): void {
483
+ if (!dequal(actual, expected)) throw new Error(`${message}: expected ${JSON.stringify(expected)}, received ${JSON.stringify(actual)}.`)
484
+ }
485
+
486
+ function record(value: unknown): Readonly<Record<string, JsonValue>> | undefined {
487
+ return value != null && typeof value == 'object' && !Array.isArray(value) ? (value as Readonly<Record<string, JsonValue>>) : undefined
488
+ }
489
+
490
+ async function response(
491
+ actual: Response,
492
+ expected: { readonly body?: string; readonly headers?: Readonly<Record<string, string | null>>; readonly status: number },
493
+ message: string,
494
+ ): Promise<void> {
495
+ equal(actual.status, expected.status, `${message} status`)
496
+ if (expected.body != null) equal(await actual.text(), expected.body, `${message} body`)
497
+ for (const [name, value] of Object.entries(expected.headers ?? {})) equal(actual.headers.get(name), value, `${message} header ${name}`)
498
+ }