@pattern-stack/codegen 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/consumer-skills/events/authoring-events.md +31 -0
  3. package/dist/{chunk-COGHTKXY.js → chunk-27ETSJ2X.js} +2 -2
  4. package/dist/{chunk-235ZMMJR.js → chunk-3YCUIGPG.js} +10 -10
  5. package/dist/{chunk-Z7PQCAVK.js → chunk-4OC5MSHO.js} +50 -4
  6. package/dist/chunk-4OC5MSHO.js.map +1 -0
  7. package/dist/{chunk-VNBC3VXM.js → chunk-5LXOJGO2.js} +6 -6
  8. package/dist/{chunk-7OVCARTQ.js → chunk-5RT7JGKT.js} +4 -4
  9. package/dist/{chunk-T6C4LFLC.js → chunk-7YGORYZD.js} +4 -4
  10. package/dist/{chunk-PKDS6QIJ.js → chunk-ATVGYF3D.js} +7 -7
  11. package/dist/{chunk-2TVVBC53.js → chunk-BORNCTH3.js} +2 -2
  12. package/dist/{chunk-E6PLM6QG.js → chunk-CUSMC2KK.js} +13 -13
  13. package/dist/{chunk-V4AF6DI4.js → chunk-DUBZOXJC.js} +9 -2
  14. package/dist/{chunk-V4AF6DI4.js.map → chunk-DUBZOXJC.js.map} +1 -1
  15. package/dist/chunk-DUUCU77W.js +211 -0
  16. package/dist/chunk-DUUCU77W.js.map +1 -0
  17. package/dist/{chunk-OFRRBC7M.js → chunk-E2BRT5IB.js} +15 -1
  18. package/dist/chunk-E2BRT5IB.js.map +1 -0
  19. package/dist/{chunk-XKWOJZZ4.js → chunk-E45CSC33.js} +2 -2
  20. package/dist/{chunk-VQOAATIG.js → chunk-FLYF76CU.js} +4 -4
  21. package/dist/{chunk-43SBT72G.js → chunk-I6UXRJ3Q.js} +4 -4
  22. package/dist/{chunk-GM3RMJIJ.js → chunk-INO47JXD.js} +3 -3
  23. package/dist/{chunk-BGULBWKJ.js → chunk-JOBQ6RUU.js} +1 -1
  24. package/dist/chunk-JOBQ6RUU.js.map +1 -0
  25. package/dist/{chunk-VDL5CJ5C.js → chunk-KBO5OOON.js} +9 -9
  26. package/dist/{chunk-OZEPJGMA.js → chunk-KHQ72A5F.js} +54 -6
  27. package/dist/chunk-KHQ72A5F.js.map +1 -0
  28. package/dist/{chunk-F7KN3U6U.js → chunk-KK5A7B2T.js} +27 -1
  29. package/dist/chunk-KK5A7B2T.js.map +1 -0
  30. package/dist/{chunk-B34G6PHD.js → chunk-LARB26EI.js} +77 -10
  31. package/dist/chunk-LARB26EI.js.map +1 -0
  32. package/dist/{chunk-65MO75WM.js → chunk-LQXBQO72.js} +8 -8
  33. package/dist/{chunk-K2I6XIK5.js → chunk-MVKW2BCR.js} +2 -2
  34. package/dist/{chunk-AZLUWG5S.js → chunk-NYJYK6J4.js} +10 -10
  35. package/dist/{chunk-BHZP6LOV.js → chunk-QSJ3J4HE.js} +7 -7
  36. package/dist/{chunk-R6F6KFIL.js → chunk-SGSWVNNB.js} +7 -7
  37. package/dist/chunk-SYVZ4MD2.js +1 -0
  38. package/dist/{chunk-7LKAMLV4.js → chunk-T6SCOJF4.js} +4 -4
  39. package/dist/{chunk-CLWBNXKF.js → chunk-W2UIDI3R.js} +4 -4
  40. package/dist/{chunk-SNH35CNA.js → chunk-WKNOEVWQ.js} +6 -6
  41. package/dist/runtime/base-classes/index.js +17 -17
  42. package/dist/runtime/subsystems/auth/auth.module.js +2 -2
  43. package/dist/runtime/subsystems/auth/index.js +7 -7
  44. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js +3 -3
  45. package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +4 -4
  46. package/dist/runtime/subsystems/bridge/bridge-delivery.schema.js +2 -2
  47. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +8 -8
  48. package/dist/runtime/subsystems/bridge/bridge.module.js +21 -21
  49. package/dist/runtime/subsystems/bridge/event-flow.service.js +2 -2
  50. package/dist/runtime/subsystems/bridge/index.js +21 -21
  51. package/dist/runtime/subsystems/cache/cache.drizzle-backend.js +2 -2
  52. package/dist/runtime/subsystems/cache/cache.module.js +3 -3
  53. package/dist/runtime/subsystems/cache/index.js +5 -5
  54. package/dist/runtime/subsystems/events/domain-events.schema.js +1 -1
  55. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +19 -32
  56. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +4 -4
  57. package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +18 -1
  58. package/dist/runtime/subsystems/events/event-bus.memory-backend.js +2 -2
  59. package/dist/runtime/subsystems/events/event-bus.protocol.d.ts +45 -1
  60. package/dist/runtime/subsystems/events/event-scheduler.d.ts +96 -0
  61. package/dist/runtime/subsystems/events/event-scheduler.js +25 -0
  62. package/dist/runtime/subsystems/events/event-scheduler.js.map +1 -0
  63. package/dist/runtime/subsystems/events/events-errors.d.ts +12 -1
  64. package/dist/runtime/subsystems/events/events-errors.js +5 -3
  65. package/dist/runtime/subsystems/events/events.module.d.ts +41 -2
  66. package/dist/runtime/subsystems/events/events.module.js +12 -9
  67. package/dist/runtime/subsystems/events/generated/bus.js +3 -3
  68. package/dist/runtime/subsystems/events/generated/index.js +3 -3
  69. package/dist/runtime/subsystems/events/generated/registry.d.ts +6 -0
  70. package/dist/runtime/subsystems/events/generated/registry.js +1 -1
  71. package/dist/runtime/subsystems/events/index.d.ts +4 -3
  72. package/dist/runtime/subsystems/events/index.js +39 -15
  73. package/dist/runtime/subsystems/index.d.ts +1 -0
  74. package/dist/runtime/subsystems/index.js +93 -92
  75. package/dist/runtime/subsystems/integration/build-change-source.js +2 -2
  76. package/dist/runtime/subsystems/integration/index.js +36 -36
  77. package/dist/runtime/subsystems/integration/integration.module.js +4 -4
  78. package/dist/runtime/subsystems/jobs/index.js +46 -46
  79. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +8 -8
  80. package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.js +4 -4
  81. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js +2 -2
  82. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +3 -3
  83. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +3 -3
  84. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +3 -3
  85. package/dist/runtime/subsystems/jobs/job-worker.js +4 -4
  86. package/dist/runtime/subsystems/jobs/job-worker.module.js +13 -13
  87. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +11 -11
  88. package/dist/runtime/subsystems/observability/index.js +3 -3
  89. package/dist/runtime/subsystems/observability/observability.module.js +3 -3
  90. package/dist/runtime/subsystems/observability/observability.service.js +2 -2
  91. package/dist/src/cli/index.js +23 -12
  92. package/dist/src/cli/index.js.map +1 -1
  93. package/dist/src/index.js +12 -12
  94. package/package.json +1 -1
  95. package/runtime/subsystems/events/domain-events.schema.ts +16 -0
  96. package/runtime/subsystems/events/event-bus.drizzle-backend.ts +103 -1
  97. package/runtime/subsystems/events/event-bus.memory-backend.ts +57 -1
  98. package/runtime/subsystems/events/event-bus.protocol.ts +47 -0
  99. package/runtime/subsystems/events/event-scheduler.ts +351 -0
  100. package/runtime/subsystems/events/events-errors.ts +14 -0
  101. package/runtime/subsystems/events/events.module.ts +78 -1
  102. package/runtime/subsystems/events/generated/registry.ts +1 -0
  103. package/runtime/subsystems/events/index.ts +25 -3
  104. package/dist/chunk-B34G6PHD.js.map +0 -1
  105. package/dist/chunk-BGULBWKJ.js.map +0 -1
  106. package/dist/chunk-F7KN3U6U.js.map +0 -1
  107. package/dist/chunk-FN2PYDPP.js +0 -1
  108. package/dist/chunk-OFRRBC7M.js.map +0 -1
  109. package/dist/chunk-OZEPJGMA.js.map +0 -1
  110. package/dist/chunk-Z7PQCAVK.js.map +0 -1
  111. /package/dist/{chunk-COGHTKXY.js.map → chunk-27ETSJ2X.js.map} +0 -0
  112. /package/dist/{chunk-235ZMMJR.js.map → chunk-3YCUIGPG.js.map} +0 -0
  113. /package/dist/{chunk-VNBC3VXM.js.map → chunk-5LXOJGO2.js.map} +0 -0
  114. /package/dist/{chunk-7OVCARTQ.js.map → chunk-5RT7JGKT.js.map} +0 -0
  115. /package/dist/{chunk-T6C4LFLC.js.map → chunk-7YGORYZD.js.map} +0 -0
  116. /package/dist/{chunk-PKDS6QIJ.js.map → chunk-ATVGYF3D.js.map} +0 -0
  117. /package/dist/{chunk-2TVVBC53.js.map → chunk-BORNCTH3.js.map} +0 -0
  118. /package/dist/{chunk-E6PLM6QG.js.map → chunk-CUSMC2KK.js.map} +0 -0
  119. /package/dist/{chunk-XKWOJZZ4.js.map → chunk-E45CSC33.js.map} +0 -0
  120. /package/dist/{chunk-VQOAATIG.js.map → chunk-FLYF76CU.js.map} +0 -0
  121. /package/dist/{chunk-43SBT72G.js.map → chunk-I6UXRJ3Q.js.map} +0 -0
  122. /package/dist/{chunk-GM3RMJIJ.js.map → chunk-INO47JXD.js.map} +0 -0
  123. /package/dist/{chunk-VDL5CJ5C.js.map → chunk-KBO5OOON.js.map} +0 -0
  124. /package/dist/{chunk-65MO75WM.js.map → chunk-LQXBQO72.js.map} +0 -0
  125. /package/dist/{chunk-K2I6XIK5.js.map → chunk-MVKW2BCR.js.map} +0 -0
  126. /package/dist/{chunk-AZLUWG5S.js.map → chunk-NYJYK6J4.js.map} +0 -0
  127. /package/dist/{chunk-BHZP6LOV.js.map → chunk-QSJ3J4HE.js.map} +0 -0
  128. /package/dist/{chunk-R6F6KFIL.js.map → chunk-SGSWVNNB.js.map} +0 -0
  129. /package/dist/{chunk-FN2PYDPP.js.map → chunk-SYVZ4MD2.js.map} +0 -0
  130. /package/dist/{chunk-7LKAMLV4.js.map → chunk-T6SCOJF4.js.map} +0 -0
  131. /package/dist/{chunk-CLWBNXKF.js.map → chunk-W2UIDI3R.js.map} +0 -0
  132. /package/dist/{chunk-SNH35CNA.js.map → chunk-WKNOEVWQ.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,62 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.20.0] — 2026-06-06
8
+
9
+ **Declarative time-based scheduling: time as an event source** (ADR-039;
10
+ swe-brain consumer-test finding — an hourly reconcile poll had to be hand-rolled
11
+ as a self-perpetuating job chain because there was no time-based trigger, and
12
+ its flat dedupe key collapsed the chain into the running parent).
13
+
14
+ ### Added
15
+
16
+ - **`schedule:` on event YAML** (`definitions/events/<domain>/*.yaml`) —
17
+ `{ every, align?, catchUp?, maxCatchUpSlots? }`. Declares that the platform
18
+ emits this event on a cadence. `every` is a duration string (`'1h'`/`'30m'`/
19
+ `'15s'`/`'500ms'`/`'1d'`) or raw ms. Domain-tier only (audit rejected). Time
20
+ is a third event **source** (peer to use-case publishes + webhook receivers),
21
+ not a fourth activation tier — consumers react through ADR-023's existing
22
+ tiers (`subscribe` or `@JobHandler({ triggers })`); no new activation
23
+ mechanism. Carried into the generated `eventRegistry` + `EventMetadata`.
24
+ - **`EventScheduler`** (`runtime/subsystems/events/event-scheduler.ts`) — a
25
+ strict producer that materialises **exactly one `domain_events` row per
26
+ (type, slot)** on a cadence; the existing outbox drain + bridge activate them.
27
+ Reconcile-on-boot (materialise the current slot, or bounded `catchUp`
28
+ backfill) + a tick pass for the next slot. Wired by `EventsModule.forRoot`
29
+ for the drizzle + memory backends (the Redis bus retains no outbox history, so
30
+ slot idempotency can't be enforced there). Pure helpers exported:
31
+ `parseEvery`, `slotStartFor`, `nextSlotStart`, `slotKeyFor`,
32
+ `scheduledEventsFromRegistry`.
33
+ - **Partial UNIQUE expression index** `idx_domain_events_schedule_slot` on
34
+ `(type, metadata->>'scheduleSlot')` — the DB-level exactly-once-per-slot
35
+ invariant (no advisory lock, no leader election; multi-instance + boot/tick
36
+ races collapse on it). Partial on the slot key so ordinary events are
37
+ untouched. Additive Atlas migration.
38
+ - **`ScheduleConfigError`** + `IEventBus.materializeScheduledEvent` /
39
+ `lastScheduledSlotMs` (both backends).
40
+
41
+ ### Misfire policy
42
+
43
+ - Down across N slots → on recovery, materialise **one** tick for the current
44
+ slot (don't replay the misses). `catchUp: true` opts into bounded backfill.
45
+
46
+ ### Provenance
47
+
48
+ - A scheduled tick carries `metadata.triggerSource = 'schedule'` +
49
+ `metadata.scheduleSlot`. A bridge run from it reads `job_run.trigger_source =
50
+ 'event'`; the clock origin is joinable via `trigger_ref → domain_events.id`.
51
+ The dormant ADR-022 `triggerSource: 'schedule'` enum is the correct stamp for
52
+ the **direct-start** path (a Tier-1 subscriber calling `orchestrator.start(…,
53
+ { triggerSource: 'schedule' })`).
54
+
55
+ ### Retires
56
+
57
+ - The self-perpetuating job-chain pattern (a handler enqueuing its own
58
+ successor) and its slot-keyed-dedupe workaround — replaced by a `schedule:`
59
+ YAML + a `triggers:` entry. Supersedes the dangling "ADR-025 scheduling
60
+ territory" pointer in `docs/specs/BULLMQ-1.md` (the future BullMQ backend maps
61
+ `schedule:` onto `upsertJobScheduler`; the YAML contract is identical).
62
+
7
63
  ## [0.19.0] — 2026-06-05
8
64
 
9
65
  **Providers catalog emission + planned providers** (ADR-038 follow-on;
@@ -65,6 +65,7 @@ After editing any `events/*.yaml`, re-run codegen (e.g. `codegen entity new --al
65
65
  | `payload` | yes | Map of snake_case keys → field specs. Keys become camelCase TS props. |
66
66
  | `pool` | no | Override the direction's default pool. Only valid within the same category (a `change` event can't opt into `events_inbound`). User pools are never valid. Rarely needed — if you reach for it, revisit the direction. **Must be omitted for `tier: audit`.** |
67
67
  | `retry` | no | `{ attempts, backoff }`; hints surfaced to the bus. |
68
+ | `schedule` | no | `{ every, align?, catchUp?, maxCatchUpSlots? }` — the platform emits this event on a cadence (ADR-039). `every` is a duration (`'1h'`/`'30m'`/`'15s'`/`'500ms'`/`'1d'`) or raw ms. **Domain-tier only.** See §Scheduled events. |
68
69
  | `version` | no | Integer, defaults to 1. Stamped on every publish; multi-version coexistence is not exercised yet. |
69
70
 
70
71
  ### Payload field types
@@ -117,6 +118,36 @@ Codegen hard-errors on misuse:
117
118
  - `tier: audit` with a `direction` → error naming the event.
118
119
  - A job whose `@JobHandler.triggers` points at an audit event → `AuditEventTriggerError`. Use a domain event or remove the trigger.
119
120
 
121
+ ## Scheduled events (time as an event source)
122
+
123
+ Add a `schedule:` block and the platform emits this event on a cadence — you react with the **same** tiers you use for any event (a `subscribe(...)` or a `@JobHandler({ triggers })`). There is no separate "cron handler" or schedule decorator; time is just a third way an event gets produced.
124
+
125
+ ```yaml
126
+ # definitions/events/messaging/reconcile_due.yaml
127
+ type: reconcile_due
128
+ direction: inbound # scheduled events are domain-tier; route by direction
129
+ schedule:
130
+ every: 1h # '1h' | '30m' | '15s' | '500ms' | '1d' | raw ms
131
+ align: true # epoch-anchored boundaries (fires at :00) — default
132
+ # catchUp: false # down across N slots → run ONCE on recovery (default)
133
+ # maxCatchUpSlots: 1000 # bound when catchUp: true
134
+ payload: {} # scheduled events are payload-free facts
135
+ ```
136
+
137
+ ```ts
138
+ // React via Tier 3 (bridge) — the cadence is on the event, the reaction on the job.
139
+ @JobHandler<ReconcileInput>('reconcile-poll', {
140
+ pool: 'batch',
141
+ concurrency: { key: 'reconcile:{{provider}}', collisionMode: 'queue' }, // serialises overruns
142
+ triggers: [{ event: 'reconcile_due', map: () => ({ provider: 'slack', windowHours: 24 }) }],
143
+ })
144
+ export class ReconcilePollHandler extends JobHandlerBase<ReconcileInput, ReconcileOutput> { /* … */ }
145
+ ```
146
+
147
+ Behavior: the framework emits **exactly one** `domain_events` row per slot (idempotent across instances + restarts), runs **reconcile-on-boot** (one tick on startup — healing any downtime gap), and by default does **not** replay missed slots. **This replaces the old "self-perpetuating job chain"** (a handler enqueuing its own successor with a slot-keyed dedupe) — delete that pattern; declare `schedule:` instead.
148
+
149
+ Rules: domain-tier only; a malformed `every` fails `gen-validate`; available on the drizzle/memory backends. See ADR-039.
150
+
120
151
  ## Entity `events:` block is sugar for change events
121
152
 
122
153
  An entity's YAML may keep an `events:` block. At parse time each entry desugars into an equivalent `events/<name>.yaml` with `direction: change` and `aggregate: <entity>`. Both paths produce the same registry entry — inline blocks are convenience, not a second source of truth.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DrizzleCacheService
3
- } from "./chunk-T6C4LFLC.js";
3
+ } from "./chunk-7YGORYZD.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-COGHTKXY.js.map
84
+ //# sourceMappingURL=chunk-27ETSJ2X.js.map
@@ -1,13 +1,9 @@
1
1
  import {
2
2
  JobWorker
3
- } from "./chunk-VDL5CJ5C.js";
3
+ } from "./chunk-KBO5OOON.js";
4
4
  import {
5
5
  JobsDomainModule
6
- } from "./chunk-AZLUWG5S.js";
7
- import {
8
- BootValidationError,
9
- ReservedPoolViolationError
10
- } from "./chunk-T4BIIU5E.js";
6
+ } from "./chunk-NYJYK6J4.js";
11
7
  import {
12
8
  BULLMQ_CONNECTION,
13
9
  BULLMQ_RESOLVED_CONFIG,
@@ -18,14 +14,18 @@ import {
18
14
  allPoolNames,
19
15
  loadPoolConfig
20
16
  } from "./chunk-RHVN6NA7.js";
21
- import {
22
- HandlerRegistry
23
- } from "./chunk-7P5ODGLA.js";
24
17
  import {
25
18
  JOB_ORCHESTRATOR,
26
19
  JOB_RUN_SERVICE,
27
20
  JOB_STEP_SERVICE
28
21
  } from "./chunk-ZPL74UQN.js";
22
+ import {
23
+ BootValidationError,
24
+ ReservedPoolViolationError
25
+ } from "./chunk-T4BIIU5E.js";
26
+ import {
27
+ HandlerRegistry
28
+ } from "./chunk-7P5ODGLA.js";
29
29
  import {
30
30
  tokenKey
31
31
  } from "./chunk-GYGNEQSC.js";
@@ -290,4 +290,4 @@ export {
290
290
  JobWorkerOrchestrator,
291
291
  JobWorkerModule
292
292
  };
293
- //# sourceMappingURL=chunk-235ZMMJR.js.map
293
+ //# sourceMappingURL=chunk-3YCUIGPG.js.map
@@ -1,17 +1,18 @@
1
+ import {
2
+ EVENTS_MODULE_OPTIONS
3
+ } from "./chunk-H5NH7KPE.js";
1
4
  import {
2
5
  clampEventLimit,
3
6
  decodeEventCursor,
4
7
  encodeEventCursor
5
8
  } from "./chunk-UQ5EHOH2.js";
6
- import {
7
- EVENTS_MODULE_OPTIONS
8
- } from "./chunk-H5NH7KPE.js";
9
9
  import {
10
10
  __decorateClass,
11
11
  __decorateParam
12
12
  } from "./chunk-2E224ZSN.js";
13
13
 
14
14
  // runtime/subsystems/events/event-bus.memory-backend.ts
15
+ import { randomUUID } from "crypto";
15
16
  import { Inject, Injectable, Logger, Optional } from "@nestjs/common";
16
17
  function toEventSummary(event) {
17
18
  const metadata = event.metadata;
@@ -58,6 +59,51 @@ var MemoryEventBus = class {
58
59
  async findById(eventId) {
59
60
  return this.publishedEvents.find((e) => e.id === eventId) ?? null;
60
61
  }
62
+ // ============================================================================
63
+ // ADR-039 — scheduled-event materialisation (memory parity)
64
+ // ============================================================================
65
+ /** Slot keys already materialised — the in-memory mirror of the partial
66
+ * UNIQUE expression index `idx_domain_events_schedule_slot`. */
67
+ materialisedSlots = /* @__PURE__ */ new Set();
68
+ /**
69
+ * Mirror of the Drizzle `ON CONFLICT DO NOTHING` insert: emit one payload-free
70
+ * tick event per slot key, no-op if the slot was already materialised. The
71
+ * "constraint" is the `materialisedSlots` set. The tick is published through
72
+ * the normal `publish` path so subscribers fire synchronously (the memory bus
73
+ * has no future-slot/poll concept — a materialised slot dispatches now, which
74
+ * is the behaviour the unit suite pins).
75
+ */
76
+ async materializeScheduledEvent(spec) {
77
+ if (this.materialisedSlots.has(spec.slotKey)) return { created: false };
78
+ this.materialisedSlots.add(spec.slotKey);
79
+ const event = {
80
+ id: randomUUID(),
81
+ type: spec.type,
82
+ aggregateId: spec.type,
83
+ aggregateType: spec.type,
84
+ payload: {},
85
+ occurredAt: spec.slotStart,
86
+ metadata: {
87
+ pool: spec.pool,
88
+ direction: spec.direction,
89
+ scheduleSlot: spec.slotKey,
90
+ triggerSource: "schedule"
91
+ }
92
+ };
93
+ await this.publish(event);
94
+ return { created: true };
95
+ }
96
+ /** Most recent scheduled tick's `occurred_at` (epoch ms) for `type`, or null. */
97
+ async lastScheduledSlotMs(type) {
98
+ let best = null;
99
+ for (const e of this.publishedEvents) {
100
+ if (e.type !== type) continue;
101
+ if (e.metadata?.["triggerSource"] !== "schedule") continue;
102
+ const ms = e.occurredAt.getTime();
103
+ if (best === null || ms > best) best = ms;
104
+ }
105
+ return best;
106
+ }
61
107
  subscribe(eventType, handler) {
62
108
  if (!this.handlers.has(eventType)) {
63
109
  this.handlers.set(eventType, /* @__PURE__ */ new Set());
@@ -197,4 +243,4 @@ MemoryEventBus = __decorateClass([
197
243
  export {
198
244
  MemoryEventBus
199
245
  };
200
- //# sourceMappingURL=chunk-Z7PQCAVK.js.map
246
+ //# sourceMappingURL=chunk-4OC5MSHO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../runtime/subsystems/events/event-bus.memory-backend.ts"],"sourcesContent":["/**\n * MemoryEventBus — in-memory backend for the event bus.\n *\n * Dispatches events synchronously to registered subscribers. The `tx`\n * parameter is ignored — all events are dispatched immediately.\n *\n * Use this backend in tests to assert event publication without a database.\n * Swap via EventsModule.forRoot({ backend: 'memory' }).\n *\n * Pool awareness (EVT-5):\n * - Mirrors the `DrizzleEventBus` per-process restriction (EVT-4). When\n * `opts.pools` is set, `publish`/`publishMany` still push the event into\n * `publishedEvents` (so test code can assert the full set of emitted\n * events regardless of pool filter), but handlers are NOT invoked for\n * events whose `metadata.pool` is outside the configured pools.\n * - `publishedEventsForPool(pool)` and `publishedEventsForDirection(dir)`\n * helpers are provided for targeted assertions.\n * - Shares the `EventsModuleOptions` shape (same token as Drizzle) rather\n * than introducing a memory-only options type — the surface is the same\n * and keeping them unified avoids drift between backends.\n */\nimport { randomUUID } from 'node:crypto';\nimport { Inject, Injectable, Logger, Optional } from '@nestjs/common';\nimport type {\n DomainEvent,\n IEventBus,\n ScheduledEventSpec,\n} from './event-bus.protocol';\nimport type {\n EventPage,\n EventSummary,\n IEventReadPort,\n ListEventsQuery,\n} from './event-read.protocol';\nimport {\n clampEventLimit,\n decodeEventCursor,\n encodeEventCursor,\n} from './event-keyset-cursor';\nimport { EVENTS_MODULE_OPTIONS } from './events.tokens';\nimport type { EventsModuleOptions } from './events.module';\n\n/**\n * Project an in-memory `DomainEvent` into the narrow `EventSummary` shape.\n * The memory backend has no first-class columns, so `pool` / `direction` /\n * `tier` / `tenantId` / `rootRunId` are read from `metadata` (mirroring how\n * the Drizzle backend stamps them onto columns at publish time). `status`\n * is reported as `'processed'` — the memory bus dispatches synchronously,\n * so once an event is in `publishedEvents` it has been handled.\n */\nfunction toEventSummary(event: DomainEvent): EventSummary {\n const metadata = event.metadata;\n const str = (key: string): string | null => {\n const v = metadata?.[key];\n return typeof v === 'string' ? v : null;\n };\n return {\n id: event.id,\n type: event.type,\n aggregateId: event.aggregateId,\n aggregateType: event.aggregateType,\n status: 'processed',\n pool: str('pool'),\n direction: str('direction'),\n tier: str('tier') ?? 'domain',\n rootRunId: str('rootRunId'),\n tenantId: str('tenantId'),\n occurredAt: event.occurredAt,\n processedAt: event.occurredAt,\n };\n}\n\n@Injectable()\nexport class MemoryEventBus implements IEventBus, IEventReadPort {\n private readonly logger = new Logger(MemoryEventBus.name);\n\n /** All events published since construction (or last clear). */\n readonly publishedEvents: DomainEvent[] = [];\n\n private readonly handlers = new Map<string, Set<(event: DomainEvent) => Promise<void>>>();\n private readonly opts: EventsModuleOptions;\n\n constructor(\n @Optional() @Inject(EVENTS_MODULE_OPTIONS) opts?: EventsModuleOptions,\n ) {\n // Default so direct construction (e.g. `new MemoryEventBus()` from a\n // unit test outside NestJS DI) keeps working without an explicit\n // options object.\n this.opts = opts ?? { backend: 'memory' };\n }\n\n async publish(event: DomainEvent): Promise<void> {\n // Mirror the `domain_events_tier_routing_check` DB constraint at the\n // memory backend boundary so misuse fails the same way regardless of\n // backend (AUDIT-1).\n this.assertTierRouting(event);\n\n // Always record the event — even if this process is configured with a\n // pool filter that excludes it. Test code relies on `publishedEvents`\n // being a complete log of what was published, not a filtered view.\n this.publishedEvents.push(event);\n\n if (this.shouldDispatch(event)) {\n await this.dispatch(event);\n }\n }\n\n async publishMany(events: DomainEvent[]): Promise<void> {\n for (const event of events) {\n await this.publish(event);\n }\n }\n\n async findById(eventId: string): Promise<DomainEvent | null> {\n return this.publishedEvents.find((e) => e.id === eventId) ?? null;\n }\n\n // ============================================================================\n // ADR-039 — scheduled-event materialisation (memory parity)\n // ============================================================================\n\n /** Slot keys already materialised — the in-memory mirror of the partial\n * UNIQUE expression index `idx_domain_events_schedule_slot`. */\n private readonly materialisedSlots = new Set<string>();\n\n /**\n * Mirror of the Drizzle `ON CONFLICT DO NOTHING` insert: emit one payload-free\n * tick event per slot key, no-op if the slot was already materialised. The\n * \"constraint\" is the `materialisedSlots` set. The tick is published through\n * the normal `publish` path so subscribers fire synchronously (the memory bus\n * has no future-slot/poll concept — a materialised slot dispatches now, which\n * is the behaviour the unit suite pins).\n */\n async materializeScheduledEvent(\n spec: ScheduledEventSpec,\n ): Promise<{ created: boolean }> {\n if (this.materialisedSlots.has(spec.slotKey)) return { created: false };\n this.materialisedSlots.add(spec.slotKey);\n const event: DomainEvent = {\n id: randomUUID(),\n type: spec.type,\n aggregateId: spec.type,\n aggregateType: spec.type,\n payload: {},\n occurredAt: spec.slotStart,\n metadata: {\n pool: spec.pool,\n direction: spec.direction,\n scheduleSlot: spec.slotKey,\n triggerSource: 'schedule',\n },\n };\n await this.publish(event);\n return { created: true };\n }\n\n /** Most recent scheduled tick's `occurred_at` (epoch ms) for `type`, or null. */\n async lastScheduledSlotMs(type: string): Promise<number | null> {\n let best: number | null = null;\n for (const e of this.publishedEvents) {\n if (e.type !== type) continue;\n if (e.metadata?.['triggerSource'] !== 'schedule') continue;\n const ms = e.occurredAt.getTime();\n if (best === null || ms > best) best = ms;\n }\n return best;\n }\n\n subscribe<T extends DomainEvent = DomainEvent>(\n eventType: string,\n handler: (event: T) => Promise<void>,\n ): () => void {\n if (!this.handlers.has(eventType)) {\n this.handlers.set(eventType, new Set());\n }\n // Cast is safe — callers pass a typed handler; we store as the base type\n const set = this.handlers.get(eventType)!;\n const h = handler as (event: DomainEvent) => Promise<void>;\n set.add(h);\n\n return () => {\n set.delete(h);\n };\n }\n\n // ============================================================================\n // IEventReadPort (OBS-LIST-1)\n // ============================================================================\n\n async listEvents(query: ListEventsQuery = {}): Promise<EventPage> {\n const limit = clampEventLimit(query.limit);\n const keyset = query.cursor ? decodeEventCursor(query.cursor) : null;\n\n const str = (e: DomainEvent, key: string): string | null => {\n const v = e.metadata?.[key];\n return typeof v === 'string' ? v : null;\n };\n\n const matched = this.publishedEvents.filter((e) => {\n if (query.poolId && str(e, 'pool') !== query.poolId) return false;\n if (query.direction && str(e, 'direction') !== query.direction)\n return false;\n if (query.rootRunId && str(e, 'rootRunId') !== query.rootRunId)\n return false;\n if (query.since && e.occurredAt.getTime() < query.since.getTime())\n return false;\n if (query.tenantId !== undefined) {\n const t = str(e, 'tenantId');\n if (query.tenantId === null) {\n if (t !== null) return false;\n } else if (t !== query.tenantId) {\n return false;\n }\n }\n return true;\n });\n\n // Order occurred_at DESC, id DESC to match the Drizzle backend keyset.\n matched.sort((a, b) => {\n const dt = b.occurredAt.getTime() - a.occurredAt.getTime();\n if (dt !== 0) return dt;\n return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;\n });\n\n const seeked = keyset\n ? matched.filter((e) => {\n const ct = e.occurredAt.getTime();\n const kt = keyset.occurredAt.getTime();\n if (ct < kt) return true;\n if (ct > kt) return false;\n return e.id < keyset.id;\n })\n : matched;\n\n const hasMore = seeked.length > limit;\n const page = hasMore ? seeked.slice(0, limit) : seeked;\n const items = page.map(toEventSummary);\n const last = page[page.length - 1];\n const nextCursor =\n hasMore && last\n ? encodeEventCursor({ occurredAt: last.occurredAt, id: last.id })\n : null;\n\n return { items, nextCursor };\n }\n\n /** Remove all published events and subscriptions. Useful in beforeEach. */\n clear(): void {\n this.publishedEvents.length = 0;\n this.handlers.clear();\n }\n\n /** Filter published events by `metadata.pool`. */\n publishedEventsForPool(pool: string): DomainEvent[] {\n return this.publishedEvents.filter((e) => e.metadata?.['pool'] === pool);\n }\n\n /** Filter published events by `metadata.direction`. */\n publishedEventsForDirection(direction: string): DomainEvent[] {\n return this.publishedEvents.filter((e) => e.metadata?.['direction'] === direction);\n }\n\n /**\n * Decide whether `event` should be dispatched to handlers given the\n * current pool filter.\n *\n * Semantics (mirroring `DrizzleEventBus.processBatch`):\n * - `opts.pools` undefined → dispatch everything (no filter).\n * - `opts.pools` empty array → treated as \"no filter\" to match the\n * Drizzle backend, where `pools && pools.length > 0` is the gate on\n * the `inArray` WHERE clause. Empty arrays dispatch everything.\n * - `opts.pools` non-empty → dispatch only when `event.metadata.pool`\n * is in the list. Events without `metadata.pool` do NOT match — they\n * are out of all configured pools by definition.\n */\n private shouldDispatch(event: DomainEvent): boolean {\n const pools = this.opts.pools;\n if (!pools || pools.length === 0) return true;\n const eventPool = event.metadata?.['pool'];\n return typeof eventPool === 'string' && pools.includes(eventPool);\n }\n\n /**\n * Mirror the `domain_events_tier_routing_check` DB CHECK at the memory\n * backend (AUDIT-1). Audit-tier events MUST have null/undefined\n * `pool` and `direction` in metadata; the bridge dispatcher relies on\n * this invariant.\n */\n private assertTierRouting(event: DomainEvent): void {\n const tier = event.metadata?.['tier'];\n if (tier !== 'audit') return;\n const pool = event.metadata?.['pool'];\n const direction = event.metadata?.['direction'];\n const poolIsNull = pool === null || pool === undefined;\n const directionIsNull = direction === null || direction === undefined;\n if (!poolIsNull || !directionIsNull) {\n throw new Error(\n `MemoryEventBus: tier='audit' events must have null pool and direction ` +\n `(got pool=${String(pool)}, direction=${String(direction)}). ` +\n `This mirrors the domain_events CHECK constraint.`,\n );\n }\n }\n\n private async dispatch(event: DomainEvent): Promise<void> {\n const set = this.handlers.get(event.type);\n if (!set) return;\n\n let firstError: unknown;\n for (const handler of set) {\n try {\n await handler(event);\n } catch (err) {\n this.logger.error(\n `Handler error for event type \"${event.type}\" (id: ${event.id}): ${err}`,\n );\n if (firstError === undefined) {\n firstError = err;\n }\n }\n }\n\n if (firstError !== undefined) {\n throw firstError;\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,19 +1,19 @@
1
- import {
2
- MissingTenantIdError
3
- } from "./chunk-T4BIIU5E.js";
4
1
  import {
5
2
  clampLimit,
6
3
  decodeKeysetCursor,
7
4
  encodeKeysetCursor,
8
5
  toJobRunSummary
9
6
  } from "./chunk-L3LZWWSX.js";
7
+ import {
8
+ jobRuns
9
+ } from "./chunk-OKXZ63IA.js";
10
10
  import {
11
11
  JOBS_MULTI_TENANT,
12
12
  JOB_ORCHESTRATOR
13
13
  } from "./chunk-ZPL74UQN.js";
14
14
  import {
15
- jobRuns
16
- } from "./chunk-OKXZ63IA.js";
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-VNBC3VXM.js.map
201
+ //# sourceMappingURL=chunk-5LXOJGO2.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";
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-7OVCARTQ.js.map
109
+ //# sourceMappingURL=chunk-5RT7JGKT.js.map
@@ -1,9 +1,9 @@
1
- import {
2
- cacheEntries
3
- } from "./chunk-FASRXRX5.js";
4
1
  import {
5
2
  CACHE_DEFAULT_TTL
6
3
  } from "./chunk-L6FTY45T.js";
4
+ import {
5
+ cacheEntries
6
+ } from "./chunk-FASRXRX5.js";
7
7
  import {
8
8
  DRIZZLE
9
9
  } from "./chunk-U64T4YZE.js";
@@ -109,4 +109,4 @@ DrizzleCacheService = __decorateClass([
109
109
  export {
110
110
  DrizzleCacheService
111
111
  };
112
- //# sourceMappingURL=chunk-T6C4LFLC.js.map
112
+ //# sourceMappingURL=chunk-7YGORYZD.js.map
@@ -7,6 +7,12 @@ import {
7
7
  import {
8
8
  PostgresCursorStore
9
9
  } from "./chunk-XWBK3XJK.js";
10
+ import {
11
+ MemoryCursorStore
12
+ } from "./chunk-AHV4GDYM.js";
13
+ import {
14
+ DeepEqualDiffer
15
+ } from "./chunk-JEINYUJH.js";
10
16
  import {
11
17
  INTEGRATION_CURSOR_STORE,
12
18
  INTEGRATION_FIELD_DIFFER,
@@ -14,12 +20,6 @@ import {
14
20
  INTEGRATION_MULTI_TENANT,
15
21
  INTEGRATION_RUN_RECORDER
16
22
  } from "./chunk-S7C6TIIF.js";
17
- import {
18
- MemoryCursorStore
19
- } from "./chunk-AHV4GDYM.js";
20
- import {
21
- DeepEqualDiffer
22
- } from "./chunk-JEINYUJH.js";
23
23
  import {
24
24
  __decorateClass
25
25
  } from "./chunk-2E224ZSN.js";
@@ -84,4 +84,4 @@ IntegrationModule = __decorateClass([
84
84
  export {
85
85
  IntegrationModule
86
86
  };
87
- //# sourceMappingURL=chunk-PKDS6QIJ.js.map
87
+ //# sourceMappingURL=chunk-ATVGYF3D.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-OKXZ63IA.js";
4
4
  import {
5
5
  domainEvents
6
- } from "./chunk-OFRRBC7M.js";
6
+ } from "./chunk-E2BRT5IB.js";
7
7
 
8
8
  // runtime/subsystems/bridge/bridge-delivery.schema.ts
9
9
  import {
@@ -89,4 +89,4 @@ export {
89
89
  bridgeDeliveryStatusEnum,
90
90
  bridgeDelivery
91
91
  };
92
- //# sourceMappingURL=chunk-2TVVBC53.js.map
92
+ //# sourceMappingURL=chunk-BORNCTH3.js.map
@@ -1,3 +1,12 @@
1
+ import {
2
+ jobRuns,
3
+ jobSteps,
4
+ jobs
5
+ } from "./chunk-OKXZ63IA.js";
6
+ import {
7
+ JOBS_LISTEN_NOTIFY,
8
+ JOBS_MULTI_TENANT
9
+ } from "./chunk-ZPL74UQN.js";
1
10
  import {
2
11
  JobCollisionError,
3
12
  JobNotReplayableError,
@@ -5,23 +14,14 @@ import {
5
14
  JobTypeNotFoundError,
6
15
  MissingTenantIdError
7
16
  } from "./chunk-T4BIIU5E.js";
8
- import {
9
- JOBS_WAKE_CHANNEL,
10
- pgNotify
11
- } from "./chunk-Q6LRJ4VI.js";
12
17
  import {
13
18
  keySelectorToTemplate,
14
19
  resolveJobKey
15
20
  } from "./chunk-7P5ODGLA.js";
16
21
  import {
17
- JOBS_LISTEN_NOTIFY,
18
- JOBS_MULTI_TENANT
19
- } from "./chunk-ZPL74UQN.js";
20
- import {
21
- jobRuns,
22
- jobSteps,
23
- jobs
24
- } from "./chunk-OKXZ63IA.js";
22
+ JOBS_WAKE_CHANNEL,
23
+ pgNotify
24
+ } from "./chunk-Q6LRJ4VI.js";
25
25
  import {
26
26
  DRIZZLE
27
27
  } from "./chunk-U64T4YZE.js";
@@ -393,4 +393,4 @@ export {
393
393
  evaluateKeyTemplate,
394
394
  DrizzleJobOrchestrator
395
395
  };
396
- //# sourceMappingURL=chunk-E6PLM6QG.js.map
396
+ //# sourceMappingURL=chunk-CUSMC2KK.js.map
@@ -9,8 +9,15 @@ var MissingTenantIdError = class extends Error {
9
9
  eventType;
10
10
  name = "MissingTenantIdError";
11
11
  };
12
+ var ScheduleConfigError = class extends Error {
13
+ name = "ScheduleConfigError";
14
+ constructor(message) {
15
+ super(`ScheduleConfigError: ${message}`);
16
+ }
17
+ };
12
18
 
13
19
  export {
14
- MissingTenantIdError
20
+ MissingTenantIdError,
21
+ ScheduleConfigError
15
22
  };
16
- //# sourceMappingURL=chunk-V4AF6DI4.js.map
23
+ //# sourceMappingURL=chunk-DUBZOXJC.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../runtime/subsystems/events/events-errors.ts"],"sourcesContent":["/**\n * Typed errors for the events subsystem (ADR-024, EVT-6).\n *\n * All thrown from the publish path of `TypedEventBus`. They exist as\n * classes so consumers can `instanceof` them in catch blocks and\n * exception filters can map them to HTTP codes.\n */\n\n/**\n * Thrown by `TypedEventBus.publish()` when the EventsModule is configured\n * with `multiTenant: true` and the caller did not supply\n * `opts.metadata.tenantId`. Multi-tenant mode requires every outbox row to\n * be attributable to a tenant — the `domain_events.tenant_id` column is\n * populated from this value and the drain loop uses it for future\n * tenant-scoped filtering (deferred — see ADR-024 §Multi-tenancy).\n *\n * Disable multi-tenancy at the module level (`multiTenant: false`, the\n * default) to opt out of the requirement entirely.\n */\nexport class MissingTenantIdError extends Error {\n override readonly name = 'MissingTenantIdError';\n constructor(public readonly eventType: string) {\n super(\n `Missing tenantId for event '${eventType}'. EventsModule is configured ` +\n `with multiTenant: true — every publish must include ` +\n `opts.metadata.tenantId. Either pass the tenantId or disable ` +\n `multi-tenancy on the module.`,\n );\n }\n}\n"],"mappings":";AAmBO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAE9C,YAA4B,WAAmB;AAC7C;AAAA,MACE,+BAA+B,SAAS;AAAA,IAI1C;AAN0B;AAAA,EAO5B;AAAA,EAP4B;AAAA,EADV,OAAO;AAS3B;","names":[]}
1
+ {"version":3,"sources":["../runtime/subsystems/events/events-errors.ts"],"sourcesContent":["/**\n * Typed errors for the events subsystem (ADR-024, EVT-6).\n *\n * All thrown from the publish path of `TypedEventBus`. They exist as\n * classes so consumers can `instanceof` them in catch blocks and\n * exception filters can map them to HTTP codes.\n */\n\n/**\n * Thrown by `TypedEventBus.publish()` when the EventsModule is configured\n * with `multiTenant: true` and the caller did not supply\n * `opts.metadata.tenantId`. Multi-tenant mode requires every outbox row to\n * be attributable to a tenant — the `domain_events.tenant_id` column is\n * populated from this value and the drain loop uses it for future\n * tenant-scoped filtering (deferred — see ADR-024 §Multi-tenancy).\n *\n * Disable multi-tenancy at the module level (`multiTenant: false`, the\n * default) to opt out of the requirement entirely.\n */\nexport class MissingTenantIdError extends Error {\n override readonly name = 'MissingTenantIdError';\n constructor(public readonly eventType: string) {\n super(\n `Missing tenantId for event '${eventType}'. EventsModule is configured ` +\n `with multiTenant: true — every publish must include ` +\n `opts.metadata.tenantId. Either pass the tenantId or disable ` +\n `multi-tenancy on the module.`,\n );\n }\n}\n\n/**\n * Thrown (ADR-039) when a scheduled event's `schedule` config is invalid at\n * runtime — a malformed `schedule.every` duration, or a scheduled event with no\n * direction/pool to route by. Codegen validates `schedule` at `gen-all` time;\n * this is the boot backstop for a hand-edited registry or version skew. Raised\n * by `EventScheduler` construction / `parseEvery`.\n */\nexport class ScheduleConfigError extends Error {\n override readonly name = 'ScheduleConfigError';\n constructor(message: string) {\n super(`ScheduleConfigError: ${message}`);\n }\n}\n"],"mappings":";AAmBO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAE9C,YAA4B,WAAmB;AAC7C;AAAA,MACE,+BAA+B,SAAS;AAAA,IAI1C;AAN0B;AAAA,EAO5B;AAAA,EAP4B;AAAA,EADV,OAAO;AAS3B;AASO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC3B,OAAO;AAAA,EACzB,YAAY,SAAiB;AAC3B,UAAM,wBAAwB,OAAO,EAAE;AAAA,EACzC;AACF;","names":[]}