@pattern-stack/codegen 0.15.3 → 0.16.1
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/CHANGELOG.md +88 -0
- package/consumer-skills/integration/change-sources-and-sinks.md +1 -1
- package/dist/{chunk-GCYKMF22.js → chunk-24WXSC3C.js} +6 -6
- package/dist/{chunk-32DOFN3T.js → chunk-2WDX6I7T.js} +2 -2
- package/dist/{chunk-WWGYCIJX.js → chunk-43SBT72G.js} +2 -2
- package/dist/{chunk-FBGHYQIZ.js → chunk-5LXOJGO2.js} +6 -6
- package/dist/{chunk-32BMMV4H.js → chunk-5RT7JGKT.js} +5 -5
- package/dist/{chunk-3NMCDN7L.js → chunk-5TK7MEN4.js} +2 -2
- package/dist/chunk-5TK7MEN4.js.map +1 -0
- package/dist/{chunk-4H3PETLM.js → chunk-AYC2HEAL.js} +12 -9
- package/dist/chunk-AYC2HEAL.js.map +1 -0
- package/dist/{chunk-27ETSJ2X.js → chunk-COGHTKXY.js} +2 -2
- package/dist/{chunk-IYNSRIGR.js → chunk-CRBVI4GE.js} +5 -5
- package/dist/{chunk-J7JMVS2B.js → chunk-CZQUOIDY.js} +4 -4
- package/dist/{chunk-O37C3YE6.js → chunk-DGYTSCKN.js} +14 -8
- package/dist/chunk-DGYTSCKN.js.map +1 -0
- package/dist/{chunk-L7BNNRGI.js → chunk-DLG62MQY.js} +26 -6
- package/dist/chunk-DLG62MQY.js.map +1 -0
- package/dist/{chunk-TNXH7BJS.js → chunk-E45CSC33.js} +2 -2
- package/dist/{chunk-4JLJYWJC.js → chunk-H6FO2ZDJ.js} +99 -11
- package/dist/chunk-H6FO2ZDJ.js.map +1 -0
- package/dist/{chunk-5Y7W3XR6.js → chunk-IT6FRTEW.js} +30 -11
- package/dist/chunk-IT6FRTEW.js.map +1 -0
- package/dist/{chunk-RC23QROE.js → chunk-JM3T27ZW.js} +78 -4
- package/dist/chunk-JM3T27ZW.js.map +1 -0
- package/dist/{chunk-Z7PQCAVK.js → chunk-LQ6PYFU6.js} +4 -4
- package/dist/chunk-MYQIQ27N.js +118 -0
- package/dist/chunk-MYQIQ27N.js.map +1 -0
- package/dist/{chunk-YTN6BKWA.js → chunk-NXNVTXKG.js} +5 -5
- package/dist/{chunk-RDVTWIYY.js → chunk-QSJ3J4HE.js} +5 -5
- package/dist/{chunk-4MVGAMUA.js → chunk-RUSUZZAF.js} +4 -4
- package/dist/{chunk-4RFHUZXU.js → chunk-T4YJRD22.js} +4 -4
- package/dist/{chunk-7YGORYZD.js → chunk-T6C4LFLC.js} +4 -4
- package/dist/{chunk-OGIZXGPY.js → chunk-TDEHU73T.js} +4 -4
- package/dist/{chunk-YLPAPPLW.js → chunk-TIZXQU26.js} +36 -9
- package/dist/chunk-TIZXQU26.js.map +1 -0
- package/dist/{chunk-EOLLMEAH.js → chunk-TKVTEUBD.js} +3 -3
- package/dist/chunk-TKVTEUBD.js.map +1 -0
- package/dist/{chunk-YPWODKD5.js → chunk-W2UIDI3R.js} +5 -5
- package/dist/chunk-W4HOHZVF.js +1 -0
- package/dist/{chunk-DCCZB4UC.js → chunk-XWBK3XJK.js} +4 -4
- package/dist/{chunk-SR7F3TJY.js → chunk-YK5JEVLX.js} +4 -4
- package/dist/{chunk-BIO6F7YI.js → chunk-ZPL74UQN.js} +4 -2
- package/dist/{chunk-BIO6F7YI.js.map → chunk-ZPL74UQN.js.map} +1 -1
- package/dist/runtime/base-classes/index.js +22 -22
- package/dist/runtime/subsystems/analytics/analytics.module.js +2 -2
- package/dist/runtime/subsystems/analytics/index.js +4 -4
- package/dist/runtime/subsystems/auth/auth.module.js +1 -1
- package/dist/runtime/subsystems/auth/index.js +7 -7
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js +3 -3
- package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +1 -1
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.d.ts +2 -1
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +6 -5
- package/dist/runtime/subsystems/bridge/bridge.module.js +16 -15
- package/dist/runtime/subsystems/bridge/event-flow.service.js +3 -3
- package/dist/runtime/subsystems/bridge/index.js +16 -15
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/cache/cache.module.js +3 -3
- package/dist/runtime/subsystems/cache/index.js +5 -5
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +20 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +4 -3
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js +2 -2
- package/dist/runtime/subsystems/events/events.module.d.ts +14 -0
- package/dist/runtime/subsystems/events/events.module.js +6 -5
- package/dist/runtime/subsystems/events/index.js +12 -11
- package/dist/runtime/subsystems/index.js +88 -87
- package/dist/runtime/subsystems/integration/build-change-source.js +2 -2
- package/dist/runtime/subsystems/integration/detection-config.schema.d.ts +23 -15
- package/dist/runtime/subsystems/integration/detection-config.schema.js +1 -1
- package/dist/runtime/subsystems/integration/execute-integration.use-case.js +2 -2
- package/dist/runtime/subsystems/integration/index.js +17 -17
- package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/integration/integration.module.js +4 -4
- package/dist/runtime/subsystems/integration/webhook-change-source.d.ts +36 -6
- package/dist/runtime/subsystems/integration/webhook-change-source.js +1 -1
- package/dist/runtime/subsystems/jobs/index.d.ts +2 -1
- package/dist/runtime/subsystems/jobs/index.js +42 -30
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +6 -5
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.d.ts +2 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.js +4 -3
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +2 -2
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-worker.d.ts +28 -0
- package/dist/runtime/subsystems/jobs/job-worker.js +4 -3
- package/dist/runtime/subsystems/jobs/job-worker.module.js +11 -10
- package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +12 -7
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +9 -8
- package/dist/runtime/subsystems/jobs/jobs-domain.tokens.d.ts +13 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.tokens.js +3 -1
- package/dist/runtime/subsystems/jobs/pg-notify.d.ts +85 -0
- package/dist/runtime/subsystems/jobs/pg-notify.js +14 -0
- package/dist/runtime/subsystems/jobs/pg-notify.js.map +1 -0
- package/dist/runtime/subsystems/observability/index.js +4 -4
- package/dist/runtime/subsystems/observability/observability.module.js +4 -4
- package/dist/runtime/subsystems/observability/observability.service.js +3 -3
- package/dist/runtime/subsystems/storage/index.js +4 -4
- package/dist/runtime/subsystems/storage/storage.module.js +2 -2
- package/dist/src/cli/index.js +53 -15
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +11 -11
- package/dist/src/index.js +9 -9
- package/package.json +1 -1
- package/runtime/subsystems/bridge/bridge-outbox-drain-hook.ts +27 -0
- package/runtime/subsystems/events/event-bus.drizzle-backend.ts +108 -4
- package/runtime/subsystems/events/events.module.ts +14 -0
- package/runtime/subsystems/integration/detection-config.schema.ts +64 -54
- package/runtime/subsystems/integration/webhook-change-source.ts +187 -133
- package/runtime/subsystems/jobs/index.ts +10 -0
- package/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.ts +29 -2
- package/runtime/subsystems/jobs/job-worker.module.ts +11 -0
- package/runtime/subsystems/jobs/job-worker.ts +98 -0
- package/runtime/subsystems/jobs/jobs-domain.module.ts +22 -7
- package/runtime/subsystems/jobs/jobs-domain.tokens.ts +13 -0
- package/runtime/subsystems/jobs/pg-notify.ts +216 -0
- package/templates/subsystem/events-config/codegen-config-events-block.ejs.t +14 -0
- package/templates/subsystem/jobs-config/codegen-config-jobs-block.ejs.t +13 -4
- package/dist/chunk-3NMCDN7L.js.map +0 -1
- package/dist/chunk-4H3PETLM.js.map +0 -1
- package/dist/chunk-4JLJYWJC.js.map +0 -1
- package/dist/chunk-5Y7W3XR6.js.map +0 -1
- package/dist/chunk-EOLLMEAH.js.map +0 -1
- package/dist/chunk-L7BNNRGI.js.map +0 -1
- package/dist/chunk-O37C3YE6.js.map +0 -1
- package/dist/chunk-RC23QROE.js.map +0 -1
- package/dist/chunk-UTN4GBPQ.js +0 -1
- package/dist/chunk-YLPAPPLW.js.map +0 -1
- /package/dist/{chunk-GCYKMF22.js.map → chunk-24WXSC3C.js.map} +0 -0
- /package/dist/{chunk-32DOFN3T.js.map → chunk-2WDX6I7T.js.map} +0 -0
- /package/dist/{chunk-WWGYCIJX.js.map → chunk-43SBT72G.js.map} +0 -0
- /package/dist/{chunk-FBGHYQIZ.js.map → chunk-5LXOJGO2.js.map} +0 -0
- /package/dist/{chunk-32BMMV4H.js.map → chunk-5RT7JGKT.js.map} +0 -0
- /package/dist/{chunk-27ETSJ2X.js.map → chunk-COGHTKXY.js.map} +0 -0
- /package/dist/{chunk-IYNSRIGR.js.map → chunk-CRBVI4GE.js.map} +0 -0
- /package/dist/{chunk-J7JMVS2B.js.map → chunk-CZQUOIDY.js.map} +0 -0
- /package/dist/{chunk-TNXH7BJS.js.map → chunk-E45CSC33.js.map} +0 -0
- /package/dist/{chunk-Z7PQCAVK.js.map → chunk-LQ6PYFU6.js.map} +0 -0
- /package/dist/{chunk-YTN6BKWA.js.map → chunk-NXNVTXKG.js.map} +0 -0
- /package/dist/{chunk-RDVTWIYY.js.map → chunk-QSJ3J4HE.js.map} +0 -0
- /package/dist/{chunk-4MVGAMUA.js.map → chunk-RUSUZZAF.js.map} +0 -0
- /package/dist/{chunk-4RFHUZXU.js.map → chunk-T4YJRD22.js.map} +0 -0
- /package/dist/{chunk-7YGORYZD.js.map → chunk-T6C4LFLC.js.map} +0 -0
- /package/dist/{chunk-OGIZXGPY.js.map → chunk-TDEHU73T.js.map} +0 -0
- /package/dist/{chunk-YPWODKD5.js.map → chunk-W2UIDI3R.js.map} +0 -0
- /package/dist/{chunk-UTN4GBPQ.js.map → chunk-W4HOHZVF.js.map} +0 -0
- /package/dist/{chunk-DCCZB4UC.js.map → chunk-XWBK3XJK.js.map} +0 -0
- /package/dist/{chunk-SR7F3TJY.js.map → chunk-YK5JEVLX.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,94 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.16.1] — 2026-06-04
|
|
8
|
+
|
|
9
|
+
**`WebhookFetchCallback<T>` yields `{ record, eventId?, cursor? }`** —
|
|
10
|
+
`WebhookChangeSource` now prefers a queue-yielded `eventId` for
|
|
11
|
+
`Change<T>.dedupKey` (gap #6 follow-through, swe-brain ADR-0009 Amendment B
|
|
12
|
+
§B5). swe-brain's Slack inbound drain documented an `eventIdField: 'externalId'`
|
|
13
|
+
substitution — delivery dedup happens at the receiver, so record identity stood
|
|
14
|
+
in for event identity. That substitution becomes genuinely unsafe once a message
|
|
15
|
+
and its edit (same `externalId`, different vendor event) can share one drain
|
|
16
|
+
batch — they'd collapse to a single `dedupKey`. Vendor delivery metadata (the
|
|
17
|
+
event id) should never need a field on the vendor-neutral canonical record; the
|
|
18
|
+
queue-yield is the right channel for it. Backward-compatible: callbacks that
|
|
19
|
+
yield `{ record, cursor? }` and configs that set `webhook.eventIdField` are
|
|
20
|
+
unchanged; opting into `eventId` is the only migration, and it's optional.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **`WebhookFetchCallback<T>` yield shape** is now `{ record: T; eventId?:
|
|
25
|
+
string; cursor?: WebhookCursor }` (was `{ record: T; cursor?: WebhookCursor }`)
|
|
26
|
+
— the new `eventId` is opt-in and additive; existing callbacks compile and run
|
|
27
|
+
unchanged.
|
|
28
|
+
- **`WebhookChangeSource.dedupKey` precedence** is now **yielded `eventId` >
|
|
29
|
+
`webhook.eventIdField` record extraction > undefined**. A non-empty yielded
|
|
30
|
+
`eventId` always wins; otherwise the configured `eventIdField` is read off the
|
|
31
|
+
emitted record (and still throws if the field is configured but absent on the
|
|
32
|
+
record); with neither, `dedupKey` is `undefined` (the orchestrator simply has
|
|
33
|
+
no delivery-level dedup signal for that change).
|
|
34
|
+
- **`detection-config.schema.ts`: `webhook.eventIdField` is now optional**
|
|
35
|
+
(`z.string().min(1).optional()`). A callback that always yields `eventId` need
|
|
36
|
+
not declare a record field for it. The `webhook` block itself stays
|
|
37
|
+
structurally required in webhook mode (an empty `{}` is valid; a missing block
|
|
38
|
+
is not), so the existing "webhook-mode missing webhook block" validation is
|
|
39
|
+
unaffected.
|
|
40
|
+
|
|
41
|
+
### Docs
|
|
42
|
+
|
|
43
|
+
- ADR-033 §1 amended (the `webhook: { eventIdField? }` shape + the yield channel
|
|
44
|
+
+ dedupKey precedence), integration `SKILL.md` + consumer
|
|
45
|
+
`change-sources-and-sinks.md` updated to the yield-preferred precedence.
|
|
46
|
+
|
|
47
|
+
## [0.16.0] — 2026-06-04
|
|
48
|
+
|
|
49
|
+
**Postgres LISTEN/NOTIFY wakeups** for the jobs worker + events outbox drainer
|
|
50
|
+
(LISTEN-NOTIFY-1, dogfood gap #7 — swe-brain's live-inbound latency). The
|
|
51
|
+
scaffold has documented `jobs.extensions.drizzle.listen_notify` since JOB-6 and
|
|
52
|
+
`JobsDomainModule` reserved the typed slot, but neither knob was wired (no
|
|
53
|
+
runtime, and the barrel never threaded `jobs.extensions.drizzle.*` into
|
|
54
|
+
`JobWorkerModule.forRoot`). This makes both real.
|
|
55
|
+
|
|
56
|
+
NOTIFY wakes the polling loop the instant work becomes claimable; **interval
|
|
57
|
+
polling remains the safety net** (alongside, never instead). Every `pg_notify`
|
|
58
|
+
is emitted **inside the same transaction** as the row write it announces, so
|
|
59
|
+
Postgres delivers it only on commit — durability is byte-for-byte unchanged. A
|
|
60
|
+
lost notification (listener down, transaction-mode pooler) degrades to today's
|
|
61
|
+
poll latency, never to lost work.
|
|
62
|
+
|
|
63
|
+
Measured on swe-brain's inbound spine (webhook → outbox drain → bridge wrapper →
|
|
64
|
+
user job): **~1.4–3.0 s → sub-500 ms** with `listen_notify: true`, zero
|
|
65
|
+
durability change.
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
|
|
69
|
+
- **`runtime/subsystems/jobs/pg-notify.ts`** — `PgNotifyListener` (dedicated
|
|
70
|
+
listener connection off `DRIZZLE.$client`, debounced dispatch,
|
|
71
|
+
reconnect-with-capped-backoff, WARN-once degradation) + in-tx `pgNotify(tx,
|
|
72
|
+
channel, payload)` + channel constants. Shared by jobs + events.
|
|
73
|
+
- **Jobs worker** — `JobWorkerOptions.listenNotify`; LISTEN on
|
|
74
|
+
`codegen_jobs_wake`, a notify for the worker's pool drives an immediate
|
|
75
|
+
debounced claim cycle. `DrizzleJobOrchestrator.start()` emits the in-tx wake
|
|
76
|
+
on enqueue, gated on the new `JOBS_LISTEN_NOTIFY` token (provided from
|
|
77
|
+
`jobs.extensions.drizzle.listen_notify`).
|
|
78
|
+
- **Events drainer** — `EventsModuleOptions.listenNotify`; LISTEN on
|
|
79
|
+
`codegen_events_wake`, `publish`/`publishMany` emit the in-tx wake; a
|
|
80
|
+
pool-filtered drainer wakes only for its lanes.
|
|
81
|
+
- **Bridge** — the `BridgeOutboxDrainHook` wrapper `job_run` insert emits the
|
|
82
|
+
jobs wake in the per-event drain tx, so reserved-pool wrappers wake too.
|
|
83
|
+
- **Generator threading** — `jobs.extensions.drizzle.{listen_notify,
|
|
84
|
+
poll_interval_ms}` flow into the generated barrel's `JobsDomainModule.forRoot`
|
|
85
|
+
+ embedded `JobWorkerModule.forRoot({ domainModuleExtensions: { drizzle: …
|
|
86
|
+
} })`; `events.extensions.drizzle.listen_notify` flows into
|
|
87
|
+
`EventsModule.forRoot({ listenNotify })`. Package and vendored emission both
|
|
88
|
+
covered. (The runtime already honored `JobWorkerOptions.pollIntervalMs`; it
|
|
89
|
+
just never received a config value — now it does.)
|
|
90
|
+
- Scaffold config comments + jobs skill updated to implemented reality, with the
|
|
91
|
+
**PgBouncer caveat**: LISTEN/NOTIFY requires a direct (or session-mode)
|
|
92
|
+
connection — session-scoped `LISTEN` does not survive a transaction-mode
|
|
93
|
+
pooler; behind one the feature degrades to polling.
|
|
94
|
+
|
|
7
95
|
## [0.15.3] — 2026-06-03
|
|
8
96
|
|
|
9
97
|
Package-mode **inbound webhook drain** — the first real exercise of
|
|
@@ -75,7 +75,7 @@ interface Change<T> {
|
|
|
75
75
|
record: T; // canonical shape — provider mapping happens in the adapter
|
|
76
76
|
cursor: unknown; // typed internally; opaque at the seam
|
|
77
77
|
source: 'poll' | 'cdc' | 'webhook'; // provenance for the run-log audit
|
|
78
|
-
dedupKey?: string; // CDC replay_id / webhook
|
|
78
|
+
dedupKey?: string; // CDC replay_id / webhook event id when available (webhook: prefers the eventId yielded by WebhookFetchCallback, else webhook.eventIdField)
|
|
79
79
|
providerChangedFields?: string[]; // CDC-only hint; lets the differ skip untouched fields
|
|
80
80
|
}
|
|
81
81
|
```
|
|
@@ -3,16 +3,13 @@ import {
|
|
|
3
3
|
} from "./chunk-EO2QPOKH.js";
|
|
4
4
|
import {
|
|
5
5
|
PostgresCursorStore
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XWBK3XJK.js";
|
|
7
7
|
import {
|
|
8
8
|
MemoryCursorStore
|
|
9
9
|
} from "./chunk-AHV4GDYM.js";
|
|
10
10
|
import {
|
|
11
11
|
DrizzleIntegrationRunRecorder
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import {
|
|
14
|
-
DeepEqualDiffer
|
|
15
|
-
} from "./chunk-36U5UGIO.js";
|
|
12
|
+
} from "./chunk-YK5JEVLX.js";
|
|
16
13
|
import {
|
|
17
14
|
INTEGRATION_CURSOR_STORE,
|
|
18
15
|
INTEGRATION_FIELD_DIFFER,
|
|
@@ -20,6 +17,9 @@ import {
|
|
|
20
17
|
INTEGRATION_MULTI_TENANT,
|
|
21
18
|
INTEGRATION_RUN_RECORDER
|
|
22
19
|
} from "./chunk-S7C6TIIF.js";
|
|
20
|
+
import {
|
|
21
|
+
DeepEqualDiffer
|
|
22
|
+
} from "./chunk-36U5UGIO.js";
|
|
23
23
|
import {
|
|
24
24
|
__decorateClass
|
|
25
25
|
} from "./chunk-2E224ZSN.js";
|
|
@@ -78,4 +78,4 @@ IntegrationModule = __decorateClass([
|
|
|
78
78
|
export {
|
|
79
79
|
IntegrationModule
|
|
80
80
|
};
|
|
81
|
-
//# sourceMappingURL=chunk-
|
|
81
|
+
//# sourceMappingURL=chunk-24WXSC3C.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DetectionConfigSchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5TK7MEN4.js";
|
|
4
4
|
|
|
5
5
|
// src/parser/load-entities.ts
|
|
6
6
|
import { resolve as resolve2 } from "path";
|
|
@@ -4039,4 +4039,4 @@ export {
|
|
|
4039
4039
|
analyzeDomain,
|
|
4040
4040
|
validateEntities
|
|
4041
4041
|
};
|
|
4042
|
-
//# sourceMappingURL=chunk-
|
|
4042
|
+
//# sourceMappingURL=chunk-2WDX6I7T.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-4MF3HKJA.js";
|
|
4
4
|
import {
|
|
5
5
|
WebhookChangeSource
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TIZXQU26.js";
|
|
7
7
|
|
|
8
8
|
// runtime/subsystems/integration/build-change-source.ts
|
|
9
9
|
function buildChangeSource(cfg, fetch, middlewares = []) {
|
|
@@ -26,4 +26,4 @@ function buildChangeSource(cfg, fetch, middlewares = []) {
|
|
|
26
26
|
export {
|
|
27
27
|
buildChangeSource
|
|
28
28
|
};
|
|
29
|
-
//# sourceMappingURL=chunk-
|
|
29
|
+
//# sourceMappingURL=chunk-43SBT72G.js.map
|
|
@@ -5,15 +5,15 @@ import {
|
|
|
5
5
|
toJobRunSummary
|
|
6
6
|
} from "./chunk-L3LZWWSX.js";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
jobRuns
|
|
9
|
+
} from "./chunk-OKXZ63IA.js";
|
|
10
10
|
import {
|
|
11
11
|
JOBS_MULTI_TENANT,
|
|
12
12
|
JOB_ORCHESTRATOR
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ZPL74UQN.js";
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
} from "./chunk-
|
|
15
|
+
MissingTenantIdError
|
|
16
|
+
} from "./chunk-T4BIIU5E.js";
|
|
17
17
|
import {
|
|
18
18
|
DRIZZLE
|
|
19
19
|
} from "./chunk-U64T4YZE.js";
|
|
@@ -198,4 +198,4 @@ DrizzleJobRunService = __decorateClass([
|
|
|
198
198
|
export {
|
|
199
199
|
DrizzleJobRunService
|
|
200
200
|
};
|
|
201
|
-
//# sourceMappingURL=chunk-
|
|
201
|
+
//# sourceMappingURL=chunk-5LXOJGO2.js.map
|
|
@@ -3,15 +3,15 @@ import {
|
|
|
3
3
|
} from "./chunk-6DWFJNIK.js";
|
|
4
4
|
import {
|
|
5
5
|
JOB_ORCHESTRATOR
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
EVENT_BUS
|
|
9
|
-
} from "./chunk-H5NH7KPE.js";
|
|
6
|
+
} from "./chunk-ZPL74UQN.js";
|
|
10
7
|
import {
|
|
11
8
|
BRIDGE_DELIVERY_REPO,
|
|
12
9
|
BRIDGE_MULTI_TENANT,
|
|
13
10
|
BRIDGE_REGISTRY
|
|
14
11
|
} from "./chunk-4LH67P4U.js";
|
|
12
|
+
import {
|
|
13
|
+
EVENT_BUS
|
|
14
|
+
} from "./chunk-H5NH7KPE.js";
|
|
15
15
|
import {
|
|
16
16
|
DRIZZLE
|
|
17
17
|
} from "./chunk-U64T4YZE.js";
|
|
@@ -106,4 +106,4 @@ EventFlowService = __decorateClass([
|
|
|
106
106
|
export {
|
|
107
107
|
EventFlowService
|
|
108
108
|
};
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
109
|
+
//# sourceMappingURL=chunk-5RT7JGKT.js.map
|
|
@@ -58,7 +58,7 @@ var PollDetectionSchema = z.object({
|
|
|
58
58
|
provenance: z.enum(["poll", "cdc"]).optional()
|
|
59
59
|
});
|
|
60
60
|
var WebhookDetectionSchema = z.object({
|
|
61
|
-
eventIdField: z.string().min(1)
|
|
61
|
+
eventIdField: z.string().min(1).optional()
|
|
62
62
|
});
|
|
63
63
|
var PollModeSchema = z.object({
|
|
64
64
|
mode: z.literal("poll"),
|
|
@@ -87,4 +87,4 @@ export {
|
|
|
87
87
|
WebhookDetectionSchema,
|
|
88
88
|
DetectionConfigSchema
|
|
89
89
|
};
|
|
90
|
-
//# sourceMappingURL=chunk-
|
|
90
|
+
//# sourceMappingURL=chunk-5TK7MEN4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../runtime/subsystems/integration/detection-config.schema.ts"],"sourcesContent":["/**\n * Integration subsystem — DetectionConfig schema (#226-1)\n *\n * Canonical Zod schema for per-entity integration detection config. The schema is\n * the single source of truth for filter/mapping shape and is consumed by:\n *\n * 1. Runtime primitives — `PollChangeSource<T>`, `WebhookChangeSource<T>`\n * (#226-3, #226-4) accept a parsed `DetectionConfig` at construction.\n * 2. Codegen — `src/schema/entity-definition.schema.ts` (#226-6) imports\n * this schema so per-entity YAML `detection:` blocks validate against\n * the same shape the runtime enforces.\n *\n * Locked decisions (see ADR-033 + decision memo Q1–Q6):\n * - Filter vocabulary is flat AND of `{ field, op, value }` triples; richer\n * boolean expressions (OR / NOT / nested) are deferred per epic open Q3.\n * - Cursor strategy is a tagged union over the four shapes the three modes\n * need (`systemModstamp`, `replayId`, `timestamp`, `eventId`). Each\n * strategy types its cursor internally; the orchestrator persists what\n * the iterator last yielded (integration skill rule 2).\n * - `mode: 'poll'` may opt into `provenance: 'cdc'` so Stripe-style event\n * endpoints (mechanically a poll, semantically CDC) reuse the poll\n * primitive while emitting `Change<T>.source = 'cdc'`. Long-lived\n * streaming CDC (SFDC Pub-Sub, Debezium) is a separate primitive\n * deferred to #226-8.\n * - `webhook` mode's `eventIdField` is optional: `WebhookChangeSource<T>`\n * prefers an `eventId` yielded by the queue iterator and falls back to the\n * `eventIdField` record extraction (precedence: yielded eventId >\n * eventIdField extraction > undefined dedupKey).\n */\nimport { z } from \"zod\";\n\n// ============================================================================\n// Field mapping — provider field → canonical target\n// ============================================================================\n\n/**\n * Maps a single provider field onto the canonical record. `transform` is an\n * opt-in tag the adapter callback may inspect (`date-iso`, `decimal-string`,\n * etc.); the schema does not enumerate transforms — adapters interpret them.\n */\nexport const FieldMappingSchema = z.object({\n\tsource: z.string().min(1),\n\ttarget: z.string().min(1),\n\ttransform: z.string().min(1).optional(),\n});\n\nexport type FieldMapping = z.infer<typeof FieldMappingSchema>;\n\n// ============================================================================\n// Resolved filter — flat-AND triple\n// ============================================================================\n\n/**\n * A single resolved filter clause applied at fetch time. `value` is `unknown`\n * to admit primitives, arrays (for `in` / `nin`), and dates as ISO strings —\n * adapters interpret per provider.\n */\nexport const ResolvedFilterSchema = z.object({\n\tfield: z.string().min(1),\n\top: z.enum([\"eq\", \"neq\", \"in\", \"nin\", \"gt\", \"gte\", \"lt\", \"lte\"]),\n\tvalue: z.unknown(),\n});\n\nexport type ResolvedFilter = z.infer<typeof ResolvedFilterSchema>;\n\n// ============================================================================\n// Cursor strategy — tagged union over the four shapes the modes need\n// ============================================================================\n\nconst SystemModstampCursorSchema = z.object({\n\tkind: z.literal(\"systemModstamp\"),\n\tfield: z.string().min(1),\n});\n\nconst ReplayIdCursorSchema = z.object({\n\tkind: z.literal(\"replayId\"),\n\tfield: z.string().min(1),\n});\n\nconst TimestampCursorSchema = z.object({\n\tkind: z.literal(\"timestamp\"),\n\tfield: z.string().min(1),\n});\n\nconst EventIdCursorSchema = z.object({\n\tkind: z.literal(\"eventId\"),\n\tfield: z.string().min(1),\n});\n\n/**\n * Gmail `historyId` (RFC-0003 §3) — an opaque, atomic vendor token. The next\n * watermark only exists at end-of-walk; there is no resumable mid-walk value.\n * `field` is metadata for codegen/adapters (the response key the token lives on).\n */\nconst HistoryIdCursorSchema = z.object({\n\tkind: z.literal(\"historyId\"),\n\tfield: z.string().min(1),\n});\n\n/**\n * Google Calendar `syncToken` (RFC-0003 §3) — an opaque, atomic sync token,\n * same divisibility profile as `historyId`.\n */\nconst SyncTokenCursorSchema = z.object({\n\tkind: z.literal(\"syncToken\"),\n\tfield: z.string().min(1),\n});\n\nexport const CursorStrategySchema = z.discriminatedUnion(\"kind\", [\n\tSystemModstampCursorSchema,\n\tReplayIdCursorSchema,\n\tTimestampCursorSchema,\n\tEventIdCursorSchema,\n\tHistoryIdCursorSchema,\n\tSyncTokenCursorSchema,\n]);\n\nexport type CursorStrategy = z.infer<typeof CursorStrategySchema>;\n\n// ============================================================================\n// Cursor divisibility (RFC-0003 §3)\n// ============================================================================\n\n/**\n * Whether a cursor strategy is *divisible* — a property of the strategy, not\n * the read primitive. Divisible cursors are sortable/monotonic watermarks whose\n * value is meaningful AS OF any single record (HubSpot `systemModstamp`, a\n * `timestamp` field, a Salesforce CDC `replayId`); the read primitive may\n * checkpoint per-ref mid-walk, so a crash resumes from the last delivered ref.\n *\n * Atomic cursors are opaque vendor tokens (Gmail `historyId`, Calendar\n * `syncToken`, a generic `eventId`) whose next value only exists at end-of-walk.\n * The primitive must withhold per-ref cursors and emit the token only at a safe\n * boundary, so an interrupted run never persists an unresumable mid-walk token\n * (it resumes all-or-nothing from the prior token — see `IncrementalReadBase`).\n *\n * `eventId` is classified atomic conservatively: a generic opaque id is treated\n * all-or-nothing unless a concrete strategy proves it monotonically resumable.\n */\nexport const CURSOR_DIVISIBILITY: Readonly<\n\tRecord<CursorStrategy[\"kind\"], boolean>\n> = {\n\tsystemModstamp: true,\n\ttimestamp: true,\n\treplayId: true,\n\teventId: false,\n\thistoryId: false,\n\tsyncToken: false,\n};\n\n/** Predicate form of {@link CURSOR_DIVISIBILITY}. */\nexport function isDivisibleCursor(kind: CursorStrategy[\"kind\"]): boolean {\n\treturn CURSOR_DIVISIBILITY[kind];\n}\n\n// ============================================================================\n// Mode-specific blocks\n// ============================================================================\n\n/**\n * Poll-mode block. `provenance: 'cdc'` opts the poll primitive into stamping\n * `Change<T>.source = 'cdc'` and populating `dedupKey` from the cursor's\n * `field` — used for Stripe-style event endpoints. Defaults to `'poll'`.\n */\nexport const PollDetectionSchema = z.object({\n\tcursor: CursorStrategySchema,\n\tprovenance: z.enum([\"poll\", \"cdc\"]).optional(),\n});\n\nexport type PollDetection = z.infer<typeof PollDetectionSchema>;\n\n/**\n * Webhook-mode block. `eventIdField`, when present, names the field on the\n * emitted canonical record that `WebhookChangeSource<T>` reads to set\n * `Change<T>.dedupKey` — used only as the fallback when the queue iterator\n * does NOT yield an `eventId` alongside the record.\n *\n * `eventIdField` is **optional**: a queue iterator that always yields an\n * `eventId` (vendor delivery metadata, the preferred channel) need not declare\n * a record field for it. dedupKey precedence is: yielded `eventId` >\n * `eventIdField` record extraction > undefined.\n */\nexport const WebhookDetectionSchema = z.object({\n\teventIdField: z.string().min(1).optional(),\n});\n\nexport type WebhookDetection = z.infer<typeof WebhookDetectionSchema>;\n\n// ============================================================================\n// DetectionConfig — top-level discriminated union over `mode`\n// ============================================================================\n\nconst PollModeSchema = z.object({\n\tmode: z.literal(\"poll\"),\n\tpoll: PollDetectionSchema,\n\tmapping: z.array(FieldMappingSchema).min(1),\n\tfilters: z.array(ResolvedFilterSchema).default([]),\n});\n\nconst WebhookModeSchema = z.object({\n\tmode: z.literal(\"webhook\"),\n\twebhook: WebhookDetectionSchema,\n\tmapping: z.array(FieldMappingSchema).min(1),\n\tfilters: z.array(ResolvedFilterSchema).default([]),\n});\n\n/**\n * Top-level detection config. Discriminated on `mode` so the relevant\n * mode-block (poll/webhook) is structurally required for that mode. CDC as a\n * long-lived streaming primitive is deferred (#226-8); CDC-as-provenance\n * (Stripe-style event endpoints) is expressed via `mode: 'poll'` with\n * `poll.provenance: 'cdc'`.\n */\nexport const DetectionConfigSchema = z.discriminatedUnion(\"mode\", [\n\tPollModeSchema,\n\tWebhookModeSchema,\n]);\n\nexport type DetectionConfig = z.infer<typeof DetectionConfigSchema>;\n"],"mappings":";AA6BA,SAAS,SAAS;AAWX,IAAM,qBAAqB,EAAE,OAAO;AAAA,EAC1C,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACxB,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACxB,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AACvC,CAAC;AAaM,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,IAAI,EAAE,KAAK,CAAC,MAAM,OAAO,MAAM,OAAO,MAAM,OAAO,MAAM,KAAK,CAAC;AAAA,EAC/D,OAAO,EAAE,QAAQ;AAClB,CAAC;AAQD,IAAM,6BAA6B,EAAE,OAAO;AAAA,EAC3C,MAAM,EAAE,QAAQ,gBAAgB;AAAA,EAChC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAED,IAAM,uBAAuB,EAAE,OAAO;AAAA,EACrC,MAAM,EAAE,QAAQ,UAAU;AAAA,EAC1B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAED,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACtC,MAAM,EAAE,QAAQ,WAAW;AAAA,EAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAED,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACpC,MAAM,EAAE,QAAQ,SAAS;AAAA,EACzB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAOD,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACtC,MAAM,EAAE,QAAQ,WAAW;AAAA,EAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAMD,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACtC,MAAM,EAAE,QAAQ,WAAW;AAAA,EAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAEM,IAAM,uBAAuB,EAAE,mBAAmB,QAAQ;AAAA,EAChE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAwBM,IAAM,sBAET;AAAA,EACH,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AAAA,EACX,WAAW;AACZ;AAGO,SAAS,kBAAkB,MAAuC;AACxE,SAAO,oBAAoB,IAAI;AAChC;AAWO,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC3C,QAAQ;AAAA,EACR,YAAY,EAAE,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,SAAS;AAC9C,CAAC;AAeM,IAAM,yBAAyB,EAAE,OAAO;AAAA,EAC9C,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAC1C,CAAC;AAQD,IAAM,iBAAiB,EAAE,OAAO;AAAA,EAC/B,MAAM,EAAE,QAAQ,MAAM;AAAA,EACtB,MAAM;AAAA,EACN,SAAS,EAAE,MAAM,kBAAkB,EAAE,IAAI,CAAC;AAAA,EAC1C,SAAS,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED,IAAM,oBAAoB,EAAE,OAAO;AAAA,EAClC,MAAM,EAAE,QAAQ,SAAS;AAAA,EACzB,SAAS;AAAA,EACT,SAAS,EAAE,MAAM,kBAAkB,EAAE,IAAI,CAAC;AAAA,EAC1C,SAAS,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AASM,IAAM,wBAAwB,EAAE,mBAAmB,QAAQ;AAAA,EACjE;AAAA,EACA;AACD,CAAC;","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JobWorker
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JM3T27ZW.js";
|
|
4
4
|
import {
|
|
5
5
|
JobsDomainModule
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DGYTSCKN.js";
|
|
7
7
|
import {
|
|
8
8
|
BULLMQ_CONNECTION,
|
|
9
9
|
BULLMQ_RESOLVED_CONFIG,
|
|
@@ -14,10 +14,6 @@ import {
|
|
|
14
14
|
allPoolNames,
|
|
15
15
|
loadPoolConfig
|
|
16
16
|
} from "./chunk-RHVN6NA7.js";
|
|
17
|
-
import {
|
|
18
|
-
BootValidationError,
|
|
19
|
-
ReservedPoolViolationError
|
|
20
|
-
} from "./chunk-T4BIIU5E.js";
|
|
21
17
|
import {
|
|
22
18
|
HandlerRegistry
|
|
23
19
|
} from "./chunk-CO6LUM72.js";
|
|
@@ -25,7 +21,11 @@ import {
|
|
|
25
21
|
JOB_ORCHESTRATOR,
|
|
26
22
|
JOB_RUN_SERVICE,
|
|
27
23
|
JOB_STEP_SERVICE
|
|
28
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-ZPL74UQN.js";
|
|
25
|
+
import {
|
|
26
|
+
BootValidationError,
|
|
27
|
+
ReservedPoolViolationError
|
|
28
|
+
} from "./chunk-T4BIIU5E.js";
|
|
29
29
|
import {
|
|
30
30
|
tokenKey
|
|
31
31
|
} from "./chunk-GYGNEQSC.js";
|
|
@@ -92,10 +92,13 @@ var JobWorkerOrchestrator = class {
|
|
|
92
92
|
`JobWorkerModule: active pool '${poolName}' is not defined in the resolved pool config. Configured pools: [${[...poolConfig.keys()].join(", ")}].`
|
|
93
93
|
);
|
|
94
94
|
}
|
|
95
|
+
const drizzleExt = backend === "drizzle" ? this.options.domainModuleExtensions?.drizzle : void 0;
|
|
95
96
|
const workerOptions = {
|
|
96
97
|
pool: poolName,
|
|
97
98
|
concurrency: def.concurrency,
|
|
98
|
-
shutdownTimeoutMs: this.options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS
|
|
99
|
+
shutdownTimeoutMs: this.options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS,
|
|
100
|
+
pollIntervalMs: drizzleExt?.pollIntervalMs,
|
|
101
|
+
listenNotify: drizzleExt?.listenNotify
|
|
99
102
|
};
|
|
100
103
|
const worker = this.options.workerFactory ? this.options.workerFactory(workerOptions) : backend === "bullmq" ? await this.spawnBullMQWorker(poolName, def.queue, def.concurrency, poolConfig) : this.spawnWorker(workerOptions);
|
|
101
104
|
await worker.onModuleInit();
|
|
@@ -287,4 +290,4 @@ export {
|
|
|
287
290
|
JobWorkerOrchestrator,
|
|
288
291
|
JobWorkerModule
|
|
289
292
|
};
|
|
290
|
-
//# sourceMappingURL=chunk-
|
|
293
|
+
//# sourceMappingURL=chunk-AYC2HEAL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../runtime/subsystems/jobs/job-worker.module.ts"],"sourcesContent":["/**\n * JobWorkerModule — `DynamicModule.forRoot({ mode, pools? })` factory that\n * boots one `JobWorker` per active pool and runs the boot-time validator\n * (Drizzle only) (ADR-022, JOB-5).\n *\n * Imports `JobsDomainModule` internally so call sites only need to add\n * `JobWorkerModule.forRoot(...)` to `AppModule.imports` — the protocol\n * tokens become available transitively via `global: true`.\n *\n * Lifecycle (`onModuleInit`, **order-critical** per JOB-5 spec):\n * 1. `loadPoolConfig()` → resolved `PoolConfig`\n * 2. `HandlerRegistry.getAll()` → registered entries\n * 3. Reserved-pool validation → throws `ReservedPoolViolationError`\n * 4. `orchestrator.upsertJobRows(entries, …)` → persist `job` definitions\n * 5. Boot validator (Drizzle only) → throws `BootValidationError`\n * (skipped entirely in memory mode — Q4 resolution 2026-04-19)\n * 6. Spawn one `JobWorker` per active pool → start polling loops\n *\n * `onModuleDestroy` calls `gracefulStop` on each worker (drains in-flight,\n * resets `running` rows, removes SIGTERM handler).\n */\nimport {\n Inject,\n Injectable,\n Logger,\n Module,\n Optional,\n type DynamicModule,\n type OnModuleDestroy,\n type OnModuleInit,\n} from '@nestjs/common';\nimport { ModuleRef } from '@nestjs/core';\nimport { tokenKey } from '../token-key';\nimport { DRIZZLE } from '../../constants/tokens';\nimport type { DrizzleClient } from '../../types/drizzle';\nimport { HandlerRegistry, type HandlerRegistryEntry } from './job-handler.base';\nimport {\n JobsDomainModule,\n type JobsDomainModuleOptions,\n} from './jobs-domain.module';\nimport {\n JOB_ORCHESTRATOR,\n JOB_RUN_SERVICE,\n JOB_STEP_SERVICE,\n} from './jobs-domain.tokens';\nimport type { IJobOrchestrator } from './job-orchestrator.protocol';\nimport type { IJobRunService } from './job-run-service.protocol';\nimport type { IJobStepService } from './job-step-service.protocol';\nimport {\n allNonReservedPoolNames,\n allPoolNames,\n loadPoolConfig,\n type PoolConfig,\n} from './pool-config.loader';\nimport { JobWorker, type JobWorkerOptions } from './job-worker';\n// #6 — `BullMQJobWorker` is lazy-loaded only when `backend: 'bullmq'` is\n// selected (`spawnBullMQWorker` below). The file is filtered out of drizzle/\n// memory installs (see `backendFileFilter`). The `ConnectionOptions` type\n// previously imported from `'bullmq'` is replaced by `BullMqConnectionOptions`\n// from `./bullmq.config` (a self-contained structural mirror that does NOT\n// require the `bullmq` peer dep to type-check).\nimport {\n BULLMQ_CONNECTION,\n BULLMQ_RESOLVED_CONFIG,\n resolvePoolQueueName,\n type BullMqConnectionOptions,\n type BullMqResolvedConfig,\n} from './bullmq.config';\nimport {\n BootValidationError,\n ReservedPoolViolationError,\n} from './jobs-errors';\n\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 30_000;\n\nexport interface JobWorkerModuleOptions {\n mode: 'embedded' | 'standalone';\n /**\n * Threads into the internal `JobsDomainModule.forRoot({ backend })`\n * import. Default `'drizzle'`. The boot-time validator runs for both\n * `'drizzle'` and `'bullmq'` (both persist `job` rows to Postgres);\n * `'memory'` skips it.\n */\n backend?: 'drizzle' | 'memory' | 'bullmq';\n /**\n * Active pool names. Defaults to every non-reserved pool in the resolved\n * config (i.e. `interactive`, `batch`, plus any user-defined pools).\n * Operators reduce this to one or two pools per worker process to scale\n * horizontally.\n */\n pools?: string[];\n /**\n * BULLMQ-1 Phase 1 — when `true`, `onModuleInit` activates **every** pool\n * in the resolved config, including the reserved `events_*` lanes. This is\n * how the standalone worker (`worker.ts`) drains bridge wrappers without\n * the consumer hand-listing `...BRIDGE_RESERVED_POOLS`. Mutually exclusive\n * with an explicit `pools` list — when both are set, `pools` wins (explicit\n * beats blanket) and `allPools` is ignored.\n *\n * `BridgeModule`'s reserved-pool guard short-circuits to \"pass\" when this\n * is `true`, since every reserved pool is provably being polled.\n */\n allPools?: boolean;\n /** SIGTERM drain budget. Default 30_000 ms. */\n shutdownTimeoutMs?: number;\n /**\n * Test-only — point the pool config loader at a specific YAML file.\n * Production code reads `${process.cwd()}/codegen.config.yaml`.\n */\n configPath?: string;\n /**\n * Forwarded into the inner `JobsDomainModule.forRoot()` call so the\n * worker module's caller can configure backend extensions in one place.\n */\n domainModuleExtensions?: JobsDomainModuleOptions['extensions'];\n /** Forwarded into `JobsDomainModule.forRoot()`. JOB-8 wires this. */\n multiTenant?: boolean;\n /**\n * Test-only escape hatch — when set, the module uses this factory\n * instead of `new JobWorker(...)` so unit tests can stub the worker\n * without spinning up the polling loop.\n */\n workerFactory?: (options: JobWorkerOptions) => Pick<JobWorker, 'onModuleInit' | 'onModuleDestroy'>;\n}\n\n/**\n * DI token for the resolved `JobWorkerModuleOptions`. Exported so other\n * subsystems can inject it `@Optional()` and inspect the active\n * configuration — e.g. `BridgeModule.onModuleInit` checks\n * `options.pools` against `BRIDGE_RESERVED_POOLS` to fail fast when a\n * reserved pool isn't being polled (BRIDGE-8).\n *\n * ADR-037: namespaced `Symbol.for(...)` (via `tokenKey()`) — matches by value\n * across runtime copies.\n */\nexport const JOB_WORKER_MODULE_OPTIONS = Symbol.for(tokenKey('jobs', 'worker-module-options'));\n\n/**\n * The lifecycle holder. Named `JobWorkerOrchestrator` in the spec to avoid\n * collision with `JobWorker` and `IJobOrchestrator`. Registered as a\n * provider on `JobWorkerModule`; Nest invokes `onModuleInit` /\n * `onModuleDestroy` automatically.\n */\n@Injectable()\nexport class JobWorkerOrchestrator implements OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(JobWorkerOrchestrator.name);\n private readonly workers: Array<Pick<JobWorker, 'onModuleInit' | 'onModuleDestroy'>> = [];\n\n constructor(\n @Inject(JOB_ORCHESTRATOR) private readonly orchestrator: IJobOrchestrator,\n @Inject(JOB_RUN_SERVICE) private readonly runService: IJobRunService,\n @Inject(JOB_STEP_SERVICE) private readonly stepService: IJobStepService,\n @Inject(JOB_WORKER_MODULE_OPTIONS)\n private readonly options: JobWorkerModuleOptions,\n /**\n * Drizzle client is only required when `backend === 'drizzle'`. Made\n * `@Optional()` so memory-mode boots in `Test.createTestingModule`\n * without supplying a `DRIZZLE` provider.\n */\n @Optional() @Inject(DRIZZLE) private readonly db: DrizzleClient | null = null,\n /**\n * ADR-037 (package-mode DI): inject `ModuleRef` EXPLICITLY via `@Inject`\n * rather than relying on `design:paramtypes` reflection. The published\n * package bundle is built without `emitDecoratorMetadata` (tsup/esbuild\n * default), so a by-type injection here would resolve to `undefined` at\n * boot in package mode — breaking the worker entirely (the\n * `ModuleRef not available` throw). Vendored mode happened to work only\n * because the consumer's own `tsc` (emitDecoratorMetadata: true)\n * recompiled the source and emitted the metadata. The explicit token is\n * mode-agnostic. `ModuleRef` is always provided by `@nestjs/core`, so no\n * `@Optional()` is needed (it's a hard dependency of the worker path).\n */\n @Inject(ModuleRef) private readonly moduleRef?: ModuleRef,\n /**\n * BULLMQ-1 — resolved BullMQ connection + config, only bound when the\n * inner `JobsDomainModule` was booted with `backend: 'bullmq'`. `@Optional()`\n * so drizzle/memory boots see `null`.\n */\n @Optional()\n @Inject(BULLMQ_CONNECTION)\n private readonly bullConnection: BullMqConnectionOptions | null = null,\n @Optional()\n @Inject(BULLMQ_RESOLVED_CONFIG)\n private readonly bullConfig: BullMqResolvedConfig | null = null,\n ) {}\n\n // ============================================================================\n // Lifecycle\n // ============================================================================\n\n async onModuleInit(): Promise<void> {\n const backend = this.options.backend ?? 'drizzle';\n\n // (1) Pool config first — every later step needs the resolved map.\n const poolConfig = loadPoolConfig(this.options.configPath);\n\n // (2) Snapshot the registry. Decorators run at class-load time so the\n // map is fully populated before any module init fires.\n const entries = HandlerRegistry.getAll();\n\n // (3) Reserved-pool validation BEFORE the upsert. Persisting a\n // reserved-pool handler row would leave the DB in a bad state for\n // the next boot to clean up.\n this.assertNoReservedPoolHandlers(entries, poolConfig);\n\n // (4) Upsert `job` definitions. Drizzle: hash-gated `ON CONFLICT DO\n // UPDATE`. Memory: populates `MemoryJobStore.jobs` + handler-class\n // registry.\n const { orphaned } = await this.orchestrator.upsertJobRows(\n entries,\n poolConfig,\n );\n\n // (5) Boot validator — Drizzle only. Memory mode never has DB rows\n // to validate (Q4 resolution 2026-04-19); the equivalent\n // protection is `MemoryJobOrchestrator.start()` throwing\n // `JobTypeNotFoundError` synchronously for unknown types.\n if (backend !== 'memory' && orphaned.length > 0) {\n throw new BootValidationError(orphaned);\n }\n\n // (6) Resolve active pool list and spawn one worker per pool.\n // Precedence: explicit `pools` > `allPools` (incl. reserved) >\n // non-reserved default. BULLMQ-1 Phase 1 adds the `allPools` rung so\n // the standalone worker drains the reserved `events_*` bridge lanes.\n const activePools = this.options.pools\n ? this.options.pools\n : this.options.allPools\n ? allPoolNames(poolConfig)\n : allNonReservedPoolNames(poolConfig);\n\n for (const poolName of activePools) {\n const def = poolConfig.get(poolName);\n if (!def) {\n throw new Error(\n `JobWorkerModule: active pool '${poolName}' is not defined in ` +\n `the resolved pool config. Configured pools: [${[...poolConfig.keys()].join(', ')}].`,\n );\n }\n // `pool` here is the logical pool name (e.g. 'crm_sync') — the same\n // value the orchestrator persists into `job_run.pool` from\n // `@JobHandler.meta.pool`, and therefore the value the worker's\n // claim query filters on. `def.queue` is a display/routing alias\n // (e.g. 'jobs-crm-sync') used by BullMQ-style backends for queue\n // naming; it MUST NOT be passed as the claim-filter pool, or the\n // worker will never match any row and the pool silently never\n // drains. See v0.4.4 fix notes.\n // LISTEN-NOTIFY-1 — thread the drizzle extension knobs into each spawned\n // worker. `pollIntervalMs` was always honored by JobWorker but never\n // received a config value; `listenNotify` is the new wake opt-in. Only\n // the drizzle backend reads these (bullmq has native wakeups + its own\n // queue topology), so we ignore them under `backend: 'bullmq'`.\n const drizzleExt =\n backend === 'drizzle'\n ? this.options.domainModuleExtensions?.drizzle\n : undefined;\n const workerOptions: JobWorkerOptions = {\n pool: poolName,\n concurrency: def.concurrency,\n shutdownTimeoutMs:\n this.options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS,\n pollIntervalMs: drizzleExt?.pollIntervalMs,\n listenNotify: drizzleExt?.listenNotify,\n };\n const worker = this.options.workerFactory\n ? this.options.workerFactory(workerOptions)\n : backend === 'bullmq'\n ? await this.spawnBullMQWorker(poolName, def.queue, def.concurrency, poolConfig)\n : this.spawnWorker(workerOptions);\n // `JobWorker` extends Nest's lifecycle hooks but the worker isn't\n // a Nest provider here (we manage the array ourselves). Call\n // `onModuleInit` to start the loop. The Drizzle/stub workers return\n // void; `BullMQJobWorker.onModuleInit` is async (it lazily loads the\n // optional `bullmq` package), so we `await` — awaiting a `void` is a\n // harmless no-op for the synchronous workers.\n await worker.onModuleInit();\n this.workers.push(worker);\n this.logger.log(\n `JobWorker started: pool='${poolName}' (queue='${def.queue}') ` +\n `concurrency=${def.concurrency} backend='${backend}'`,\n );\n }\n }\n\n async onModuleDestroy(): Promise<void> {\n // Tear down in reverse order so the most recently started worker\n // drains first — keeps the SIGTERM handler graph predictable.\n for (let i = this.workers.length - 1; i >= 0; i--) {\n const worker = this.workers[i];\n if (!worker) continue;\n try {\n await worker.onModuleDestroy();\n } catch (err) {\n this.logger.error(\n `JobWorker shutdown failed: ${(err as Error).message}`,\n );\n }\n }\n this.workers.length = 0;\n\n // BULLMQ-1 — close the orchestrator's producer-side Queue/FlowProducer\n // connections so the process can exit cleanly. The orchestrator is the\n // BullMQ producer; workers are the consumers (closed above).\n const orch = this.orchestrator as { closeConnections?: () => Promise<void> };\n if (typeof orch.closeConnections === 'function') {\n try {\n await orch.closeConnections();\n } catch (err) {\n this.logger.error(\n `BullMQ orchestrator connection close failed: ${(err as Error).message}`,\n );\n }\n }\n }\n\n // ============================================================================\n // Internals\n // ============================================================================\n\n /**\n * Walk every registered handler; collect any whose declared `pool`\n * targets a reserved pool from the resolved config. If non-empty,\n * throw `ReservedPoolViolationError` with the offender list so the\n * operator sees every violating class on a single boot.\n */\n private assertNoReservedPoolHandlers(\n entries: HandlerRegistryEntry[],\n poolConfig: PoolConfig,\n ): void {\n const offenders: Array<{ handlerClass: string; pool: string }> = [];\n for (const entry of entries) {\n // Framework-owned handlers (`@framework/*` job types) are allowed in\n // reserved pools — that is in fact the entire point of the reserved\n // `events_*` pools (ADR-022 + ADR-023). The reserved-pool guard\n // exists to keep USER handlers out, not the framework's own\n // bridge-delivery handler. BRIDGE-5 introduced this exemption.\n if (entry.type.startsWith('@framework/')) continue;\n const declaredPool = entry.meta.pool ?? 'batch';\n const def = poolConfig.get(declaredPool);\n if (def?.reserved) {\n offenders.push({\n handlerClass: entry.handlerClass.name,\n pool: declaredPool,\n });\n }\n }\n if (offenders.length > 0) {\n throw new ReservedPoolViolationError(offenders);\n }\n }\n\n /**\n * Production worker spawn. `JobWorker` requires `DRIZZLE` so this only\n * succeeds when the module was booted with `backend: 'drizzle'`. Memory\n * mode tests must supply `workerFactory` — the memory backend has no\n * polling loop equivalent (`MemoryJobOrchestrator` is direct-invocation\n * only).\n *\n * We instantiate outside the Nest container because the module spawns\n * N workers from a single options shape, which doesn't fit Nest's\n * \"one provider per token\" model. The dependencies are passed\n * positionally; the constructor's `@Inject` decorators are unused on\n * this path (Nest still uses them when `JobWorker` is a provider — e.g.\n * in JOB-6's standalone `worker.ts` entrypoint).\n */\n private spawnWorker(workerOptions: JobWorkerOptions): JobWorker {\n if (!this.db) {\n throw new Error(\n `JobWorkerModule: in-process worker spawning requires the Drizzle ` +\n `backend (no DRIZZLE provider available). Memory-mode tests must ` +\n `pass 'workerFactory' to inject a stub.`,\n );\n }\n if (!this.moduleRef) {\n throw new Error(\n `JobWorkerModule: ModuleRef not available — cannot construct JobWorker ` +\n `with handler DI support. Ensure the orchestrator is resolved through ` +\n `the Nest container (not instantiated manually in tests).`,\n );\n }\n return new JobWorker(\n this.db,\n this.orchestrator,\n this.runService,\n this.stepService,\n workerOptions,\n this.moduleRef,\n );\n }\n\n /**\n * BULLMQ-1 — spawn a per-pool `BullMQJobWorker`. Requires the Drizzle\n * client (the worker drives `job_run` as the source of truth) AND the\n * resolved BullMQ connection (bound by `JobsDomainModule` when\n * `backend: 'bullmq'`). The queue name is derived identically to the\n * orchestrator's `dispatch` via `resolvePoolQueueName(pool, …)` so producer\n * and consumer agree.\n */\n /**\n * #6 — async + dynamic-import. The `job-worker.bullmq-backend.ts` file is\n * filtered out of the vendor set for drizzle/memory installs (no `bullmq`\n * peer dep needed). The non-literal import specifier makes TS treat the\n * module as `any` so the consumer's tsc never tries to resolve an absent\n * file. This method is only entered when `backend === 'bullmq'` — at which\n * point the file IS vendored.\n */\n private async spawnBullMQWorker(\n pool: string,\n _queueAlias: string,\n concurrency: number,\n poolConfig: PoolConfig,\n ): Promise<Pick<JobWorker, 'onModuleInit' | 'onModuleDestroy'>> {\n if (!this.db) {\n throw new Error(\n `JobWorkerModule: BullMQ worker spawning requires the Drizzle client ` +\n `(no DRIZZLE provider available) — job_run remains the source of truth.`,\n );\n }\n if (!this.bullConnection) {\n throw new Error(\n `JobWorkerModule: BullMQ worker spawning requires a resolved ` +\n `BULLMQ_CONNECTION. Ensure JobsDomainModule was booted with ` +\n `backend: 'bullmq'.`,\n );\n }\n if (!this.moduleRef) {\n throw new Error(\n `JobWorkerModule: ModuleRef not available — cannot construct ` +\n `BullMQJobWorker with handler DI support.`,\n );\n }\n const queueName = resolvePoolQueueName(pool, this.bullConfig, poolConfig);\n const specifier = './job-worker.bullmq-backend';\n const mod = (await import(specifier)) as {\n BullMQJobWorker: new (...args: unknown[]) => Pick<\n JobWorker,\n 'onModuleInit' | 'onModuleDestroy'\n >;\n };\n return new mod.BullMQJobWorker(\n this.db,\n this.orchestrator,\n this.stepService,\n {\n pool,\n queueName,\n concurrency,\n connection: this.bullConnection,\n },\n this.moduleRef,\n );\n }\n}\n\n@Module({})\nexport class JobWorkerModule {\n static forRoot(opts: JobWorkerModuleOptions): DynamicModule {\n return {\n module: JobWorkerModule,\n imports: [\n JobsDomainModule.forRoot({\n backend: opts.backend ?? 'drizzle',\n extensions: opts.domainModuleExtensions,\n multiTenant: opts.multiTenant,\n }),\n ],\n providers: [\n { provide: JOB_WORKER_MODULE_OPTIONS, useValue: opts },\n JobWorkerOrchestrator,\n ],\n // BULLMQ-1 Phase 1 — export the options token so `BridgeModule`'s\n // reserved-pool guard (`onModuleInit`) can actually inject it.\n // Previously `exports: []` left the `@Optional()` inject resolving to\n // `undefined` and the guard silently no-opped (a dead check). With the\n // token exported the guard fires for real; consumers that omit the\n // reserved pools (and don't set `allPools`) now fail fast with\n // `BridgeReservedPoolsNotPolledError` — which is correct.\n exports: [JOB_WORKER_MODULE_OPTIONS],\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DrizzleCacheService
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-T6C4LFLC.js";
|
|
4
4
|
import {
|
|
5
5
|
MemoryCacheService
|
|
6
6
|
} from "./chunk-IF5I3DAA.js";
|
|
@@ -81,4 +81,4 @@ CacheModule = __decorateClass([
|
|
|
81
81
|
export {
|
|
82
82
|
CacheModule
|
|
83
83
|
};
|
|
84
|
-
//# sourceMappingURL=chunk-
|
|
84
|
+
//# sourceMappingURL=chunk-COGHTKXY.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-5A432NZJ.js";
|
|
4
4
|
import {
|
|
5
5
|
EventFlowService
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5RT7JGKT.js";
|
|
7
7
|
import {
|
|
8
8
|
BRIDGE_RESERVED_POOLS
|
|
9
9
|
} from "./chunk-EDKJU5BO.js";
|
|
@@ -15,16 +15,16 @@ import {
|
|
|
15
15
|
} from "./chunk-4DOJBQTP.js";
|
|
16
16
|
import {
|
|
17
17
|
BridgeOutboxDrainHook
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-DLG62MQY.js";
|
|
19
19
|
import {
|
|
20
20
|
BridgeDeliveryHandler
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-NXNVTXKG.js";
|
|
22
22
|
import {
|
|
23
23
|
BridgeReservedPoolsNotPolledError
|
|
24
24
|
} from "./chunk-NXXDZ6ZF.js";
|
|
25
25
|
import {
|
|
26
26
|
JOB_WORKER_MODULE_OPTIONS
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-AYC2HEAL.js";
|
|
28
28
|
import {
|
|
29
29
|
BRIDGE_DELIVERY_REPO,
|
|
30
30
|
BRIDGE_MODULE_OPTIONS,
|
|
@@ -119,4 +119,4 @@ BridgeModule = __decorateClass([
|
|
|
119
119
|
export {
|
|
120
120
|
BridgeModule
|
|
121
121
|
};
|
|
122
|
-
//# sourceMappingURL=chunk-
|
|
122
|
+
//# sourceMappingURL=chunk-CRBVI4GE.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CubeAnalyticsBackend
|
|
3
3
|
} from "./chunk-7B3RYX45.js";
|
|
4
|
-
import {
|
|
5
|
-
NoopAnalyticsBackend
|
|
6
|
-
} from "./chunk-J37YWU7Y.js";
|
|
7
4
|
import {
|
|
8
5
|
ANALYTICS_QUERY,
|
|
9
6
|
CUBE_API_SECRET,
|
|
10
7
|
CUBE_API_URL
|
|
11
8
|
} from "./chunk-6I7ULIN6.js";
|
|
9
|
+
import {
|
|
10
|
+
NoopAnalyticsBackend
|
|
11
|
+
} from "./chunk-J37YWU7Y.js";
|
|
12
12
|
import {
|
|
13
13
|
__decorateClass
|
|
14
14
|
} from "./chunk-2E224ZSN.js";
|
|
@@ -50,4 +50,4 @@ AnalyticsModule = __decorateClass([
|
|
|
50
50
|
export {
|
|
51
51
|
AnalyticsModule
|
|
52
52
|
};
|
|
53
|
-
//# sourceMappingURL=chunk-
|
|
53
|
+
//# sourceMappingURL=chunk-CZQUOIDY.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DrizzleJobRunService
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5LXOJGO2.js";
|
|
4
4
|
import {
|
|
5
5
|
MemoryJobRunService
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QSJ3J4HE.js";
|
|
7
7
|
import {
|
|
8
8
|
DrizzleJobStepService
|
|
9
9
|
} from "./chunk-DV4RV2DC.js";
|
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
} from "./chunk-I6MVCB5A.js";
|
|
15
15
|
import {
|
|
16
16
|
DrizzleJobOrchestrator
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-IT6FRTEW.js";
|
|
18
18
|
import {
|
|
19
19
|
MemoryJobOrchestrator
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-T4YJRD22.js";
|
|
21
21
|
import {
|
|
22
22
|
MemoryJobStepService
|
|
23
23
|
} from "./chunk-PNZSGAB2.js";
|
|
@@ -25,11 +25,12 @@ import {
|
|
|
25
25
|
MemoryJobStore
|
|
26
26
|
} from "./chunk-SNQ3TOWP.js";
|
|
27
27
|
import {
|
|
28
|
+
JOBS_LISTEN_NOTIFY,
|
|
28
29
|
JOBS_MULTI_TENANT,
|
|
29
30
|
JOB_ORCHESTRATOR,
|
|
30
31
|
JOB_RUN_SERVICE,
|
|
31
32
|
JOB_STEP_SERVICE
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-ZPL74UQN.js";
|
|
33
34
|
import {
|
|
34
35
|
DRIZZLE
|
|
35
36
|
} from "./chunk-U64T4YZE.js";
|
|
@@ -42,13 +43,17 @@ import { Module } from "@nestjs/common";
|
|
|
42
43
|
var JobsDomainModule = class {
|
|
43
44
|
static forRoot(opts) {
|
|
44
45
|
const multiTenant = opts.multiTenant ?? false;
|
|
46
|
+
const listenNotify = opts.backend === "drizzle" && Boolean(opts.extensions?.drizzle?.listenNotify);
|
|
45
47
|
const providers = [
|
|
46
48
|
// JOB-8 — boolean provider consumed by the four service-layer backends.
|
|
47
49
|
// Always provided (even when `multiTenant === false`) so `@Inject`
|
|
48
50
|
// always resolves; backends short-circuit the enforcement path when
|
|
49
51
|
// the value is `false`. See `jobs-domain.tokens.ts` for the claim-loop
|
|
50
52
|
// cross-tenant-by-design decision.
|
|
51
|
-
{ provide: JOBS_MULTI_TENANT, useValue: multiTenant }
|
|
53
|
+
{ provide: JOBS_MULTI_TENANT, useValue: multiTenant },
|
|
54
|
+
// LISTEN-NOTIFY-1 — always provided so the orchestrator's `@Inject`
|
|
55
|
+
// resolves; the orchestrator skips the `pg_notify` emit when `false`.
|
|
56
|
+
{ provide: JOBS_LISTEN_NOTIFY, useValue: listenNotify }
|
|
52
57
|
];
|
|
53
58
|
if (opts.backend === "memory") {
|
|
54
59
|
const store = new MemoryJobStore();
|
|
@@ -88,7 +93,8 @@ var JobsDomainModule = class {
|
|
|
88
93
|
JOB_ORCHESTRATOR,
|
|
89
94
|
JOB_RUN_SERVICE,
|
|
90
95
|
JOB_STEP_SERVICE,
|
|
91
|
-
JOBS_MULTI_TENANT
|
|
96
|
+
JOBS_MULTI_TENANT,
|
|
97
|
+
JOBS_LISTEN_NOTIFY
|
|
92
98
|
];
|
|
93
99
|
if (opts.backend === "bullmq") {
|
|
94
100
|
exports.push(BULLMQ_CONNECTION, BULLMQ_RESOLVED_CONFIG);
|
|
@@ -108,4 +114,4 @@ JobsDomainModule = __decorateClass([
|
|
|
108
114
|
export {
|
|
109
115
|
JobsDomainModule
|
|
110
116
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
117
|
+
//# sourceMappingURL=chunk-DGYTSCKN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../runtime/subsystems/jobs/jobs-domain.module.ts"],"sourcesContent":["/**\n * JobsDomainModule — `DynamicModule.forRoot({ backend })` factory wiring\n * the three jobs-domain protocol tokens to a backend implementation\n * (ADR-022, JOB-5).\n *\n * Mirrors `EventsModule.forRoot()` exactly:\n * - `global: true` so consumer entity modules don't have to import this\n * individually — `JOB_ORCHESTRATOR` / `JOB_RUN_SERVICE` /\n * `JOB_STEP_SERVICE` are available project-wide.\n * - One backend at a time (Drizzle for production, Memory for tests).\n *\n * Backend swappability follows the core/extension protocol from CLAUDE.md:\n * the three tokens are the **core contract**; backend-specific tunables\n * live under `extensions.<backend>` so opting into a feature is explicit\n * and the type system reserves the slot.\n */\nimport { Module, type DynamicModule, type Provider } from '@nestjs/common';\nimport { DRIZZLE } from '../../constants/tokens';\nimport {\n JOB_ORCHESTRATOR,\n JOB_RUN_SERVICE,\n JOB_STEP_SERVICE,\n JOBS_MULTI_TENANT,\n JOBS_LISTEN_NOTIFY,\n} from './jobs-domain.tokens';\nimport { DrizzleJobOrchestrator } from './job-orchestrator.drizzle-backend';\nimport { DrizzleJobRunService } from './job-run-service.drizzle-backend';\nimport { DrizzleJobStepService } from './job-step-service.drizzle-backend';\n// #6 — `BullMQJobOrchestrator` is lazy-loaded only when `backend: 'bullmq'`\n// is selected. The backend file is filtered out of drizzle/memory installs\n// (see `backendFileFilter`); a non-literal dynamic import below sidesteps\n// consumer-side tsc resolution of an absent file.\nimport { MemoryJobOrchestrator } from './job-orchestrator.memory-backend';\nimport { MemoryJobRunService } from './job-run-service.memory-backend';\nimport { MemoryJobStepService } from './job-step-service.memory-backend';\nimport { MemoryJobStore } from './memory-job-store';\nimport {\n BULLMQ_CONNECTION,\n BULLMQ_RESOLVED_CONFIG,\n resolveBullMqConfig,\n type BullMqExtensionsConfig,\n} from './bullmq.config';\n\n/**\n * Drizzle backend extensions surface (LISTEN-NOTIFY-1 wires both fields).\n *\n * - `listenNotify` → provided as `JOBS_LISTEN_NOTIFY` so the orchestrator emits\n * in-tx `pg_notify` on enqueue, and threaded into each spawned `JobWorker`\n * (which holds the listener connection). Off by default.\n * - `pollIntervalMs` → threaded into the spawned `JobWorker`'s\n * `JobWorkerOptions.pollIntervalMs` (the worker already honored this; it just\n * never received a config value). Default 1000.\n *\n * Both run ALONGSIDE interval polling — `listenNotify` only adds an early wake;\n * polling remains the durability heartbeat.\n */\nexport interface DrizzleBackendExtensions {\n /** Use Postgres LISTEN/NOTIFY to wake the polling loop. Default false. */\n listenNotify?: boolean;\n /** Polling interval (ms). Default 1000. */\n pollIntervalMs?: number;\n}\n\nexport interface JobsDomainModuleOptions {\n backend: 'drizzle' | 'memory' | 'bullmq';\n /**\n * Backend-specific extensions. Only the matching backend's extensions\n * are read at boot; non-matching keys are ignored. This is the\n * core/extension protocol surface — see CLAUDE.md.\n */\n extensions?: {\n drizzle?: DrizzleBackendExtensions;\n /**\n * BullMQ backend extensions (BULLMQ-1). Snake_case mirrors the YAML\n * under `jobs.extensions.bullmq`. `redis_url` falls back to\n * `process.env.REDIS_URL` then `redis://localhost:6379`.\n */\n bullmq?: BullMqExtensionsConfig;\n };\n /** Multi-tenancy opt-in. Wired by JOB-8; module signature stays stable. */\n multiTenant?: boolean;\n}\n\n@Module({})\nexport class JobsDomainModule {\n static forRoot(opts: JobsDomainModuleOptions): DynamicModule {\n const multiTenant = opts.multiTenant ?? false;\n // LISTEN-NOTIFY-1 — drizzle-only extension. `listen_notify` is meaningless\n // for memory (no DB) and redundant for bullmq (native wakeups); only the\n // drizzle backend's orchestrator reads it.\n const listenNotify =\n opts.backend === 'drizzle' && Boolean(opts.extensions?.drizzle?.listenNotify);\n\n const providers: Provider[] = [\n // JOB-8 — boolean provider consumed by the four service-layer backends.\n // Always provided (even when `multiTenant === false`) so `@Inject`\n // always resolves; backends short-circuit the enforcement path when\n // the value is `false`. See `jobs-domain.tokens.ts` for the claim-loop\n // cross-tenant-by-design decision.\n { provide: JOBS_MULTI_TENANT, useValue: multiTenant },\n // LISTEN-NOTIFY-1 — always provided so the orchestrator's `@Inject`\n // resolves; the orchestrator skips the `pg_notify` emit when `false`.\n { provide: JOBS_LISTEN_NOTIFY, useValue: listenNotify },\n ];\n\n if (opts.backend === 'memory') {\n // The store is a plain class — wired as a singleton `useValue` so\n // unit tests can pull it out via `.get(MemoryJobStore)` for direct\n // assertions (matches the access pattern in JOB-4 specs).\n const store = new MemoryJobStore();\n providers.push({ provide: MemoryJobStore, useValue: store });\n providers.push(MemoryJobStepService);\n providers.push({ provide: JOB_STEP_SERVICE, useExisting: MemoryJobStepService });\n providers.push(MemoryJobOrchestrator);\n providers.push({ provide: JOB_ORCHESTRATOR, useExisting: MemoryJobOrchestrator });\n providers.push(MemoryJobRunService);\n providers.push({ provide: JOB_RUN_SERVICE, useExisting: MemoryJobRunService });\n } else if (opts.backend === 'bullmq') {\n // BULLMQ-1 — BullMQ orchestrator over a Postgres source of truth. The\n // run/step services stay Drizzle (domain reads + `listForScope` are\n // Postgres queries, unchanged per spec). Only the orchestrator's\n // claim/dispatch half swaps to BullMQ.\n //\n // #6 — the bullmq backend module is filtered out of drizzle/memory\n // installs (no `bullmq` peer dep, no consumer-side tsc compile of an\n // unused file). The factory below dynamic-imports it via a non-literal\n // specifier so TS treats the module type as `any` and never tries to\n // resolve the absent file on a drizzle/memory consumer.\n const resolved = resolveBullMqConfig(opts.extensions?.bullmq);\n providers.push({ provide: BULLMQ_CONNECTION, useValue: resolved.connection });\n providers.push({ provide: BULLMQ_RESOLVED_CONFIG, useValue: resolved });\n providers.push({\n provide: JOB_ORCHESTRATOR,\n useFactory: async (...args: unknown[]): Promise<object> => {\n const specifier = './job-orchestrator.bullmq-backend';\n const mod = (await import(specifier)) as {\n BullMQJobOrchestrator: new (...args: unknown[]) => object;\n };\n return new mod.BullMQJobOrchestrator(...args);\n },\n // The bullmq orchestrator constructor mirrors DrizzleJobOrchestrator's\n // injection list: DRIZZLE + JOBS_MULTI_TENANT + the resolved BullMQ\n // tokens. Importing token references would force a static dep on the\n // tokens file in this module's import graph; using the existing\n // symbols already in scope is sufficient.\n inject: [DRIZZLE, JOBS_MULTI_TENANT, BULLMQ_CONNECTION, BULLMQ_RESOLVED_CONFIG],\n });\n providers.push({ provide: JOB_RUN_SERVICE, useClass: DrizzleJobRunService });\n providers.push({ provide: JOB_STEP_SERVICE, useClass: DrizzleJobStepService });\n } else {\n providers.push({ provide: JOB_ORCHESTRATOR, useClass: DrizzleJobOrchestrator });\n providers.push({ provide: JOB_RUN_SERVICE, useClass: DrizzleJobRunService });\n providers.push({ provide: JOB_STEP_SERVICE, useClass: DrizzleJobStepService });\n }\n\n const exports = [\n JOB_ORCHESTRATOR,\n JOB_RUN_SERVICE,\n JOB_STEP_SERVICE,\n JOBS_MULTI_TENANT,\n JOBS_LISTEN_NOTIFY,\n ];\n // BULLMQ-1 — only export the BullMQ tokens when they were actually\n // provided. Nest throws \"exported but not provided\" otherwise. Exported so\n // JobWorkerModule (which imports this module) can read the resolved\n // connection/config to spawn BullMQ workers.\n if (opts.backend === 'bullmq') {\n exports.push(BULLMQ_CONNECTION, BULLMQ_RESOLVED_CONFIG);\n }\n\n return {\n module: JobsDomainModule,\n global: true,\n providers,\n exports,\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,SAAS,cAAiD;AAoEnD,IAAM,mBAAN,MAAuB;AAAA,EAC5B,OAAO,QAAQ,MAA8C;AAC3D,UAAM,cAAc,KAAK,eAAe;AAIxC,UAAM,eACJ,KAAK,YAAY,aAAa,QAAQ,KAAK,YAAY,SAAS,YAAY;AAE9E,UAAM,YAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAM5B,EAAE,SAAS,mBAAmB,UAAU,YAAY;AAAA;AAAA;AAAA,MAGpD,EAAE,SAAS,oBAAoB,UAAU,aAAa;AAAA,IACxD;AAEA,QAAI,KAAK,YAAY,UAAU;AAI7B,YAAM,QAAQ,IAAI,eAAe;AACjC,gBAAU,KAAK,EAAE,SAAS,gBAAgB,UAAU,MAAM,CAAC;AAC3D,gBAAU,KAAK,oBAAoB;AACnC,gBAAU,KAAK,EAAE,SAAS,kBAAkB,aAAa,qBAAqB,CAAC;AAC/E,gBAAU,KAAK,qBAAqB;AACpC,gBAAU,KAAK,EAAE,SAAS,kBAAkB,aAAa,sBAAsB,CAAC;AAChF,gBAAU,KAAK,mBAAmB;AAClC,gBAAU,KAAK,EAAE,SAAS,iBAAiB,aAAa,oBAAoB,CAAC;AAAA,IAC/E,WAAW,KAAK,YAAY,UAAU;AAWpC,YAAM,WAAW,oBAAoB,KAAK,YAAY,MAAM;AAC5D,gBAAU,KAAK,EAAE,SAAS,mBAAmB,UAAU,SAAS,WAAW,CAAC;AAC5E,gBAAU,KAAK,EAAE,SAAS,wBAAwB,UAAU,SAAS,CAAC;AACtE,gBAAU,KAAK;AAAA,QACb,SAAS;AAAA,QACT,YAAY,UAAU,SAAqC;AACzD,gBAAM,YAAY;AAClB,gBAAM,MAAO,MAAM,OAAO;AAG1B,iBAAO,IAAI,IAAI,sBAAsB,GAAG,IAAI;AAAA,QAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMA,QAAQ,CAAC,SAAS,mBAAmB,mBAAmB,sBAAsB;AAAA,MAChF,CAAC;AACD,gBAAU,KAAK,EAAE,SAAS,iBAAiB,UAAU,qBAAqB,CAAC;AAC3E,gBAAU,KAAK,EAAE,SAAS,kBAAkB,UAAU,sBAAsB,CAAC;AAAA,IAC/E,OAAO;AACL,gBAAU,KAAK,EAAE,SAAS,kBAAkB,UAAU,uBAAuB,CAAC;AAC9E,gBAAU,KAAK,EAAE,SAAS,iBAAiB,UAAU,qBAAqB,CAAC;AAC3E,gBAAU,KAAK,EAAE,SAAS,kBAAkB,UAAU,sBAAsB,CAAC;AAAA,IAC/E;AAEA,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAKA,QAAI,KAAK,YAAY,UAAU;AAC7B,cAAQ,KAAK,mBAAmB,sBAAsB;AAAA,IACxD;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AA7Fa,mBAAN;AAAA,EADN,OAAO,CAAC,CAAC;AAAA,GACG;","names":[]}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BRIDGE_DELIVERY_JOB_TYPE
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NXNVTXKG.js";
|
|
4
4
|
import {
|
|
5
5
|
bridgeDelivery
|
|
6
6
|
} from "./chunk-2TVVBC53.js";
|
|
7
|
+
import {
|
|
8
|
+
jobRuns
|
|
9
|
+
} from "./chunk-OKXZ63IA.js";
|
|
10
|
+
import {
|
|
11
|
+
JOBS_LISTEN_NOTIFY
|
|
12
|
+
} from "./chunk-ZPL74UQN.js";
|
|
7
13
|
import {
|
|
8
14
|
BRIDGE_REGISTRY
|
|
9
15
|
} from "./chunk-4LH67P4U.js";
|
|
10
16
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
JOBS_WAKE_CHANNEL,
|
|
18
|
+
pgNotify
|
|
19
|
+
} from "./chunk-MYQIQ27N.js";
|
|
13
20
|
import {
|
|
14
21
|
__decorateClass,
|
|
15
22
|
__decorateParam
|
|
@@ -25,10 +32,12 @@ var POOL_BY_DIRECTION = {
|
|
|
25
32
|
outbound: "events_outbound"
|
|
26
33
|
};
|
|
27
34
|
var BridgeOutboxDrainHook = class {
|
|
28
|
-
constructor(registry = {}) {
|
|
35
|
+
constructor(registry = {}, listenNotify = false) {
|
|
29
36
|
this.registry = registry;
|
|
37
|
+
this.listenNotify = listenNotify;
|
|
30
38
|
}
|
|
31
39
|
registry;
|
|
40
|
+
listenNotify;
|
|
32
41
|
logger = new Logger(BridgeOutboxDrainHook.name);
|
|
33
42
|
warnedNullDirection = false;
|
|
34
43
|
warnedAuditTypes = /* @__PURE__ */ new Set();
|
|
@@ -101,6 +110,15 @@ var BridgeOutboxDrainHook = class {
|
|
|
101
110
|
dedupSkips++;
|
|
102
111
|
continue;
|
|
103
112
|
}
|
|
113
|
+
if (this.listenNotify) {
|
|
114
|
+
try {
|
|
115
|
+
await pgNotify(tx, JOBS_WAKE_CHANNEL, wrapperPool);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
this.logger.warn(
|
|
118
|
+
`pg_notify(${JOBS_WAKE_CHANNEL}, ${wrapperPool}) failed for wrapper run ${wrapperRunId}: ${err.message} (non-fatal \u2014 the reserved-pool worker still polls).`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
104
122
|
delivered++;
|
|
105
123
|
}
|
|
106
124
|
return {
|
|
@@ -125,10 +143,12 @@ var BridgeOutboxDrainHook = class {
|
|
|
125
143
|
BridgeOutboxDrainHook = __decorateClass([
|
|
126
144
|
Injectable(),
|
|
127
145
|
__decorateParam(0, Optional()),
|
|
128
|
-
__decorateParam(0, Inject(BRIDGE_REGISTRY))
|
|
146
|
+
__decorateParam(0, Inject(BRIDGE_REGISTRY)),
|
|
147
|
+
__decorateParam(1, Optional()),
|
|
148
|
+
__decorateParam(1, Inject(JOBS_LISTEN_NOTIFY))
|
|
129
149
|
], BridgeOutboxDrainHook);
|
|
130
150
|
|
|
131
151
|
export {
|
|
132
152
|
BridgeOutboxDrainHook
|
|
133
153
|
};
|
|
134
|
-
//# sourceMappingURL=chunk-
|
|
154
|
+
//# sourceMappingURL=chunk-DLG62MQY.js.map
|