@shipfox/api-triggers 2.0.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 (331) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +224 -0
  5. package/LICENSE +21 -0
  6. package/README.md +300 -0
  7. package/dist/config.d.ts +10 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +42 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/core/compute-next-fire-at.d.ts +9 -0
  12. package/dist/core/compute-next-fire-at.d.ts.map +1 -0
  13. package/dist/core/compute-next-fire-at.js +28 -0
  14. package/dist/core/compute-next-fire-at.js.map +1 -0
  15. package/dist/core/config.d.ts +26 -0
  16. package/dist/core/config.d.ts.map +1 -0
  17. package/dist/core/config.js +68 -0
  18. package/dist/core/config.js.map +1 -0
  19. package/dist/core/dispatch-integration-event.d.ts +14 -0
  20. package/dist/core/dispatch-integration-event.d.ts.map +1 -0
  21. package/dist/core/dispatch-integration-event.js +136 -0
  22. package/dist/core/dispatch-integration-event.js.map +1 -0
  23. package/dist/core/drain-cron-schedules.d.ts +30 -0
  24. package/dist/core/drain-cron-schedules.d.ts.map +1 -0
  25. package/dist/core/drain-cron-schedules.js +76 -0
  26. package/dist/core/drain-cron-schedules.js.map +1 -0
  27. package/dist/core/entities/cron-schedule.d.ts +11 -0
  28. package/dist/core/entities/cron-schedule.d.ts.map +1 -0
  29. package/dist/core/entities/cron-schedule.js +3 -0
  30. package/dist/core/entities/cron-schedule.js.map +1 -0
  31. package/dist/core/entities/decision.d.ts +23 -0
  32. package/dist/core/entities/decision.d.ts.map +1 -0
  33. package/dist/core/entities/decision.js +7 -0
  34. package/dist/core/entities/decision.js.map +1 -0
  35. package/dist/core/entities/index.d.ts +3 -0
  36. package/dist/core/entities/index.d.ts.map +1 -0
  37. package/dist/core/entities/index.js +3 -0
  38. package/dist/core/entities/index.js.map +1 -0
  39. package/dist/core/entities/job-listener-subscription.d.ts +14 -0
  40. package/dist/core/entities/job-listener-subscription.d.ts.map +1 -0
  41. package/dist/core/entities/job-listener-subscription.js +3 -0
  42. package/dist/core/entities/job-listener-subscription.js.map +1 -0
  43. package/dist/core/entities/received-event.d.ts +28 -0
  44. package/dist/core/entities/received-event.d.ts.map +1 -0
  45. package/dist/core/entities/received-event.js +14 -0
  46. package/dist/core/entities/received-event.js.map +1 -0
  47. package/dist/core/entities/subscription.d.ts +13 -0
  48. package/dist/core/entities/subscription.d.ts.map +1 -0
  49. package/dist/core/entities/subscription.js +3 -0
  50. package/dist/core/entities/subscription.js.map +1 -0
  51. package/dist/core/errors.d.ts +25 -0
  52. package/dist/core/errors.d.ts.map +1 -0
  53. package/dist/core/errors.js +41 -0
  54. package/dist/core/errors.js.map +1 -0
  55. package/dist/core/fire-cron.d.ts +28 -0
  56. package/dist/core/fire-cron.d.ts.map +1 -0
  57. package/dist/core/fire-cron.js +85 -0
  58. package/dist/core/fire-cron.js.map +1 -0
  59. package/dist/core/fire-manual.d.ts +9 -0
  60. package/dist/core/fire-manual.d.ts.map +1 -0
  61. package/dist/core/fire-manual.js +87 -0
  62. package/dist/core/fire-manual.js.map +1 -0
  63. package/dist/core/index.d.ts +11 -0
  64. package/dist/core/index.d.ts.map +1 -0
  65. package/dist/core/index.js +10 -0
  66. package/dist/core/index.js.map +1 -0
  67. package/dist/core/record-trigger-history.d.ts +35 -0
  68. package/dist/core/record-trigger-history.d.ts.map +1 -0
  69. package/dist/core/record-trigger-history.js +75 -0
  70. package/dist/core/record-trigger-history.js.map +1 -0
  71. package/dist/core/route-event-to-job-listeners.d.ts +31 -0
  72. package/dist/core/route-event-to-job-listeners.d.ts.map +1 -0
  73. package/dist/core/route-event-to-job-listeners.js +140 -0
  74. package/dist/core/route-event-to-job-listeners.js.map +1 -0
  75. package/dist/db/cron-schedules.d.ts +36 -0
  76. package/dist/db/cron-schedules.d.ts.map +1 -0
  77. package/dist/db/cron-schedules.js +104 -0
  78. package/dist/db/cron-schedules.js.map +1 -0
  79. package/dist/db/db.d.ts +2515 -0
  80. package/dist/db/db.d.ts.map +1 -0
  81. package/dist/db/db.js +28 -0
  82. package/dist/db/db.js.map +1 -0
  83. package/dist/db/event-history.d.ts +50 -0
  84. package/dist/db/event-history.d.ts.map +1 -0
  85. package/dist/db/event-history.js +207 -0
  86. package/dist/db/event-history.js.map +1 -0
  87. package/dist/db/event-queries.d.ts +38 -0
  88. package/dist/db/event-queries.d.ts.map +1 -0
  89. package/dist/db/event-queries.js +78 -0
  90. package/dist/db/event-queries.js.map +1 -0
  91. package/dist/db/index.d.ts +11 -0
  92. package/dist/db/index.d.ts.map +1 -0
  93. package/dist/db/index.js +14 -0
  94. package/dist/db/index.js.map +1 -0
  95. package/dist/db/job-listener-subscriptions.d.ts +24 -0
  96. package/dist/db/job-listener-subscriptions.d.ts.map +1 -0
  97. package/dist/db/job-listener-subscriptions.js +69 -0
  98. package/dist/db/job-listener-subscriptions.js.map +1 -0
  99. package/dist/db/received-events.d.ts +7 -0
  100. package/dist/db/received-events.d.ts.map +1 -0
  101. package/dist/db/received-events.js +11 -0
  102. package/dist/db/received-events.js.map +1 -0
  103. package/dist/db/schema/common.d.ts +2 -0
  104. package/dist/db/schema/common.d.ts.map +1 -0
  105. package/dist/db/schema/common.js +4 -0
  106. package/dist/db/schema/common.js.map +1 -0
  107. package/dist/db/schema/cron-schedules.d.ts +148 -0
  108. package/dist/db/schema/cron-schedules.d.ts.map +1 -0
  109. package/dist/db/schema/cron-schedules.js +39 -0
  110. package/dist/db/schema/cron-schedules.js.map +1 -0
  111. package/dist/db/schema/decisions.d.ts +284 -0
  112. package/dist/db/schema/decisions.d.ts.map +1 -0
  113. package/dist/db/schema/decisions.js +70 -0
  114. package/dist/db/schema/decisions.js.map +1 -0
  115. package/dist/db/schema/job-listener-subscriptions.d.ts +185 -0
  116. package/dist/db/schema/job-listener-subscriptions.d.ts.map +1 -0
  117. package/dist/db/schema/job-listener-subscriptions.js +41 -0
  118. package/dist/db/schema/job-listener-subscriptions.js.map +1 -0
  119. package/dist/db/schema/outbox.d.ts +195 -0
  120. package/dist/db/schema/outbox.d.ts.map +1 -0
  121. package/dist/db/schema/outbox.js +5 -0
  122. package/dist/db/schema/outbox.js.map +1 -0
  123. package/dist/db/schema/received-events.d.ts +544 -0
  124. package/dist/db/schema/received-events.d.ts.map +1 -0
  125. package/dist/db/schema/received-events.js +86 -0
  126. package/dist/db/schema/received-events.js.map +1 -0
  127. package/dist/db/schema/subscriptions.d.ts +182 -0
  128. package/dist/db/schema/subscriptions.d.ts.map +1 -0
  129. package/dist/db/schema/subscriptions.js +39 -0
  130. package/dist/db/schema/subscriptions.js.map +1 -0
  131. package/dist/db/subscriptions.d.ts +26 -0
  132. package/dist/db/subscriptions.d.ts.map +1 -0
  133. package/dist/db/subscriptions.js +103 -0
  134. package/dist/db/subscriptions.js.map +1 -0
  135. package/dist/index.d.ts +7 -0
  136. package/dist/index.d.ts.map +1 -0
  137. package/dist/index.js +68 -0
  138. package/dist/index.js.map +1 -0
  139. package/dist/metrics/index.d.ts +3 -0
  140. package/dist/metrics/index.d.ts.map +1 -0
  141. package/dist/metrics/index.js +4 -0
  142. package/dist/metrics/index.js.map +1 -0
  143. package/dist/metrics/instance.d.ts +15 -0
  144. package/dist/metrics/instance.d.ts.map +1 -0
  145. package/dist/metrics/instance.js +32 -0
  146. package/dist/metrics/instance.js.map +1 -0
  147. package/dist/metrics/service.d.ts +2 -0
  148. package/dist/metrics/service.d.ts.map +1 -0
  149. package/dist/metrics/service.js +15 -0
  150. package/dist/metrics/service.js.map +1 -0
  151. package/dist/presentation/dto/trigger-events.d.ts +7 -0
  152. package/dist/presentation/dto/trigger-events.d.ts.map +1 -0
  153. package/dist/presentation/dto/trigger-events.js +47 -0
  154. package/dist/presentation/dto/trigger-events.js.map +1 -0
  155. package/dist/presentation/index.d.ts +3 -0
  156. package/dist/presentation/index.d.ts.map +1 -0
  157. package/dist/presentation/index.js +4 -0
  158. package/dist/presentation/index.js.map +1 -0
  159. package/dist/presentation/routes/fire-manual.d.ts +2 -0
  160. package/dist/presentation/routes/fire-manual.d.ts.map +1 -0
  161. package/dist/presentation/routes/fire-manual.js +71 -0
  162. package/dist/presentation/routes/fire-manual.js.map +1 -0
  163. package/dist/presentation/routes/get-trigger-event.d.ts +2 -0
  164. package/dist/presentation/routes/get-trigger-event.d.ts.map +1 -0
  165. package/dist/presentation/routes/get-trigger-event.js +37 -0
  166. package/dist/presentation/routes/get-trigger-event.js.map +1 -0
  167. package/dist/presentation/routes/index.d.ts +3 -0
  168. package/dist/presentation/routes/index.d.ts.map +1 -0
  169. package/dist/presentation/routes/index.js +27 -0
  170. package/dist/presentation/routes/index.js.map +1 -0
  171. package/dist/presentation/routes/list-trigger-event-facets.d.ts +2 -0
  172. package/dist/presentation/routes/list-trigger-event-facets.d.ts.map +1 -0
  173. package/dist/presentation/routes/list-trigger-event-facets.js +29 -0
  174. package/dist/presentation/routes/list-trigger-event-facets.js.map +1 -0
  175. package/dist/presentation/routes/list-trigger-events.d.ts +2 -0
  176. package/dist/presentation/routes/list-trigger-events.d.ts.map +1 -0
  177. package/dist/presentation/routes/list-trigger-events.js +56 -0
  178. package/dist/presentation/routes/list-trigger-events.js.map +1 -0
  179. package/dist/presentation/subscribers/index.d.ts +6 -0
  180. package/dist/presentation/subscribers/index.d.ts.map +1 -0
  181. package/dist/presentation/subscribers/index.js +7 -0
  182. package/dist/presentation/subscribers/index.js.map +1 -0
  183. package/dist/presentation/subscribers/on-definition-deleted.d.ts +3 -0
  184. package/dist/presentation/subscribers/on-definition-deleted.d.ts.map +1 -0
  185. package/dist/presentation/subscribers/on-definition-deleted.js +8 -0
  186. package/dist/presentation/subscribers/on-definition-deleted.js.map +1 -0
  187. package/dist/presentation/subscribers/on-definition-resolved.d.ts +3 -0
  188. package/dist/presentation/subscribers/on-definition-resolved.d.ts.map +1 -0
  189. package/dist/presentation/subscribers/on-definition-resolved.js +11 -0
  190. package/dist/presentation/subscribers/on-definition-resolved.js.map +1 -0
  191. package/dist/presentation/subscribers/on-integration-event-received.d.ts +4 -0
  192. package/dist/presentation/subscribers/on-integration-event-received.d.ts.map +1 -0
  193. package/dist/presentation/subscribers/on-integration-event-received.js +17 -0
  194. package/dist/presentation/subscribers/on-integration-event-received.js.map +1 -0
  195. package/dist/presentation/subscribers/on-job-activated.d.ts +3 -0
  196. package/dist/presentation/subscribers/on-job-activated.d.ts.map +1 -0
  197. package/dist/presentation/subscribers/on-job-activated.js +13 -0
  198. package/dist/presentation/subscribers/on-job-activated.js.map +1 -0
  199. package/dist/presentation/subscribers/on-job-terminated.d.ts +3 -0
  200. package/dist/presentation/subscribers/on-job-terminated.d.ts.map +1 -0
  201. package/dist/presentation/subscribers/on-job-terminated.js +6 -0
  202. package/dist/presentation/subscribers/on-job-terminated.js.map +1 -0
  203. package/dist/temporal/activities/drain-cron-batch.d.ts +4 -0
  204. package/dist/temporal/activities/drain-cron-batch.d.ts.map +1 -0
  205. package/dist/temporal/activities/drain-cron-batch.js +20 -0
  206. package/dist/temporal/activities/drain-cron-batch.js.map +1 -0
  207. package/dist/temporal/activities/index.d.ts +10 -0
  208. package/dist/temporal/activities/index.d.ts.map +1 -0
  209. package/dist/temporal/activities/index.js +15 -0
  210. package/dist/temporal/activities/index.js.map +1 -0
  211. package/dist/temporal/activities/prune-trigger-events.d.ts +4 -0
  212. package/dist/temporal/activities/prune-trigger-events.d.ts.map +1 -0
  213. package/dist/temporal/activities/prune-trigger-events.js +11 -0
  214. package/dist/temporal/activities/prune-trigger-events.js.map +1 -0
  215. package/dist/temporal/constants.d.ts +7 -0
  216. package/dist/temporal/constants.d.ts.map +1 -0
  217. package/dist/temporal/constants.js +7 -0
  218. package/dist/temporal/constants.js.map +1 -0
  219. package/dist/temporal/workflows/cron-tick-cron.d.ts +9 -0
  220. package/dist/temporal/workflows/cron-tick-cron.d.ts.map +1 -0
  221. package/dist/temporal/workflows/cron-tick-cron.js +54 -0
  222. package/dist/temporal/workflows/cron-tick-cron.js.map +1 -0
  223. package/dist/temporal/workflows/index.d.ts +3 -0
  224. package/dist/temporal/workflows/index.d.ts.map +1 -0
  225. package/dist/temporal/workflows/index.js +4 -0
  226. package/dist/temporal/workflows/index.js.map +1 -0
  227. package/dist/temporal/workflows/prune-trigger-events-cron.d.ts +2 -0
  228. package/dist/temporal/workflows/prune-trigger-events-cron.d.ts.map +1 -0
  229. package/dist/temporal/workflows/prune-trigger-events-cron.js +18 -0
  230. package/dist/temporal/workflows/prune-trigger-events-cron.js.map +1 -0
  231. package/dist/tsconfig.test.tsbuildinfo +1 -0
  232. package/drizzle/0000_initial.sql +109 -0
  233. package/drizzle/meta/0000_snapshot.json +905 -0
  234. package/drizzle/meta/_journal.json +13 -0
  235. package/drizzle.config.ts +14 -0
  236. package/package.json +83 -0
  237. package/src/config.test.ts +33 -0
  238. package/src/config.ts +53 -0
  239. package/src/core/compute-next-fire-at.test.ts +69 -0
  240. package/src/core/compute-next-fire-at.ts +37 -0
  241. package/src/core/config.test.ts +213 -0
  242. package/src/core/config.ts +86 -0
  243. package/src/core/dispatch-integration-event.history-failure.test.ts +66 -0
  244. package/src/core/dispatch-integration-event.test.ts +1006 -0
  245. package/src/core/dispatch-integration-event.ts +150 -0
  246. package/src/core/drain-cron-schedules.test.ts +265 -0
  247. package/src/core/drain-cron-schedules.ts +90 -0
  248. package/src/core/entities/cron-schedule.ts +10 -0
  249. package/src/core/entities/decision.ts +24 -0
  250. package/src/core/entities/index.ts +5 -0
  251. package/src/core/entities/job-listener-subscription.ts +14 -0
  252. package/src/core/entities/received-event.ts +36 -0
  253. package/src/core/entities/subscription.ts +12 -0
  254. package/src/core/errors.ts +63 -0
  255. package/src/core/fire-cron.test.ts +146 -0
  256. package/src/core/fire-cron.ts +98 -0
  257. package/src/core/fire-manual.test.ts +157 -0
  258. package/src/core/fire-manual.ts +92 -0
  259. package/src/core/index.ts +34 -0
  260. package/src/core/record-trigger-history.test.ts +159 -0
  261. package/src/core/record-trigger-history.ts +145 -0
  262. package/src/core/route-event-to-job-listeners.test.ts +369 -0
  263. package/src/core/route-event-to-job-listeners.ts +184 -0
  264. package/src/db/cron-schedules.test.ts +358 -0
  265. package/src/db/cron-schedules.ts +163 -0
  266. package/src/db/db.ts +31 -0
  267. package/src/db/event-history.test.ts +421 -0
  268. package/src/db/event-history.ts +279 -0
  269. package/src/db/event-queries.ts +140 -0
  270. package/src/db/index.ts +51 -0
  271. package/src/db/job-listener-subscriptions.test.ts +270 -0
  272. package/src/db/job-listener-subscriptions.ts +133 -0
  273. package/src/db/received-events.ts +16 -0
  274. package/src/db/schema/common.ts +3 -0
  275. package/src/db/schema/cron-schedules.ts +37 -0
  276. package/src/db/schema/decisions.test.ts +279 -0
  277. package/src/db/schema/decisions.ts +77 -0
  278. package/src/db/schema/job-listener-subscriptions.ts +65 -0
  279. package/src/db/schema/outbox.ts +4 -0
  280. package/src/db/schema/received-events.test.ts +135 -0
  281. package/src/db/schema/received-events.ts +93 -0
  282. package/src/db/schema/subscriptions.ts +46 -0
  283. package/src/db/subscriptions.test.ts +340 -0
  284. package/src/db/subscriptions.ts +179 -0
  285. package/src/index.ts +108 -0
  286. package/src/metrics/index.ts +8 -0
  287. package/src/metrics/instance.ts +33 -0
  288. package/src/metrics/service.ts +18 -0
  289. package/src/presentation/dto/trigger-events.test.ts +144 -0
  290. package/src/presentation/dto/trigger-events.ts +60 -0
  291. package/src/presentation/index.ts +6 -0
  292. package/src/presentation/routes/fire-manual.test.ts +118 -0
  293. package/src/presentation/routes/fire-manual.ts +70 -0
  294. package/src/presentation/routes/get-trigger-event.test.ts +184 -0
  295. package/src/presentation/routes/get-trigger-event.ts +37 -0
  296. package/src/presentation/routes/index.ts +21 -0
  297. package/src/presentation/routes/list-trigger-event-facets.test.ts +116 -0
  298. package/src/presentation/routes/list-trigger-event-facets.ts +29 -0
  299. package/src/presentation/routes/list-trigger-events.test.ts +379 -0
  300. package/src/presentation/routes/list-trigger-events.ts +68 -0
  301. package/src/presentation/subscribers/index.ts +5 -0
  302. package/src/presentation/subscribers/on-definition-deleted.test.ts +42 -0
  303. package/src/presentation/subscribers/on-definition-deleted.ts +6 -0
  304. package/src/presentation/subscribers/on-definition-resolved.test.ts +67 -0
  305. package/src/presentation/subscribers/on-definition-resolved.ts +11 -0
  306. package/src/presentation/subscribers/on-integration-event-received.test.ts +53 -0
  307. package/src/presentation/subscribers/on-integration-event-received.ts +21 -0
  308. package/src/presentation/subscribers/on-job-activated.ts +14 -0
  309. package/src/presentation/subscribers/on-job-terminated.ts +6 -0
  310. package/src/temporal/activities/drain-cron-batch.ts +20 -0
  311. package/src/temporal/activities/index.ts +15 -0
  312. package/src/temporal/activities/prune-trigger-events.test.ts +78 -0
  313. package/src/temporal/activities/prune-trigger-events.ts +9 -0
  314. package/src/temporal/constants.ts +7 -0
  315. package/src/temporal/workflows/cron-tick-cron.ts +56 -0
  316. package/src/temporal/workflows/index.ts +2 -0
  317. package/src/temporal/workflows/prune-trigger-events-cron.ts +17 -0
  318. package/test/env.ts +8 -0
  319. package/test/factories/cron-schedule.ts +34 -0
  320. package/test/factories/decision.ts +49 -0
  321. package/test/factories/job-listener-subscription.ts +42 -0
  322. package/test/factories/received-event.ts +49 -0
  323. package/test/factories/subscription.ts +38 -0
  324. package/test/globalSetup.ts +20 -0
  325. package/test/index.ts +5 -0
  326. package/test/setup.ts +17 -0
  327. package/tsconfig.build.json +9 -0
  328. package/tsconfig.build.tsbuildinfo +1 -0
  329. package/tsconfig.json +3 -0
  330. package/tsconfig.test.json +8 -0
  331. package/vitest.config.ts +11 -0
@@ -0,0 +1,279 @@
1
+ import {eq} from 'drizzle-orm';
2
+ import {getTableConfig} from 'drizzle-orm/pg-core';
3
+ import {db} from '../db.js';
4
+ import {
5
+ type TriggerDecisionDb,
6
+ type TriggerDecisionInsertDb,
7
+ toTriggerDecision,
8
+ triggersDecisions,
9
+ } from './decisions.js';
10
+ import {triggersReceivedEvents} from './received-events.js';
11
+
12
+ describe('toTriggerDecision', () => {
13
+ test('maps a fully populated row to the domain entity', () => {
14
+ const row: TriggerDecisionDb = {
15
+ id: '019e98ab-6656-7ca1-b9ad-1ca4442c479d',
16
+ receivedEventId: '019e98ab-b90f-7265-b13c-8b441c991381',
17
+ subscriptionKind: 'trigger',
18
+ subscriptionId: '019e98ab-b90f-7265-b13c-8b441c991382',
19
+ subscriptionName: 'Deploy production',
20
+ workflowDefinitionId: '019e98ab-b90f-7265-b13c-8b441c991383',
21
+ projectId: '019e98ab-b90f-7265-b13c-8b441c991384',
22
+ workflowRunId: null,
23
+ jobId: null,
24
+ matcherKind: null,
25
+ matcherOrdinal: null,
26
+ decision: 'triggered',
27
+ runId: '019e98ab-b90f-7265-b13c-8b441c991385',
28
+ runName: 'Build and test',
29
+ reason: null,
30
+ createdAt: new Date('2026-06-09T10:00:02.000Z'),
31
+ };
32
+
33
+ const result = toTriggerDecision(row);
34
+
35
+ expect(result).toEqual({
36
+ id: row.id,
37
+ receivedEventId: row.receivedEventId,
38
+ subscriptionKind: 'trigger',
39
+ subscriptionId: row.subscriptionId,
40
+ subscriptionName: 'Deploy production',
41
+ workflowDefinitionId: row.workflowDefinitionId,
42
+ projectId: row.projectId,
43
+ workflowRunId: null,
44
+ jobId: null,
45
+ matcherKind: null,
46
+ matcherOrdinal: null,
47
+ decision: 'triggered',
48
+ runId: row.runId,
49
+ runName: 'Build and test',
50
+ reason: null,
51
+ createdAt: row.createdAt,
52
+ });
53
+ });
54
+
55
+ test('passes through null run_id, run_name, and a populated reason', () => {
56
+ const row: TriggerDecisionDb = {
57
+ id: '019e98ab-6656-7ca1-b9ad-1ca4442c479d',
58
+ receivedEventId: '019e98ab-b90f-7265-b13c-8b441c991381',
59
+ subscriptionKind: 'trigger',
60
+ subscriptionId: '019e98ab-b90f-7265-b13c-8b441c991382',
61
+ subscriptionName: 'Deploy production',
62
+ workflowDefinitionId: '019e98ab-b90f-7265-b13c-8b441c991383',
63
+ projectId: '019e98ab-b90f-7265-b13c-8b441c991384',
64
+ workflowRunId: null,
65
+ jobId: null,
66
+ matcherKind: null,
67
+ matcherOrdinal: null,
68
+ decision: 'dispatch-error',
69
+ runId: null,
70
+ runName: null,
71
+ reason: 'runWorkflow threw',
72
+ createdAt: new Date('2026-06-09T10:00:02.000Z'),
73
+ };
74
+
75
+ const result = toTriggerDecision(row);
76
+
77
+ expect(result.decision).toBe('dispatch-error');
78
+ expect(result.runId).toBeNull();
79
+ expect(result.runName).toBeNull();
80
+ expect(result.reason).toBe('runWorkflow threw');
81
+ });
82
+
83
+ test('maps legacy errored rows to dispatch-error', () => {
84
+ const row = {
85
+ id: '019e98ab-6656-7ca1-b9ad-1ca4442c479d',
86
+ receivedEventId: '019e98ab-b90f-7265-b13c-8b441c991381',
87
+ subscriptionKind: 'trigger',
88
+ subscriptionId: '019e98ab-b90f-7265-b13c-8b441c991382',
89
+ subscriptionName: 'Deploy production',
90
+ workflowDefinitionId: '019e98ab-b90f-7265-b13c-8b441c991383',
91
+ projectId: '019e98ab-b90f-7265-b13c-8b441c991384',
92
+ workflowRunId: null,
93
+ jobId: null,
94
+ matcherKind: null,
95
+ matcherOrdinal: null,
96
+ decision: 'errored',
97
+ runId: null,
98
+ runName: null,
99
+ reason: 'runWorkflow threw',
100
+ createdAt: new Date('2026-06-09T10:00:02.000Z'),
101
+ } as unknown as TriggerDecisionDb;
102
+
103
+ const result = toTriggerDecision(row);
104
+
105
+ expect(result.decision).toBe('dispatch-error');
106
+ });
107
+ });
108
+
109
+ describe('triggers_decisions schema', () => {
110
+ async function insertEvent(): Promise<string> {
111
+ const [event] = await db()
112
+ .insert(triggersReceivedEvents)
113
+ .values({
114
+ eventRef: crypto.randomUUID(),
115
+ origin: 'integration',
116
+ workspaceId: crypto.randomUUID(),
117
+ source: 'github',
118
+ event: 'push',
119
+ receivedAt: new Date(),
120
+ })
121
+ .returning();
122
+ if (!event) throw new Error('insert returned no rows');
123
+ return event.id;
124
+ }
125
+
126
+ test('applies defaults and maps an inserted row', async () => {
127
+ const receivedEventId = await insertEvent();
128
+ const values: TriggerDecisionInsertDb = {
129
+ receivedEventId,
130
+ subscriptionKind: 'trigger',
131
+ subscriptionId: crypto.randomUUID(),
132
+ subscriptionName: 'Deploy production',
133
+ workflowDefinitionId: crypto.randomUUID(),
134
+ projectId: crypto.randomUUID(),
135
+ decision: 'triggered',
136
+ };
137
+
138
+ const [inserted] = await db()
139
+ .insert(triggersDecisions)
140
+ .values(values)
141
+ .returning({id: triggersDecisions.id});
142
+ if (!inserted) throw new Error('insert returned no rows');
143
+ const [row] = await db()
144
+ .select()
145
+ .from(triggersDecisions)
146
+ .where(eq(triggersDecisions.id, inserted.id));
147
+ if (!row) throw new Error('select returned no rows');
148
+ const result = toTriggerDecision(row);
149
+
150
+ expect(row.createdAt).toBeInstanceOf(Date);
151
+ expect(row.runId).toBeNull();
152
+ expect(row.runName).toBeNull();
153
+ expect(row.reason).toBeNull();
154
+ expect(result).toMatchObject({
155
+ receivedEventId,
156
+ subscriptionKind: 'trigger',
157
+ subscriptionId: values.subscriptionId,
158
+ subscriptionName: 'Deploy production',
159
+ workflowDefinitionId: values.workflowDefinitionId,
160
+ projectId: values.projectId,
161
+ decision: 'triggered',
162
+ });
163
+ });
164
+
165
+ test('cascades decision deletes when the parent event is removed', async () => {
166
+ const receivedEventId = await insertEvent();
167
+ await db().insert(triggersDecisions).values({
168
+ receivedEventId,
169
+ subscriptionKind: 'trigger',
170
+ subscriptionId: crypto.randomUUID(),
171
+ subscriptionName: 'Deploy production',
172
+ workflowDefinitionId: crypto.randomUUID(),
173
+ projectId: crypto.randomUUID(),
174
+ decision: 'triggered',
175
+ });
176
+
177
+ await db().delete(triggersReceivedEvents).where(eq(triggersReceivedEvents.id, receivedEventId));
178
+
179
+ const rows = await db()
180
+ .select()
181
+ .from(triggersDecisions)
182
+ .where(eq(triggersDecisions.receivedEventId, receivedEventId));
183
+ expect(rows).toHaveLength(0);
184
+ });
185
+
186
+ test('rejects a duplicate (received_event_id, subscription_kind, subscription_id)', async () => {
187
+ const receivedEventId = await insertEvent();
188
+ const values: TriggerDecisionInsertDb = {
189
+ receivedEventId,
190
+ subscriptionKind: 'trigger',
191
+ subscriptionId: crypto.randomUUID(),
192
+ subscriptionName: 'Deploy production',
193
+ workflowDefinitionId: crypto.randomUUID(),
194
+ projectId: crypto.randomUUID(),
195
+ decision: 'triggered',
196
+ };
197
+ await db().insert(triggersDecisions).values(values);
198
+
199
+ const duplicate = db().insert(triggersDecisions).values(values);
200
+
201
+ await expect(duplicate).rejects.toThrow();
202
+ });
203
+
204
+ test('allows trigger and listener decisions with the same subscription id', async () => {
205
+ const receivedEventId = await insertEvent();
206
+ const subscriptionId = crypto.randomUUID();
207
+ await db().insert(triggersDecisions).values({
208
+ receivedEventId,
209
+ subscriptionKind: 'trigger',
210
+ subscriptionId,
211
+ subscriptionName: 'Deploy production',
212
+ workflowDefinitionId: crypto.randomUUID(),
213
+ projectId: crypto.randomUUID(),
214
+ decision: 'triggered',
215
+ });
216
+
217
+ await db().insert(triggersDecisions).values({
218
+ receivedEventId,
219
+ subscriptionKind: 'listener',
220
+ subscriptionId,
221
+ subscriptionName: 'listener on[0] github/push',
222
+ workflowRunId: crypto.randomUUID(),
223
+ jobId: crypto.randomUUID(),
224
+ matcherKind: 'on',
225
+ matcherOrdinal: 0,
226
+ decision: 'triggered',
227
+ });
228
+
229
+ const rows = await db()
230
+ .select()
231
+ .from(triggersDecisions)
232
+ .where(eq(triggersDecisions.receivedEventId, receivedEventId));
233
+ expect(rows).toHaveLength(2);
234
+ });
235
+
236
+ test('declares a database check for valid subscription kinds', () => {
237
+ const subscriptionKindCheck = getTableConfig(triggersDecisions).checks.find(
238
+ (check) => check.name === 'triggers_decisions_subscription_kind_ck',
239
+ );
240
+
241
+ expect(subscriptionKindCheck).toBeDefined();
242
+ });
243
+
244
+ test('maps listener identity fields', async () => {
245
+ const receivedEventId = await insertEvent();
246
+ const workflowRunId = crypto.randomUUID();
247
+ const jobId = crypto.randomUUID();
248
+ const [row] = await db()
249
+ .insert(triggersDecisions)
250
+ .values({
251
+ receivedEventId,
252
+ subscriptionKind: 'listener',
253
+ subscriptionId: crypto.randomUUID(),
254
+ subscriptionName: 'listener until[1] github/pull_request.closed',
255
+ workflowRunId,
256
+ jobId,
257
+ matcherKind: 'until',
258
+ matcherOrdinal: 1,
259
+ decision: 'dispatch-error',
260
+ reason: 'workflow db down',
261
+ })
262
+ .returning();
263
+ if (!row) throw new Error('insert returned no rows');
264
+
265
+ const result = toTriggerDecision(row);
266
+
267
+ expect(result).toMatchObject({
268
+ subscriptionKind: 'listener',
269
+ workflowDefinitionId: null,
270
+ projectId: null,
271
+ workflowRunId,
272
+ jobId,
273
+ matcherKind: 'until',
274
+ matcherOrdinal: 1,
275
+ decision: 'dispatch-error',
276
+ reason: 'workflow db down',
277
+ });
278
+ });
279
+ });
@@ -0,0 +1,77 @@
1
+ import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
+ import {sql} from 'drizzle-orm';
3
+ import {check, index, integer, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
4
+ import {type TriggerDecision, triggerDecisionOutcomes} from '#core/entities/decision.js';
5
+ import {pgTable} from './common.js';
6
+ import {jobListenerMatcherKindEnum} from './job-listener-subscriptions.js';
7
+ import {triggersReceivedEvents} from './received-events.js';
8
+
9
+ const triggerDecisionSubscriptionKinds = ['trigger', 'listener'] as const;
10
+
11
+ export const triggersDecisions = pgTable(
12
+ 'decisions',
13
+ {
14
+ id: uuidv7PrimaryKey(),
15
+ receivedEventId: uuid('received_event_id')
16
+ .notNull()
17
+ .references(() => triggersReceivedEvents.id, {onDelete: 'cascade'}),
18
+ subscriptionKind: text('subscription_kind', {enum: triggerDecisionSubscriptionKinds}).notNull(),
19
+ subscriptionId: uuid('subscription_id').notNull(),
20
+ subscriptionName: text('subscription_name').notNull(),
21
+ workflowDefinitionId: uuid('workflow_definition_id'),
22
+ projectId: uuid('project_id'),
23
+ workflowRunId: uuid('workflow_run_id'),
24
+ jobId: uuid('job_id'),
25
+ matcherKind: jobListenerMatcherKindEnum('matcher_kind'),
26
+ matcherOrdinal: integer('matcher_ordinal'),
27
+ decision: text('decision', {enum: triggerDecisionOutcomes}).notNull(),
28
+ runId: uuid('run_id'),
29
+ runName: text('run_name'),
30
+ reason: text('reason'),
31
+ createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
32
+ },
33
+ (table) => [
34
+ uniqueIndex('triggers_decisions_event_subscription_unique').on(
35
+ table.receivedEventId,
36
+ table.subscriptionKind,
37
+ table.subscriptionId,
38
+ ),
39
+ index('triggers_decisions_run_idx').on(table.runId),
40
+ check(
41
+ 'triggers_decisions_subscription_kind_ck',
42
+ sql`${table.subscriptionKind} IN ('trigger', 'listener')`,
43
+ ),
44
+ ],
45
+ );
46
+
47
+ export type TriggerDecisionDb = typeof triggersDecisions.$inferSelect;
48
+ export type TriggerDecisionInsertDb = typeof triggersDecisions.$inferInsert;
49
+
50
+ export function toTriggerDecision(row: TriggerDecisionDb): TriggerDecision {
51
+ return {
52
+ id: row.id,
53
+ receivedEventId: row.receivedEventId,
54
+ subscriptionKind: row.subscriptionKind,
55
+ subscriptionId: row.subscriptionId,
56
+ subscriptionName: row.subscriptionName,
57
+ workflowDefinitionId: row.workflowDefinitionId,
58
+ projectId: row.projectId,
59
+ workflowRunId: row.workflowRunId,
60
+ jobId: row.jobId,
61
+ matcherKind: row.matcherKind,
62
+ matcherOrdinal: row.matcherOrdinal,
63
+ decision: toTriggerDecisionOutcome(row.decision),
64
+ runId: row.runId,
65
+ runName: row.runName,
66
+ reason: row.reason,
67
+ createdAt: row.createdAt,
68
+ };
69
+ }
70
+
71
+ function toTriggerDecisionOutcome(decision: string): TriggerDecision['decision'] {
72
+ if (decision === 'errored') return 'dispatch-error';
73
+ if (decision === 'triggered' || decision === 'filter-error' || decision === 'dispatch-error') {
74
+ return decision;
75
+ }
76
+ return 'dispatch-error';
77
+ }
@@ -0,0 +1,65 @@
1
+ import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
+ import {
3
+ index,
4
+ integer,
5
+ jsonb,
6
+ pgEnum,
7
+ text,
8
+ timestamp,
9
+ uniqueIndex,
10
+ uuid,
11
+ } from 'drizzle-orm/pg-core';
12
+ import type {JobListenerSubscription} from '#core/entities/job-listener-subscription.js';
13
+ import {pgTable} from './common.js';
14
+
15
+ export const jobListenerMatcherKindEnum = pgEnum('triggers_job_listener_matcher_kind', [
16
+ 'on',
17
+ 'until',
18
+ ]);
19
+
20
+ export const jobListenerSubscriptions = pgTable(
21
+ 'job_listener_subscriptions',
22
+ {
23
+ id: uuidv7PrimaryKey(),
24
+ workspaceId: uuid('workspace_id').notNull(),
25
+ workflowRunId: uuid('workflow_run_id').notNull(),
26
+ jobId: uuid('job_id').notNull(),
27
+ kind: jobListenerMatcherKindEnum('kind').notNull(),
28
+ matcherOrdinal: integer('matcher_ordinal').notNull(),
29
+ source: text('source').notNull(),
30
+ event: text('event').notNull(),
31
+ config: jsonb('config').notNull().$type<Record<string, unknown>>(),
32
+ createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
33
+ },
34
+ (table) => [
35
+ uniqueIndex('triggers_job_listener_subscriptions_job_kind_ordinal_unique').on(
36
+ table.jobId,
37
+ table.kind,
38
+ table.matcherOrdinal,
39
+ ),
40
+ index('triggers_job_listener_subscriptions_match_idx').on(
41
+ table.workspaceId,
42
+ table.source,
43
+ table.event,
44
+ ),
45
+ index('triggers_job_listener_subscriptions_job_idx').on(table.jobId),
46
+ ],
47
+ );
48
+
49
+ export type JobListenerSubscriptionDb = typeof jobListenerSubscriptions.$inferSelect;
50
+ export type JobListenerSubscriptionInsertDb = typeof jobListenerSubscriptions.$inferInsert;
51
+
52
+ export function toJobListenerSubscription(row: JobListenerSubscriptionDb): JobListenerSubscription {
53
+ return {
54
+ id: row.id,
55
+ workspaceId: row.workspaceId,
56
+ workflowRunId: row.workflowRunId,
57
+ jobId: row.jobId,
58
+ kind: row.kind,
59
+ matcherOrdinal: row.matcherOrdinal,
60
+ source: row.source,
61
+ event: row.event,
62
+ config: row.config,
63
+ createdAt: row.createdAt,
64
+ };
65
+ }
@@ -0,0 +1,4 @@
1
+ import {createOutboxTable} from '@shipfox/node-outbox';
2
+ import {pgTable} from './common.js';
3
+
4
+ export const triggersOutbox = createOutboxTable(pgTable);
@@ -0,0 +1,135 @@
1
+ import {eq} from 'drizzle-orm';
2
+ import {db} from '../db.js';
3
+ import {
4
+ type TriggerReceivedEventDb,
5
+ type TriggerReceivedEventInsertDb,
6
+ toTriggerReceivedEvent,
7
+ triggersReceivedEvents,
8
+ } from './received-events.js';
9
+
10
+ describe('toTriggerReceivedEvent', () => {
11
+ test('maps a fully populated row to the domain entity', () => {
12
+ const row: TriggerReceivedEventDb = {
13
+ id: '019e98ab-6656-7ca1-b9ad-1ca4442c479d',
14
+ eventRef: 'evt-1',
15
+ origin: 'integration',
16
+ workspaceId: '019e98ab-b90f-7265-b13c-8b441c991381',
17
+ provider: 'github',
18
+ source: 'github',
19
+ event: 'push',
20
+ deliveryId: 'delivery-1',
21
+ connectionId: '019e98ab-b90f-7265-b13c-8b441c991382',
22
+ connectionName: 'Acme Production',
23
+ outcome: 'routed',
24
+ matchedCount: 3,
25
+ payload: {ref: 'refs/heads/main'},
26
+ receivedAt: new Date('2026-06-09T10:00:00.000Z'),
27
+ processedAt: new Date('2026-06-09T10:00:01.000Z'),
28
+ createdAt: new Date('2026-06-09T10:00:02.000Z'),
29
+ };
30
+
31
+ const result = toTriggerReceivedEvent(row);
32
+
33
+ expect(result).toEqual({
34
+ id: row.id,
35
+ eventRef: 'evt-1',
36
+ origin: 'integration',
37
+ workspaceId: row.workspaceId,
38
+ provider: 'github',
39
+ source: 'github',
40
+ event: 'push',
41
+ deliveryId: 'delivery-1',
42
+ connectionId: row.connectionId,
43
+ connectionName: 'Acme Production',
44
+ outcome: 'routed',
45
+ matchedCount: 3,
46
+ payload: {ref: 'refs/heads/main'},
47
+ receivedAt: row.receivedAt,
48
+ processedAt: row.processedAt,
49
+ createdAt: row.createdAt,
50
+ });
51
+ });
52
+
53
+ test('passes through null delivery, connection, payload, and processed_at', () => {
54
+ const row: TriggerReceivedEventDb = {
55
+ id: '019e98ab-6656-7ca1-b9ad-1ca4442c479d',
56
+ eventRef: 'evt-2',
57
+ origin: 'manual',
58
+ workspaceId: '019e98ab-b90f-7265-b13c-8b441c991381',
59
+ provider: null,
60
+ source: 'manual',
61
+ event: 'fire',
62
+ deliveryId: null,
63
+ connectionId: null,
64
+ connectionName: null,
65
+ outcome: 'received',
66
+ matchedCount: 0,
67
+ payload: null,
68
+ receivedAt: new Date('2026-06-09T10:00:00.000Z'),
69
+ processedAt: null,
70
+ createdAt: new Date('2026-06-09T10:00:02.000Z'),
71
+ };
72
+
73
+ const result = toTriggerReceivedEvent(row);
74
+
75
+ expect(result.deliveryId).toBeNull();
76
+ expect(result.connectionId).toBeNull();
77
+ expect(result.connectionName).toBeNull();
78
+ expect(result.payload).toBeNull();
79
+ expect(result.processedAt).toBeNull();
80
+ });
81
+ });
82
+
83
+ describe('triggers_received_events schema', () => {
84
+ test('applies defaults and maps an inserted row', async () => {
85
+ const values: TriggerReceivedEventInsertDb = {
86
+ eventRef: crypto.randomUUID(),
87
+ origin: 'integration',
88
+ workspaceId: crypto.randomUUID(),
89
+ source: 'github',
90
+ event: 'push',
91
+ receivedAt: new Date(),
92
+ };
93
+
94
+ const [inserted] = await db()
95
+ .insert(triggersReceivedEvents)
96
+ .values(values)
97
+ .returning({id: triggersReceivedEvents.id});
98
+ if (!inserted) throw new Error('insert returned no rows');
99
+ const [row] = await db()
100
+ .select()
101
+ .from(triggersReceivedEvents)
102
+ .where(eq(triggersReceivedEvents.id, inserted.id));
103
+ if (!row) throw new Error('select returned no rows');
104
+ const result = toTriggerReceivedEvent(row);
105
+
106
+ expect(row.outcome).toBe('received');
107
+ expect(row.matchedCount).toBe(0);
108
+ expect(row.createdAt).toBeInstanceOf(Date);
109
+ expect(result).toMatchObject({
110
+ eventRef: values.eventRef,
111
+ origin: 'integration',
112
+ workspaceId: values.workspaceId,
113
+ source: 'github',
114
+ event: 'push',
115
+ outcome: 'received',
116
+ matchedCount: 0,
117
+ });
118
+ });
119
+
120
+ test('rejects a duplicate event_ref', async () => {
121
+ const values: TriggerReceivedEventInsertDb = {
122
+ eventRef: crypto.randomUUID(),
123
+ origin: 'integration',
124
+ workspaceId: crypto.randomUUID(),
125
+ source: 'github',
126
+ event: 'push',
127
+ receivedAt: new Date(),
128
+ };
129
+ await db().insert(triggersReceivedEvents).values(values);
130
+
131
+ const duplicate = db().insert(triggersReceivedEvents).values(values);
132
+
133
+ await expect(duplicate).rejects.toThrow();
134
+ });
135
+ });
@@ -0,0 +1,93 @@
1
+ import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
+ import {index, integer, jsonb, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
3
+ import {
4
+ type TriggerReceivedEvent,
5
+ type TriggerReceivedEventSummary,
6
+ triggerEventOrigins,
7
+ triggerEventOutcomes,
8
+ } from '#core/entities/received-event.js';
9
+ import {pgTable} from './common.js';
10
+
11
+ export const triggersReceivedEvents = pgTable(
12
+ 'received_events',
13
+ {
14
+ id: uuidv7PrimaryKey(),
15
+ // Globally-unique idempotency key (the outbox event id), not the provider
16
+ // delivery id: delivery ids are only unique per provider and would collide
17
+ // across workspaces under the global event_ref unique index below.
18
+ eventRef: text('event_ref').notNull(),
19
+ origin: text('origin', {enum: triggerEventOrigins}).notNull(),
20
+ workspaceId: uuid('workspace_id').notNull(),
21
+ provider: text('provider'),
22
+ source: text('source').notNull(),
23
+ event: text('event').notNull(),
24
+ deliveryId: text('delivery_id'),
25
+ connectionId: uuid('connection_id'),
26
+ connectionName: text('connection_name'),
27
+ outcome: text('outcome', {enum: triggerEventOutcomes}).notNull().default('received'),
28
+ matchedCount: integer('matched_count').notNull().default(0),
29
+ payload: jsonb('payload').$type<Record<string, unknown>>(),
30
+ receivedAt: timestamp('received_at', {withTimezone: true}).notNull(),
31
+ processedAt: timestamp('processed_at', {withTimezone: true}),
32
+ createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
33
+ },
34
+ (table) => [
35
+ uniqueIndex('triggers_received_events_event_ref_unique').on(table.eventRef),
36
+ index('triggers_received_events_workspace_received_idx').on(
37
+ table.workspaceId,
38
+ table.receivedAt.desc(),
39
+ ),
40
+ index('triggers_received_events_prune_idx').on(table.createdAt),
41
+ // Back the workspace-scoped facet group-by (distinct source / event values).
42
+ index('triggers_received_events_workspace_source_idx').on(table.workspaceId, table.source),
43
+ index('triggers_received_events_workspace_event_idx').on(table.workspaceId, table.event),
44
+ ],
45
+ );
46
+
47
+ export type TriggerReceivedEventDb = typeof triggersReceivedEvents.$inferSelect;
48
+ export type TriggerReceivedEventInsertDb = typeof triggersReceivedEvents.$inferInsert;
49
+
50
+ export function toTriggerReceivedEvent(row: TriggerReceivedEventDb): TriggerReceivedEvent {
51
+ return {
52
+ id: row.id,
53
+ eventRef: row.eventRef,
54
+ origin: row.origin,
55
+ workspaceId: row.workspaceId,
56
+ provider: row.provider,
57
+ source: row.source,
58
+ event: row.event,
59
+ deliveryId: row.deliveryId,
60
+ connectionId: row.connectionId,
61
+ connectionName: row.connectionName,
62
+ outcome: row.outcome,
63
+ matchedCount: row.matchedCount,
64
+ payload: row.payload,
65
+ receivedAt: row.receivedAt,
66
+ processedAt: row.processedAt,
67
+ createdAt: row.createdAt,
68
+ };
69
+ }
70
+
71
+ export const triggerReceivedEventSummaryColumns = {
72
+ id: triggersReceivedEvents.id,
73
+ eventRef: triggersReceivedEvents.eventRef,
74
+ origin: triggersReceivedEvents.origin,
75
+ workspaceId: triggersReceivedEvents.workspaceId,
76
+ provider: triggersReceivedEvents.provider,
77
+ source: triggersReceivedEvents.source,
78
+ event: triggersReceivedEvents.event,
79
+ deliveryId: triggersReceivedEvents.deliveryId,
80
+ connectionId: triggersReceivedEvents.connectionId,
81
+ connectionName: triggersReceivedEvents.connectionName,
82
+ outcome: triggersReceivedEvents.outcome,
83
+ matchedCount: triggersReceivedEvents.matchedCount,
84
+ receivedAt: triggersReceivedEvents.receivedAt,
85
+ processedAt: triggersReceivedEvents.processedAt,
86
+ createdAt: triggersReceivedEvents.createdAt,
87
+ } as const satisfies Record<keyof TriggerReceivedEventSummary, unknown>;
88
+
89
+ export function toTriggerReceivedEventSummary(
90
+ row: Omit<TriggerReceivedEventDb, 'payload'>,
91
+ ): TriggerReceivedEventSummary {
92
+ return {...row};
93
+ }