@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.
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +224 -0
- package/LICENSE +21 -0
- package/README.md +300 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +42 -0
- package/dist/config.js.map +1 -0
- package/dist/core/compute-next-fire-at.d.ts +9 -0
- package/dist/core/compute-next-fire-at.d.ts.map +1 -0
- package/dist/core/compute-next-fire-at.js +28 -0
- package/dist/core/compute-next-fire-at.js.map +1 -0
- package/dist/core/config.d.ts +26 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +68 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/dispatch-integration-event.d.ts +14 -0
- package/dist/core/dispatch-integration-event.d.ts.map +1 -0
- package/dist/core/dispatch-integration-event.js +136 -0
- package/dist/core/dispatch-integration-event.js.map +1 -0
- package/dist/core/drain-cron-schedules.d.ts +30 -0
- package/dist/core/drain-cron-schedules.d.ts.map +1 -0
- package/dist/core/drain-cron-schedules.js +76 -0
- package/dist/core/drain-cron-schedules.js.map +1 -0
- package/dist/core/entities/cron-schedule.d.ts +11 -0
- package/dist/core/entities/cron-schedule.d.ts.map +1 -0
- package/dist/core/entities/cron-schedule.js +3 -0
- package/dist/core/entities/cron-schedule.js.map +1 -0
- package/dist/core/entities/decision.d.ts +23 -0
- package/dist/core/entities/decision.d.ts.map +1 -0
- package/dist/core/entities/decision.js +7 -0
- package/dist/core/entities/decision.js.map +1 -0
- package/dist/core/entities/index.d.ts +3 -0
- package/dist/core/entities/index.d.ts.map +1 -0
- package/dist/core/entities/index.js +3 -0
- package/dist/core/entities/index.js.map +1 -0
- package/dist/core/entities/job-listener-subscription.d.ts +14 -0
- package/dist/core/entities/job-listener-subscription.d.ts.map +1 -0
- package/dist/core/entities/job-listener-subscription.js +3 -0
- package/dist/core/entities/job-listener-subscription.js.map +1 -0
- package/dist/core/entities/received-event.d.ts +28 -0
- package/dist/core/entities/received-event.d.ts.map +1 -0
- package/dist/core/entities/received-event.js +14 -0
- package/dist/core/entities/received-event.js.map +1 -0
- package/dist/core/entities/subscription.d.ts +13 -0
- package/dist/core/entities/subscription.d.ts.map +1 -0
- package/dist/core/entities/subscription.js +3 -0
- package/dist/core/entities/subscription.js.map +1 -0
- package/dist/core/errors.d.ts +25 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +41 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/fire-cron.d.ts +28 -0
- package/dist/core/fire-cron.d.ts.map +1 -0
- package/dist/core/fire-cron.js +85 -0
- package/dist/core/fire-cron.js.map +1 -0
- package/dist/core/fire-manual.d.ts +9 -0
- package/dist/core/fire-manual.d.ts.map +1 -0
- package/dist/core/fire-manual.js +87 -0
- package/dist/core/fire-manual.js.map +1 -0
- package/dist/core/index.d.ts +11 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +10 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/record-trigger-history.d.ts +35 -0
- package/dist/core/record-trigger-history.d.ts.map +1 -0
- package/dist/core/record-trigger-history.js +75 -0
- package/dist/core/record-trigger-history.js.map +1 -0
- package/dist/core/route-event-to-job-listeners.d.ts +31 -0
- package/dist/core/route-event-to-job-listeners.d.ts.map +1 -0
- package/dist/core/route-event-to-job-listeners.js +140 -0
- package/dist/core/route-event-to-job-listeners.js.map +1 -0
- package/dist/db/cron-schedules.d.ts +36 -0
- package/dist/db/cron-schedules.d.ts.map +1 -0
- package/dist/db/cron-schedules.js +104 -0
- package/dist/db/cron-schedules.js.map +1 -0
- package/dist/db/db.d.ts +2515 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +28 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/event-history.d.ts +50 -0
- package/dist/db/event-history.d.ts.map +1 -0
- package/dist/db/event-history.js +207 -0
- package/dist/db/event-history.js.map +1 -0
- package/dist/db/event-queries.d.ts +38 -0
- package/dist/db/event-queries.d.ts.map +1 -0
- package/dist/db/event-queries.js +78 -0
- package/dist/db/event-queries.js.map +1 -0
- package/dist/db/index.d.ts +11 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +14 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/job-listener-subscriptions.d.ts +24 -0
- package/dist/db/job-listener-subscriptions.d.ts.map +1 -0
- package/dist/db/job-listener-subscriptions.js +69 -0
- package/dist/db/job-listener-subscriptions.js.map +1 -0
- package/dist/db/received-events.d.ts +7 -0
- package/dist/db/received-events.d.ts.map +1 -0
- package/dist/db/received-events.js +11 -0
- package/dist/db/received-events.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/cron-schedules.d.ts +148 -0
- package/dist/db/schema/cron-schedules.d.ts.map +1 -0
- package/dist/db/schema/cron-schedules.js +39 -0
- package/dist/db/schema/cron-schedules.js.map +1 -0
- package/dist/db/schema/decisions.d.ts +284 -0
- package/dist/db/schema/decisions.d.ts.map +1 -0
- package/dist/db/schema/decisions.js +70 -0
- package/dist/db/schema/decisions.js.map +1 -0
- package/dist/db/schema/job-listener-subscriptions.d.ts +185 -0
- package/dist/db/schema/job-listener-subscriptions.d.ts.map +1 -0
- package/dist/db/schema/job-listener-subscriptions.js +41 -0
- package/dist/db/schema/job-listener-subscriptions.js.map +1 -0
- package/dist/db/schema/outbox.d.ts +195 -0
- package/dist/db/schema/outbox.d.ts.map +1 -0
- package/dist/db/schema/outbox.js +5 -0
- package/dist/db/schema/outbox.js.map +1 -0
- package/dist/db/schema/received-events.d.ts +544 -0
- package/dist/db/schema/received-events.d.ts.map +1 -0
- package/dist/db/schema/received-events.js +86 -0
- package/dist/db/schema/received-events.js.map +1 -0
- package/dist/db/schema/subscriptions.d.ts +182 -0
- package/dist/db/schema/subscriptions.d.ts.map +1 -0
- package/dist/db/schema/subscriptions.js +39 -0
- package/dist/db/schema/subscriptions.js.map +1 -0
- package/dist/db/subscriptions.d.ts +26 -0
- package/dist/db/subscriptions.d.ts.map +1 -0
- package/dist/db/subscriptions.js +103 -0
- package/dist/db/subscriptions.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/index.d.ts +3 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +4 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instance.d.ts +15 -0
- package/dist/metrics/instance.d.ts.map +1 -0
- package/dist/metrics/instance.js +32 -0
- package/dist/metrics/instance.js.map +1 -0
- package/dist/metrics/service.d.ts +2 -0
- package/dist/metrics/service.d.ts.map +1 -0
- package/dist/metrics/service.js +15 -0
- package/dist/metrics/service.js.map +1 -0
- package/dist/presentation/dto/trigger-events.d.ts +7 -0
- package/dist/presentation/dto/trigger-events.d.ts.map +1 -0
- package/dist/presentation/dto/trigger-events.js +47 -0
- package/dist/presentation/dto/trigger-events.js.map +1 -0
- package/dist/presentation/index.d.ts +3 -0
- package/dist/presentation/index.d.ts.map +1 -0
- package/dist/presentation/index.js +4 -0
- package/dist/presentation/index.js.map +1 -0
- package/dist/presentation/routes/fire-manual.d.ts +2 -0
- package/dist/presentation/routes/fire-manual.d.ts.map +1 -0
- package/dist/presentation/routes/fire-manual.js +71 -0
- package/dist/presentation/routes/fire-manual.js.map +1 -0
- package/dist/presentation/routes/get-trigger-event.d.ts +2 -0
- package/dist/presentation/routes/get-trigger-event.d.ts.map +1 -0
- package/dist/presentation/routes/get-trigger-event.js +37 -0
- package/dist/presentation/routes/get-trigger-event.js.map +1 -0
- package/dist/presentation/routes/index.d.ts +3 -0
- package/dist/presentation/routes/index.d.ts.map +1 -0
- package/dist/presentation/routes/index.js +27 -0
- package/dist/presentation/routes/index.js.map +1 -0
- package/dist/presentation/routes/list-trigger-event-facets.d.ts +2 -0
- package/dist/presentation/routes/list-trigger-event-facets.d.ts.map +1 -0
- package/dist/presentation/routes/list-trigger-event-facets.js +29 -0
- package/dist/presentation/routes/list-trigger-event-facets.js.map +1 -0
- package/dist/presentation/routes/list-trigger-events.d.ts +2 -0
- package/dist/presentation/routes/list-trigger-events.d.ts.map +1 -0
- package/dist/presentation/routes/list-trigger-events.js +56 -0
- package/dist/presentation/routes/list-trigger-events.js.map +1 -0
- package/dist/presentation/subscribers/index.d.ts +6 -0
- package/dist/presentation/subscribers/index.d.ts.map +1 -0
- package/dist/presentation/subscribers/index.js +7 -0
- package/dist/presentation/subscribers/index.js.map +1 -0
- package/dist/presentation/subscribers/on-definition-deleted.d.ts +3 -0
- package/dist/presentation/subscribers/on-definition-deleted.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-definition-deleted.js +8 -0
- package/dist/presentation/subscribers/on-definition-deleted.js.map +1 -0
- package/dist/presentation/subscribers/on-definition-resolved.d.ts +3 -0
- package/dist/presentation/subscribers/on-definition-resolved.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-definition-resolved.js +11 -0
- package/dist/presentation/subscribers/on-definition-resolved.js.map +1 -0
- package/dist/presentation/subscribers/on-integration-event-received.d.ts +4 -0
- package/dist/presentation/subscribers/on-integration-event-received.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-integration-event-received.js +17 -0
- package/dist/presentation/subscribers/on-integration-event-received.js.map +1 -0
- package/dist/presentation/subscribers/on-job-activated.d.ts +3 -0
- package/dist/presentation/subscribers/on-job-activated.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-job-activated.js +13 -0
- package/dist/presentation/subscribers/on-job-activated.js.map +1 -0
- package/dist/presentation/subscribers/on-job-terminated.d.ts +3 -0
- package/dist/presentation/subscribers/on-job-terminated.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-job-terminated.js +6 -0
- package/dist/presentation/subscribers/on-job-terminated.js.map +1 -0
- package/dist/temporal/activities/drain-cron-batch.d.ts +4 -0
- package/dist/temporal/activities/drain-cron-batch.d.ts.map +1 -0
- package/dist/temporal/activities/drain-cron-batch.js +20 -0
- package/dist/temporal/activities/drain-cron-batch.js.map +1 -0
- package/dist/temporal/activities/index.d.ts +10 -0
- package/dist/temporal/activities/index.d.ts.map +1 -0
- package/dist/temporal/activities/index.js +15 -0
- package/dist/temporal/activities/index.js.map +1 -0
- package/dist/temporal/activities/prune-trigger-events.d.ts +4 -0
- package/dist/temporal/activities/prune-trigger-events.d.ts.map +1 -0
- package/dist/temporal/activities/prune-trigger-events.js +11 -0
- package/dist/temporal/activities/prune-trigger-events.js.map +1 -0
- package/dist/temporal/constants.d.ts +7 -0
- package/dist/temporal/constants.d.ts.map +1 -0
- package/dist/temporal/constants.js +7 -0
- package/dist/temporal/constants.js.map +1 -0
- package/dist/temporal/workflows/cron-tick-cron.d.ts +9 -0
- package/dist/temporal/workflows/cron-tick-cron.d.ts.map +1 -0
- package/dist/temporal/workflows/cron-tick-cron.js +54 -0
- package/dist/temporal/workflows/cron-tick-cron.js.map +1 -0
- package/dist/temporal/workflows/index.d.ts +3 -0
- package/dist/temporal/workflows/index.d.ts.map +1 -0
- package/dist/temporal/workflows/index.js +4 -0
- package/dist/temporal/workflows/index.js.map +1 -0
- package/dist/temporal/workflows/prune-trigger-events-cron.d.ts +2 -0
- package/dist/temporal/workflows/prune-trigger-events-cron.d.ts.map +1 -0
- package/dist/temporal/workflows/prune-trigger-events-cron.js +18 -0
- package/dist/temporal/workflows/prune-trigger-events-cron.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_initial.sql +109 -0
- package/drizzle/meta/0000_snapshot.json +905 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +14 -0
- package/package.json +83 -0
- package/src/config.test.ts +33 -0
- package/src/config.ts +53 -0
- package/src/core/compute-next-fire-at.test.ts +69 -0
- package/src/core/compute-next-fire-at.ts +37 -0
- package/src/core/config.test.ts +213 -0
- package/src/core/config.ts +86 -0
- package/src/core/dispatch-integration-event.history-failure.test.ts +66 -0
- package/src/core/dispatch-integration-event.test.ts +1006 -0
- package/src/core/dispatch-integration-event.ts +150 -0
- package/src/core/drain-cron-schedules.test.ts +265 -0
- package/src/core/drain-cron-schedules.ts +90 -0
- package/src/core/entities/cron-schedule.ts +10 -0
- package/src/core/entities/decision.ts +24 -0
- package/src/core/entities/index.ts +5 -0
- package/src/core/entities/job-listener-subscription.ts +14 -0
- package/src/core/entities/received-event.ts +36 -0
- package/src/core/entities/subscription.ts +12 -0
- package/src/core/errors.ts +63 -0
- package/src/core/fire-cron.test.ts +146 -0
- package/src/core/fire-cron.ts +98 -0
- package/src/core/fire-manual.test.ts +157 -0
- package/src/core/fire-manual.ts +92 -0
- package/src/core/index.ts +34 -0
- package/src/core/record-trigger-history.test.ts +159 -0
- package/src/core/record-trigger-history.ts +145 -0
- package/src/core/route-event-to-job-listeners.test.ts +369 -0
- package/src/core/route-event-to-job-listeners.ts +184 -0
- package/src/db/cron-schedules.test.ts +358 -0
- package/src/db/cron-schedules.ts +163 -0
- package/src/db/db.ts +31 -0
- package/src/db/event-history.test.ts +421 -0
- package/src/db/event-history.ts +279 -0
- package/src/db/event-queries.ts +140 -0
- package/src/db/index.ts +51 -0
- package/src/db/job-listener-subscriptions.test.ts +270 -0
- package/src/db/job-listener-subscriptions.ts +133 -0
- package/src/db/received-events.ts +16 -0
- package/src/db/schema/common.ts +3 -0
- package/src/db/schema/cron-schedules.ts +37 -0
- package/src/db/schema/decisions.test.ts +279 -0
- package/src/db/schema/decisions.ts +77 -0
- package/src/db/schema/job-listener-subscriptions.ts +65 -0
- package/src/db/schema/outbox.ts +4 -0
- package/src/db/schema/received-events.test.ts +135 -0
- package/src/db/schema/received-events.ts +93 -0
- package/src/db/schema/subscriptions.ts +46 -0
- package/src/db/subscriptions.test.ts +340 -0
- package/src/db/subscriptions.ts +179 -0
- package/src/index.ts +108 -0
- package/src/metrics/index.ts +8 -0
- package/src/metrics/instance.ts +33 -0
- package/src/metrics/service.ts +18 -0
- package/src/presentation/dto/trigger-events.test.ts +144 -0
- package/src/presentation/dto/trigger-events.ts +60 -0
- package/src/presentation/index.ts +6 -0
- package/src/presentation/routes/fire-manual.test.ts +118 -0
- package/src/presentation/routes/fire-manual.ts +70 -0
- package/src/presentation/routes/get-trigger-event.test.ts +184 -0
- package/src/presentation/routes/get-trigger-event.ts +37 -0
- package/src/presentation/routes/index.ts +21 -0
- package/src/presentation/routes/list-trigger-event-facets.test.ts +116 -0
- package/src/presentation/routes/list-trigger-event-facets.ts +29 -0
- package/src/presentation/routes/list-trigger-events.test.ts +379 -0
- package/src/presentation/routes/list-trigger-events.ts +68 -0
- package/src/presentation/subscribers/index.ts +5 -0
- package/src/presentation/subscribers/on-definition-deleted.test.ts +42 -0
- package/src/presentation/subscribers/on-definition-deleted.ts +6 -0
- package/src/presentation/subscribers/on-definition-resolved.test.ts +67 -0
- package/src/presentation/subscribers/on-definition-resolved.ts +11 -0
- package/src/presentation/subscribers/on-integration-event-received.test.ts +53 -0
- package/src/presentation/subscribers/on-integration-event-received.ts +21 -0
- package/src/presentation/subscribers/on-job-activated.ts +14 -0
- package/src/presentation/subscribers/on-job-terminated.ts +6 -0
- package/src/temporal/activities/drain-cron-batch.ts +20 -0
- package/src/temporal/activities/index.ts +15 -0
- package/src/temporal/activities/prune-trigger-events.test.ts +78 -0
- package/src/temporal/activities/prune-trigger-events.ts +9 -0
- package/src/temporal/constants.ts +7 -0
- package/src/temporal/workflows/cron-tick-cron.ts +56 -0
- package/src/temporal/workflows/index.ts +2 -0
- package/src/temporal/workflows/prune-trigger-events-cron.ts +17 -0
- package/test/env.ts +8 -0
- package/test/factories/cron-schedule.ts +34 -0
- package/test/factories/decision.ts +49 -0
- package/test/factories/job-listener-subscription.ts +42 -0
- package/test/factories/received-event.ts +49 -0
- package/test/factories/subscription.ts +38 -0
- package/test/globalSetup.ts +20 -0
- package/test/index.ts +5 -0
- package/test/setup.ts +17 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ shipfox-tsc-emit
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ shipfox-tsc-check
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# @shipfox/api-triggers
|
|
2
|
+
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1b0d344: Publishes the complete API runtime closure with packed-consumer-safe internal imports and records its exact package set in application releases.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [0cd6dd4]
|
|
12
|
+
- Updated dependencies [a68458a]
|
|
13
|
+
- Updated dependencies [6eba800]
|
|
14
|
+
- Updated dependencies [1b0d344]
|
|
15
|
+
- Updated dependencies [521e006]
|
|
16
|
+
- @shipfox/node-module@0.2.0
|
|
17
|
+
- @shipfox/node-temporal@0.2.0
|
|
18
|
+
- @shipfox/api-auth-context@2.0.0
|
|
19
|
+
- @shipfox/api-definitions-dto@2.0.0
|
|
20
|
+
- @shipfox/api-integration-core-dto@2.0.0
|
|
21
|
+
- @shipfox/api-projects@2.0.0
|
|
22
|
+
- @shipfox/api-triggers-dto@2.0.0
|
|
23
|
+
- @shipfox/api-workflows@2.0.0
|
|
24
|
+
- @shipfox/api-workflows-dto@2.0.0
|
|
25
|
+
- @shipfox/config@1.2.1
|
|
26
|
+
- @shipfox/expression@1.1.1
|
|
27
|
+
- @shipfox/node-drizzle@0.2.1
|
|
28
|
+
- @shipfox/node-fastify@0.2.1
|
|
29
|
+
- @shipfox/node-opentelemetry@0.5.0
|
|
30
|
+
- @shipfox/node-outbox@0.2.1
|
|
31
|
+
- @shipfox/node-postgres@0.4.1
|
|
32
|
+
- @shipfox/workflow-document@2.0.1
|
|
33
|
+
|
|
34
|
+
## 0.1.2
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [705dd43]
|
|
39
|
+
- @shipfox/node-outbox@0.2.0
|
|
40
|
+
- @shipfox/api-projects@0.1.2
|
|
41
|
+
- @shipfox/api-workflows@0.1.2
|
|
42
|
+
- @shipfox/node-module@0.1.2
|
|
43
|
+
|
|
44
|
+
## 0.1.1
|
|
45
|
+
|
|
46
|
+
### Patch Changes
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [ec75cd5]
|
|
49
|
+
- Updated dependencies [6a1fb54]
|
|
50
|
+
- @shipfox/node-drizzle@0.2.0
|
|
51
|
+
- @shipfox/node-postgres@0.4.0
|
|
52
|
+
- @shipfox/api-projects@0.1.1
|
|
53
|
+
- @shipfox/api-workflows@0.1.1
|
|
54
|
+
- @shipfox/node-module@0.1.1
|
|
55
|
+
- @shipfox/node-outbox@0.1.1
|
|
56
|
+
|
|
57
|
+
## 0.1.0
|
|
58
|
+
|
|
59
|
+
### Minor Changes
|
|
60
|
+
|
|
61
|
+
- a460020: Add trigger event detail decisions with stored subscription names, run links, and payload inspection.
|
|
62
|
+
- 5ec8367: Adds trigger event inspection endpoints with matching DTO schemas for listing received events and reading event decisions.
|
|
63
|
+
|
|
64
|
+
### Patch Changes
|
|
65
|
+
|
|
66
|
+
- ae7a63c: Adds daily dispatched outbox row retention with bounded cleanup batches and retention indexes on module outbox tables.
|
|
67
|
+
- 555299c: Add the append-only `triggers_received_events` and `triggers_decisions` tables that back the trigger event history. DB layer only: Drizzle schema, inferred types, and row→domain mappers, folded into the triggers module's baseline migration. No write or read path is wired yet.
|
|
68
|
+
- 9a4807d: Add the hourly Temporal prune cron and `TRIGGER_EVENT_RETENTION_DAYS` config var that bound trigger event history growth. The cron deletes `triggers_received_events` older than the retention window (default 30 days); `triggers_decisions` go with them via FK cascade.
|
|
69
|
+
- e5d2f13: Add the workspace **Events** page in Settings: a filterable, cursor-paginated table of
|
|
70
|
+
trigger events (status dot, source/event, routing summary, delivery id, received time)
|
|
71
|
+
mounted at `/workspaces/$wid/settings/events` and wired into the settings sub-nav. Filters
|
|
72
|
+
(date range, source, event, outcome) live in the URL via `validateSearch`, so a filtered
|
|
73
|
+
view is shareable. Source and event filters are populated by a new
|
|
74
|
+
`GET /trigger-events/facets` endpoint that returns each workspace's distinct source/event
|
|
75
|
+
values with counts (top 50, backed by `(workspace_id, source)` / `(workspace_id, event)`
|
|
76
|
+
indexes); the list still renders if facets fail to load.
|
|
77
|
+
- a982f20: Stop a permanently-broken trigger subscription from starving its siblings or wedging the outbox. Integration dispatch now attempts every matched subscription and classifies each `runWorkflow` failure: a permanent error (deleted definition or project mismatch) is recorded and skipped, while a transient one re-throws so the outbox replays the event and converges. The event reaches a terminal outcome once no transient error remains (`routed` when any run was created, otherwise the new `errored` outcome), with a guarded write that never records `errored` over an event that already produced a run. The manual-fire path records the same terminal outcome, and `@shipfox/api-workflows` exports an `isPermanentRunWorkflowError` classifier. The trigger-events read API (`triggerEventOutcomeSchema`) accepts the new `errored` outcome for serialization and filtering.
|
|
78
|
+
- 3dcd751: Adds listener filter snapshots to job activation events and persists them on listener subscriptions.
|
|
79
|
+
- 6077301: Adds shared timestamp/id keyset pagination helpers and migrates workflow run and trigger event lists onto them.
|
|
80
|
+
- 3bea87f: Adds a typed `subscriberFactory` that binds each outbox event name to its payload type at construction, so subscriber handlers receive a typed `(payload, event)` and the per-handler `event.payload as X` casts are gone; a private brand makes the factory the only way to build a module subscriber.
|
|
81
|
+
- b74f635: Adds workflow run interpolation context resolution while preserving authored step configuration for reruns and diagnostics.
|
|
82
|
+
- e192d86: Adds the cron firing engine: a once-per-minute tick fans out bounded drain activities that claim due schedules (FOR UPDATE SKIP LOCKED), advance their next fire time, and fire the workflow deduplicated and crash-safe, recorded in trigger history with a `cron` origin and surfaced through cron fire and backlog metrics.
|
|
83
|
+
- 638ac4d: Adds cron trigger schedule persistence and deterministic next-fire computation for resolved workflow definitions.
|
|
84
|
+
- Updated dependencies [eb40964]
|
|
85
|
+
- Updated dependencies [7bc7498]
|
|
86
|
+
- Updated dependencies [5c18360]
|
|
87
|
+
- Updated dependencies [2c156d2]
|
|
88
|
+
- Updated dependencies [26fea4b]
|
|
89
|
+
- Updated dependencies [0cf66c4]
|
|
90
|
+
- Updated dependencies [34ba284]
|
|
91
|
+
- Updated dependencies [a56748d]
|
|
92
|
+
- Updated dependencies [8f51daf]
|
|
93
|
+
- Updated dependencies [5707d6d]
|
|
94
|
+
- Updated dependencies [e689abf]
|
|
95
|
+
- Updated dependencies [59ba68b]
|
|
96
|
+
- Updated dependencies [7a9943d]
|
|
97
|
+
- Updated dependencies [ce3e5ca]
|
|
98
|
+
- Updated dependencies [f788565]
|
|
99
|
+
- Updated dependencies [b9c3f32]
|
|
100
|
+
- Updated dependencies [c17dd6e]
|
|
101
|
+
- Updated dependencies [a81b68c]
|
|
102
|
+
- Updated dependencies [115655e]
|
|
103
|
+
- Updated dependencies [c0a883c]
|
|
104
|
+
- Updated dependencies [72ce351]
|
|
105
|
+
- Updated dependencies [cdf8989]
|
|
106
|
+
- Updated dependencies [e47f8da]
|
|
107
|
+
- Updated dependencies [736249b]
|
|
108
|
+
- Updated dependencies [2bc5595]
|
|
109
|
+
- Updated dependencies [b1f57d1]
|
|
110
|
+
- Updated dependencies [1127ba2]
|
|
111
|
+
- Updated dependencies [36f871d]
|
|
112
|
+
- Updated dependencies [e7b01dd]
|
|
113
|
+
- Updated dependencies [d546b88]
|
|
114
|
+
- Updated dependencies [58c05ed]
|
|
115
|
+
- Updated dependencies [ce062a9]
|
|
116
|
+
- Updated dependencies [9086e65]
|
|
117
|
+
- Updated dependencies [7b175f5]
|
|
118
|
+
- Updated dependencies [7ca4c65]
|
|
119
|
+
- Updated dependencies [e9056c7]
|
|
120
|
+
- Updated dependencies [8e9c6cb]
|
|
121
|
+
- Updated dependencies [97162dd]
|
|
122
|
+
- Updated dependencies [b694b09]
|
|
123
|
+
- Updated dependencies [c47be09]
|
|
124
|
+
- Updated dependencies [f9f059e]
|
|
125
|
+
- Updated dependencies [940696a]
|
|
126
|
+
- Updated dependencies [f3614ae]
|
|
127
|
+
- Updated dependencies [f98c2be]
|
|
128
|
+
- Updated dependencies [e9396c9]
|
|
129
|
+
- Updated dependencies [ae7a63c]
|
|
130
|
+
- Updated dependencies [5729548]
|
|
131
|
+
- Updated dependencies [f92122b]
|
|
132
|
+
- Updated dependencies [e250c4c]
|
|
133
|
+
- Updated dependencies [b525dcd]
|
|
134
|
+
- Updated dependencies [f8f339a]
|
|
135
|
+
- Updated dependencies [857fd73]
|
|
136
|
+
- Updated dependencies [e5d2f13]
|
|
137
|
+
- Updated dependencies [a982f20]
|
|
138
|
+
- Updated dependencies [7fa8f0b]
|
|
139
|
+
- Updated dependencies [998eba3]
|
|
140
|
+
- Updated dependencies [5327934]
|
|
141
|
+
- Updated dependencies [314e84e]
|
|
142
|
+
- Updated dependencies [a460020]
|
|
143
|
+
- Updated dependencies [3afb7e3]
|
|
144
|
+
- Updated dependencies [139e3be]
|
|
145
|
+
- Updated dependencies [eb7d5e8]
|
|
146
|
+
- Updated dependencies [247cbd6]
|
|
147
|
+
- Updated dependencies [5d53ed4]
|
|
148
|
+
- Updated dependencies [c652a68]
|
|
149
|
+
- Updated dependencies [121b42e]
|
|
150
|
+
- Updated dependencies [75520ff]
|
|
151
|
+
- Updated dependencies [e87731a]
|
|
152
|
+
- Updated dependencies [795f440]
|
|
153
|
+
- Updated dependencies [3dcd751]
|
|
154
|
+
- Updated dependencies [c0a883c]
|
|
155
|
+
- Updated dependencies [362b3eb]
|
|
156
|
+
- Updated dependencies [b8e49ff]
|
|
157
|
+
- Updated dependencies [d6d4862]
|
|
158
|
+
- Updated dependencies [c0a883c]
|
|
159
|
+
- Updated dependencies [6077301]
|
|
160
|
+
- Updated dependencies [f85b223]
|
|
161
|
+
- Updated dependencies [9c1c947]
|
|
162
|
+
- Updated dependencies [f0afdf8]
|
|
163
|
+
- Updated dependencies [9d3b43a]
|
|
164
|
+
- Updated dependencies [d635979]
|
|
165
|
+
- Updated dependencies [3bea87f]
|
|
166
|
+
- Updated dependencies [82d22e4]
|
|
167
|
+
- Updated dependencies [d69b164]
|
|
168
|
+
- Updated dependencies [69d02e5]
|
|
169
|
+
- Updated dependencies [2fb3e87]
|
|
170
|
+
- Updated dependencies [01be723]
|
|
171
|
+
- Updated dependencies [f63c6b0]
|
|
172
|
+
- Updated dependencies [e0fee57]
|
|
173
|
+
- Updated dependencies [b74f635]
|
|
174
|
+
- Updated dependencies [fa67aa3]
|
|
175
|
+
- Updated dependencies [e192d86]
|
|
176
|
+
- Updated dependencies [9a5aac4]
|
|
177
|
+
- Updated dependencies [30d1c82]
|
|
178
|
+
- Updated dependencies [5ec8367]
|
|
179
|
+
- Updated dependencies [ef1e917]
|
|
180
|
+
- Updated dependencies [51eb38a]
|
|
181
|
+
- Updated dependencies [61de795]
|
|
182
|
+
- Updated dependencies [c6eb2ee]
|
|
183
|
+
- Updated dependencies [e2fbef8]
|
|
184
|
+
- Updated dependencies [8ecba0f]
|
|
185
|
+
- Updated dependencies [f9153e8]
|
|
186
|
+
- Updated dependencies [27770eb]
|
|
187
|
+
- Updated dependencies [2933c33]
|
|
188
|
+
- Updated dependencies [2ad300c]
|
|
189
|
+
- Updated dependencies [a314b05]
|
|
190
|
+
- Updated dependencies [43fd0c1]
|
|
191
|
+
- Updated dependencies [950ebef]
|
|
192
|
+
- Updated dependencies [6181819]
|
|
193
|
+
- Updated dependencies [a8905ed]
|
|
194
|
+
- Updated dependencies [1ea2f6a]
|
|
195
|
+
- Updated dependencies [e699508]
|
|
196
|
+
- Updated dependencies [ad6056b]
|
|
197
|
+
- Updated dependencies [282e66a]
|
|
198
|
+
- Updated dependencies [0dd23a7]
|
|
199
|
+
- Updated dependencies [9c149d1]
|
|
200
|
+
- Updated dependencies [f88aac9]
|
|
201
|
+
- Updated dependencies [e1d4972]
|
|
202
|
+
- Updated dependencies [f9bf446]
|
|
203
|
+
- Updated dependencies [a856155]
|
|
204
|
+
- Updated dependencies [78527ce]
|
|
205
|
+
- Updated dependencies [b8919da]
|
|
206
|
+
- Updated dependencies [8ecc121]
|
|
207
|
+
- Updated dependencies [d7b9596]
|
|
208
|
+
- @shipfox/workflow-document@2.0.0
|
|
209
|
+
- @shipfox/api-workflows@0.1.0
|
|
210
|
+
- @shipfox/api-workflows-dto@0.1.0
|
|
211
|
+
- @shipfox/expression@1.1.0
|
|
212
|
+
- @shipfox/node-fastify@0.2.0
|
|
213
|
+
- @shipfox/node-drizzle@0.1.0
|
|
214
|
+
- @shipfox/api-definitions-dto@0.0.1
|
|
215
|
+
- @shipfox/api-auth-context@0.1.0
|
|
216
|
+
- @shipfox/api-integration-core-dto@0.1.0
|
|
217
|
+
- @shipfox/api-projects@0.1.0
|
|
218
|
+
- @shipfox/node-opentelemetry@0.4.2
|
|
219
|
+
- @shipfox/node-postgres@0.3.2
|
|
220
|
+
- @shipfox/node-temporal@0.1.1
|
|
221
|
+
- @shipfox/node-module@0.1.0
|
|
222
|
+
- @shipfox/node-outbox@0.1.0
|
|
223
|
+
- @shipfox/api-triggers-dto@0.1.0
|
|
224
|
+
- @shipfox/config@1.2.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shipfox
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# Shipfox API Triggers
|
|
2
|
+
|
|
3
|
+
Shipfox API Triggers decides when a workflow run starts. It projects the
|
|
4
|
+
`triggers` map from each workflow YAML into an indexed subscription table,
|
|
5
|
+
matches incoming integration events against those subscriptions, and starts
|
|
6
|
+
a workflow run for every match. It also exposes HTTP routes that fire manual
|
|
7
|
+
runs and inspect received trigger events.
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
|
|
11
|
+
Register the module with the API module runner:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {triggersModule} from '@shipfox/api-triggers';
|
|
15
|
+
import {createApp, listen} from '@shipfox/node-fastify';
|
|
16
|
+
import {initializeModules, startModuleWorkers} from '@shipfox/node-module';
|
|
17
|
+
|
|
18
|
+
const {auth, routes, workers} = await initializeModules({
|
|
19
|
+
modules: [triggersModule /* and other modules */],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await createApp({auth, routes});
|
|
23
|
+
await startModuleWorkers({workers});
|
|
24
|
+
await listen();
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This adds:
|
|
28
|
+
|
|
29
|
+
- triggers database migrations from `libs/api/triggers/drizzle`
|
|
30
|
+
- the `POST /workflow-definitions/:definitionId/fire-manual` route
|
|
31
|
+
- the `GET /trigger-events`, `GET /trigger-events/facets`, and `GET /trigger-events/:id` inspection routes
|
|
32
|
+
- subscribers for `DEFINITION_RESOLVED`, `DEFINITION_DELETED`, and
|
|
33
|
+
`INTEGRATION_EVENT_RECEIVED`
|
|
34
|
+
- the `triggers` outbox publisher
|
|
35
|
+
- the hourly `triggers-prune-trigger-events` Temporal cron, which deletes old
|
|
36
|
+
rows from `triggers_received_events`
|
|
37
|
+
|
|
38
|
+
A workflow YAML opts into triggers like this:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
triggers:
|
|
42
|
+
on_demand:
|
|
43
|
+
source: manual
|
|
44
|
+
on_push:
|
|
45
|
+
source: github_acme
|
|
46
|
+
event: push
|
|
47
|
+
on: main
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Each source is an integration connection slug. Each map key is the trigger's `name`. A workflow may declare any number of
|
|
51
|
+
integration triggers and at most one `source: manual` trigger; the manual
|
|
52
|
+
invariant is enforced at parse time so the fire route stays unambiguous.
|
|
53
|
+
The `event` field is optional when `source: manual` and defaults to
|
|
54
|
+
`fire`.
|
|
55
|
+
|
|
56
|
+
Integration triggers may include a CEL `filter` predicate. Dispatch evaluates
|
|
57
|
+
the predicate for every subscription that matches `(workspace_id, source,
|
|
58
|
+
event)` with the context `{event: payload, trigger: {source, event}}`. A
|
|
59
|
+
missing filter matches; `true` creates a run; `false` or a non-boolean result
|
|
60
|
+
skips the subscription and can leave the event `discarded`; an evaluation
|
|
61
|
+
failure records a `filter-error` decision and fails closed without creating a
|
|
62
|
+
run.
|
|
63
|
+
|
|
64
|
+
Webhook triggers use the webhook connection slug as `source` and the fixed
|
|
65
|
+
event name `received`. A delivery to `POST /webhook/:connectionId` publishes
|
|
66
|
+
an `INTEGRATION_EVENT_RECEIVED` envelope with `provider: webhook`,
|
|
67
|
+
`source: <connection.slug>`, `event: received`, and payload
|
|
68
|
+
`{method, headers, query, body}`. Trigger dispatch matches only on
|
|
69
|
+
`(workspace_id, source, event)`, so a workflow that listens to a webhook uses
|
|
70
|
+
the slug plus `event: received`:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
triggers:
|
|
74
|
+
on_stripe:
|
|
75
|
+
source: stripe_prod
|
|
76
|
+
event: received
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Workflow interpolation exposes the webhook envelope as `event`, so steps can
|
|
80
|
+
read values such as `${{ event.body.payment_id }}` and
|
|
81
|
+
`${{ event.headers["x-stripe-signature"] }}`. Header/body-derived event
|
|
82
|
+
subtypes are not part of this contract.
|
|
83
|
+
|
|
84
|
+
GitHub triggers use the raw GitHub webhook resource name, plus the payload
|
|
85
|
+
`action` when one is present. For example, a pull request open event is
|
|
86
|
+
`pull_request.opened`, an issue close event is `issues.closed`, and a
|
|
87
|
+
release publish event is `release.published`. Events without an action use
|
|
88
|
+
the bare resource name, such as `push` or `fork`. Common GitHub events are:
|
|
89
|
+
`push`, `pull_request.opened`, `pull_request.closed`, `issues.opened`,
|
|
90
|
+
`issues.closed`, `issue_comment.created`, `release.published`,
|
|
91
|
+
`workflow_run.completed`, `installation.created`, and
|
|
92
|
+
`installation_repositories.added`. GitHub only sends events that the GitHub
|
|
93
|
+
App is subscribed to in its Permissions & events settings.
|
|
94
|
+
|
|
95
|
+
## Setup
|
|
96
|
+
|
|
97
|
+
Install the package from the registry:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
pnpm add @shipfox/api-triggers
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The matching HTTP contract lives in
|
|
104
|
+
[`@shipfox/api-triggers-dto`](../triggers-dto). Import Zod schemas and DTO
|
|
105
|
+
types from there when you call the route from the client.
|
|
106
|
+
|
|
107
|
+
The package reads `TRIGGER_EVENT_RETENTION_DAYS` to decide how many days of
|
|
108
|
+
received trigger events to keep before the maintenance cron deletes them.
|
|
109
|
+
It also depends on the API database connection from `@shipfox/node-postgres`.
|
|
110
|
+
|
|
111
|
+
## Routes
|
|
112
|
+
|
|
113
|
+
The routes are mounted by the host app under the `/workflow-definitions` and
|
|
114
|
+
`/trigger-events` prefixes.
|
|
115
|
+
|
|
116
|
+
| Method | Path | Auth | Result |
|
|
117
|
+
| --- | --- | --- | --- |
|
|
118
|
+
| `POST` | `/workflow-definitions/:definitionId/fire-manual` | bearer token | Fires the workflow's manual trigger and returns the new `workflow_run_id`. Optional `inputs` in the body are forwarded to the run. |
|
|
119
|
+
| `GET` | `/trigger-events?workspace_id=:workspaceId` | bearer token | Lists received trigger events for a workspace, newest first. Supports source, event, outcome, received-at window, limit, and cursor filters. |
|
|
120
|
+
| `GET` | `/trigger-events/facets?workspace_id=:workspaceId` | bearer token | Returns the workspace's distinct `source` and `event` filter values with counts (top 50 each, by count). Backs the Events page filter dropdowns. |
|
|
121
|
+
| `GET` | `/trigger-events/:id` | bearer token | Returns one received trigger event with its full payload and routing decisions. Cross-workspace ids return `404`. |
|
|
122
|
+
|
|
123
|
+
The manual route is keyed by workflow definition id, not subscription id. The
|
|
124
|
+
server resolves the manual subscription for the workflow internally; the
|
|
125
|
+
"at most one manual trigger per workflow" invariant from the parser keeps
|
|
126
|
+
that lookup unambiguous. Integration sources (github, etc.) fire through
|
|
127
|
+
the event bus and have no HTTP entry point.
|
|
128
|
+
|
|
129
|
+
## Vocabulary
|
|
130
|
+
|
|
131
|
+
Three words, used the same way at every layer.
|
|
132
|
+
|
|
133
|
+
| Word | Meaning | Examples |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| **source** | Where the trigger came from. | `github`, `gitlab`, `sentry`, `manual`, `cron` |
|
|
136
|
+
| **event** | The specific thing that happened, scoped to a source. | `push`, `issue_comment`, `alert_triggered`, `fire`, `tick` |
|
|
137
|
+
| **payload** | The data carried by the event, set by the producing integration. Triggers passes it through opaquely. | GitHub's raw webhook JSON for `(github, push)` |
|
|
138
|
+
|
|
139
|
+
The `name` field on a subscription is the YAML map key (for example
|
|
140
|
+
`on_push`). It identifies the trigger inside a workflow definition and is
|
|
141
|
+
unique per `(workflow_definition_id, name)`.
|
|
142
|
+
|
|
143
|
+
### Words we do not use
|
|
144
|
+
|
|
145
|
+
- `provider` — reserved for the integration module's identity
|
|
146
|
+
(`integrations_connections.provider`). Trigger code says `source`.
|
|
147
|
+
- `eventType` / `type` — replaced by `event`. The bare word `type` would
|
|
148
|
+
collide with TypeScript discriminators and with the `IntegrationProvider`
|
|
149
|
+
capability `type` field.
|
|
150
|
+
- `triggerContext` — the runtime payload on a run is called
|
|
151
|
+
`triggerPayload`.
|
|
152
|
+
- `kind` — every trigger is identified by `(source, event)`. There is no
|
|
153
|
+
separate axis.
|
|
154
|
+
|
|
155
|
+
## Architecture
|
|
156
|
+
|
|
157
|
+
Triggers flow through three layers. The YAML map is the source of truth,
|
|
158
|
+
the subscription table is the queryable projection, and the workflow run
|
|
159
|
+
table is the immutable history.
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
┌─────────────────────────────────────────────────────┐
|
|
163
|
+
│ workflow_definitions.definition (JSONB) │
|
|
164
|
+
│ triggers: {on_push: {source, event, ...}} │
|
|
165
|
+
└──────────────────────────┬──────────────────────────┘
|
|
166
|
+
│
|
|
167
|
+
│ DEFINITION_RESOLVED
|
|
168
|
+
│ / DEFINITION_DELETED
|
|
169
|
+
▼
|
|
170
|
+
┌─────────────────────────────────────────────────────┐
|
|
171
|
+
│ triggers_subscriptions │
|
|
172
|
+
│ (workspace_id, project_id, │
|
|
173
|
+
│ workflow_definition_id, name, │
|
|
174
|
+
│ source, event, config) │
|
|
175
|
+
└──────────────────────────┬──────────────────────────┘
|
|
176
|
+
│
|
|
177
|
+
INTEGRATION_EVENT_RECEIVED → match on (workspace, source, event)
|
|
178
|
+
or POST /workflow-definitions/:definitionId/fire-manual → look up manual subscription
|
|
179
|
+
│
|
|
180
|
+
▼
|
|
181
|
+
┌─────────────────────────────────────────────────────┐
|
|
182
|
+
│ workflow_runs │
|
|
183
|
+
│ trigger_source, trigger_event (indexed text) │
|
|
184
|
+
│ trigger_payload (jsonb) │
|
|
185
|
+
└─────────────────────────────────────────────────────┘
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Layer 1 — workflow definition (source of truth)
|
|
189
|
+
|
|
190
|
+
The YAML `triggers` map lives inside `workflow_definitions.definition`
|
|
191
|
+
(JSONB owned by the definitions module). That is the only place trigger
|
|
192
|
+
declarations live in raw form.
|
|
193
|
+
|
|
194
|
+
### Layer 2 — projection (queryable)
|
|
195
|
+
|
|
196
|
+
`triggers_subscriptions` is rebuilt from `DEFINITION_RESOLVED` events,
|
|
197
|
+
which carry the parsed `triggers` map. The triggers module never reads the
|
|
198
|
+
definitions table — the event is the contract.
|
|
199
|
+
|
|
200
|
+
Cron triggers also project into `triggers_cron_schedules`, keyed by
|
|
201
|
+
`subscription_id`. That table stores the resolved cron expression, timezone,
|
|
202
|
+
next fire time, and last fire time used by the cron firing engine.
|
|
203
|
+
|
|
204
|
+
Indexes:
|
|
205
|
+
|
|
206
|
+
- `(workflow_definition_id, name)` — unique. One row per YAML trigger.
|
|
207
|
+
- `(workspace_id, source, event)` — the hot path for matching incoming
|
|
208
|
+
integration events at workspace scope.
|
|
209
|
+
- `(workflow_definition_id)` — used to clean up the projection on
|
|
210
|
+
`DEFINITION_DELETED`.
|
|
211
|
+
- `triggers_cron_schedules.next_fire_at` — used to drain due cron schedules
|
|
212
|
+
in next-fire order.
|
|
213
|
+
|
|
214
|
+
### Layer 3 — run history (immutable)
|
|
215
|
+
|
|
216
|
+
`workflow_runs.trigger_source` and `trigger_event` are indexed text
|
|
217
|
+
columns. `trigger_payload` is a JSONB column typed by `TriggerPayload`:
|
|
218
|
+
`manual`/`cron` carry their own typed shapes, while integration events use
|
|
219
|
+
a generic `{source, event, deliveryId, data}` shape that forwards the raw
|
|
220
|
+
event payload as `data`. The `triggerSource` value on a row always equals
|
|
221
|
+
`triggerPayload.source`. The duplication is deliberate: the indexed column
|
|
222
|
+
is for filtering, the payload is for inspection.
|
|
223
|
+
|
|
224
|
+
## Events
|
|
225
|
+
|
|
226
|
+
| Event | Published by | Consumed by | Purpose |
|
|
227
|
+
| --- | --- | --- | --- |
|
|
228
|
+
| `INTEGRATION_EVENT_RECEIVED` | `integration/*` | triggers, projects | An integration received a webhook and validated it. The payload is the integration's domain event. |
|
|
229
|
+
| `DEFINITION_RESOLVED` | definitions | triggers | A workflow definition was created or updated. The payload includes the parsed `triggers` map. |
|
|
230
|
+
| `DEFINITION_DELETED` | definitions | triggers | A workflow definition was soft-deleted. Subscription rows are removed. |
|
|
231
|
+
|
|
232
|
+
## API
|
|
233
|
+
|
|
234
|
+
The package exports the module entry point:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import {triggersModule} from '@shipfox/api-triggers';
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
It also exports lower-level pieces for tests and advanced wiring:
|
|
241
|
+
|
|
242
|
+
- `fireManualSubscription()`: core function used by the route. Throws
|
|
243
|
+
`TriggerSubscriptionNotFoundError`,
|
|
244
|
+
`TriggerSubscriptionNotManualError`, or
|
|
245
|
+
`TriggerWorkspaceMismatchError`.
|
|
246
|
+
- `ManualTriggerNotFoundError`: thrown by the route handler when the
|
|
247
|
+
caller's workspace cannot reach the workflow, or the workflow declares
|
|
248
|
+
no manual trigger. Surfaced as `404 manual-trigger-not-found`.
|
|
249
|
+
- `findMatchingSubscriptions()`: hot-path lookup by
|
|
250
|
+
`(workspace_id, source, event)`.
|
|
251
|
+
- `getManualSubscriptionByDefinitionId()`: resolves the single manual
|
|
252
|
+
subscription for a workflow definition (or `undefined` if none).
|
|
253
|
+
- `getTriggerSubscriptionById()` and
|
|
254
|
+
`listSubscriptionsByWorkflowDefinitionIds()`: read helpers.
|
|
255
|
+
- `db`, `migrationsPath`, and `triggersOutbox`: the Drizzle handle,
|
|
256
|
+
migration path, and outbox table.
|
|
257
|
+
- Entity type: `TriggerSubscription`.
|
|
258
|
+
|
|
259
|
+
## Adding a new source
|
|
260
|
+
|
|
261
|
+
To wire a new integration source (for example GitLab):
|
|
262
|
+
|
|
263
|
+
1. Add a payload type for each supported event to
|
|
264
|
+
`@shipfox/api-integration-core-dto` (for example `GitlabPushPayload`).
|
|
265
|
+
2. In the new `integration/<source>` package, receive webhooks and publish
|
|
266
|
+
`INTEGRATION_EVENT_RECEIVED` with `source: '<name>'`,
|
|
267
|
+
`event: '<type>'`, and the payload.
|
|
268
|
+
|
|
269
|
+
That is the whole list. The triggers subscriber is source-agnostic: it
|
|
270
|
+
matches subscriptions on `(workspace, source, event)` and forwards the raw
|
|
271
|
+
payload through the generic `TriggerPayload` shape, so no change to the
|
|
272
|
+
triggers module, the projection schema, the run table, or `TriggerPayload`
|
|
273
|
+
is needed for a new source. Author workflows that subscribe to the new
|
|
274
|
+
`(source, event)` and narrow `triggerPayload.data` themselves.
|
|
275
|
+
|
|
276
|
+
For Sentry's supported `(source, event)` values, see
|
|
277
|
+
[`@shipfox/api-integration-sentry`](../integration/sentry).
|
|
278
|
+
|
|
279
|
+
## Development
|
|
280
|
+
|
|
281
|
+
Run checks for this package:
|
|
282
|
+
|
|
283
|
+
```sh
|
|
284
|
+
turbo check --filter=@shipfox/api-triggers
|
|
285
|
+
turbo type --filter=@shipfox/api-triggers
|
|
286
|
+
turbo test --filter=@shipfox/api-triggers
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Tests use Vitest and a real PostgreSQL database. Start local services
|
|
290
|
+
before running the test suite:
|
|
291
|
+
|
|
292
|
+
```sh
|
|
293
|
+
docker compose up -d
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The test environment uses the `api_test` database, set in `test/env.ts`.
|
|
297
|
+
|
|
298
|
+
## License
|
|
299
|
+
|
|
300
|
+
MIT
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const config: Readonly<{
|
|
2
|
+
TRIGGER_EVENT_RETENTION_DAYS: number;
|
|
3
|
+
TRIGGER_CRON_JITTER_WINDOW_SECONDS: number;
|
|
4
|
+
TRIGGER_CRON_FANOUT: number;
|
|
5
|
+
TRIGGER_CRON_CLAIM_BATCH: number;
|
|
6
|
+
} & import("@shipfox/config").CleanedEnvAccessors>;
|
|
7
|
+
export declare function assertRetentionDaysWithinBounds(days: number): void;
|
|
8
|
+
export declare function assertCronConfigWithinBounds(jitterWindowSeconds: number): void;
|
|
9
|
+
export declare function assertCronThroughputWithinBounds(fanout: number, claimBatch: number): void;
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;;;;;kDAiBjB,CAAC;AAEH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMlE;AAID,wBAAgB,4BAA4B,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAM9E;AAID,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CASzF"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createConfig, num } from '@shipfox/config';
|
|
2
|
+
export const config = createConfig({
|
|
3
|
+
TRIGGER_EVENT_RETENTION_DAYS: num({
|
|
4
|
+
desc: 'How many days a recorded trigger event (and its decisions) is kept before the hourly prune cron deletes it. Must be at least 1; a smaller value moves the cutoff to now or the future and would delete freshly recorded events. Defaults to 30.',
|
|
5
|
+
default: 30
|
|
6
|
+
}),
|
|
7
|
+
TRIGGER_CRON_JITTER_WINDOW_SECONDS: num({
|
|
8
|
+
desc: 'Maximum number of seconds added as deterministic jitter to a cron trigger fire time. Use 0 to disable jitter. Values below about 60 seconds are usually no-ops when cron ticking runs at minute resolution; multi-minute windows spread large schedule herds.',
|
|
9
|
+
default: 0
|
|
10
|
+
}),
|
|
11
|
+
TRIGGER_CRON_FANOUT: num({
|
|
12
|
+
desc: 'How many drain activities the once-per-minute cron tick runs in parallel. Each activity claims and fires one bounded batch, so the per-minute ceiling is TRIGGER_CRON_FANOUT times TRIGGER_CRON_CLAIM_BATCH runs. Raise it (and pod count) to fire more schedules per minute. Must be at least 1. Defaults to 2.',
|
|
13
|
+
default: 2
|
|
14
|
+
}),
|
|
15
|
+
TRIGGER_CRON_CLAIM_BATCH: num({
|
|
16
|
+
desc: 'How many due cron schedules a single drain activity claims and fires per tick. Bounds the load a cron burst can put on the database and the run-creation path. Must be at least 1. Defaults to 100.',
|
|
17
|
+
default: 100
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
export function assertRetentionDaysWithinBounds(days) {
|
|
21
|
+
if (!Number.isFinite(days) || days < 1) {
|
|
22
|
+
throw new Error(`TRIGGER_EVENT_RETENTION_DAYS must be a finite number of at least 1, received ${days}.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
assertRetentionDaysWithinBounds(config.TRIGGER_EVENT_RETENTION_DAYS);
|
|
26
|
+
export function assertCronConfigWithinBounds(jitterWindowSeconds) {
|
|
27
|
+
if (!Number.isFinite(jitterWindowSeconds) || jitterWindowSeconds < 0) {
|
|
28
|
+
throw new Error(`TRIGGER_CRON_JITTER_WINDOW_SECONDS must be a finite non-negative number, received ${jitterWindowSeconds}.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
assertCronConfigWithinBounds(config.TRIGGER_CRON_JITTER_WINDOW_SECONDS);
|
|
32
|
+
export function assertCronThroughputWithinBounds(fanout, claimBatch) {
|
|
33
|
+
if (!Number.isInteger(fanout) || fanout < 1) {
|
|
34
|
+
throw new Error(`TRIGGER_CRON_FANOUT must be an integer of at least 1, received ${fanout}.`);
|
|
35
|
+
}
|
|
36
|
+
if (!Number.isInteger(claimBatch) || claimBatch < 1) {
|
|
37
|
+
throw new Error(`TRIGGER_CRON_CLAIM_BATCH must be an integer of at least 1, received ${claimBatch}.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
assertCronThroughputWithinBounds(config.TRIGGER_CRON_FANOUT, config.TRIGGER_CRON_CLAIM_BATCH);
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import {createConfig, num} from '@shipfox/config';\n\nexport const config = createConfig({\n TRIGGER_EVENT_RETENTION_DAYS: num({\n desc: 'How many days a recorded trigger event (and its decisions) is kept before the hourly prune cron deletes it. Must be at least 1; a smaller value moves the cutoff to now or the future and would delete freshly recorded events. Defaults to 30.',\n default: 30,\n }),\n TRIGGER_CRON_JITTER_WINDOW_SECONDS: num({\n desc: 'Maximum number of seconds added as deterministic jitter to a cron trigger fire time. Use 0 to disable jitter. Values below about 60 seconds are usually no-ops when cron ticking runs at minute resolution; multi-minute windows spread large schedule herds.',\n default: 0,\n }),\n TRIGGER_CRON_FANOUT: num({\n desc: 'How many drain activities the once-per-minute cron tick runs in parallel. Each activity claims and fires one bounded batch, so the per-minute ceiling is TRIGGER_CRON_FANOUT times TRIGGER_CRON_CLAIM_BATCH runs. Raise it (and pod count) to fire more schedules per minute. Must be at least 1. Defaults to 2.',\n default: 2,\n }),\n TRIGGER_CRON_CLAIM_BATCH: num({\n desc: 'How many due cron schedules a single drain activity claims and fires per tick. Bounds the load a cron burst can put on the database and the run-creation path. Must be at least 1. Defaults to 100.',\n default: 100,\n }),\n});\n\nexport function assertRetentionDaysWithinBounds(days: number): void {\n if (!Number.isFinite(days) || days < 1) {\n throw new Error(\n `TRIGGER_EVENT_RETENTION_DAYS must be a finite number of at least 1, received ${days}.`,\n );\n }\n}\n\nassertRetentionDaysWithinBounds(config.TRIGGER_EVENT_RETENTION_DAYS);\n\nexport function assertCronConfigWithinBounds(jitterWindowSeconds: number): void {\n if (!Number.isFinite(jitterWindowSeconds) || jitterWindowSeconds < 0) {\n throw new Error(\n `TRIGGER_CRON_JITTER_WINDOW_SECONDS must be a finite non-negative number, received ${jitterWindowSeconds}.`,\n );\n }\n}\n\nassertCronConfigWithinBounds(config.TRIGGER_CRON_JITTER_WINDOW_SECONDS);\n\nexport function assertCronThroughputWithinBounds(fanout: number, claimBatch: number): void {\n if (!Number.isInteger(fanout) || fanout < 1) {\n throw new Error(`TRIGGER_CRON_FANOUT must be an integer of at least 1, received ${fanout}.`);\n }\n if (!Number.isInteger(claimBatch) || claimBatch < 1) {\n throw new Error(\n `TRIGGER_CRON_CLAIM_BATCH must be an integer of at least 1, received ${claimBatch}.`,\n );\n }\n}\n\nassertCronThroughputWithinBounds(config.TRIGGER_CRON_FANOUT, config.TRIGGER_CRON_CLAIM_BATCH);\n"],"names":["createConfig","num","config","TRIGGER_EVENT_RETENTION_DAYS","desc","default","TRIGGER_CRON_JITTER_WINDOW_SECONDS","TRIGGER_CRON_FANOUT","TRIGGER_CRON_CLAIM_BATCH","assertRetentionDaysWithinBounds","days","Number","isFinite","Error","assertCronConfigWithinBounds","jitterWindowSeconds","assertCronThroughputWithinBounds","fanout","claimBatch","isInteger"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,GAAG,QAAO,kBAAkB;AAElD,OAAO,MAAMC,SAASF,aAAa;IACjCG,8BAA8BF,IAAI;QAChCG,MAAM;QACNC,SAAS;IACX;IACAC,oCAAoCL,IAAI;QACtCG,MAAM;QACNC,SAAS;IACX;IACAE,qBAAqBN,IAAI;QACvBG,MAAM;QACNC,SAAS;IACX;IACAG,0BAA0BP,IAAI;QAC5BG,MAAM;QACNC,SAAS;IACX;AACF,GAAG;AAEH,OAAO,SAASI,gCAAgCC,IAAY;IAC1D,IAAI,CAACC,OAAOC,QAAQ,CAACF,SAASA,OAAO,GAAG;QACtC,MAAM,IAAIG,MACR,CAAC,6EAA6E,EAAEH,KAAK,CAAC,CAAC;IAE3F;AACF;AAEAD,gCAAgCP,OAAOC,4BAA4B;AAEnE,OAAO,SAASW,6BAA6BC,mBAA2B;IACtE,IAAI,CAACJ,OAAOC,QAAQ,CAACG,wBAAwBA,sBAAsB,GAAG;QACpE,MAAM,IAAIF,MACR,CAAC,kFAAkF,EAAEE,oBAAoB,CAAC,CAAC;IAE/G;AACF;AAEAD,6BAA6BZ,OAAOI,kCAAkC;AAEtE,OAAO,SAASU,iCAAiCC,MAAc,EAAEC,UAAkB;IACjF,IAAI,CAACP,OAAOQ,SAAS,CAACF,WAAWA,SAAS,GAAG;QAC3C,MAAM,IAAIJ,MAAM,CAAC,+DAA+D,EAAEI,OAAO,CAAC,CAAC;IAC7F;IACA,IAAI,CAACN,OAAOQ,SAAS,CAACD,eAAeA,aAAa,GAAG;QACnD,MAAM,IAAIL,MACR,CAAC,oEAAoE,EAAEK,WAAW,CAAC,CAAC;IAExF;AACF;AAEAF,iCAAiCd,OAAOK,mBAAmB,EAAEL,OAAOM,wBAAwB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface ComputeNextFireAtParams {
|
|
2
|
+
readonly cronExpression: string;
|
|
3
|
+
readonly timezone: string;
|
|
4
|
+
readonly from: Date;
|
|
5
|
+
readonly subscriptionId: string;
|
|
6
|
+
readonly jitterWindowSeconds: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function computeNextFireAt(params: ComputeNextFireAtParams): Date;
|
|
9
|
+
//# sourceMappingURL=compute-next-fire-at.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compute-next-fire-at.d.ts","sourceRoot":"","sources":["../../src/core/compute-next-fire-at.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAUvE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CronExpressionParser } from 'cron-parser';
|
|
2
|
+
export function computeNextFireAt(params) {
|
|
3
|
+
const iterator = CronExpressionParser.parse(params.cronExpression, {
|
|
4
|
+
currentDate: params.from,
|
|
5
|
+
tz: params.timezone
|
|
6
|
+
});
|
|
7
|
+
const occurrence = iterator.next().toDate();
|
|
8
|
+
const following = iterator.next().toDate();
|
|
9
|
+
const gapMs = following.getTime() - occurrence.getTime();
|
|
10
|
+
const offsetMs = jitterOffsetMs(params.subscriptionId, params.jitterWindowSeconds, gapMs);
|
|
11
|
+
return new Date(occurrence.getTime() + offsetMs);
|
|
12
|
+
}
|
|
13
|
+
function jitterOffsetMs(subscriptionId, windowSeconds, gapMs) {
|
|
14
|
+
const windowMs = Math.floor(windowSeconds * 1000);
|
|
15
|
+
const clampMs = Math.min(windowMs, gapMs);
|
|
16
|
+
if (clampMs <= 0) return 0;
|
|
17
|
+
return hashStringToUint32(subscriptionId) % clampMs;
|
|
18
|
+
}
|
|
19
|
+
function hashStringToUint32(value) {
|
|
20
|
+
let hash = 0x811c9dc5;
|
|
21
|
+
for(let index = 0; index < value.length; index += 1){
|
|
22
|
+
hash ^= value.charCodeAt(index);
|
|
23
|
+
hash = Math.imul(hash, 0x01000193);
|
|
24
|
+
}
|
|
25
|
+
return hash >>> 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=compute-next-fire-at.js.map
|