@shipfox/api-triggers 12.0.0 → 12.2.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 (31) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +30 -0
  3. package/dist/core/entities/decision.d.ts +1 -1
  4. package/dist/core/entities/decision.d.ts.map +1 -1
  5. package/dist/core/entities/received-event.d.ts +2 -2
  6. package/dist/core/entities/received-event.d.ts.map +1 -1
  7. package/dist/core/errors.d.ts.map +1 -1
  8. package/dist/core/route-event-to-job-listeners.d.ts.map +1 -1
  9. package/dist/core/route-event-to-job-listeners.js +130 -13
  10. package/dist/core/route-event-to-job-listeners.js.map +1 -1
  11. package/dist/core/workflows-client.d.ts +12 -12
  12. package/dist/db/db.d.ts +20 -20
  13. package/dist/db/job-listener-subscriptions.d.ts +1 -0
  14. package/dist/db/job-listener-subscriptions.d.ts.map +1 -1
  15. package/dist/db/job-listener-subscriptions.js +3 -0
  16. package/dist/db/job-listener-subscriptions.js.map +1 -1
  17. package/dist/db/schema/cron-schedules.d.ts +1 -1
  18. package/dist/db/schema/decisions.d.ts +3 -3
  19. package/dist/db/schema/job-listener-subscriptions.d.ts +1 -1
  20. package/dist/db/schema/outbox.d.ts +1 -1
  21. package/dist/db/schema/received-events.d.ts +5 -5
  22. package/dist/db/schema/subscriptions.d.ts +1 -1
  23. package/dist/metrics/instance.d.ts +2 -2
  24. package/dist/presentation/subscribers/on-integration-event-received.d.ts.map +1 -1
  25. package/dist/tsconfig.test.tsbuildinfo +1 -1
  26. package/package.json +11 -11
  27. package/src/core/route-event-to-job-listeners.test.ts +97 -0
  28. package/src/core/route-event-to-job-listeners.ts +157 -11
  29. package/src/db/job-listener-subscriptions.test.ts +3 -0
  30. package/src/db/job-listener-subscriptions.ts +4 -0
  31. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,6 +1,6 @@
1
1
  $ shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js
2
- Successfully compiled: 58 files with swc (406.13ms)
3
- 2026-08-04T08:17:12.242Z [INFO] asset workflow-bundle-69915b80080285c7230d.js 3.17 MiB [emitted] [immutable] (name: main)
2
+ Successfully compiled: 58 files with swc (414.39ms)
3
+ 2026-08-06T13:12:28.900Z [INFO] asset workflow-bundle-69915b80080285c7230d.js 3.17 MiB [emitted] [immutable] (name: main)
4
4
  orphan modules 33.5 KiB [orphan] 21 modules
5
5
  runtime modules 1.13 KiB 5 modules
6
6
  modules by path ../../../node_modules/.pnpm/ 970 KiB 196 modules
@@ -13,6 +13,6 @@ optional modules 30 bytes [optional]
13
13
  __temporal_custom_payload_converter (ignored) 15 bytes [optional] [built] [code generated]
14
14
  __temporal_custom_failure_converter (ignored) 15 bytes [optional] [built] [code generated]
15
15
  ../../shared/node/temporal/dist/workflow-error-interceptor.js 1.21 KiB [built] [code generated]
16
- webpack 5.107.2 compiled successfully in 4485 ms
17
- 2026-08-04T08:17:12.280Z [INFO] Workflow bundle created { size: '3.17MB' }
16
+ webpack 5.107.2 compiled successfully in 6889 ms
17
+ 2026-08-06T13:12:28.918Z [INFO] Workflow bundle created { size: '3.17MB' }
18
18
  /home/runner/work/shipfox/shipfox/libs/api/triggers/dist/temporal/workflows/index.js: 3329015 bytes
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @shipfox/api-triggers
2
2
 
3
+ ## 12.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ce0984d: Preserve structured values when jobs map typed step outputs, normalize them for JSON persistence, and bound materialized job output sizes and entry counts.
8
+
9
+ ### Patch Changes
10
+
11
+ - 214b8f2: Expose execution and run numeric fields as CEL integers, preserve zero-based execution indices,
12
+ and keep listener filter snapshots aligned with live CEL values after persistence.
13
+ - Updated dependencies [7901a60]
14
+ - Updated dependencies [ce0984d]
15
+ - @shipfox/api-integration-core-dto@12.2.0
16
+ - @shipfox/expression@2.1.0
17
+ - @shipfox/api-workflows-dto@12.2.0
18
+ - @shipfox/workflow-document@3.0.1
19
+ - @shipfox/node-opentelemetry@0.6.4
20
+ - @shipfox/api-definitions-dto@12.2.0
21
+ - @shipfox/node-fastify@0.4.2
22
+ - @shipfox/node-module@1.0.6
23
+ - @shipfox/node-temporal@0.4.5
24
+ - @shipfox/api-auth-context@12.2.0
25
+
26
+ ## 12.1.0
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies [312a137]
31
+ - @shipfox/api-workflows-dto@12.1.0
32
+
3
33
  ## 12.0.0
4
34
 
5
35
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  import type { JobListenerMatcherKind } from './job-listener-subscription.js';
2
- export declare const triggerDecisionOutcomes: readonly ["triggered", "filter-error", "dispatch-error"];
2
+ export declare const triggerDecisionOutcomes: readonly ['triggered', 'filter-error', 'dispatch-error'];
3
3
  export type TriggerDecisionOutcome = (typeof triggerDecisionOutcomes)[number];
4
4
  export type TriggerDecisionSubscriptionKind = 'trigger' | 'listener';
5
5
  export interface TriggerDecision {
@@ -1 +1 @@
1
- {"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../../../src/core/entities/decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,gCAAgC,CAAC;AAE3E,eAAO,MAAM,uBAAuB,0DAA2D,CAAC;AAChG,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC9E,MAAM,MAAM,+BAA+B,GAAG,SAAS,GAAG,UAAU,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,+BAA+B,CAAC;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC3C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;CACjB"}
1
+ {"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../../../src/core/entities/decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,gCAAgC,CAAC;AAE3E,eAAO,MAAM,uBAAuB,YAAI,WAAW,EAAE,cAAc,EAAE,gBAAgB,CAAU,CAAC;AAChG,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC9E,MAAM,MAAM,+BAA+B,GAAG,SAAS,GAAG,UAAU,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,+BAA+B,CAAC;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC3C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;CACjB"}
@@ -1,6 +1,6 @@
1
- export declare const triggerEventOrigins: readonly ["integration", "manual", "cron"];
1
+ export declare const triggerEventOrigins: readonly ['integration', 'manual', 'cron'];
2
2
  export type TriggerEventOrigin = (typeof triggerEventOrigins)[number];
3
- export declare const triggerEventOutcomes: readonly ["received", "routed", "discarded", "failed", "errored"];
3
+ export declare const triggerEventOutcomes: readonly ['received', 'routed', 'discarded', 'failed', 'errored'];
4
4
  export type TriggerEventOutcome = (typeof triggerEventOutcomes)[number];
5
5
  export interface TriggerReceivedEvent {
6
6
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"received-event.d.ts","sourceRoot":"","sources":["../../../src/core/entities/received-event.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,4CAA6C,CAAC;AAC9E,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,eAAO,MAAM,oBAAoB,mEAMvB,CAAC;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,UAAU,EAAE,IAAI,CAAC;IACjB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"received-event.d.ts","sourceRoot":"","sources":["../../../src/core/entities/received-event.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,YAAI,aAAa,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AAC9E,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,eAAO,MAAM,oBAAoB,YAC/B,UAAU,EACV,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,SAAS,CACD,CAAC;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,UAAU,EAAE,IAAI,CAAC;IACjB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;gBAEpB,cAAc,EAAE,MAAM;CAKnC;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;gBAE1B,oBAAoB,EAAE,MAAM;CAKzC;AAED,qBAAa,iCAAkC,SAAQ,KAAK;IAC1D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQnD;AAED,qBAAa,+BAAgC,SAAQ,KAAK;IACxD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAQnD;AAED,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;gBAEvB,cAAc,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM;CAS/F"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,YAAY,cAAc,EAAE,MAAM,EAIjC;CACF;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEtC,YAAY,oBAAoB,EAAE,MAAM,EAIvC;CACF;AAED,qBAAa,iCAAkC,SAAQ,KAAK;IAC1D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,YAAY,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAOjD;CACF;AAED,qBAAa,+BAAgC,SAAQ,KAAK;IACxD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,YAAY,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAOjD;CACF;AAED,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAEnC,YAAY,cAAc,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAQ7F;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"route-event-to-job-listeners.d.ts","sourceRoot":"","sources":["../../src/core/route-event-to-job-listeners.ts"],"names":[],"mappings":"AAKA,OAAO,EAAC,KAAK,sBAAsB,EAAW,MAAM,6BAA6B,CAAC;AAClF,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,qBAAqB,CAAC;IACjC,OAAO,EAAE,sBAAsB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,8BAA8B,GACrC,OAAO,CAAC,8BAA8B,CAAC,CAsFzC"}
1
+ {"version":3,"file":"route-event-to-job-listeners.d.ts","sourceRoot":"","sources":["../../src/core/route-event-to-job-listeners.ts"],"names":[],"mappings":"AASA,OAAO,EAAC,KAAK,sBAAsB,EAAW,MAAM,6BAA6B,CAAC;AAClF,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,uBAAuB,CAAC;AAI/B,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,qBAAqB,CAAC;IACjC,OAAO,EAAE,sBAAsB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,8BAA8B,GACrC,OAAO,CAAC,8BAA8B,CAAC,CAsFzC"}
@@ -1,9 +1,11 @@
1
- import { projectWorkflowPredicateContext } from '@shipfox/expression';
1
+ import { listenerFilterOutputTypesSchema } from '@shipfox/api-workflows-dto';
2
+ import { projectWorkflowPredicateContext, rehydrateJsonExpressionRecord } from '@shipfox/expression';
2
3
  import { logger } from '@shipfox/node-opentelemetry';
3
4
  import { findMatchingJobListenerSubscriptions } from '#db/job-listener-subscriptions.js';
4
5
  import { evaluateStoredFilter } from './config.js';
5
6
  import { toReason } from './record-trigger-history.js';
6
7
  import { isPermanentDeliverEventToJobListenerError } from './workflows-client.js';
8
+ const LISTENER_INTEGER_PATTERN = /^-?\d+$/;
7
9
  export async function routeEventToJobListeners(params) {
8
10
  const subscriptions = await findMatchingJobListenerSubscriptions({
9
11
  workspaceId: params.workspaceId,
@@ -106,7 +108,7 @@ function evaluateListenerFilter(params) {
106
108
  return evaluateStoredFilter({
107
109
  value: filter,
108
110
  context: projectWorkflowPredicateContext(params.subscription.kind === 'on' ? 'listener.on' : 'listener.until', {
109
- ...snapshot.value,
111
+ ...rehydrateListenerFilterSnapshot(snapshot.value, snapshot.outputTypes),
110
112
  event: params.payload
111
113
  }),
112
114
  invalidReason: 'Listener subscription filter must be a non-empty string when set',
@@ -115,24 +117,139 @@ function evaluateListenerFilter(params) {
115
117
  }
116
118
  function readFilterSnapshot(subscription) {
117
119
  const value = subscription.config.filter_snapshot;
118
- if (value === undefined) return {
119
- kind: 'valid',
120
- value: {}
121
- };
122
- if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
120
+ if (value !== undefined && (value === null || typeof value !== 'object' || Array.isArray(value))) {
121
+ return {
122
+ kind: 'invalid',
123
+ result: {
124
+ kind: 'filter-error',
125
+ reason: 'Listener filter snapshot must be an object when set'
126
+ }
127
+ };
128
+ }
129
+ const outputTypesValue = subscription.config.filter_output_types;
130
+ if (outputTypesValue !== undefined) {
131
+ const parsedOutputTypes = listenerFilterOutputTypesSchema.safeParse(outputTypesValue);
132
+ if (!parsedOutputTypes.success) {
133
+ return {
134
+ kind: 'invalid',
135
+ result: {
136
+ kind: 'filter-error',
137
+ reason: 'Listener filter output types must be valid when set'
138
+ }
139
+ };
140
+ }
123
141
  return {
124
142
  kind: 'valid',
125
- value: value
143
+ value: value ?? {},
144
+ outputTypes: parsedOutputTypes.data
126
145
  };
127
146
  }
128
147
  return {
129
- kind: 'invalid',
130
- result: {
131
- kind: 'filter-error',
132
- reason: 'Listener filter snapshot must be an object when set'
133
- }
148
+ kind: 'valid',
149
+ value: value ?? {}
150
+ };
151
+ }
152
+ function rehydrateListenerFilterSnapshot(snapshot, outputTypes) {
153
+ let result = snapshot;
154
+ if (outputTypes !== undefined && isRecord(snapshot.jobs)) {
155
+ const jobs = Object.fromEntries(Object.entries(snapshot.jobs).map(([jobKey, jobContext])=>{
156
+ const jobOutputTypes = Object.hasOwn(outputTypes, jobKey) ? outputTypes[jobKey] : undefined;
157
+ if (jobOutputTypes === undefined || !isRecord(jobContext)) {
158
+ return [
159
+ jobKey,
160
+ jobContext
161
+ ];
162
+ }
163
+ const outputs = isRecord(jobContext.outputs) ? rehydrateJsonExpressionRecord(jobContext.outputs, jobOutputTypes) : jobContext.outputs;
164
+ const executions = Array.isArray(jobContext.executions) ? jobContext.executions.map((execution)=>{
165
+ if (!isRecord(execution) || !isRecord(execution.outputs)) return execution;
166
+ return {
167
+ ...execution,
168
+ outputs: rehydrateJsonExpressionRecord(execution.outputs, jobOutputTypes)
169
+ };
170
+ }) : jobContext.executions;
171
+ return [
172
+ jobKey,
173
+ {
174
+ ...jobContext,
175
+ outputs,
176
+ executions
177
+ }
178
+ ];
179
+ }));
180
+ result = {
181
+ ...snapshot,
182
+ jobs
183
+ };
184
+ }
185
+ const rehydrated = {
186
+ ...result
187
+ };
188
+ if (isRecord(result.run)) rehydrated.run = rehydrateRunSnapshot(result.run);
189
+ if (isRecord(result.jobs)) rehydrated.jobs = rehydrateJobsSnapshot(result.jobs);
190
+ return rehydrated;
191
+ }
192
+ function isRecord(value) {
193
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
194
+ }
195
+ function rehydrateRunSnapshot(run) {
196
+ const result = {
197
+ ...run
198
+ };
199
+ if ('number' in run) result.number = rehydrateListenerInt(run.number);
200
+ if ('created_at' in run) result.created_at = rehydrateListenerTimestamp(run.created_at);
201
+ return result;
202
+ }
203
+ function rehydrateJobsSnapshot(jobs) {
204
+ return Object.fromEntries(Object.entries(jobs).map(([jobKey, value])=>[
205
+ jobKey,
206
+ isRecord(value) ? rehydrateJobSnapshot(value) : value
207
+ ]));
208
+ }
209
+ function rehydrateJobSnapshot(job) {
210
+ if (!Array.isArray(job.executions)) return job;
211
+ return {
212
+ ...job,
213
+ executions: job.executions.map((value)=>isRecord(value) ? rehydrateExecutionSnapshot(value) : value)
214
+ };
215
+ }
216
+ function rehydrateExecutionSnapshot(execution) {
217
+ const result = {
218
+ ...execution
219
+ };
220
+ if ('index' in execution) result.index = rehydrateListenerInt(execution.index);
221
+ if ('started_at' in execution) {
222
+ result.started_at = rehydrateListenerTimestamp(execution.started_at);
223
+ }
224
+ if ('finished_at' in execution) {
225
+ result.finished_at = rehydrateListenerTimestamp(execution.finished_at);
226
+ }
227
+ if (Array.isArray(execution.events)) {
228
+ result.events = execution.events.map((value)=>isRecord(value) ? rehydrateExecutionEventSnapshot(value) : value);
229
+ }
230
+ return result;
231
+ }
232
+ function rehydrateExecutionEventSnapshot(event) {
233
+ if (!('received_at' in event)) return event;
234
+ return {
235
+ ...event,
236
+ received_at: rehydrateListenerTimestamp(event.received_at)
134
237
  };
135
238
  }
239
+ function rehydrateListenerInt(value) {
240
+ if (typeof value === 'number' && Number.isSafeInteger(value)) return BigInt(value);
241
+ if (typeof value !== 'string' || !LISTENER_INTEGER_PATTERN.test(value)) return value;
242
+ try {
243
+ return BigInt(value);
244
+ } catch {
245
+ return value;
246
+ }
247
+ }
248
+ function rehydrateListenerTimestamp(value) {
249
+ if (typeof value !== 'string') return value;
250
+ const date = new Date(value);
251
+ return Number.isNaN(date.getTime()) ? value : date;
252
+ }
136
253
  function listenerDisposition(subscription) {
137
254
  return subscription.kind === 'until' ? 'resolve' : 'fire';
138
255
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/route-event-to-job-listeners.ts"],"sourcesContent":["import {projectWorkflowPredicateContext} from '@shipfox/expression';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {findMatchingJobListenerSubscriptions} from '#db/job-listener-subscriptions.js';\nimport {evaluateStoredFilter, type StoredFilterEvaluation} from './config.js';\nimport type {JobListenerSubscription} from './entities/job-listener-subscription.js';\nimport {type TriggerHistoryRecorder, toReason} from './record-trigger-history.js';\nimport {\n isPermanentDeliverEventToJobListenerError,\n type WorkflowsModuleClient,\n} from './workflows-client.js';\n\nexport interface RouteEventToJobListenersParams {\n workflows: WorkflowsModuleClient;\n history: TriggerHistoryRecorder;\n eventRef: string;\n workspaceId: string;\n connectionId: string;\n provider: string;\n source: string;\n event: string;\n deliveryId: string;\n payload: unknown;\n receivedAt: Date;\n}\n\nexport interface RouteEventToJobListenersResult {\n /**\n * Listener outcomes that should contribute to received_event.matched_count:\n * filter errors, accepted deliveries, and dispatch errors. Stale skipped jobs\n * are intentionally excluded because they did not produce an auditable decision.\n */\n engagedCount: number;\n /**\n * Jobs with an effective matcher after source/event/filter checks, including\n * stale skipped jobs.\n */\n matchedJobCount: number;\n acceptedJobCount: number;\n deliveredCount: number;\n transientErrored: boolean;\n transientError: unknown;\n}\n\nexport async function routeEventToJobListeners(\n params: RouteEventToJobListenersParams,\n): Promise<RouteEventToJobListenersResult> {\n const subscriptions = await findMatchingJobListenerSubscriptions({\n workspaceId: params.workspaceId,\n source: params.source,\n event: params.event,\n });\n\n let filterErrorCount = 0;\n const effectiveMatchByJobId = new Map<string, JobListenerSubscription>();\n for (const subscription of subscriptions) {\n const filterResult = evaluateListenerFilter({subscription, payload: params.payload});\n if (filterResult.kind === 'filtered') continue;\n if (filterResult.kind === 'filter-error') {\n filterErrorCount += 1;\n await params.history.listenerFilterErrored(subscription, filterResult.reason);\n continue;\n }\n\n const previous = effectiveMatchByJobId.get(subscription.jobId);\n if (!previous || shouldReplaceEffectiveMatcher(previous, subscription)) {\n effectiveMatchByJobId.set(subscription.jobId, subscription);\n }\n }\n\n const fireDeliveryNeeded = [...effectiveMatchByJobId.values()].some(\n (subscription) => listenerDisposition(subscription) === 'fire',\n );\n const triggerReference = fireDeliveryNeeded\n ? await params.workflows.resolveWorkflowRunTriggerReference({\n workspaceId: params.workspaceId,\n triggerConnectionId: params.connectionId,\n triggerPayload: {\n provider: params.provider,\n source: params.source,\n event: params.event,\n deliveryId: params.deliveryId,\n data: params.payload,\n },\n })\n : null;\n\n let acceptedJobCount = 0;\n let deliveredCount = 0;\n let sawTransientError = false;\n let dispatchErrorCount = 0;\n let firstTransientError: unknown;\n\n for (const subscription of effectiveMatchByJobId.values()) {\n const disposition = listenerDisposition(subscription);\n try {\n const result = await params.workflows.deliverEventToJobListener({\n jobId: subscription.jobId,\n disposition,\n eventRef: params.eventRef,\n deliveryId: params.deliveryId,\n source: params.source,\n event: params.event,\n provider: params.provider,\n triggerConnectionId: params.connectionId,\n triggerReference,\n payload: params.payload,\n receivedAt: params.receivedAt.toISOString(),\n });\n if (!result.skipped) {\n acceptedJobCount += 1;\n await params.history.listenerTriggered(subscription);\n }\n if (result.buffered) deliveredCount += 1;\n } catch (error) {\n dispatchErrorCount += 1;\n await params.history.listenerDispatchErrored(subscription, toReason(error));\n if (!isPermanentDeliverEventToJobListenerError(error) && !sawTransientError) {\n sawTransientError = true;\n firstTransientError = error;\n }\n }\n }\n\n return {\n engagedCount: filterErrorCount + acceptedJobCount + dispatchErrorCount,\n matchedJobCount: effectiveMatchByJobId.size,\n acceptedJobCount,\n deliveredCount,\n transientErrored: sawTransientError,\n transientError: sawTransientError ? firstTransientError : undefined,\n };\n}\n\ninterface EvaluateListenerFilterParams {\n subscription: JobListenerSubscription;\n payload: unknown;\n}\n\nfunction evaluateListenerFilter(params: EvaluateListenerFilterParams): StoredFilterEvaluation {\n const filter = params.subscription.config.filter;\n if (filter === null || filter === undefined) return {kind: 'matched'};\n if (typeof filter !== 'string' || filter.trim() === '') {\n return evaluateStoredFilter({\n value: filter,\n context: projectWorkflowPredicateContext(\n params.subscription.kind === 'on' ? 'listener.on' : 'listener.until',\n {event: params.payload},\n ),\n invalidReason: 'Listener subscription filter must be a non-empty string when set',\n evaluationFailedReason: 'Listener filter evaluation failed',\n });\n }\n\n const snapshot = readFilterSnapshot(params.subscription);\n if (snapshot.kind === 'invalid') return snapshot.result;\n\n return evaluateStoredFilter({\n value: filter,\n context: projectWorkflowPredicateContext(\n params.subscription.kind === 'on' ? 'listener.on' : 'listener.until',\n {...snapshot.value, event: params.payload},\n ),\n invalidReason: 'Listener subscription filter must be a non-empty string when set',\n evaluationFailedReason: 'Listener filter evaluation failed',\n });\n}\n\nfunction readFilterSnapshot(\n subscription: JobListenerSubscription,\n):\n | {kind: 'valid'; value: Record<string, unknown>}\n | {kind: 'invalid'; result: Extract<StoredFilterEvaluation, {kind: 'filter-error'}>} {\n const value = subscription.config.filter_snapshot;\n if (value === undefined) return {kind: 'valid', value: {}};\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n return {kind: 'valid', value: value as Record<string, unknown>};\n }\n return {\n kind: 'invalid',\n result: {\n kind: 'filter-error',\n reason: 'Listener filter snapshot must be an object when set',\n },\n };\n}\n\nfunction listenerDisposition(subscription: JobListenerSubscription): 'fire' | 'resolve' {\n return subscription.kind === 'until' ? 'resolve' : 'fire';\n}\n\nfunction listenerMatcherSortKey(subscription: JobListenerSubscription): string {\n const kindRank = subscription.kind === 'until' ? '0' : '1';\n return `${kindRank}:${subscription.matcherOrdinal.toString().padStart(10, '0')}`;\n}\n\nfunction shouldReplaceEffectiveMatcher(\n previous: JobListenerSubscription,\n candidate: JobListenerSubscription,\n): boolean {\n const previousKey = listenerMatcherSortKey(previous);\n const candidateKey = listenerMatcherSortKey(candidate);\n if (candidateKey !== previousKey) return candidateKey < previousKey;\n\n logger().warn(\n {\n jobId: candidate.jobId,\n keptSubscriptionId: previous.id,\n candidateSubscriptionId: candidate.id,\n matcherKind: candidate.kind,\n matcherOrdinal: candidate.matcherOrdinal,\n },\n 'duplicate job listener matcher key encountered; choosing deterministic subscription id order',\n );\n return candidate.id < previous.id;\n}\n"],"names":["projectWorkflowPredicateContext","logger","findMatchingJobListenerSubscriptions","evaluateStoredFilter","toReason","isPermanentDeliverEventToJobListenerError","routeEventToJobListeners","params","subscriptions","workspaceId","source","event","filterErrorCount","effectiveMatchByJobId","Map","subscription","filterResult","evaluateListenerFilter","payload","kind","history","listenerFilterErrored","reason","previous","get","jobId","shouldReplaceEffectiveMatcher","set","fireDeliveryNeeded","values","some","listenerDisposition","triggerReference","workflows","resolveWorkflowRunTriggerReference","triggerConnectionId","connectionId","triggerPayload","provider","deliveryId","data","acceptedJobCount","deliveredCount","sawTransientError","dispatchErrorCount","firstTransientError","disposition","result","deliverEventToJobListener","eventRef","receivedAt","toISOString","skipped","listenerTriggered","buffered","error","listenerDispatchErrored","engagedCount","matchedJobCount","size","transientErrored","transientError","undefined","filter","config","trim","value","context","invalidReason","evaluationFailedReason","snapshot","readFilterSnapshot","filter_snapshot","Array","isArray","listenerMatcherSortKey","kindRank","matcherOrdinal","toString","padStart","candidate","previousKey","candidateKey","warn","keptSubscriptionId","id","candidateSubscriptionId","matcherKind"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/route-event-to-job-listeners.ts"],"sourcesContent":["import {\n type ListenerFilterOutputTypes,\n listenerFilterOutputTypesSchema,\n} from '@shipfox/api-workflows-dto';\nimport {projectWorkflowPredicateContext, rehydrateJsonExpressionRecord} from '@shipfox/expression';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {findMatchingJobListenerSubscriptions} from '#db/job-listener-subscriptions.js';\nimport {evaluateStoredFilter, type StoredFilterEvaluation} from './config.js';\nimport type {JobListenerSubscription} from './entities/job-listener-subscription.js';\nimport {type TriggerHistoryRecorder, toReason} from './record-trigger-history.js';\nimport {\n isPermanentDeliverEventToJobListenerError,\n type WorkflowsModuleClient,\n} from './workflows-client.js';\n\nconst LISTENER_INTEGER_PATTERN = /^-?\\d+$/;\n\nexport interface RouteEventToJobListenersParams {\n workflows: WorkflowsModuleClient;\n history: TriggerHistoryRecorder;\n eventRef: string;\n workspaceId: string;\n connectionId: string;\n provider: string;\n source: string;\n event: string;\n deliveryId: string;\n payload: unknown;\n receivedAt: Date;\n}\n\nexport interface RouteEventToJobListenersResult {\n /**\n * Listener outcomes that should contribute to received_event.matched_count:\n * filter errors, accepted deliveries, and dispatch errors. Stale skipped jobs\n * are intentionally excluded because they did not produce an auditable decision.\n */\n engagedCount: number;\n /**\n * Jobs with an effective matcher after source/event/filter checks, including\n * stale skipped jobs.\n */\n matchedJobCount: number;\n acceptedJobCount: number;\n deliveredCount: number;\n transientErrored: boolean;\n transientError: unknown;\n}\n\nexport async function routeEventToJobListeners(\n params: RouteEventToJobListenersParams,\n): Promise<RouteEventToJobListenersResult> {\n const subscriptions = await findMatchingJobListenerSubscriptions({\n workspaceId: params.workspaceId,\n source: params.source,\n event: params.event,\n });\n\n let filterErrorCount = 0;\n const effectiveMatchByJobId = new Map<string, JobListenerSubscription>();\n for (const subscription of subscriptions) {\n const filterResult = evaluateListenerFilter({subscription, payload: params.payload});\n if (filterResult.kind === 'filtered') continue;\n if (filterResult.kind === 'filter-error') {\n filterErrorCount += 1;\n await params.history.listenerFilterErrored(subscription, filterResult.reason);\n continue;\n }\n\n const previous = effectiveMatchByJobId.get(subscription.jobId);\n if (!previous || shouldReplaceEffectiveMatcher(previous, subscription)) {\n effectiveMatchByJobId.set(subscription.jobId, subscription);\n }\n }\n\n const fireDeliveryNeeded = [...effectiveMatchByJobId.values()].some(\n (subscription) => listenerDisposition(subscription) === 'fire',\n );\n const triggerReference = fireDeliveryNeeded\n ? await params.workflows.resolveWorkflowRunTriggerReference({\n workspaceId: params.workspaceId,\n triggerConnectionId: params.connectionId,\n triggerPayload: {\n provider: params.provider,\n source: params.source,\n event: params.event,\n deliveryId: params.deliveryId,\n data: params.payload,\n },\n })\n : null;\n\n let acceptedJobCount = 0;\n let deliveredCount = 0;\n let sawTransientError = false;\n let dispatchErrorCount = 0;\n let firstTransientError: unknown;\n\n for (const subscription of effectiveMatchByJobId.values()) {\n const disposition = listenerDisposition(subscription);\n try {\n const result = await params.workflows.deliverEventToJobListener({\n jobId: subscription.jobId,\n disposition,\n eventRef: params.eventRef,\n deliveryId: params.deliveryId,\n source: params.source,\n event: params.event,\n provider: params.provider,\n triggerConnectionId: params.connectionId,\n triggerReference,\n payload: params.payload,\n receivedAt: params.receivedAt.toISOString(),\n });\n if (!result.skipped) {\n acceptedJobCount += 1;\n await params.history.listenerTriggered(subscription);\n }\n if (result.buffered) deliveredCount += 1;\n } catch (error) {\n dispatchErrorCount += 1;\n await params.history.listenerDispatchErrored(subscription, toReason(error));\n if (!isPermanentDeliverEventToJobListenerError(error) && !sawTransientError) {\n sawTransientError = true;\n firstTransientError = error;\n }\n }\n }\n\n return {\n engagedCount: filterErrorCount + acceptedJobCount + dispatchErrorCount,\n matchedJobCount: effectiveMatchByJobId.size,\n acceptedJobCount,\n deliveredCount,\n transientErrored: sawTransientError,\n transientError: sawTransientError ? firstTransientError : undefined,\n };\n}\n\ninterface EvaluateListenerFilterParams {\n subscription: JobListenerSubscription;\n payload: unknown;\n}\n\nfunction evaluateListenerFilter(params: EvaluateListenerFilterParams): StoredFilterEvaluation {\n const filter = params.subscription.config.filter;\n if (filter === null || filter === undefined) return {kind: 'matched'};\n if (typeof filter !== 'string' || filter.trim() === '') {\n return evaluateStoredFilter({\n value: filter,\n context: projectWorkflowPredicateContext(\n params.subscription.kind === 'on' ? 'listener.on' : 'listener.until',\n {event: params.payload},\n ),\n invalidReason: 'Listener subscription filter must be a non-empty string when set',\n evaluationFailedReason: 'Listener filter evaluation failed',\n });\n }\n\n const snapshot = readFilterSnapshot(params.subscription);\n if (snapshot.kind === 'invalid') return snapshot.result;\n\n return evaluateStoredFilter({\n value: filter,\n context: projectWorkflowPredicateContext(\n params.subscription.kind === 'on' ? 'listener.on' : 'listener.until',\n {\n ...rehydrateListenerFilterSnapshot(snapshot.value, snapshot.outputTypes),\n event: params.payload,\n },\n ),\n invalidReason: 'Listener subscription filter must be a non-empty string when set',\n evaluationFailedReason: 'Listener filter evaluation failed',\n });\n}\n\nfunction readFilterSnapshot(\n subscription: JobListenerSubscription,\n):\n | {kind: 'valid'; value: Record<string, unknown>; outputTypes?: ListenerFilterOutputTypes}\n | {kind: 'invalid'; result: Extract<StoredFilterEvaluation, {kind: 'filter-error'}>} {\n const value = subscription.config.filter_snapshot;\n if (\n value !== undefined &&\n (value === null || typeof value !== 'object' || Array.isArray(value))\n ) {\n return {\n kind: 'invalid',\n result: {\n kind: 'filter-error',\n reason: 'Listener filter snapshot must be an object when set',\n },\n };\n }\n\n const outputTypesValue = subscription.config.filter_output_types;\n if (outputTypesValue !== undefined) {\n const parsedOutputTypes = listenerFilterOutputTypesSchema.safeParse(outputTypesValue);\n if (!parsedOutputTypes.success) {\n return {\n kind: 'invalid',\n result: {\n kind: 'filter-error',\n reason: 'Listener filter output types must be valid when set',\n },\n };\n }\n return {\n kind: 'valid',\n value: (value ?? {}) as Record<string, unknown>,\n outputTypes: parsedOutputTypes.data,\n };\n }\n\n return {kind: 'valid', value: (value ?? {}) as Record<string, unknown>};\n}\n\nfunction rehydrateListenerFilterSnapshot(\n snapshot: Record<string, unknown>,\n outputTypes: ListenerFilterOutputTypes | undefined,\n): Record<string, unknown> {\n let result = snapshot;\n if (outputTypes !== undefined && isRecord(snapshot.jobs)) {\n const jobs = Object.fromEntries(\n Object.entries(snapshot.jobs).map(([jobKey, jobContext]) => {\n const jobOutputTypes = Object.hasOwn(outputTypes, jobKey) ? outputTypes[jobKey] : undefined;\n if (jobOutputTypes === undefined || !isRecord(jobContext)) {\n return [jobKey, jobContext];\n }\n\n const outputs = isRecord(jobContext.outputs)\n ? rehydrateJsonExpressionRecord(jobContext.outputs, jobOutputTypes)\n : jobContext.outputs;\n const executions = Array.isArray(jobContext.executions)\n ? jobContext.executions.map((execution) => {\n if (!isRecord(execution) || !isRecord(execution.outputs)) return execution;\n return {\n ...execution,\n outputs: rehydrateJsonExpressionRecord(execution.outputs, jobOutputTypes),\n };\n })\n : jobContext.executions;\n\n return [\n jobKey,\n {\n ...jobContext,\n outputs,\n executions,\n },\n ];\n }),\n );\n result = {...snapshot, jobs};\n }\n\n const rehydrated = {...result};\n if (isRecord(result.run)) rehydrated.run = rehydrateRunSnapshot(result.run);\n if (isRecord(result.jobs)) rehydrated.jobs = rehydrateJobsSnapshot(result.jobs);\n return rehydrated;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction rehydrateRunSnapshot(run: Record<string, unknown>): Record<string, unknown> {\n const result = {...run};\n if ('number' in run) result.number = rehydrateListenerInt(run.number);\n if ('created_at' in run) result.created_at = rehydrateListenerTimestamp(run.created_at);\n return result;\n}\n\nfunction rehydrateJobsSnapshot(jobs: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(jobs).map(([jobKey, value]) => [\n jobKey,\n isRecord(value) ? rehydrateJobSnapshot(value) : value,\n ]),\n );\n}\n\nfunction rehydrateJobSnapshot(job: Record<string, unknown>): Record<string, unknown> {\n if (!Array.isArray(job.executions)) return job;\n\n return {\n ...job,\n executions: job.executions.map((value) =>\n isRecord(value) ? rehydrateExecutionSnapshot(value) : value,\n ),\n };\n}\n\nfunction rehydrateExecutionSnapshot(execution: Record<string, unknown>): Record<string, unknown> {\n const result = {...execution};\n if ('index' in execution) result.index = rehydrateListenerInt(execution.index);\n if ('started_at' in execution) {\n result.started_at = rehydrateListenerTimestamp(execution.started_at);\n }\n if ('finished_at' in execution) {\n result.finished_at = rehydrateListenerTimestamp(execution.finished_at);\n }\n if (Array.isArray(execution.events)) {\n result.events = execution.events.map((value) =>\n isRecord(value) ? rehydrateExecutionEventSnapshot(value) : value,\n );\n }\n return result;\n}\n\nfunction rehydrateExecutionEventSnapshot(event: Record<string, unknown>): Record<string, unknown> {\n if (!('received_at' in event)) return event;\n return {...event, received_at: rehydrateListenerTimestamp(event.received_at)};\n}\n\nfunction rehydrateListenerInt(value: unknown): unknown {\n if (typeof value === 'number' && Number.isSafeInteger(value)) return BigInt(value);\n if (typeof value !== 'string' || !LISTENER_INTEGER_PATTERN.test(value)) return value;\n\n try {\n return BigInt(value);\n } catch {\n return value;\n }\n}\n\nfunction rehydrateListenerTimestamp(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? value : date;\n}\n\nfunction listenerDisposition(subscription: JobListenerSubscription): 'fire' | 'resolve' {\n return subscription.kind === 'until' ? 'resolve' : 'fire';\n}\n\nfunction listenerMatcherSortKey(subscription: JobListenerSubscription): string {\n const kindRank = subscription.kind === 'until' ? '0' : '1';\n return `${kindRank}:${subscription.matcherOrdinal.toString().padStart(10, '0')}`;\n}\n\nfunction shouldReplaceEffectiveMatcher(\n previous: JobListenerSubscription,\n candidate: JobListenerSubscription,\n): boolean {\n const previousKey = listenerMatcherSortKey(previous);\n const candidateKey = listenerMatcherSortKey(candidate);\n if (candidateKey !== previousKey) return candidateKey < previousKey;\n\n logger().warn(\n {\n jobId: candidate.jobId,\n keptSubscriptionId: previous.id,\n candidateSubscriptionId: candidate.id,\n matcherKind: candidate.kind,\n matcherOrdinal: candidate.matcherOrdinal,\n },\n 'duplicate job listener matcher key encountered; choosing deterministic subscription id order',\n );\n return candidate.id < previous.id;\n}\n"],"names":["listenerFilterOutputTypesSchema","projectWorkflowPredicateContext","rehydrateJsonExpressionRecord","logger","findMatchingJobListenerSubscriptions","evaluateStoredFilter","toReason","isPermanentDeliverEventToJobListenerError","LISTENER_INTEGER_PATTERN","routeEventToJobListeners","params","subscriptions","workspaceId","source","event","filterErrorCount","effectiveMatchByJobId","Map","subscription","filterResult","evaluateListenerFilter","payload","kind","history","listenerFilterErrored","reason","previous","get","jobId","shouldReplaceEffectiveMatcher","set","fireDeliveryNeeded","values","some","listenerDisposition","triggerReference","workflows","resolveWorkflowRunTriggerReference","triggerConnectionId","connectionId","triggerPayload","provider","deliveryId","data","acceptedJobCount","deliveredCount","sawTransientError","dispatchErrorCount","firstTransientError","disposition","result","deliverEventToJobListener","eventRef","receivedAt","toISOString","skipped","listenerTriggered","buffered","error","listenerDispatchErrored","engagedCount","matchedJobCount","size","transientErrored","transientError","undefined","filter","config","trim","value","context","invalidReason","evaluationFailedReason","snapshot","readFilterSnapshot","rehydrateListenerFilterSnapshot","outputTypes","filter_snapshot","Array","isArray","outputTypesValue","filter_output_types","parsedOutputTypes","safeParse","success","isRecord","jobs","Object","fromEntries","entries","map","jobKey","jobContext","jobOutputTypes","hasOwn","outputs","executions","execution","rehydrated","run","rehydrateRunSnapshot","rehydrateJobsSnapshot","number","rehydrateListenerInt","created_at","rehydrateListenerTimestamp","rehydrateJobSnapshot","job","rehydrateExecutionSnapshot","index","started_at","finished_at","events","rehydrateExecutionEventSnapshot","received_at","Number","isSafeInteger","BigInt","test","date","Date","isNaN","getTime","listenerMatcherSortKey","kindRank","matcherOrdinal","toString","padStart","candidate","previousKey","candidateKey","warn","keptSubscriptionId","id","candidateSubscriptionId","matcherKind"],"mappings":"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"}
@@ -71,24 +71,24 @@ declare function startRunKnownError(error: unknown): import("@shipfox/inter-modu
71
71
  readonly 'interpolation-unresolvable': import("zod").ZodObject<{
72
72
  definitionId: import("zod").ZodString;
73
73
  field: import("zod").ZodEnum<{
74
- run: "run";
75
- env: "env";
76
- "agent.prompt": "agent.prompt";
77
74
  "agent.model": "agent.model";
75
+ "agent.prompt": "agent.prompt";
78
76
  "agent.provider": "agent.provider";
79
77
  "agent.thinking": "agent.thinking";
80
- "job.runner": "job.runner";
81
- "job.outputs": "job.outputs";
78
+ "checkout.connection": "checkout.connection";
79
+ "checkout.path": "checkout.path";
80
+ "checkout.project": "checkout.project";
81
+ "checkout.ref": "checkout.ref";
82
+ "checkout.repository": "checkout.repository";
83
+ env: "env";
82
84
  "job.execution_name": "job.execution_name";
83
- "workflow.run_name": "workflow.run_name";
85
+ "job.outputs": "job.outputs";
86
+ "job.runner": "job.runner";
87
+ run: "run";
88
+ "step.feedback": "step.feedback";
84
89
  "step.name": "step.name";
85
90
  "step.working_directory": "step.working_directory";
86
- "step.feedback": "step.feedback";
87
- "checkout.project": "checkout.project";
88
- "checkout.connection": "checkout.connection";
89
- "checkout.repository": "checkout.repository";
90
- "checkout.ref": "checkout.ref";
91
- "checkout.path": "checkout.path";
91
+ "workflow.run_name": "workflow.run_name";
92
92
  }>;
93
93
  source: import("zod").ZodString;
94
94
  envKey: import("zod").ZodOptional<import("zod").ZodString>;
package/dist/db/db.d.ts CHANGED
@@ -141,7 +141,7 @@ export declare const schema: {
141
141
  generated: undefined;
142
142
  }, {}, {}>;
143
143
  };
144
- dialect: "pg";
144
+ dialect: 'pg';
145
145
  }>;
146
146
  jobListenerSubscriptions: import("drizzle-orm/pg-core").PgTableWithColumns<{
147
147
  name: "job_listener_subscriptions";
@@ -320,7 +320,7 @@ export declare const schema: {
320
320
  generated: undefined;
321
321
  }, {}, {}>;
322
322
  };
323
- dialect: "pg";
323
+ dialect: 'pg';
324
324
  }>;
325
325
  triggerSubscriptions: import("drizzle-orm/pg-core").PgTableWithColumns<{
326
326
  name: "subscriptions";
@@ -497,7 +497,7 @@ export declare const schema: {
497
497
  generated: undefined;
498
498
  }, {}, {}>;
499
499
  };
500
- dialect: "pg";
500
+ dialect: 'pg';
501
501
  }>;
502
502
  triggersOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
503
503
  name: "outbox";
@@ -691,7 +691,7 @@ export declare const schema: {
691
691
  generated: undefined;
692
692
  }, {}, {}>;
693
693
  };
694
- dialect: "pg";
694
+ dialect: 'pg';
695
695
  }>;
696
696
  triggersReceivedEvents: import("drizzle-orm/pg-core").PgTableWithColumns<{
697
697
  name: "received_events";
@@ -736,7 +736,7 @@ export declare const schema: {
736
736
  tableName: "received_events";
737
737
  dataType: "string";
738
738
  columnType: "PgText";
739
- data: "integration" | "manual" | "cron";
739
+ data: "cron" | "integration" | "manual";
740
740
  driverParam: string;
741
741
  notNull: true;
742
742
  hasDefault: false;
@@ -872,7 +872,7 @@ export declare const schema: {
872
872
  tableName: "received_events";
873
873
  dataType: "string";
874
874
  columnType: "PgText";
875
- data: "received" | "routed" | "discarded" | "failed" | "errored";
875
+ data: "discarded" | "errored" | "failed" | "received" | "routed";
876
876
  driverParam: string;
877
877
  notNull: true;
878
878
  hasDefault: true;
@@ -972,7 +972,7 @@ export declare const schema: {
972
972
  generated: undefined;
973
973
  }, {}, {}>;
974
974
  };
975
- dialect: "pg";
975
+ dialect: 'pg';
976
976
  }>;
977
977
  triggersDecisions: import("drizzle-orm/pg-core").PgTableWithColumns<{
978
978
  name: "decisions";
@@ -1017,7 +1017,7 @@ export declare const schema: {
1017
1017
  tableName: "decisions";
1018
1018
  dataType: "string";
1019
1019
  columnType: "PgText";
1020
- data: "trigger" | "listener";
1020
+ data: "listener" | "trigger";
1021
1021
  driverParam: string;
1022
1022
  notNull: true;
1023
1023
  hasDefault: false;
@@ -1170,7 +1170,7 @@ export declare const schema: {
1170
1170
  tableName: "decisions";
1171
1171
  dataType: "string";
1172
1172
  columnType: "PgText";
1173
- data: "triggered" | "filter-error" | "dispatch-error";
1173
+ data: "dispatch-error" | "filter-error" | "triggered";
1174
1174
  driverParam: string;
1175
1175
  notNull: true;
1176
1176
  hasDefault: false;
@@ -1251,7 +1251,7 @@ export declare const schema: {
1251
1251
  generated: undefined;
1252
1252
  }, {}, {}>;
1253
1253
  };
1254
- dialect: "pg";
1254
+ dialect: 'pg';
1255
1255
  }>;
1256
1256
  };
1257
1257
  export declare function db(): NodePgDatabase<{
@@ -1396,7 +1396,7 @@ export declare function db(): NodePgDatabase<{
1396
1396
  generated: undefined;
1397
1397
  }, {}, {}>;
1398
1398
  };
1399
- dialect: "pg";
1399
+ dialect: 'pg';
1400
1400
  }>;
1401
1401
  jobListenerSubscriptions: import("drizzle-orm/pg-core").PgTableWithColumns<{
1402
1402
  name: "job_listener_subscriptions";
@@ -1575,7 +1575,7 @@ export declare function db(): NodePgDatabase<{
1575
1575
  generated: undefined;
1576
1576
  }, {}, {}>;
1577
1577
  };
1578
- dialect: "pg";
1578
+ dialect: 'pg';
1579
1579
  }>;
1580
1580
  triggerSubscriptions: import("drizzle-orm/pg-core").PgTableWithColumns<{
1581
1581
  name: "subscriptions";
@@ -1752,7 +1752,7 @@ export declare function db(): NodePgDatabase<{
1752
1752
  generated: undefined;
1753
1753
  }, {}, {}>;
1754
1754
  };
1755
- dialect: "pg";
1755
+ dialect: 'pg';
1756
1756
  }>;
1757
1757
  triggersOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
1758
1758
  name: "outbox";
@@ -1946,7 +1946,7 @@ export declare function db(): NodePgDatabase<{
1946
1946
  generated: undefined;
1947
1947
  }, {}, {}>;
1948
1948
  };
1949
- dialect: "pg";
1949
+ dialect: 'pg';
1950
1950
  }>;
1951
1951
  triggersReceivedEvents: import("drizzle-orm/pg-core").PgTableWithColumns<{
1952
1952
  name: "received_events";
@@ -1991,7 +1991,7 @@ export declare function db(): NodePgDatabase<{
1991
1991
  tableName: "received_events";
1992
1992
  dataType: "string";
1993
1993
  columnType: "PgText";
1994
- data: "integration" | "manual" | "cron";
1994
+ data: "cron" | "integration" | "manual";
1995
1995
  driverParam: string;
1996
1996
  notNull: true;
1997
1997
  hasDefault: false;
@@ -2127,7 +2127,7 @@ export declare function db(): NodePgDatabase<{
2127
2127
  tableName: "received_events";
2128
2128
  dataType: "string";
2129
2129
  columnType: "PgText";
2130
- data: "received" | "routed" | "discarded" | "failed" | "errored";
2130
+ data: "discarded" | "errored" | "failed" | "received" | "routed";
2131
2131
  driverParam: string;
2132
2132
  notNull: true;
2133
2133
  hasDefault: true;
@@ -2227,7 +2227,7 @@ export declare function db(): NodePgDatabase<{
2227
2227
  generated: undefined;
2228
2228
  }, {}, {}>;
2229
2229
  };
2230
- dialect: "pg";
2230
+ dialect: 'pg';
2231
2231
  }>;
2232
2232
  triggersDecisions: import("drizzle-orm/pg-core").PgTableWithColumns<{
2233
2233
  name: "decisions";
@@ -2272,7 +2272,7 @@ export declare function db(): NodePgDatabase<{
2272
2272
  tableName: "decisions";
2273
2273
  dataType: "string";
2274
2274
  columnType: "PgText";
2275
- data: "trigger" | "listener";
2275
+ data: "listener" | "trigger";
2276
2276
  driverParam: string;
2277
2277
  notNull: true;
2278
2278
  hasDefault: false;
@@ -2425,7 +2425,7 @@ export declare function db(): NodePgDatabase<{
2425
2425
  tableName: "decisions";
2426
2426
  dataType: "string";
2427
2427
  columnType: "PgText";
2428
- data: "triggered" | "filter-error" | "dispatch-error";
2428
+ data: "dispatch-error" | "filter-error" | "triggered";
2429
2429
  driverParam: string;
2430
2430
  notNull: true;
2431
2431
  hasDefault: false;
@@ -2506,7 +2506,7 @@ export declare function db(): NodePgDatabase<{
2506
2506
  generated: undefined;
2507
2507
  }, {}, {}>;
2508
2508
  };
2509
- dialect: "pg";
2509
+ dialect: 'pg';
2510
2510
  }>;
2511
2511
  }>;
2512
2512
  export type Tx = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];
@@ -5,6 +5,7 @@ export interface ListenerMatcher {
5
5
  inputs?: Readonly<Record<string, unknown>> | undefined;
6
6
  filter?: string | undefined;
7
7
  filter_snapshot?: Readonly<Record<string, unknown>> | undefined;
8
+ filter_output_types?: Readonly<Record<string, unknown>> | undefined;
8
9
  }
9
10
  export interface ProjectJobListenerSubscriptionsParams {
10
11
  workspaceId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"job-listener-subscriptions.d.ts","sourceRoot":"","sources":["../../src/db/job-listener-subscriptions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,uBAAuB,EACxB,MAAM,6CAA6C,CAAC;AASrD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACvD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CACjE;AAED,MAAM,WAAW,qCAAqC;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI,CAAC;IACtC,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI,CAAC;CAC1C;AAED,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,qCAAqC,GAC5C,OAAO,CAAC,IAAI,CAAC,CA4Cf;AA6BD,wBAAsB,oCAAoC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzF;AAED,MAAM,WAAW,0CAA0C;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,oCAAoC,CACxD,MAAM,EAAE,0CAA0C,GACjD,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAYpC;AAED,wBAAsB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOjF"}
1
+ {"version":3,"file":"job-listener-subscriptions.d.ts","sourceRoot":"","sources":["../../src/db/job-listener-subscriptions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,uBAAuB,EACxB,MAAM,6CAA6C,CAAC;AASrD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACvD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAChE,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CACrE;AAED,MAAM,WAAW,qCAAqC;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI,CAAC;IACtC,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI,CAAC;CAC1C;AAED,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,qCAAqC,GAC5C,OAAO,CAAC,IAAI,CAAC,CA+Cf;AA6BD,wBAAsB,oCAAoC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzF;AAED,MAAM,WAAW,0CAA0C;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,oCAAoC,CACxD,MAAM,EAAE,0CAA0C,GACjD,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAYpC;AAED,wBAAsB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOjF"}
@@ -20,6 +20,9 @@ export async function projectJobListenerSubscriptions(params) {
20
20
  if (matcher.inputs !== undefined) config.inputs = matcher.inputs;
21
21
  if (matcher.filter !== undefined) config.filter = matcher.filter;
22
22
  if (matcher.filter_snapshot !== undefined) config.filter_snapshot = matcher.filter_snapshot;
23
+ if (matcher.filter_output_types !== undefined) {
24
+ config.filter_output_types = matcher.filter_output_types;
25
+ }
23
26
  await tx.insert(jobListenerSubscriptions).values({
24
27
  workspaceId: params.workspaceId,
25
28
  workflowRunId: params.workflowRunId,