@pattern-stack/codegen 0.17.0 → 0.17.2
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 +104 -0
- package/consumer-skills/integration/audit-and-detection.md +29 -4
- package/dist/{chunk-GJDEPTPY.js → chunk-235ZMMJR.js} +8 -8
- package/dist/{chunk-DTXH24LR.js → chunk-65MO75WM.js} +9 -9
- package/dist/{chunk-5RT7JGKT.js → chunk-7OVCARTQ.js} +4 -4
- package/dist/{chunk-CO6LUM72.js → chunk-7P5ODGLA.js} +34 -2
- package/dist/chunk-7P5ODGLA.js.map +1 -0
- package/dist/{chunk-P3AYBRP6.js → chunk-ATVGYF3D.js} +11 -5
- package/dist/chunk-ATVGYF3D.js.map +1 -0
- package/dist/{chunk-3MAZ4TQH.js → chunk-AZLUWG5S.js} +9 -9
- package/dist/{chunk-UTNWFHJF.js → chunk-B34G6PHD.js} +10 -10
- package/dist/{chunk-CDLWYZVQ.js → chunk-BHZP6LOV.js} +7 -7
- package/dist/{chunk-W2UIDI3R.js → chunk-CLWBNXKF.js} +4 -4
- package/dist/{chunk-OTR44OH6.js → chunk-E6PLM6QG.js} +34 -13
- package/dist/chunk-E6PLM6QG.js.map +1 -0
- package/dist/{chunk-43SBT72G.js → chunk-I6UXRJ3Q.js} +4 -4
- package/dist/{chunk-36U5UGIO.js → chunk-JEINYUJH.js} +8 -5
- package/dist/chunk-JEINYUJH.js.map +1 -0
- package/dist/{chunk-K2I6XIK5.js → chunk-KSTZIULO.js} +4 -4
- package/dist/{chunk-L3VJ47BU.js → chunk-KZDHMZ45.js} +6 -6
- package/dist/{chunk-RHYNACZS.js → chunk-OZEPJGMA.js} +2 -2
- package/dist/{chunk-MYQIQ27N.js → chunk-Q6LRJ4VI.js} +51 -2
- package/dist/chunk-Q6LRJ4VI.js.map +1 -0
- package/dist/{chunk-NXNVTXKG.js → chunk-R6F6KFIL.js} +5 -5
- package/dist/{chunk-7LKAMLV4.js → chunk-T6SCOJF4.js} +4 -4
- package/dist/{chunk-OGIZXGPY.js → chunk-TDEHU73T.js} +4 -4
- package/dist/{chunk-OITTYGJS.js → chunk-VDL5CJ5C.js} +24 -14
- package/dist/chunk-VDL5CJ5C.js.map +1 -0
- package/dist/{chunk-3VEVGL74.js → chunk-VNBC3VXM.js} +4 -4
- package/dist/{chunk-BULPAAD3.js → chunk-VQOAATIG.js} +42 -11
- package/dist/chunk-VQOAATIG.js.map +1 -0
- package/dist/{chunk-E45CSC33.js → chunk-XKWOJZZ4.js} +2 -2
- package/dist/{chunk-DCCZB4UC.js → chunk-XWBK3XJK.js} +4 -4
- package/dist/{chunk-4GLNY5V6.js → chunk-Y7GDG744.js} +5 -5
- package/dist/{chunk-SR7F3TJY.js → chunk-YK5JEVLX.js} +4 -4
- package/dist/{job-orchestrator.protocol-DubMVbm9.d.ts → job-orchestrator.protocol-ZuJ3ow-O.d.ts} +77 -3
- package/dist/runtime/base-classes/index.js +12 -12
- package/dist/runtime/shared/openapi/index.js +5 -5
- package/dist/runtime/shared/openapi/registry.js +2 -2
- package/dist/runtime/subsystems/auth/auth.module.js +3 -3
- package/dist/runtime/subsystems/auth/index.js +12 -12
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js +3 -3
- package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +3 -3
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +7 -7
- package/dist/runtime/subsystems/bridge/bridge.module.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.js +19 -19
- package/dist/runtime/subsystems/bridge/event-flow.service.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/event-flow.service.js +2 -2
- package/dist/runtime/subsystems/bridge/index.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/index.js +21 -21
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +4 -4
- package/dist/runtime/subsystems/events/events.module.js +5 -5
- package/dist/runtime/subsystems/events/index.js +7 -7
- package/dist/runtime/subsystems/index.d.ts +1 -1
- package/dist/runtime/subsystems/index.js +100 -100
- package/dist/runtime/subsystems/integration/build-change-source.js +2 -2
- package/dist/runtime/subsystems/integration/deep-equal.differ.d.ts +19 -0
- package/dist/runtime/subsystems/integration/deep-equal.differ.js +1 -1
- package/dist/runtime/subsystems/integration/execute-integration.use-case.js +2 -2
- package/dist/runtime/subsystems/integration/index.js +39 -39
- 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.d.ts +20 -0
- package/dist/runtime/subsystems/integration/integration.module.js +6 -6
- package/dist/runtime/subsystems/jobs/index.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/index.js +27 -27
- package/dist/runtime/subsystems/jobs/job-handler.base.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-handler.base.js +11 -3
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.d.ts +1 -1
- 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 +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.js +3 -2
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.d.ts +11 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js +2 -2
- package/dist/runtime/subsystems/jobs/job-orchestrator.protocol.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +2 -2
- package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.d.ts +9 -1
- package/dist/runtime/subsystems/jobs/job-worker.js +3 -3
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.module.js +11 -11
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +9 -9
- package/dist/runtime/subsystems/jobs/jobs-errors.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/pg-notify.d.ts +25 -1
- package/dist/runtime/subsystems/jobs/pg-notify.js +1 -1
- package/dist/runtime/subsystems/observability/index.d.ts +1 -1
- package/dist/runtime/subsystems/observability/index.js +3 -3
- package/dist/runtime/subsystems/observability/observability.module.js +3 -3
- package/dist/runtime/subsystems/observability/observability.protocol.d.ts +1 -1
- package/dist/runtime/subsystems/observability/observability.service.d.ts +1 -1
- package/dist/runtime/subsystems/observability/observability.service.js +2 -2
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +1 -1
- package/dist/runtime/subsystems/observability/reporters/index.d.ts +1 -1
- package/dist/runtime/subsystems/storage/index.js +1 -1
- package/dist/runtime/subsystems/storage/storage.module.js +1 -1
- package/dist/src/cli/index.js +38 -16
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.js +13 -13
- package/package.json +1 -1
- package/runtime/subsystems/integration/deep-equal.differ.ts +34 -5
- package/runtime/subsystems/integration/integration.module.ts +26 -2
- package/runtime/subsystems/jobs/job-handler.base.ts +115 -2
- package/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.ts +43 -16
- package/runtime/subsystems/jobs/job-orchestrator.memory-backend.ts +58 -18
- package/runtime/subsystems/jobs/job-worker.ts +29 -11
- package/runtime/subsystems/jobs/pg-notify.ts +63 -3
- package/templates/subsystem/integration-config/codegen-config-integration-block.ejs.t +17 -0
- package/dist/chunk-36U5UGIO.js.map +0 -1
- package/dist/chunk-BULPAAD3.js.map +0 -1
- package/dist/chunk-CO6LUM72.js.map +0 -1
- package/dist/chunk-MYQIQ27N.js.map +0 -1
- package/dist/chunk-OITTYGJS.js.map +0 -1
- package/dist/chunk-OTR44OH6.js.map +0 -1
- package/dist/chunk-P3AYBRP6.js.map +0 -1
- /package/dist/{chunk-GJDEPTPY.js.map → chunk-235ZMMJR.js.map} +0 -0
- /package/dist/{chunk-DTXH24LR.js.map → chunk-65MO75WM.js.map} +0 -0
- /package/dist/{chunk-5RT7JGKT.js.map → chunk-7OVCARTQ.js.map} +0 -0
- /package/dist/{chunk-3MAZ4TQH.js.map → chunk-AZLUWG5S.js.map} +0 -0
- /package/dist/{chunk-UTNWFHJF.js.map → chunk-B34G6PHD.js.map} +0 -0
- /package/dist/{chunk-CDLWYZVQ.js.map → chunk-BHZP6LOV.js.map} +0 -0
- /package/dist/{chunk-W2UIDI3R.js.map → chunk-CLWBNXKF.js.map} +0 -0
- /package/dist/{chunk-43SBT72G.js.map → chunk-I6UXRJ3Q.js.map} +0 -0
- /package/dist/{chunk-K2I6XIK5.js.map → chunk-KSTZIULO.js.map} +0 -0
- /package/dist/{chunk-L3VJ47BU.js.map → chunk-KZDHMZ45.js.map} +0 -0
- /package/dist/{chunk-RHYNACZS.js.map → chunk-OZEPJGMA.js.map} +0 -0
- /package/dist/{chunk-NXNVTXKG.js.map → chunk-R6F6KFIL.js.map} +0 -0
- /package/dist/{chunk-7LKAMLV4.js.map → chunk-T6SCOJF4.js.map} +0 -0
- /package/dist/{chunk-OGIZXGPY.js.map → chunk-TDEHU73T.js.map} +0 -0
- /package/dist/{chunk-3VEVGL74.js.map → chunk-VNBC3VXM.js.map} +0 -0
- /package/dist/{chunk-E45CSC33.js.map → chunk-XKWOJZZ4.js.map} +0 -0
- /package/dist/{chunk-DCCZB4UC.js.map → chunk-XWBK3XJK.js.map} +0 -0
- /package/dist/{chunk-4GLNY5V6.js.map → chunk-Y7GDG744.js.map} +0 -0
- /package/dist/{chunk-SR7F3TJY.js.map → chunk-YK5JEVLX.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,110 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.17.2] — 2026-06-04
|
|
8
|
+
|
|
9
|
+
**Shutdown leak fix** (LISTEN-NOTIFY-2; swe-brain dogfood). With
|
|
10
|
+
`listen_notify: true` (the LISTEN/NOTIFY wake extension shipped in 0.16.0), a
|
|
11
|
+
Nest app that booted and then `app.close()`d — e.g. a boot-check / CI smoke step —
|
|
12
|
+
never exited: at least one `LISTEN codegen_jobs_wake` client survived
|
|
13
|
+
`app.close()`, holding an ESTABLISHED Postgres socket open forever (two swe-brain
|
|
14
|
+
CI runs hung for hours). Backward-compatible; affects only consumers that opted
|
|
15
|
+
into `listen_notify`.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **`PgNotifyListener.stop()` is race-safe against an in-flight `connect()`**
|
|
20
|
+
(LISTEN-NOTIFY-2 RC1 — the defect that actually fired). `connect()` checked
|
|
21
|
+
`this.stopped` only at entry, then `await pool.connect()`, wired handlers,
|
|
22
|
+
issued `LISTEN`, and assigned `this.client` last. A `stop()` arriving during
|
|
23
|
+
the checkout await ran `releaseClient()` against a still-null `this.client`
|
|
24
|
+
(released nothing); the resuming `connect()` then assigned the client and
|
|
25
|
+
issued `LISTEN` — leaking a checked-out connection with no owner left to
|
|
26
|
+
release it. With 5–6 listeners (one per jobs pool + the events drainer) all
|
|
27
|
+
starting at bootstrap and a tight `app.close()`, the race fired on ~1 of 6
|
|
28
|
+
listeners — exactly the observed signature (one survivor, the rest clean).
|
|
29
|
+
Now `connect()` re-checks `stopped` after the checkout AND after `LISTEN`,
|
|
30
|
+
destroying the just-acquired client and bailing before assignment; `stop()`
|
|
31
|
+
tracks and awaits the in-flight connect promise before its own release, so
|
|
32
|
+
`app.close()` can't return while a checkout is still mid-flight. Releases use
|
|
33
|
+
`release(true)` (destroy) so a half-listening socket is never reused.
|
|
34
|
+
- **`JobWorker.onModuleDestroy` stops the wake listener on EVERY destroy path**
|
|
35
|
+
(LISTEN-NOTIFY-2 RC2 — latent). The listener `stop()` lived only on the first
|
|
36
|
+
(non-`shuttingDown`) branch, so a SIGTERM-then-Nest double-destroy hit the
|
|
37
|
+
`if (this.shuttingDown) { …; return; }` early return and skipped it, leaking
|
|
38
|
+
the listener under the normal SIGTERM shutdown path. Teardown is now an
|
|
39
|
+
idempotent `stopNotifyListener()` called unconditionally at the top of every
|
|
40
|
+
destroy. `DrizzleEventBus` already stopped its listener unconditionally; it
|
|
41
|
+
shared `PgNotifyListener` and so benefits from the RC1 fix directly.
|
|
42
|
+
|
|
43
|
+
## [0.17.1] — 2026-06-04
|
|
44
|
+
|
|
45
|
+
**Two dogfood fixes that bit the same swe-brain mutation drain** (ADR-0009
|
|
46
|
+
Amendment B): the jobs orchestrator silently dropped function-form
|
|
47
|
+
concurrency/dedupe keys, and the integration differ unconditionally ignored
|
|
48
|
+
`deletedAt`. Both are honored now; both are backward-compatible.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **`@JobHandler` function-form `concurrency.key` / `dedupe.key` are honored
|
|
53
|
+
end-to-end** (JOB-FN-KEY; swe-brain ADR-0009 Amendment B §B3). The typed API
|
|
54
|
+
(`ConcurrencyPolicy.key`/`DedupePolicy.key`) had ALWAYS required a function,
|
|
55
|
+
but registration (`upsertJobRows`) stored only `typeof key === 'string' ? key
|
|
56
|
+
: null` — a function key persisted as a NULL `concurrency_key_template`, so
|
|
57
|
+
`start()` wrote a NULL `job_run.concurrency_key` and the worker's queue-release
|
|
58
|
+
gate (which keys off `claimed.concurrencyKey`) never engaged. Observed in
|
|
59
|
+
swe-brain: three `inbound-sync` runs the handler believed shared one
|
|
60
|
+
`collisionMode: 'queue'` lane ran fully concurrently, three `integration_runs`
|
|
61
|
+
racing one message row. Now both backends persist a function key as the
|
|
62
|
+
`FN_KEY_SENTINEL` marker (non-null, so the collision/dedupe path engages, and
|
|
63
|
+
hash-stable so the definition-hash gate doesn't churn) and re-resolve the live
|
|
64
|
+
function from `JOB_HANDLER_REGISTRY` at `start()`. A `FN_KEY_SENTINEL` with no
|
|
65
|
+
live function throws `JobKeyFunctionUnavailableError` (fail loud, never
|
|
66
|
+
silently degrade to no-key). Drizzle + memory + BullMQ (which delegates to the
|
|
67
|
+
Drizzle `start`) all agree.
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
|
|
71
|
+
- **`ConcurrencyPolicy.key` / `DedupePolicy.key` widen to
|
|
72
|
+
`JobKeySelector<TInput> = string | ((input) => string)`.** The string form is
|
|
73
|
+
documented as a `{{field}}` template (evaluated by `evaluateKeyTemplate`); the
|
|
74
|
+
function form is the one that previously type-checked but was dropped. Existing
|
|
75
|
+
function keys now WORK (were silently no-key before); existing string-template
|
|
76
|
+
keys behave identically. New exports from `@pattern-stack/codegen/runtime/*`
|
|
77
|
+
jobs: `JobKeySelector`, `FN_KEY_SENTINEL`, `keySelectorToTemplate`,
|
|
78
|
+
`resolveJobKey`, `JobKeyFunctionUnavailableError`.
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- **`DeepEqualDifferOptions.unignore`** — the inverse of `ignore`, subtracted
|
|
83
|
+
from the default ignore set after the merge (DIFFER-UNIGNORE; swe-brain
|
|
84
|
+
ADR-0009 Amendment B §B4). Lets a consumer declare that a normally-metadata
|
|
85
|
+
column is DOMAIN DATA for their entity. The canonical case: an entity with
|
|
86
|
+
`softDelete: false` whose `deletedAt` carries a vendor-observed retraction
|
|
87
|
+
tombstone ON the canonical record (a Slack `message_deleted` → `deletedAt`,
|
|
88
|
+
ADR-0008 §1). `deletedAt` is in `DEFAULT_IGNORE_FIELDS`, so the tombstone
|
|
89
|
+
overlay diffed to `'noop'` → the upsert was skipped → `deleted_at` never
|
|
90
|
+
landed (observed: `integration_run_items` `{operation: noop, changed_fields:
|
|
91
|
+
{}}` for every delete). `new DeepEqualDiffer({ unignore: ['deletedAt'] })` now
|
|
92
|
+
makes the field register as a change. `unignore` wins over a field also in
|
|
93
|
+
`ignore`; un-ignoring a field not in the set is a harmless no-op; per-instance
|
|
94
|
+
(never mutates `DEFAULT_IGNORE_FIELDS`).
|
|
95
|
+
- **`IntegrationModuleOptions.differ` + `integration.differ.{ignore,unignore}`
|
|
96
|
+
config threading.** `IntegrationModule.forRoot({ differ: { unignore:
|
|
97
|
+
[...] } })` threads into the default `DeepEqualDiffer` bound to
|
|
98
|
+
`INTEGRATION_FIELD_DIFFER`, and the subsystem barrel generator emits that
|
|
99
|
+
`forRoot` option from `integration.differ.*` in `codegen.config.yaml` (same
|
|
100
|
+
off-by-default config-threading shape as 0.16.0's `listen_notify`; vendored +
|
|
101
|
+
package mode both covered). A feature module that binds its own
|
|
102
|
+
`IFieldDiffer<T>` still overrides entirely.
|
|
103
|
+
|
|
104
|
+
### Docs
|
|
105
|
+
|
|
106
|
+
- Differ header comment + `integration` consumer skill (`audit-and-detection.md`,
|
|
107
|
+
`protocols-and-ports.md`) document the `unignore` knob and the
|
|
108
|
+
`integration.differ.*` config path; the `integration-config` codegen.config
|
|
109
|
+
template gains a commented `differ:` block.
|
|
110
|
+
|
|
7
111
|
## [0.17.0] — 2026-06-04
|
|
8
112
|
|
|
9
113
|
**`ActivityPattern` subject scoping is config-driven** (ACTIVITY-SUBJECT-1) —
|
|
@@ -285,15 +285,40 @@ as changes), but the diff output preserves the *raw* values:
|
|
|
285
285
|
Drizzle while adapters deliver numbers; a numeric and a finite-parseable
|
|
286
286
|
string compare equal (with an empty-string guard against silent 0-equality).
|
|
287
287
|
|
|
288
|
-
**Augment the ignore list
|
|
289
|
-
cannot remove defaults):
|
|
288
|
+
**Augment the ignore list** (`ignore` values merge with the defaults):
|
|
290
289
|
|
|
291
290
|
```ts
|
|
292
291
|
{ provide: INTEGRATION_FIELD_DIFFER, useValue: new DeepEqualDiffer({ ignore: ['integration_version'] }) }
|
|
293
292
|
```
|
|
294
293
|
|
|
295
|
-
|
|
296
|
-
|
|
294
|
+
**Un-ignore a default** (`unignore` — the inverse knob). A normally-metadata
|
|
295
|
+
column can be *domain data* for a given entity. The canonical case: an entity
|
|
296
|
+
with `softDelete: false` whose `deletedAt` carries a vendor-observed retraction
|
|
297
|
+
tombstone *on the canonical record* (e.g. a Slack `message_deleted` maps to
|
|
298
|
+
`deletedAt`). Because `deletedAt` is in the default ignore list, the tombstone
|
|
299
|
+
overlay diffs to `'noop'`, the upsert is skipped, and `deleted_at` never lands.
|
|
300
|
+
`unignore` removes it from the ignore set so it registers as a field change:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
{ provide: INTEGRATION_FIELD_DIFFER, useValue: new DeepEqualDiffer({ unignore: ['deletedAt'] }) }
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
`unignore` is subtracted after `ignore` is merged, so it wins on a field listed
|
|
307
|
+
in both. Un-ignoring a field that isn't in the (merged) set is a harmless no-op.
|
|
308
|
+
|
|
309
|
+
**Set it once for the whole app via config** instead of binding per feature
|
|
310
|
+
module — `integration.differ.{ignore,unignore}` in `codegen.config.yaml` threads
|
|
311
|
+
into the default differ that `IntegrationModule.forRoot` provides:
|
|
312
|
+
|
|
313
|
+
```yaml
|
|
314
|
+
integration:
|
|
315
|
+
backend: drizzle
|
|
316
|
+
differ:
|
|
317
|
+
unignore: [deletedAt] # this entity's deletedAt is domain data
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Bind a per-module differ as `useValue: new DeepEqualDiffer(...)`, not `useClass`
|
|
321
|
+
— the constructor's optional options object confuses Nest's metadata reflection.
|
|
297
322
|
|
|
298
323
|
**`providerChangedFields` is advisory.** When a CDC provider tells you which
|
|
299
324
|
columns changed, set it on the `Change<T>` and the differ skips deep-equal over
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JobWorker
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VDL5CJ5C.js";
|
|
4
4
|
import {
|
|
5
5
|
JobsDomainModule
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-AZLUWG5S.js";
|
|
7
|
+
import {
|
|
8
|
+
BootValidationError,
|
|
9
|
+
ReservedPoolViolationError
|
|
10
|
+
} from "./chunk-T4BIIU5E.js";
|
|
7
11
|
import {
|
|
8
12
|
BULLMQ_CONNECTION,
|
|
9
13
|
BULLMQ_RESOLVED_CONFIG,
|
|
@@ -14,13 +18,9 @@ import {
|
|
|
14
18
|
allPoolNames,
|
|
15
19
|
loadPoolConfig
|
|
16
20
|
} from "./chunk-RHVN6NA7.js";
|
|
17
|
-
import {
|
|
18
|
-
BootValidationError,
|
|
19
|
-
ReservedPoolViolationError
|
|
20
|
-
} from "./chunk-T4BIIU5E.js";
|
|
21
21
|
import {
|
|
22
22
|
HandlerRegistry
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-7P5ODGLA.js";
|
|
24
24
|
import {
|
|
25
25
|
JOB_ORCHESTRATOR,
|
|
26
26
|
JOB_RUN_SERVICE,
|
|
@@ -290,4 +290,4 @@ export {
|
|
|
290
290
|
JobWorkerOrchestrator,
|
|
291
291
|
JobWorkerModule
|
|
292
292
|
};
|
|
293
|
-
//# sourceMappingURL=chunk-
|
|
293
|
+
//# sourceMappingURL=chunk-235ZMMJR.js.map
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BRIDGE_DELIVERY_JOB_TYPE
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-R6F6KFIL.js";
|
|
4
4
|
import {
|
|
5
5
|
bridgeDelivery
|
|
6
6
|
} from "./chunk-2TVVBC53.js";
|
|
7
|
-
import {
|
|
8
|
-
JOBS_LISTEN_NOTIFY
|
|
9
|
-
} from "./chunk-ZPL74UQN.js";
|
|
10
|
-
import {
|
|
11
|
-
jobRuns
|
|
12
|
-
} from "./chunk-OKXZ63IA.js";
|
|
13
7
|
import {
|
|
14
8
|
JOBS_WAKE_CHANNEL,
|
|
15
9
|
pgNotify
|
|
16
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-Q6LRJ4VI.js";
|
|
11
|
+
import {
|
|
12
|
+
JOBS_LISTEN_NOTIFY
|
|
13
|
+
} from "./chunk-ZPL74UQN.js";
|
|
17
14
|
import {
|
|
18
15
|
BRIDGE_REGISTRY
|
|
19
16
|
} from "./chunk-4LH67P4U.js";
|
|
17
|
+
import {
|
|
18
|
+
jobRuns
|
|
19
|
+
} from "./chunk-OKXZ63IA.js";
|
|
20
20
|
import {
|
|
21
21
|
__decorateClass,
|
|
22
22
|
__decorateParam
|
|
@@ -151,4 +151,4 @@ BridgeOutboxDrainHook = __decorateClass([
|
|
|
151
151
|
export {
|
|
152
152
|
BridgeOutboxDrainHook
|
|
153
153
|
};
|
|
154
|
-
//# sourceMappingURL=chunk-
|
|
154
|
+
//# sourceMappingURL=chunk-65MO75WM.js.map
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
JOB_ORCHESTRATOR
|
|
6
6
|
} from "./chunk-ZPL74UQN.js";
|
|
7
|
+
import {
|
|
8
|
+
EVENT_BUS
|
|
9
|
+
} from "./chunk-H5NH7KPE.js";
|
|
7
10
|
import {
|
|
8
11
|
BRIDGE_DELIVERY_REPO,
|
|
9
12
|
BRIDGE_MULTI_TENANT,
|
|
10
13
|
BRIDGE_REGISTRY
|
|
11
14
|
} 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-7OVCARTQ.js.map
|
|
@@ -47,6 +47,34 @@ var HandlerRegistry;
|
|
|
47
47
|
}
|
|
48
48
|
HandlerRegistry2.get = get;
|
|
49
49
|
})(HandlerRegistry || (HandlerRegistry = {}));
|
|
50
|
+
var FN_KEY_SENTINEL = "<fn>";
|
|
51
|
+
function keySelectorToTemplate(key) {
|
|
52
|
+
if (typeof key === "string") return key;
|
|
53
|
+
if (typeof key === "function") return FN_KEY_SENTINEL;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
function resolveJobKey(kind, type, template, payload, evaluateTemplate) {
|
|
57
|
+
if (template == null) return null;
|
|
58
|
+
if (template !== FN_KEY_SENTINEL) return evaluateTemplate(template, payload);
|
|
59
|
+
const meta = JOB_HANDLER_REGISTRY.get(type)?.meta;
|
|
60
|
+
const key = meta?.[kind]?.key;
|
|
61
|
+
if (typeof key !== "function") {
|
|
62
|
+
throw new JobKeyFunctionUnavailableError(type, kind);
|
|
63
|
+
}
|
|
64
|
+
return key(payload);
|
|
65
|
+
}
|
|
66
|
+
var JobKeyFunctionUnavailableError = class extends Error {
|
|
67
|
+
constructor(jobType, kind) {
|
|
68
|
+
super(
|
|
69
|
+
`[jobs] ${kind} key for job '${jobType}' was persisted as a function sentinel ('${FN_KEY_SENTINEL}') but no live function is registered for it. The @JobHandler must be imported before start() so its meta is in JOB_HANDLER_REGISTRY.`
|
|
70
|
+
);
|
|
71
|
+
this.jobType = jobType;
|
|
72
|
+
this.kind = kind;
|
|
73
|
+
this.name = "JobKeyFunctionUnavailableError";
|
|
74
|
+
}
|
|
75
|
+
jobType;
|
|
76
|
+
kind;
|
|
77
|
+
};
|
|
50
78
|
|
|
51
79
|
export {
|
|
52
80
|
ParentClosePolicy,
|
|
@@ -54,6 +82,10 @@ export {
|
|
|
54
82
|
JOB_HANDLER_REGISTRY,
|
|
55
83
|
JOB_HANDLER_METADATA_KEY,
|
|
56
84
|
JobHandler,
|
|
57
|
-
HandlerRegistry
|
|
85
|
+
HandlerRegistry,
|
|
86
|
+
FN_KEY_SENTINEL,
|
|
87
|
+
keySelectorToTemplate,
|
|
88
|
+
resolveJobKey,
|
|
89
|
+
JobKeyFunctionUnavailableError
|
|
58
90
|
};
|
|
59
|
-
//# sourceMappingURL=chunk-
|
|
91
|
+
//# sourceMappingURL=chunk-7P5ODGLA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../runtime/subsystems/jobs/job-handler.base.ts"],"sourcesContent":["/**\n * Handler base class, JobContext, @JobHandler decorator, and policy types\n * for the job orchestration domain (ADR-022, JOB-2).\n *\n * User-authored jobs subclass `JobHandlerBase<TInput, TOutput>` and decorate\n * the class with `@JobHandler<TInput>('job_type', meta)`. The decorator\n * 1. stores metadata via `Reflect.defineMetadata` so Nest's reflector can\n * pick it up at module boot, and\n * 2. populates `JOB_HANDLER_REGISTRY` — a module-singleton map consumed by\n * `JobWorkerModule` (JOB-5) to materialise `job` rows and resolve\n * handler classes during claim/execute.\n *\n * No runtime orchestration lives here; this file is a pure type + decorator\n * surface so downstream PRs (JOB-3..JOB-5) can implement against a stable\n * shape.\n */\n// TODO(logging-subsystem): swap to ILogger once ADR-028 lands\nimport type { Logger } from '@nestjs/common';\nimport { tokenKey } from '../token-key';\nimport type { EventOfType, EventTypeName } from '../events/event-registry';\nimport type { JobRun } from './job-orchestrator.protocol';\n\n// ─── ParentClosePolicy ──────────────────────────────────────────────────────\n\n/**\n * What happens to running child runs when a parent enters a terminal state.\n * Stored on the child at spawn; changes to the parent after spawn do NOT\n * retroactively rewrite children.\n */\nexport enum ParentClosePolicy {\n Terminate = 'terminate',\n Cancel = 'cancel',\n Abandon = 'abandon',\n}\n\n// ─── Policy types ───────────────────────────────────────────────────────────\n\nexport interface RetryPolicy {\n attempts: number;\n backoff: 'fixed' | 'exponential';\n baseMs: number;\n nonRetryableErrors?: string[];\n}\n\n/**\n * Concurrency lane key (JOB-FN-KEY, 0.16.2).\n *\n * Two authoring forms, both honored end-to-end (the typed function form was\n * previously dropped to `null` at registration — see `upsertJobRows` — so\n * `collisionMode` silently never engaged):\n *\n * - **`string`** — a `{{field}}` template evaluated against the start\n * payload by `evaluateKeyTemplate` (single-key substitution, no dotted\n * paths). Persisted verbatim to `job.concurrency_key_template`.\n * - **`(input) => string`** — an arbitrary function of the input. Persisted\n * as the `FN_KEY_SENTINEL` marker so the definition-hash gate stays stable\n * and the collision path engages; `start()` re-resolves the live function\n * from `JOB_HANDLER_REGISTRY` and evaluates it against the payload.\n *\n * Both forms produce a per-lane key; same key + in-flight incumbent ⇒\n * `collisionMode` ('queue' | 'reject' | 'replace') decides.\n */\nexport type JobKeySelector<TInput> = string | ((input: TInput) => string);\n\nexport interface ConcurrencyPolicy<TInput> {\n key: JobKeySelector<TInput>;\n collisionMode: 'queue' | 'reject' | 'replace';\n}\n\nexport interface DedupePolicy<TInput> {\n key: JobKeySelector<TInput>;\n windowMs: number;\n}\n\n/**\n * Declarative scope reference. `TScope` is parameterised so JOB-7 can narrow\n * `entity` to the generated `ScopeEntityType` union at the call site without\n * modifying this file (OQ-1 resolution, 2026-04-20).\n */\nexport interface ScopeRef<TInput, TScope extends string = string> {\n entity: TScope;\n from: (input: TInput) => string;\n}\n\n/**\n * Bridge trigger authoring shape (BRIDGE-6 follow-up — BRIDGE-6 shipped the\n * generator + runtime for `@JobHandler({ triggers })` but never added the\n * authoring field to this type; the generator's tests scan source as strings,\n * so a real decorator was never compiled and the gap went uncaught).\n *\n * Declared on `@JobHandler({ triggers })`; the codegen bridge-registry\n * generator (`src/cli/shared/bridge-registry-generator.ts`) scans these from\n * source and emits `bridge/generated/registry.ts`, validating each `event`\n * against the generated `eventRegistry` at `gen-all`. The distributed union\n * narrows `map`/`when` per `event`, so callbacks are typed against the event\n * payload (ADR-023, \"typed against PayloadOfType<T>\").\n *\n * Typed against events' generated types — the same `import type` coupling the\n * bridge already has (erased at runtime). `jobs` must NOT import `bridge`, so\n * the post-gen `BridgeTriggerEntry` is deliberately not referenced here;\n * `triggerId`/`jobType` are computed by the generator, not authored.\n */\nexport type JobTrigger<TInput> = {\n [T in EventTypeName]: {\n /** Event type that fires this trigger. Validated against `eventRegistry`. */\n event: T;\n /** Maps the event to the job input. Inlined verbatim into the registry. */\n map: (event: EventOfType<T>) => TInput;\n /** Optional guard; `false` → wrapper records `status='skipped'`. */\n when?: (event: EventOfType<T>) => boolean;\n };\n}[EventTypeName];\n\nexport interface JobHandlerMeta<TInput> {\n pool?: string;\n scope?: ScopeRef<TInput>;\n retry?: RetryPolicy;\n concurrency?: ConcurrencyPolicy<TInput>;\n dedupe?: DedupePolicy<TInput>;\n timeoutMs?: number;\n replayFrom?: 'scratch' | 'last_step' | 'last_checkpoint';\n /**\n * Bridge triggers (ADR-023 Tier 3). Codegen scans these into `bridgeRegistry`;\n * the framework `BridgeDeliveryHandler` starts this job per matched event.\n * Absent for jobs started directly or via `IEventFlow.publishAndStart`.\n */\n triggers?: readonly JobTrigger<TInput>[];\n}\n\n// ─── Runtime option shapes ──────────────────────────────────────────────────\n\nexport interface StepOptions {\n retry?: RetryPolicy;\n timeoutMs?: number;\n}\n\nexport interface SpawnChildOptions {\n closePolicy?: ParentClosePolicy;\n runAt?: Date;\n priority?: number;\n tags?: Record<string, string>;\n}\n\n// ─── JobContext ─────────────────────────────────────────────────────────────\n\nexport interface JobContext<TInput> {\n readonly input: TInput;\n readonly run: JobRun;\n step<TOutput>(\n stepId: string,\n fn: () => Promise<TOutput>,\n opts?: StepOptions,\n ): Promise<TOutput>;\n spawnChild(type: string, input: unknown, opts?: SpawnChildOptions): Promise<JobRun>;\n readonly logger: Logger;\n // NOT in Phase 1 — deferred to ADR-025:\n // waitFor(kind, token, opts)\n // signal(token, payload)\n // sleep(ms)\n}\n\n// ─── JobHandlerBase ─────────────────────────────────────────────────────────\n\nexport abstract class JobHandlerBase<TInput, TOutput = unknown> {\n abstract run(ctx: JobContext<TInput>): Promise<TOutput>;\n}\n\n// ─── Registry + decorator ───────────────────────────────────────────────────\n\n/**\n * Module-singleton map keyed by job type. Populated by the `@JobHandler`\n * decorator at class definition time; consumed by `JobWorkerModule` (JOB-5)\n * to upsert `job` rows and resolve handler classes during claim/execute.\n */\nexport const JOB_HANDLER_REGISTRY = new Map<\n string,\n {\n type: string;\n meta: JobHandlerMeta<unknown>;\n handlerClass: new (...args: unknown[]) => JobHandlerBase<unknown>;\n }\n>();\n\n// ADR-037: namespaced `Symbol.for(...)` (via `tokenKey()`) so the reflection-metadata\n// key matches by value across import boundaries (the @JobHandler decorator and the\n// reader may resolve different runtime copies). Distinct from the DI tokens but\n// subject to the same dual-package identity hazard.\nexport const JOB_HANDLER_METADATA_KEY = Symbol.for(tokenKey('jobs', 'handler-metadata'));\n\n/**\n * Class decorator that registers a handler with the job type, the full\n * metadata shape, and the target class constructor.\n *\n * Duplicate-type behaviour (OQ-3, resolved 2026-04-18):\n * - `NODE_ENV === 'production'` → throw; silent overwrite in prod is a\n * correctness bug.\n * - `NODE_ENV === 'test'` → silent overwrite (tests intentionally\n * re-register handlers).\n * - otherwise (dev) → `console.warn` + overwrite. `console`\n * is used intentionally instead of the Nest `Logger` — decorators run\n * at module-load time before any Nest container exists.\n */\nexport function JobHandler<TInput>(\n type: string,\n meta: JobHandlerMeta<TInput>,\n): ClassDecorator {\n return (target) => {\n if (JOB_HANDLER_REGISTRY.has(type)) {\n const env = process.env.NODE_ENV;\n if (env === 'production') {\n throw new Error(\n `[JobHandler] Duplicate registration for job type '${type}'. ` +\n `Each @JobHandler must declare a unique type.`,\n );\n }\n if (env !== 'test') {\n // eslint-disable-next-line no-console\n console.warn(\n `[JobHandler] Duplicate registration for job type '${type}'. ` +\n `Overwriting previous handler — this is almost certainly a bug.`,\n );\n }\n }\n\n Reflect.defineMetadata(JOB_HANDLER_METADATA_KEY, { type, meta }, target);\n JOB_HANDLER_REGISTRY.set(type, {\n type,\n meta: meta as JobHandlerMeta<unknown>,\n handlerClass: target as unknown as new (\n ...args: unknown[]\n ) => JobHandlerBase<unknown>,\n });\n };\n}\n\n// ─── HandlerRegistry — read helpers consumed by JobWorkerModule (JOB-5) ─────\n\n/**\n * Single entry shape returned by `HandlerRegistry.getAll()` / `.get()` and\n * exposed to `JobWorkerModule.onModuleInit` for boot-time upserts.\n *\n * Structurally compatible with `IJobOrchestrator.upsertJobRows`'s\n * `JobUpsertEntry` so the worker module can pass entries through verbatim\n * without re-mapping.\n */\nexport interface HandlerRegistryEntry {\n type: string;\n meta: JobHandlerMeta<unknown>;\n handlerClass: new (...args: unknown[]) => JobHandlerBase<unknown>;\n}\n\n/**\n * Read facade over `JOB_HANDLER_REGISTRY`. The decorator's write path is\n * unchanged; this namespace exists so consumers (the worker module, tests)\n * don't import the raw `Map` and accidentally mutate it.\n */\nexport namespace HandlerRegistry {\n /** All registered entries in insertion order. */\n export function getAll(): HandlerRegistryEntry[] {\n return Array.from(JOB_HANDLER_REGISTRY.values());\n }\n\n /** Lookup by job type, or `undefined` if no `@JobHandler` is registered. */\n export function get(type: string): HandlerRegistryEntry | undefined {\n return JOB_HANDLER_REGISTRY.get(type);\n }\n}\n\n// ─── Key resolution (JOB-FN-KEY, 0.16.2) ────────────────────────────────────\n\n/**\n * Sentinel persisted to `job.concurrency_key_template` / `dedupe_key_template`\n * when the authored `key` is a function rather than a `{{field}}` template.\n *\n * Why a sentinel (not `null`): the collision/dedupe paths in both backends gate\n * on `definition.concurrencyKeyTemplate != null`. A function key persisted as\n * `null` (the pre-0.16.2 bug) left those columns empty, so `collisionMode` /\n * the dedupe window never engaged — the job ran with NO key. A stable sentinel\n * keeps the column non-null (path engages) AND keeps the definition-hash gate\n * (`upsertJobRows`' `IS DISTINCT FROM` clause) stable across boots, since the\n * function identity itself can't be hashed. `start()` detects the sentinel and\n * re-resolves the live function from `JOB_HANDLER_REGISTRY`.\n *\n * Chosen as an angle-bracketed token so it can never collide with a real\n * `{{field}}` template (which never contains a literal `<`).\n */\nexport const FN_KEY_SENTINEL = '<fn>';\n\n/**\n * Registration-time projection: collapse an authored `JobKeySelector` to the\n * string stored in the `job` definition row. A string template is stored\n * verbatim; a function is stored as `FN_KEY_SENTINEL`; absence stays `null`.\n */\nexport function keySelectorToTemplate(\n key: JobKeySelector<unknown> | undefined,\n): string | null {\n if (typeof key === 'string') return key;\n if (typeof key === 'function') return FN_KEY_SENTINEL;\n return null;\n}\n\n/** Which meta policy a key belongs to — selects the live fn at `start()`. */\nexport type KeyKind = 'concurrency' | 'dedupe';\n\n/**\n * `start()`-time resolution shared by every backend. Turns the persisted\n * template column into the concrete per-run key for the given payload.\n *\n * - `template == null` → `null` (no key; caller skips the collision/dedupe path).\n * - `template === FN_KEY_SENTINEL` → look the live `@JobHandler` meta up in\n * `JOB_HANDLER_REGISTRY`, pull `meta[kind].key`, and invoke it against the\n * payload. The registry is the runtime source of truth (the worker already\n * resolves handler classes the same way), so the function survives the DB\n * round-trip even though it can't be persisted.\n * - otherwise → a `{{field}}` template, evaluated via the injected\n * `evaluateTemplate` (each backend passes its own copy to avoid a runtime\n * import cycle).\n *\n * Throws `JobKeyFunctionUnavailableError` if the sentinel is present but no\n * live function can be found (e.g. the registry was reset, or a function key\n * was persisted by a newer build and read by an older one). Failing loud beats\n * silently degrading to no-key — the exact regression this fix exists to kill.\n */\nexport function resolveJobKey(\n kind: KeyKind,\n type: string,\n template: string | null,\n payload: Record<string, unknown>,\n evaluateTemplate: (template: string, payload: Record<string, unknown>) => string,\n): string | null {\n if (template == null) return null;\n if (template !== FN_KEY_SENTINEL) return evaluateTemplate(template, payload);\n\n const meta = JOB_HANDLER_REGISTRY.get(type)?.meta;\n const key = (meta?.[kind] as { key?: unknown } | undefined)?.key;\n if (typeof key !== 'function') {\n throw new JobKeyFunctionUnavailableError(type, kind);\n }\n return (key as (input: unknown) => string)(payload);\n}\n\n/**\n * Raised when a `${FN_KEY_SENTINEL}` template is read but the live function\n * key is missing from `JOB_HANDLER_REGISTRY`. Kept here (not in `jobs-errors`)\n * so `job-handler.base` stays import-cycle-free.\n */\nexport class JobKeyFunctionUnavailableError extends Error {\n constructor(\n readonly jobType: string,\n readonly kind: KeyKind,\n ) {\n super(\n `[jobs] ${kind} key for job '${jobType}' was persisted as a function ` +\n `sentinel ('${FN_KEY_SENTINEL}') but no live function is registered ` +\n `for it. The @JobHandler must be imported before start() so its meta ` +\n `is in JOB_HANDLER_REGISTRY.`,\n );\n this.name = 'JobKeyFunctionUnavailableError';\n }\n}\n"],"mappings":";;;;;AA6BO,IAAK,oBAAL,kBAAKA,uBAAL;AACL,EAAAA,mBAAA,eAAY;AACZ,EAAAA,mBAAA,YAAS;AACT,EAAAA,mBAAA,aAAU;AAHA,SAAAA;AAAA,GAAA;AAsIL,IAAe,iBAAf,MAAyD;AAEhE;AASO,IAAM,uBAAuB,oBAAI,IAOtC;AAMK,IAAM,2BAA2B,OAAO,IAAI,SAAS,QAAQ,kBAAkB,CAAC;AAehF,SAAS,WACd,MACA,MACgB;AAChB,SAAO,CAAC,WAAW;AACjB,QAAI,qBAAqB,IAAI,IAAI,GAAG;AAClC,YAAM,MAAM,QAAQ,IAAI;AACxB,UAAI,QAAQ,cAAc;AACxB,cAAM,IAAI;AAAA,UACR,qDAAqD,IAAI;AAAA,QAE3D;AAAA,MACF;AACA,UAAI,QAAQ,QAAQ;AAElB,gBAAQ;AAAA,UACN,qDAAqD,IAAI;AAAA,QAE3D;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,eAAe,0BAA0B,EAAE,MAAM,KAAK,GAAG,MAAM;AACvE,yBAAqB,IAAI,MAAM;AAAA,MAC7B;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAGhB,CAAC;AAAA,EACH;AACF;AAuBO,IAAU;AAAA,CAAV,CAAUC,qBAAV;AAEE,WAAS,SAAiC;AAC/C,WAAO,MAAM,KAAK,qBAAqB,OAAO,CAAC;AAAA,EACjD;AAFO,EAAAA,iBAAS;AAKT,WAAS,IAAI,MAAgD;AAClE,WAAO,qBAAqB,IAAI,IAAI;AAAA,EACtC;AAFO,EAAAA,iBAAS;AAAA,GAPD;AA8BV,IAAM,kBAAkB;AAOxB,SAAS,sBACd,KACe;AACf,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,MAAI,OAAO,QAAQ,WAAY,QAAO;AACtC,SAAO;AACT;AAwBO,SAAS,cACd,MACA,MACA,UACA,SACA,kBACe;AACf,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,aAAa,gBAAiB,QAAO,iBAAiB,UAAU,OAAO;AAE3E,QAAM,OAAO,qBAAqB,IAAI,IAAI,GAAG;AAC7C,QAAM,MAAO,OAAO,IAAI,GAAqC;AAC7D,MAAI,OAAO,QAAQ,YAAY;AAC7B,UAAM,IAAI,+BAA+B,MAAM,IAAI;AAAA,EACrD;AACA,SAAQ,IAAmC,OAAO;AACpD;AAOO,IAAM,iCAAN,cAA6C,MAAM;AAAA,EACxD,YACW,SACA,MACT;AACA;AAAA,MACE,UAAU,IAAI,iBAAiB,OAAO,4CACtB,eAAe;AAAA,IAGjC;AARS;AACA;AAQT,SAAK,OAAO;AAAA,EACd;AAAA,EAVW;AAAA,EACA;AAUb;","names":["ParentClosePolicy","HandlerRegistry"]}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DrizzleIntegrationRunRecorder
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YK5JEVLX.js";
|
|
4
4
|
import {
|
|
5
5
|
MemoryRunRecorder
|
|
6
6
|
} from "./chunk-EO2QPOKH.js";
|
|
7
7
|
import {
|
|
8
8
|
PostgresCursorStore
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XWBK3XJK.js";
|
|
10
10
|
import {
|
|
11
11
|
MemoryCursorStore
|
|
12
12
|
} from "./chunk-AHV4GDYM.js";
|
|
13
13
|
import {
|
|
14
14
|
DeepEqualDiffer
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-JEINYUJH.js";
|
|
16
16
|
import {
|
|
17
17
|
INTEGRATION_CURSOR_STORE,
|
|
18
18
|
INTEGRATION_FIELD_DIFFER,
|
|
@@ -34,7 +34,13 @@ var IntegrationModule = class {
|
|
|
34
34
|
{ provide: INTEGRATION_MULTI_TENANT, useValue: multiTenant },
|
|
35
35
|
// Default differ — consumers can override by binding a different
|
|
36
36
|
// `IFieldDiffer<T>` to `INTEGRATION_FIELD_DIFFER` in their feature module.
|
|
37
|
-
|
|
37
|
+
// DIFFER-UNIGNORE: `options.differ` (ignore/unignore) is threaded here so
|
|
38
|
+
// a consumer can declare a default-ignored column (e.g. `deletedAt`) as
|
|
39
|
+
// domain data for their entities without binding a bespoke differ.
|
|
40
|
+
{
|
|
41
|
+
provide: INTEGRATION_FIELD_DIFFER,
|
|
42
|
+
useValue: new DeepEqualDiffer(options.differ ?? {})
|
|
43
|
+
}
|
|
38
44
|
];
|
|
39
45
|
const backendProviders = options.backend === "memory" ? [
|
|
40
46
|
// Wired as singletons via `useValue` so tests can pull
|
|
@@ -78,4 +84,4 @@ IntegrationModule = __decorateClass([
|
|
|
78
84
|
export {
|
|
79
85
|
IntegrationModule
|
|
80
86
|
};
|
|
81
|
-
//# sourceMappingURL=chunk-
|
|
87
|
+
//# sourceMappingURL=chunk-ATVGYF3D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../runtime/subsystems/integration/integration.module.ts"],"sourcesContent":["/**\n * IntegrationModule — `DynamicModule.forRoot({ backend, multiTenant? })` factory\n * wiring the integration subsystem's substrate (SYNC-6, ADR-008 subsystem pattern).\n *\n * ## What this module provides\n *\n * - `INTEGRATION_CURSOR_STORE` — Drizzle or Memory cursor store\n * - `INTEGRATION_RUN_RECORDER` — Drizzle or Memory run recorder\n * - `INTEGRATION_FIELD_DIFFER` — default `DeepEqualDiffer`\n * - `INTEGRATION_MULTI_TENANT` — resolved boolean flag (defaults to false)\n * - `INTEGRATION_MODULE_OPTIONS` — the options object itself, for backends\n * that need to inspect config at construction time\n *\n * ## What this module does NOT provide\n *\n * - `INTEGRATION_CHANGE_SOURCE` — per-provider per-entity; consumer binds in\n * their feature module (e.g. `OpportunityIntegrationModule` provides a\n * `SalesforceOpportunityChangeSource`). Loopback suppression — when\n * needed — is composed into the primitive's middleware chain via\n * `createLoopbackMiddleware(store)` (#226-5 / ADR-033); the\n * orchestrator no longer accepts a fingerprint store directly.\n * - `INTEGRATION_SINK` — per canonical entity; consumer binds in their feature\n * module.\n * - `ExecuteIntegrationUseCase` — registered by the feature module alongside\n * its source + sink bindings. Providing the orchestrator here would\n * force Nest to resolve INTEGRATION_CHANGE_SOURCE + INTEGRATION_SINK at module\n * compile time, which fails when the feature module hasn't been\n * imported yet. Consumers register `ExecuteIntegrationUseCase` in the same\n * `providers` array as their source + sink so resolution is local\n * to where all three are bound.\n *\n * Same shape as `EventsModule.forRoot` — the module wires the bus; you\n * bring your own handlers. Here: the module wires the substrate; you\n * bring your own source + sink.\n *\n * ## Usage\n *\n * ```ts\n * // AppModule — single source of truth for backend + multi-tenancy.\n * @Module({\n * imports: [IntegrationModule.forRoot({ backend: 'drizzle' })],\n * })\n * export class AppModule {}\n *\n * // Per-entity feature module — binds source + sink, gets the\n * // orchestrator for free.\n * @Module({\n * providers: [\n * { provide: INTEGRATION_CHANGE_SOURCE, useClass: SalesforceOpportunitySource },\n * { provide: INTEGRATION_SINK, useClass: OpportunityIntegrationSink },\n * ExecuteIntegrationUseCase,\n * ],\n * })\n * export class OpportunityIntegrationModule {\n * constructor(\n * private readonly execute: ExecuteIntegrationUseCase<CanonicalOpportunity>,\n * ) {}\n * }\n * ```\n *\n * `global: true` means feature modules do not need to re-import\n * `IntegrationModule` — the substrate tokens are available project-wide.\n */\nimport { Module, type DynamicModule, type Provider } from '@nestjs/common';\nimport {\n INTEGRATION_CURSOR_STORE,\n INTEGRATION_FIELD_DIFFER,\n INTEGRATION_MODULE_OPTIONS,\n INTEGRATION_MULTI_TENANT,\n INTEGRATION_RUN_RECORDER,\n} from './integration.tokens';\nimport { MemoryCursorStore } from './integration-cursor-store.memory-backend';\nimport { MemoryRunRecorder } from './integration-run-recorder.memory-backend';\nimport { PostgresCursorStore } from './integration-cursor-store.drizzle-backend';\nimport { DrizzleIntegrationRunRecorder } from './integration-run-recorder.drizzle-backend';\nimport { DeepEqualDiffer, type DeepEqualDifferOptions } from './deep-equal.differ';\n\nexport interface IntegrationModuleOptions {\n /**\n * Backend selection. `drizzle` wires the Postgres cursor store +\n * run-log recorder; `memory` wires in-memory doubles suitable for\n * tests + local dev.\n */\n backend: 'drizzle' | 'memory';\n\n /**\n * Multi-tenancy opt-in (SYNC-6).\n *\n * When `true`, every call to the orchestrator + both Drizzle backends\n * must supply a non-null `tenantId`; missing values throw\n * `MissingTenantIdError`. Defense-in-depth: the orchestrator rejects\n * at entry (no dangling `status=running` rows) AND the Drizzle\n * backends reject at their write boundary (belt-and-braces for any\n * path that bypasses the orchestrator). Both sites use the shared\n * `assertTenantId` helper so error messages match.\n *\n * Memory backends accept `tenantId` unconditionally — their state is\n * process-local; cross-tenant isolation there is not meaningful.\n *\n * Defaults to `false`.\n */\n multiTenant?: boolean;\n\n /**\n * Default-differ configuration (DIFFER-UNIGNORE, 0.17.1). Threaded into the\n * `DeepEqualDiffer` bound to `INTEGRATION_FIELD_DIFFER`. Omit for the\n * historical behaviour (the default ignore list, unchanged).\n *\n * Mirrors `DeepEqualDifferOptions`:\n * - `ignore` — extra field names to ignore (merged with the defaults).\n * - `unignore` — default-ignored field names to RE-include as domain data\n * (e.g. `['deletedAt']` for an entity whose `deletedAt` is a\n * vendor-observed retraction tombstone, not row metadata — swe-brain\n * ADR-0008 §1). Subtracted after the merge, so it wins.\n *\n * A feature module that binds its own `IFieldDiffer<T>` to\n * `INTEGRATION_FIELD_DIFFER` overrides this entirely (per-entity escape hatch\n * unchanged).\n */\n differ?: DeepEqualDifferOptions;\n}\n\n@Module({})\nexport class IntegrationModule {\n static forRoot(options: IntegrationModuleOptions): DynamicModule {\n const multiTenant = options.multiTenant ?? false;\n\n const sharedProviders: Provider[] = [\n { provide: INTEGRATION_MODULE_OPTIONS, useValue: options },\n { provide: INTEGRATION_MULTI_TENANT, useValue: multiTenant },\n // Default differ — consumers can override by binding a different\n // `IFieldDiffer<T>` to `INTEGRATION_FIELD_DIFFER` in their feature module.\n // DIFFER-UNIGNORE: `options.differ` (ignore/unignore) is threaded here so\n // a consumer can declare a default-ignored column (e.g. `deletedAt`) as\n // domain data for their entities without binding a bespoke differ.\n {\n provide: INTEGRATION_FIELD_DIFFER,\n useValue: new DeepEqualDiffer(options.differ ?? {}),\n },\n ];\n\n const backendProviders: Provider[] =\n options.backend === 'memory'\n ? [\n // Wired as singletons via `useValue` so tests can pull\n // them out via `moduleRef.get(MemoryCursorStore)` for\n // direct assertions. Matches JOB-4 / MemoryJobStore shape.\n { provide: MemoryCursorStore, useValue: new MemoryCursorStore() },\n {\n provide: INTEGRATION_CURSOR_STORE,\n useExisting: MemoryCursorStore,\n },\n { provide: MemoryRunRecorder, useValue: new MemoryRunRecorder() },\n {\n provide: INTEGRATION_RUN_RECORDER,\n useExisting: MemoryRunRecorder,\n },\n ]\n : [\n // Drizzle backends — injected with DRIZZLE (provided by the\n // consumer's DrizzleModule) + the INTEGRATION_MULTI_TENANT flag\n // we bound above.\n { provide: INTEGRATION_CURSOR_STORE, useClass: PostgresCursorStore },\n { provide: INTEGRATION_RUN_RECORDER, useClass: DrizzleIntegrationRunRecorder },\n ];\n\n return {\n module: IntegrationModule,\n global: true,\n providers: [...sharedProviders, ...backendProviders],\n exports: [\n INTEGRATION_MODULE_OPTIONS,\n INTEGRATION_MULTI_TENANT,\n INTEGRATION_FIELD_DIFFER,\n INTEGRATION_CURSOR_STORE,\n INTEGRATION_RUN_RECORDER,\n ],\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,SAAS,cAAiD;AA4DnD,IAAM,oBAAN,MAAwB;AAAA,EAC7B,OAAO,QAAQ,SAAkD;AAC/D,UAAM,cAAc,QAAQ,eAAe;AAE3C,UAAM,kBAA8B;AAAA,MAClC,EAAE,SAAS,4BAA4B,UAAU,QAAQ;AAAA,MACzD,EAAE,SAAS,0BAA0B,UAAU,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAM3D;AAAA,QACE,SAAS;AAAA,QACT,UAAU,IAAI,gBAAgB,QAAQ,UAAU,CAAC,CAAC;AAAA,MACpD;AAAA,IACF;AAEA,UAAM,mBACJ,QAAQ,YAAY,WAChB;AAAA;AAAA;AAAA;AAAA,MAIE,EAAE,SAAS,mBAAmB,UAAU,IAAI,kBAAkB,EAAE;AAAA,MAChE;AAAA,QACE,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,EAAE,SAAS,mBAAmB,UAAU,IAAI,kBAAkB,EAAE;AAAA,MAChE;AAAA,QACE,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,IACF,IACA;AAAA;AAAA;AAAA;AAAA,MAIE,EAAE,SAAS,0BAA0B,UAAU,oBAAoB;AAAA,MACnE,EAAE,SAAS,0BAA0B,UAAU,8BAA8B;AAAA,IAC/E;AAEN,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,WAAW,CAAC,GAAG,iBAAiB,GAAG,gBAAgB;AAAA,MACnD,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAxDa,oBAAN;AAAA,EADN,OAAO,CAAC,CAAC;AAAA,GACG;","names":[]}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MemoryJobOrchestrator
|
|
3
|
-
} from "./chunk-BULPAAD3.js";
|
|
4
1
|
import {
|
|
5
2
|
DrizzleJobRunService
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VNBC3VXM.js";
|
|
7
4
|
import {
|
|
8
5
|
MemoryJobRunService
|
|
9
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BHZP6LOV.js";
|
|
10
7
|
import {
|
|
11
8
|
DrizzleJobStepService
|
|
12
9
|
} from "./chunk-DV4RV2DC.js";
|
|
10
|
+
import {
|
|
11
|
+
DrizzleJobOrchestrator
|
|
12
|
+
} from "./chunk-E6PLM6QG.js";
|
|
13
|
+
import {
|
|
14
|
+
MemoryJobOrchestrator
|
|
15
|
+
} from "./chunk-VQOAATIG.js";
|
|
13
16
|
import {
|
|
14
17
|
MemoryJobStepService
|
|
15
18
|
} from "./chunk-PNZSGAB2.js";
|
|
@@ -21,9 +24,6 @@ import {
|
|
|
21
24
|
BULLMQ_RESOLVED_CONFIG,
|
|
22
25
|
resolveBullMqConfig
|
|
23
26
|
} from "./chunk-I6MVCB5A.js";
|
|
24
|
-
import {
|
|
25
|
-
DrizzleJobOrchestrator
|
|
26
|
-
} from "./chunk-OTR44OH6.js";
|
|
27
27
|
import {
|
|
28
28
|
JOBS_LISTEN_NOTIFY,
|
|
29
29
|
JOBS_MULTI_TENANT,
|
|
@@ -114,4 +114,4 @@ JobsDomainModule = __decorateClass([
|
|
|
114
114
|
export {
|
|
115
115
|
JobsDomainModule
|
|
116
116
|
};
|
|
117
|
-
//# sourceMappingURL=chunk-
|
|
117
|
+
//# sourceMappingURL=chunk-AZLUWG5S.js.map
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clampEventLimit,
|
|
3
|
+
decodeEventCursor,
|
|
4
|
+
encodeEventCursor
|
|
5
|
+
} from "./chunk-UQ5EHOH2.js";
|
|
1
6
|
import {
|
|
2
7
|
EVENTS_WAKE_CHANNEL,
|
|
3
8
|
PgNotifyListener,
|
|
4
9
|
pgNotify
|
|
5
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-Q6LRJ4VI.js";
|
|
11
|
+
import {
|
|
12
|
+
EVENTS_MODULE_OPTIONS
|
|
13
|
+
} from "./chunk-H5NH7KPE.js";
|
|
6
14
|
import {
|
|
7
15
|
BRIDGE_OUTBOX_DRAIN_HOOK
|
|
8
16
|
} from "./chunk-4LH67P4U.js";
|
|
9
17
|
import {
|
|
10
18
|
domainEvents
|
|
11
19
|
} from "./chunk-OFRRBC7M.js";
|
|
12
|
-
import {
|
|
13
|
-
clampEventLimit,
|
|
14
|
-
decodeEventCursor,
|
|
15
|
-
encodeEventCursor
|
|
16
|
-
} from "./chunk-UQ5EHOH2.js";
|
|
17
|
-
import {
|
|
18
|
-
EVENTS_MODULE_OPTIONS
|
|
19
|
-
} from "./chunk-H5NH7KPE.js";
|
|
20
20
|
import {
|
|
21
21
|
DRIZZLE
|
|
22
22
|
} from "./chunk-U64T4YZE.js";
|
|
@@ -393,4 +393,4 @@ DrizzleEventBus = __decorateClass([
|
|
|
393
393
|
export {
|
|
394
394
|
DrizzleEventBus
|
|
395
395
|
};
|
|
396
|
-
//# sourceMappingURL=chunk-
|
|
396
|
+
//# sourceMappingURL=chunk-B34G6PHD.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
clampLimit,
|
|
3
|
-
decodeKeysetCursor,
|
|
4
|
-
encodeKeysetCursor,
|
|
5
|
-
toJobRunSummary
|
|
6
|
-
} from "./chunk-L3LZWWSX.js";
|
|
7
1
|
import {
|
|
8
2
|
MemoryJobStore
|
|
9
3
|
} from "./chunk-SNQ3TOWP.js";
|
|
10
4
|
import {
|
|
11
5
|
MissingTenantIdError
|
|
12
6
|
} from "./chunk-T4BIIU5E.js";
|
|
7
|
+
import {
|
|
8
|
+
clampLimit,
|
|
9
|
+
decodeKeysetCursor,
|
|
10
|
+
encodeKeysetCursor,
|
|
11
|
+
toJobRunSummary
|
|
12
|
+
} from "./chunk-L3LZWWSX.js";
|
|
13
13
|
import {
|
|
14
14
|
JOBS_MULTI_TENANT,
|
|
15
15
|
JOB_ORCHESTRATOR
|
|
@@ -209,4 +209,4 @@ function compareBy(a, b, order) {
|
|
|
209
209
|
export {
|
|
210
210
|
MemoryJobRunService
|
|
211
211
|
};
|
|
212
|
-
//# sourceMappingURL=chunk-
|
|
212
|
+
//# sourceMappingURL=chunk-BHZP6LOV.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JOB_RUN_SERVICE
|
|
3
3
|
} from "./chunk-ZPL74UQN.js";
|
|
4
|
-
import {
|
|
5
|
-
BRIDGE_DELIVERY_REPO
|
|
6
|
-
} from "./chunk-4LH67P4U.js";
|
|
7
4
|
import {
|
|
8
5
|
EVENT_READ_PORT
|
|
9
6
|
} from "./chunk-H5NH7KPE.js";
|
|
7
|
+
import {
|
|
8
|
+
BRIDGE_DELIVERY_REPO
|
|
9
|
+
} from "./chunk-4LH67P4U.js";
|
|
10
10
|
import {
|
|
11
11
|
INTEGRATION_CURSOR_STORE,
|
|
12
12
|
INTEGRATION_RUN_RECORDER
|
|
@@ -181,4 +181,4 @@ ObservabilityService = __decorateClass([
|
|
|
181
181
|
export {
|
|
182
182
|
ObservabilityService
|
|
183
183
|
};
|
|
184
|
-
//# sourceMappingURL=chunk-
|
|
184
|
+
//# sourceMappingURL=chunk-CLWBNXKF.js.map
|
|
@@ -5,6 +5,14 @@ import {
|
|
|
5
5
|
JobTypeNotFoundError,
|
|
6
6
|
MissingTenantIdError
|
|
7
7
|
} from "./chunk-T4BIIU5E.js";
|
|
8
|
+
import {
|
|
9
|
+
JOBS_WAKE_CHANNEL,
|
|
10
|
+
pgNotify
|
|
11
|
+
} from "./chunk-Q6LRJ4VI.js";
|
|
12
|
+
import {
|
|
13
|
+
keySelectorToTemplate,
|
|
14
|
+
resolveJobKey
|
|
15
|
+
} from "./chunk-7P5ODGLA.js";
|
|
8
16
|
import {
|
|
9
17
|
JOBS_LISTEN_NOTIFY,
|
|
10
18
|
JOBS_MULTI_TENANT
|
|
@@ -14,10 +22,6 @@ import {
|
|
|
14
22
|
jobSteps,
|
|
15
23
|
jobs
|
|
16
24
|
} from "./chunk-OKXZ63IA.js";
|
|
17
|
-
import {
|
|
18
|
-
JOBS_WAKE_CHANNEL,
|
|
19
|
-
pgNotify
|
|
20
|
-
} from "./chunk-MYQIQ27N.js";
|
|
21
25
|
import {
|
|
22
26
|
DRIZZLE
|
|
23
27
|
} from "./chunk-U64T4YZE.js";
|
|
@@ -81,7 +85,13 @@ var DrizzleJobOrchestrator = class {
|
|
|
81
85
|
if (!def) throw new JobTypeNotFoundError(type);
|
|
82
86
|
const definition = def;
|
|
83
87
|
if (definition.dedupeKeyTemplate && definition.dedupeWindowMs) {
|
|
84
|
-
const dedupeKey2 =
|
|
88
|
+
const dedupeKey2 = resolveJobKey(
|
|
89
|
+
"dedupe",
|
|
90
|
+
type,
|
|
91
|
+
definition.dedupeKeyTemplate,
|
|
92
|
+
payload,
|
|
93
|
+
evaluateKeyTemplate
|
|
94
|
+
);
|
|
85
95
|
const windowStart = new Date(Date.now() - definition.dedupeWindowMs);
|
|
86
96
|
const existing = await client.select().from(jobRuns).where(
|
|
87
97
|
and(
|
|
@@ -98,9 +108,12 @@ var DrizzleJobOrchestrator = class {
|
|
|
98
108
|
}
|
|
99
109
|
let concurrencyKey = null;
|
|
100
110
|
if (definition.concurrencyKeyTemplate) {
|
|
101
|
-
concurrencyKey =
|
|
111
|
+
concurrencyKey = resolveJobKey(
|
|
112
|
+
"concurrency",
|
|
113
|
+
type,
|
|
102
114
|
definition.concurrencyKeyTemplate,
|
|
103
|
-
payload
|
|
115
|
+
payload,
|
|
116
|
+
evaluateKeyTemplate
|
|
104
117
|
);
|
|
105
118
|
const inFlight = await client.select().from(jobRuns).where(
|
|
106
119
|
and(
|
|
@@ -136,7 +149,13 @@ var DrizzleJobOrchestrator = class {
|
|
|
136
149
|
}
|
|
137
150
|
rootRunId = parent.rootRunId;
|
|
138
151
|
}
|
|
139
|
-
const dedupeKey =
|
|
152
|
+
const dedupeKey = resolveJobKey(
|
|
153
|
+
"dedupe",
|
|
154
|
+
type,
|
|
155
|
+
definition.dedupeKeyTemplate,
|
|
156
|
+
payload,
|
|
157
|
+
evaluateKeyTemplate
|
|
158
|
+
);
|
|
140
159
|
const [inserted] = await client.insert(jobRuns).values({
|
|
141
160
|
id: newId,
|
|
142
161
|
jobType: type,
|
|
@@ -293,11 +312,13 @@ var DrizzleJobOrchestrator = class {
|
|
|
293
312
|
backoff: "fixed",
|
|
294
313
|
baseMs: 0
|
|
295
314
|
};
|
|
296
|
-
const
|
|
297
|
-
|
|
315
|
+
const concurrencyKeyTemplateStr = keySelectorToTemplate(
|
|
316
|
+
meta.concurrency?.key
|
|
317
|
+
);
|
|
298
318
|
const collisionMode = meta.concurrency?.collisionMode ?? "queue";
|
|
299
|
-
const
|
|
300
|
-
|
|
319
|
+
const dedupeKeyTemplateStr = keySelectorToTemplate(
|
|
320
|
+
meta.dedupe?.key
|
|
321
|
+
);
|
|
301
322
|
const dedupeWindowMs = meta.dedupe?.windowMs ?? null;
|
|
302
323
|
const timeoutMs = meta.timeoutMs ?? null;
|
|
303
324
|
const replayFrom = meta.replayFrom ?? "last_checkpoint";
|
|
@@ -372,4 +393,4 @@ export {
|
|
|
372
393
|
evaluateKeyTemplate,
|
|
373
394
|
DrizzleJobOrchestrator
|
|
374
395
|
};
|
|
375
|
-
//# sourceMappingURL=chunk-
|
|
396
|
+
//# sourceMappingURL=chunk-E6PLM6QG.js.map
|