@pattern-stack/codegen 0.18.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 (129) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +51 -0
  3. package/consumer-skills/codegen/SKILL.md +32 -0
  4. package/consumer-skills/entities/SKILL.md +2 -0
  5. package/consumer-skills/events/authoring-events.md +31 -0
  6. package/dist/{chunk-COGHTKXY.js → chunk-27ETSJ2X.js} +2 -2
  7. package/dist/{chunk-X6BP6LI5.js → chunk-3YCUIGPG.js} +10 -10
  8. package/dist/{chunk-BK5ICA2F.js → chunk-4MVGAMUA.js} +4 -4
  9. package/dist/{chunk-LQ6PYFU6.js → chunk-4OC5MSHO.js} +47 -1
  10. package/dist/chunk-4OC5MSHO.js.map +1 -0
  11. package/dist/{chunk-3A34R6CI.js → chunk-5LXOJGO2.js} +4 -4
  12. package/dist/{chunk-T6C4LFLC.js → chunk-7YGORYZD.js} +4 -4
  13. package/dist/{chunk-PKDS6QIJ.js → chunk-ATVGYF3D.js} +7 -7
  14. package/dist/{chunk-2TVVBC53.js → chunk-BORNCTH3.js} +2 -2
  15. package/dist/{chunk-7MMS36AN.js → chunk-CUSMC2KK.js} +10 -10
  16. package/dist/{chunk-V4AF6DI4.js → chunk-DUBZOXJC.js} +9 -2
  17. package/dist/{chunk-V4AF6DI4.js.map → chunk-DUBZOXJC.js.map} +1 -1
  18. package/dist/chunk-DUUCU77W.js +211 -0
  19. package/dist/chunk-DUUCU77W.js.map +1 -0
  20. package/dist/{chunk-OFRRBC7M.js → chunk-E2BRT5IB.js} +15 -1
  21. package/dist/chunk-E2BRT5IB.js.map +1 -0
  22. package/dist/{chunk-VQOAATIG.js → chunk-FLYF76CU.js} +4 -4
  23. package/dist/{chunk-43SBT72G.js → chunk-I6UXRJ3Q.js} +4 -4
  24. package/dist/{chunk-VHAR2BGH.js → chunk-INO47JXD.js} +5 -5
  25. package/dist/{chunk-BGULBWKJ.js → chunk-JOBQ6RUU.js} +1 -1
  26. package/dist/chunk-JOBQ6RUU.js.map +1 -0
  27. package/dist/{chunk-C5E7H553.js → chunk-KBO5OOON.js} +4 -4
  28. package/dist/{chunk-R4BPUUB5.js → chunk-KHQ72A5F.js} +54 -6
  29. package/dist/chunk-KHQ72A5F.js.map +1 -0
  30. package/dist/{chunk-CFFTPWHM.js → chunk-KK5A7B2T.js} +70 -5
  31. package/dist/chunk-KK5A7B2T.js.map +1 -0
  32. package/dist/{chunk-RKNW56RU.js → chunk-LARB26EI.js} +73 -6
  33. package/dist/chunk-LARB26EI.js.map +1 -0
  34. package/dist/{chunk-2VGVSL2D.js → chunk-LQXBQO72.js} +6 -6
  35. package/dist/{chunk-K2I6XIK5.js → chunk-MVKW2BCR.js} +2 -2
  36. package/dist/{chunk-EWYI5GGJ.js → chunk-NYJYK6J4.js} +15 -15
  37. package/dist/{chunk-IN3EWFB4.js → chunk-QSJ3J4HE.js} +6 -6
  38. package/dist/{chunk-TBGTMALE.js → chunk-SGSWVNNB.js} +4 -4
  39. package/dist/chunk-SYVZ4MD2.js +1 -0
  40. package/dist/{chunk-NXHL5YII.js → chunk-T6SCOJF4.js} +4 -4
  41. package/dist/{chunk-YZLBU6O2.js → chunk-WKNOEVWQ.js} +5 -5
  42. package/dist/runtime/base-classes/index.js +17 -17
  43. package/dist/runtime/subsystems/auth/auth.module.js +3 -3
  44. package/dist/runtime/subsystems/auth/index.js +10 -10
  45. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js +2 -2
  46. package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +3 -3
  47. package/dist/runtime/subsystems/bridge/bridge-delivery.schema.js +2 -2
  48. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +6 -6
  49. package/dist/runtime/subsystems/bridge/bridge.module.js +18 -18
  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 +3 -3
  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 +1 -1
  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 +11 -8
  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 +38 -14
  73. package/dist/runtime/subsystems/index.d.ts +1 -0
  74. package/dist/runtime/subsystems/index.js +117 -116
  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 +33 -33
  79. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +5 -5
  80. package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.js +3 -3
  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 +2 -2
  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 +2 -2
  86. package/dist/runtime/subsystems/jobs/job-worker.module.js +11 -11
  87. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +9 -9
  88. package/dist/runtime/subsystems/storage/index.js +4 -4
  89. package/dist/runtime/subsystems/storage/storage.module.js +2 -2
  90. package/dist/src/cli/index.js +168 -22
  91. package/dist/src/cli/index.js.map +1 -1
  92. package/dist/src/index.js +12 -12
  93. package/package.json +1 -1
  94. package/runtime/subsystems/events/domain-events.schema.ts +16 -0
  95. package/runtime/subsystems/events/event-bus.drizzle-backend.ts +103 -1
  96. package/runtime/subsystems/events/event-bus.memory-backend.ts +57 -1
  97. package/runtime/subsystems/events/event-bus.protocol.ts +47 -0
  98. package/runtime/subsystems/events/event-scheduler.ts +351 -0
  99. package/runtime/subsystems/events/events-errors.ts +14 -0
  100. package/runtime/subsystems/events/events.module.ts +78 -1
  101. package/runtime/subsystems/events/generated/registry.ts +1 -0
  102. package/runtime/subsystems/events/index.ts +25 -3
  103. package/dist/chunk-BGULBWKJ.js.map +0 -1
  104. package/dist/chunk-CFFTPWHM.js.map +0 -1
  105. package/dist/chunk-FN2PYDPP.js +0 -1
  106. package/dist/chunk-LQ6PYFU6.js.map +0 -1
  107. package/dist/chunk-OFRRBC7M.js.map +0 -1
  108. package/dist/chunk-R4BPUUB5.js.map +0 -1
  109. package/dist/chunk-RKNW56RU.js.map +0 -1
  110. /package/dist/{chunk-COGHTKXY.js.map → chunk-27ETSJ2X.js.map} +0 -0
  111. /package/dist/{chunk-X6BP6LI5.js.map → chunk-3YCUIGPG.js.map} +0 -0
  112. /package/dist/{chunk-BK5ICA2F.js.map → chunk-4MVGAMUA.js.map} +0 -0
  113. /package/dist/{chunk-3A34R6CI.js.map → chunk-5LXOJGO2.js.map} +0 -0
  114. /package/dist/{chunk-T6C4LFLC.js.map → chunk-7YGORYZD.js.map} +0 -0
  115. /package/dist/{chunk-PKDS6QIJ.js.map → chunk-ATVGYF3D.js.map} +0 -0
  116. /package/dist/{chunk-2TVVBC53.js.map → chunk-BORNCTH3.js.map} +0 -0
  117. /package/dist/{chunk-7MMS36AN.js.map → chunk-CUSMC2KK.js.map} +0 -0
  118. /package/dist/{chunk-VQOAATIG.js.map → chunk-FLYF76CU.js.map} +0 -0
  119. /package/dist/{chunk-43SBT72G.js.map → chunk-I6UXRJ3Q.js.map} +0 -0
  120. /package/dist/{chunk-VHAR2BGH.js.map → chunk-INO47JXD.js.map} +0 -0
  121. /package/dist/{chunk-C5E7H553.js.map → chunk-KBO5OOON.js.map} +0 -0
  122. /package/dist/{chunk-2VGVSL2D.js.map → chunk-LQXBQO72.js.map} +0 -0
  123. /package/dist/{chunk-K2I6XIK5.js.map → chunk-MVKW2BCR.js.map} +0 -0
  124. /package/dist/{chunk-EWYI5GGJ.js.map → chunk-NYJYK6J4.js.map} +0 -0
  125. /package/dist/{chunk-IN3EWFB4.js.map → chunk-QSJ3J4HE.js.map} +0 -0
  126. /package/dist/{chunk-TBGTMALE.js.map → chunk-SGSWVNNB.js.map} +0 -0
  127. /package/dist/{chunk-FN2PYDPP.js.map → chunk-SYVZ4MD2.js.map} +0 -0
  128. /package/dist/{chunk-NXHL5YII.js.map → chunk-T6SCOJF4.js.map} +0 -0
  129. /package/dist/{chunk-YZLBU6O2.js.map → chunk-WKNOEVWQ.js.map} +0 -0
@@ -1,37 +1,37 @@
1
1
  import {
2
2
  BridgeModule
3
- } from "../../../chunk-YZLBU6O2.js";
3
+ } from "../../../chunk-WKNOEVWQ.js";
4
4
  import "../../../chunk-5A432NZJ.js";
5
- import "../../../chunk-2VGVSL2D.js";
5
+ import "../../../chunk-LQXBQO72.js";
6
6
  import "../../../chunk-5RT7JGKT.js";
7
7
  import "../../../chunk-EDKJU5BO.js";
8
- import "../../../chunk-TBGTMALE.js";
9
- import "../../../chunk-K2I6XIK5.js";
8
+ import "../../../chunk-SGSWVNNB.js";
9
+ import "../../../chunk-MVKW2BCR.js";
10
10
  import "../../../chunk-6DWFJNIK.js";
11
11
  import "../../../chunk-4DOJBQTP.js";
12
- import "../../../chunk-2TVVBC53.js";
13
12
  import "../../../chunk-NXXDZ6ZF.js";
14
- import "../../../chunk-X6BP6LI5.js";
15
- import "../../../chunk-C5E7H553.js";
16
- import "../../../chunk-EWYI5GGJ.js";
17
- import "../../../chunk-VQOAATIG.js";
18
- import "../../../chunk-PNZSGAB2.js";
19
- import "../../../chunk-3A34R6CI.js";
20
- import "../../../chunk-IN3EWFB4.js";
21
- import "../../../chunk-SNQ3TOWP.js";
13
+ import "../../../chunk-BORNCTH3.js";
14
+ import "../../../chunk-3YCUIGPG.js";
15
+ import "../../../chunk-KBO5OOON.js";
16
+ import "../../../chunk-NYJYK6J4.js";
17
+ import "../../../chunk-5LXOJGO2.js";
18
+ import "../../../chunk-QSJ3J4HE.js";
22
19
  import "../../../chunk-L3LZWWSX.js";
23
20
  import "../../../chunk-DV4RV2DC.js";
24
- import "../../../chunk-7MMS36AN.js";
25
- import "../../../chunk-T4BIIU5E.js";
26
21
  import "../../../chunk-I6MVCB5A.js";
27
22
  import "../../../chunk-RHVN6NA7.js";
28
- import "../../../chunk-7P5ODGLA.js";
23
+ import "../../../chunk-CUSMC2KK.js";
29
24
  import "../../../chunk-OKXZ63IA.js";
25
+ import "../../../chunk-FLYF76CU.js";
26
+ import "../../../chunk-PNZSGAB2.js";
27
+ import "../../../chunk-SNQ3TOWP.js";
30
28
  import "../../../chunk-ZPL74UQN.js";
31
- import "../../../chunk-Q6LRJ4VI.js";
29
+ import "../../../chunk-T4BIIU5E.js";
30
+ import "../../../chunk-7P5ODGLA.js";
32
31
  import "../../../chunk-4LH67P4U.js";
32
+ import "../../../chunk-Q6LRJ4VI.js";
33
+ import "../../../chunk-E2BRT5IB.js";
33
34
  import "../../../chunk-H5NH7KPE.js";
34
- import "../../../chunk-OFRRBC7M.js";
35
35
  import "../../../chunk-GYGNEQSC.js";
36
36
  import "../../../chunk-U64T4YZE.js";
37
37
  import "../../../chunk-2E224ZSN.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  BridgeModule
3
- } from "../../../chunk-YZLBU6O2.js";
3
+ } from "../../../chunk-WKNOEVWQ.js";
4
4
  import "../../../chunk-5A432NZJ.js";
5
5
  import {
6
6
  BridgeOutboxDrainHook
7
- } from "../../../chunk-2VGVSL2D.js";
7
+ } from "../../../chunk-LQXBQO72.js";
8
8
  import {
9
9
  EventFlowService
10
10
  } from "../../../chunk-5RT7JGKT.js";
@@ -14,43 +14,42 @@ import {
14
14
  import {
15
15
  BRIDGE_DELIVERY_JOB_TYPE,
16
16
  BridgeDeliveryHandler
17
- } from "../../../chunk-TBGTMALE.js";
17
+ } from "../../../chunk-SGSWVNNB.js";
18
18
  import {
19
19
  DrizzleBridgeDeliveryRepo
20
- } from "../../../chunk-K2I6XIK5.js";
20
+ } from "../../../chunk-MVKW2BCR.js";
21
21
  import {
22
22
  assertTenantId
23
23
  } from "../../../chunk-6DWFJNIK.js";
24
24
  import {
25
25
  MemoryBridgeDeliveryRepo
26
26
  } from "../../../chunk-4DOJBQTP.js";
27
- import {
28
- bridgeDelivery,
29
- bridgeDeliveryStatusEnum
30
- } from "../../../chunk-2TVVBC53.js";
31
27
  import {
32
28
  BridgeReservedPoolsNotPolledError,
33
29
  MissingTenantIdError,
34
30
  UniqueConstraintError
35
31
  } from "../../../chunk-NXXDZ6ZF.js";
36
- import "../../../chunk-X6BP6LI5.js";
37
- import "../../../chunk-C5E7H553.js";
38
- import "../../../chunk-EWYI5GGJ.js";
39
- import "../../../chunk-VQOAATIG.js";
40
- import "../../../chunk-PNZSGAB2.js";
41
- import "../../../chunk-3A34R6CI.js";
42
- import "../../../chunk-IN3EWFB4.js";
43
- import "../../../chunk-SNQ3TOWP.js";
32
+ import {
33
+ bridgeDelivery,
34
+ bridgeDeliveryStatusEnum
35
+ } from "../../../chunk-BORNCTH3.js";
36
+ import "../../../chunk-3YCUIGPG.js";
37
+ import "../../../chunk-KBO5OOON.js";
38
+ import "../../../chunk-NYJYK6J4.js";
39
+ import "../../../chunk-5LXOJGO2.js";
40
+ import "../../../chunk-QSJ3J4HE.js";
44
41
  import "../../../chunk-L3LZWWSX.js";
45
42
  import "../../../chunk-DV4RV2DC.js";
46
- import "../../../chunk-7MMS36AN.js";
47
- import "../../../chunk-T4BIIU5E.js";
48
43
  import "../../../chunk-I6MVCB5A.js";
49
44
  import "../../../chunk-RHVN6NA7.js";
50
- import "../../../chunk-7P5ODGLA.js";
45
+ import "../../../chunk-CUSMC2KK.js";
51
46
  import "../../../chunk-OKXZ63IA.js";
47
+ import "../../../chunk-FLYF76CU.js";
48
+ import "../../../chunk-PNZSGAB2.js";
49
+ import "../../../chunk-SNQ3TOWP.js";
52
50
  import "../../../chunk-ZPL74UQN.js";
53
- import "../../../chunk-Q6LRJ4VI.js";
51
+ import "../../../chunk-T4BIIU5E.js";
52
+ import "../../../chunk-7P5ODGLA.js";
54
53
  import {
55
54
  BRIDGE_DELIVERY_REPO,
56
55
  BRIDGE_MODULE_OPTIONS,
@@ -59,8 +58,9 @@ import {
59
58
  BRIDGE_REGISTRY,
60
59
  EVENT_FLOW
61
60
  } from "../../../chunk-4LH67P4U.js";
61
+ import "../../../chunk-Q6LRJ4VI.js";
62
+ import "../../../chunk-E2BRT5IB.js";
62
63
  import "../../../chunk-H5NH7KPE.js";
63
- import "../../../chunk-OFRRBC7M.js";
64
64
  import "../../../chunk-GYGNEQSC.js";
65
65
  import "../../../chunk-U64T4YZE.js";
66
66
  import "../../../chunk-2E224ZSN.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  DrizzleCacheService
3
- } from "../../../chunk-T6C4LFLC.js";
4
- import "../../../chunk-FASRXRX5.js";
3
+ } from "../../../chunk-7YGORYZD.js";
5
4
  import {
6
5
  CACHE_DEFAULT_TTL
7
6
  } from "../../../chunk-L6FTY45T.js";
7
+ import "../../../chunk-FASRXRX5.js";
8
8
  import "../../../chunk-GYGNEQSC.js";
9
9
  import "../../../chunk-U64T4YZE.js";
10
10
  import "../../../chunk-2E224ZSN.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  CacheModule
3
- } from "../../../chunk-COGHTKXY.js";
4
- import "../../../chunk-T6C4LFLC.js";
3
+ } from "../../../chunk-27ETSJ2X.js";
4
+ import "../../../chunk-7YGORYZD.js";
5
5
  import "../../../chunk-IF5I3DAA.js";
6
- import "../../../chunk-FASRXRX5.js";
7
6
  import "../../../chunk-L6FTY45T.js";
7
+ import "../../../chunk-FASRXRX5.js";
8
8
  import "../../../chunk-GYGNEQSC.js";
9
9
  import "../../../chunk-U64T4YZE.js";
10
10
  import "../../../chunk-2E224ZSN.js";
@@ -1,20 +1,20 @@
1
1
  import "../../../chunk-IWAOY6KC.js";
2
2
  import {
3
3
  CacheModule
4
- } from "../../../chunk-COGHTKXY.js";
4
+ } from "../../../chunk-27ETSJ2X.js";
5
5
  import {
6
6
  DrizzleCacheService
7
- } from "../../../chunk-T6C4LFLC.js";
7
+ } from "../../../chunk-7YGORYZD.js";
8
8
  import {
9
9
  MemoryCacheService
10
10
  } from "../../../chunk-IF5I3DAA.js";
11
- import {
12
- cacheEntries
13
- } from "../../../chunk-FASRXRX5.js";
14
11
  import {
15
12
  CACHE,
16
13
  CACHE_DEFAULT_TTL
17
14
  } from "../../../chunk-L6FTY45T.js";
15
+ import {
16
+ cacheEntries
17
+ } from "../../../chunk-FASRXRX5.js";
18
18
  import "../../../chunk-GYGNEQSC.js";
19
19
  import "../../../chunk-U64T4YZE.js";
20
20
  import "../../../chunk-2E224ZSN.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  domainEvents
3
- } from "../../../chunk-OFRRBC7M.js";
3
+ } from "../../../chunk-E2BRT5IB.js";
4
4
  import "../../../chunk-2E224ZSN.js";
5
5
  export {
6
6
  domainEvents
@@ -1,46 +1,16 @@
1
1
  import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
2
- import { IEventBus, DomainEvent, DrizzleTransaction } from './event-bus.protocol.js';
2
+ import { IEventBus, DomainEvent, DrizzleTransaction, ScheduledEventSpec } from './event-bus.protocol.js';
3
3
  import { IEventReadPort, ListEventsQuery, EventPage } from './event-read.protocol.js';
4
4
  import { DrizzleClient } from '../../types/drizzle.js';
5
5
  import { EventsModuleOptions } from './events.module.js';
6
6
  import { IBridgeOutboxDrainHook } from '../bridge/bridge.protocol.js';
7
7
  import 'drizzle-orm/node-postgres';
8
+ import './event-scheduler.js';
8
9
  import 'drizzle-orm';
9
10
  import './event-registry.js';
10
11
  import '../bridge/bridge-delivery.schema.js';
11
12
  import 'drizzle-orm/pg-core';
12
13
 
13
- /**
14
- * DrizzleEventBus — Postgres-backed event bus using the transactional outbox pattern.
15
- *
16
- * Events are inserted into the `domain_events` table within the caller's
17
- * Drizzle transaction. A background polling loop (started on module init)
18
- * reads unprocessed events and dispatches them to registered subscribers.
19
- *
20
- * When the transaction rolls back, the event is never persisted — no
21
- * phantom events.
22
- *
23
- * Pool awareness (EVT-4):
24
- * - On `publish`/`publishMany` the backend writes `metadata.pool`,
25
- * `metadata.direction`, and `metadata.tenantId` into the first-class
26
- * `pool` / `direction` / `tenant_id` columns (metadata JSON is still
27
- * written unchanged for protocol stability).
28
- * - The drain loop filters by `opts.pools` when provided, so separate
29
- * processes (e.g. one per `events_inbound` / `events_change` /
30
- * `events_outbound`) can claim only their own lane. `pools: undefined`
31
- * drains all pending rows (backwards-compatible behaviour).
32
- *
33
- * EVT-Q7: No stale-event sweeper. `FOR UPDATE SKIP LOCKED` is
34
- * self-healing — the row is only locked for the duration of the
35
- * enclosing polling transaction; the `status='processed'` update happens
36
- * within that same transaction. There is no `claimed_at` semantic (unlike
37
- * jobs), so no stale rows can exist.
38
- *
39
- * This backend is suitable until you need real-time fan-out or very high
40
- * throughput. At that point, swap the backend for Redis Streams or similar
41
- * via EventsModule.forRoot({ backend: '...' }) without touching use cases.
42
- */
43
-
44
14
  declare class DrizzleEventBus implements IEventBus, IEventReadPort, OnModuleInit, OnModuleDestroy {
45
15
  private readonly db;
46
16
  /**
@@ -99,6 +69,23 @@ declare class DrizzleEventBus implements IEventBus, IEventReadPort, OnModuleInit
99
69
  private emitWakeNotify;
100
70
  findById(eventId: string): Promise<DomainEvent | null>;
101
71
  subscribe<T extends DomainEvent = DomainEvent>(eventType: string, handler: (event: T) => Promise<void>): () => void;
72
+ /**
73
+ * Insert one scheduled tick event idempotently. The slot key is stamped onto
74
+ * `metadata.scheduleSlot`; `ON CONFLICT DO NOTHING` against the partial UNIQUE
75
+ * expression index `idx_domain_events_schedule_slot` makes a duplicate insert
76
+ * a no-op — the DB constraint is the exactly-one-event-per-slot invariant.
77
+ *
78
+ * Reuses the standard outbox row shape (pool/direction/metadata) so the
79
+ * existing drain carries the tick like any other event. A LISTEN/NOTIFY wake
80
+ * fires for an immediately-due tick (boot/catch-up rows whose slot is already
81
+ * in the past); a future slot is claimed by polling once `occurred_at` passes.
82
+ */
83
+ materializeScheduledEvent(spec: ScheduledEventSpec): Promise<{
84
+ created: boolean;
85
+ }>;
86
+ /** Most recent scheduled tick's `occurred_at` (epoch ms) for `type`, or null.
87
+ * Read by the scheduler's catch-up backfill. */
88
+ lastScheduledSlotMs(type: string): Promise<number | null>;
102
89
  listEvents(query?: ListEventsQuery): Promise<EventPage>;
103
90
  /**
104
91
  * Test-only hook. Runs exactly one drain cycle and returns. Production
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  DrizzleEventBus
3
- } from "../../../chunk-RKNW56RU.js";
4
- import "../../../chunk-Q6LRJ4VI.js";
3
+ } from "../../../chunk-LARB26EI.js";
5
4
  import "../../../chunk-4LH67P4U.js";
5
+ import "../../../chunk-Q6LRJ4VI.js";
6
+ import "../../../chunk-E2BRT5IB.js";
6
7
  import "../../../chunk-H5NH7KPE.js";
7
8
  import "../../../chunk-UQ5EHOH2.js";
8
- import "../../../chunk-OFRRBC7M.js";
9
9
  import "../../../chunk-GYGNEQSC.js";
10
10
  import "../../../chunk-U64T4YZE.js";
11
11
  import "../../../chunk-2E224ZSN.js";
@@ -1,9 +1,10 @@
1
- import { IEventBus, DomainEvent } from './event-bus.protocol.js';
1
+ import { IEventBus, DomainEvent, ScheduledEventSpec } from './event-bus.protocol.js';
2
2
  import { IEventReadPort, ListEventsQuery, EventPage } from './event-read.protocol.js';
3
3
  import { EventsModuleOptions } from './events.module.js';
4
4
  import '../../types/drizzle.js';
5
5
  import 'drizzle-orm/node-postgres';
6
6
  import '@nestjs/common';
7
+ import './event-scheduler.js';
7
8
 
8
9
  declare class MemoryEventBus implements IEventBus, IEventReadPort {
9
10
  private readonly logger;
@@ -15,6 +16,22 @@ declare class MemoryEventBus implements IEventBus, IEventReadPort {
15
16
  publish(event: DomainEvent): Promise<void>;
16
17
  publishMany(events: DomainEvent[]): Promise<void>;
17
18
  findById(eventId: string): Promise<DomainEvent | null>;
19
+ /** Slot keys already materialised — the in-memory mirror of the partial
20
+ * UNIQUE expression index `idx_domain_events_schedule_slot`. */
21
+ private readonly materialisedSlots;
22
+ /**
23
+ * Mirror of the Drizzle `ON CONFLICT DO NOTHING` insert: emit one payload-free
24
+ * tick event per slot key, no-op if the slot was already materialised. The
25
+ * "constraint" is the `materialisedSlots` set. The tick is published through
26
+ * the normal `publish` path so subscribers fire synchronously (the memory bus
27
+ * has no future-slot/poll concept — a materialised slot dispatches now, which
28
+ * is the behaviour the unit suite pins).
29
+ */
30
+ materializeScheduledEvent(spec: ScheduledEventSpec): Promise<{
31
+ created: boolean;
32
+ }>;
33
+ /** Most recent scheduled tick's `occurred_at` (epoch ms) for `type`, or null. */
34
+ lastScheduledSlotMs(type: string): Promise<number | null>;
18
35
  subscribe<T extends DomainEvent = DomainEvent>(eventType: string, handler: (event: T) => Promise<void>): () => void;
19
36
  listEvents(query?: ListEventsQuery): Promise<EventPage>;
20
37
  /** Remove all published events and subscriptions. Useful in beforeEach. */
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MemoryEventBus
3
- } from "../../../chunk-LQ6PYFU6.js";
3
+ } from "../../../chunk-4OC5MSHO.js";
4
4
  import "../../../chunk-H5NH7KPE.js";
5
5
  import "../../../chunk-UQ5EHOH2.js";
6
6
  import "../../../chunk-GYGNEQSC.js";
@@ -65,6 +65,50 @@ interface IEventBus {
65
65
  * of Redis backend is unsupported.
66
66
  */
67
67
  findById(eventId: string): Promise<DomainEvent | null>;
68
+ /**
69
+ * Materialise exactly one scheduled tick event (ADR-039 — time as an event
70
+ * source). Called by the framework `EventScheduler` on its boot + tick passes.
71
+ *
72
+ * The `slotKey` (`@schedule/<type>/<slotStartMs>`) is a pure function of
73
+ * (type, slot) — every instance computes the same key — and is stamped onto
74
+ * `metadata.scheduleSlot` (+ `metadata.triggerSource='schedule'`). The insert
75
+ * is deterministic and idempotent:
76
+ * - Drizzle: `INSERT … ON CONFLICT DO NOTHING` against the partial UNIQUE
77
+ * expression index on `(type, metadata->>'scheduleSlot')`. The DB
78
+ * constraint — not a read, not a lock — is the exactly-one-event-per-slot
79
+ * invariant across multi-instance deploys and boot/tick races.
80
+ * - Memory: a slot-key marker set mirrors the constraint.
81
+ *
82
+ * Returns `created: false` when the slot event already existed (the no-op
83
+ * case). Optional on the protocol — only the scheduler calls it, and the
84
+ * Redis backend (no outbox history) does not implement it (the scheduler is
85
+ * drizzle/memory only).
86
+ */
87
+ materializeScheduledEvent?(spec: ScheduledEventSpec): Promise<{
88
+ created: boolean;
89
+ }>;
90
+ /**
91
+ * Optional (ADR-039) — `occurred_at` (epoch ms) of the most recent scheduled
92
+ * tick for `type`, or `null`. Read by the scheduler's catch-up backfill as
93
+ * `MAX(occurred_at) WHERE type=? AND metadata->>'triggerSource'='schedule'`.
94
+ */
95
+ lastScheduledSlotMs?(type: string): Promise<number | null>;
96
+ }
97
+ /**
98
+ * The fully-resolved shape the scheduler hands a backend to materialise one
99
+ * tick. Carries the routing fields the outbox row needs (direction/pool from
100
+ * the event's registry metadata) plus the slot key + boundary.
101
+ */
102
+ interface ScheduledEventSpec {
103
+ type: string;
104
+ /** `@schedule/<type>/<slotStartMs>` — the idempotency key. */
105
+ slotKey: string;
106
+ /** Slot boundary → the event's `occurred_at`. */
107
+ slotStart: Date;
108
+ /** `inbound | change | outbound` from the event's registry metadata. */
109
+ direction: string;
110
+ /** `events_inbound | events_change | events_outbound` from the registry. */
111
+ pool: string;
68
112
  }
69
113
 
70
- export type { DomainEvent, DrizzleTransaction, IEventBus };
114
+ export type { DomainEvent, DrizzleTransaction, IEventBus, ScheduledEventSpec };
@@ -0,0 +1,96 @@
1
+ import { IEventBus } from './event-bus.protocol.js';
2
+ import '../../types/drizzle.js';
3
+ import 'drizzle-orm/node-postgres';
4
+
5
+ /**
6
+ * Parse a `schedule.every` into milliseconds. Accepts a positive number (ms) or
7
+ * a duration string `<number><unit>` (unit ∈ ms|s|m|h|d; decimals allowed).
8
+ * Throws `ScheduleConfigError` synchronously on anything unparseable, ≤0, or
9
+ * non-finite — so a bad schedule surfaces at boot before the tick loop starts.
10
+ */
11
+ declare function parseEvery(every: string | number, eventType?: string): number;
12
+ /**
13
+ * The start of the slot containing `atMs`, for a schedule of `everyMs`.
14
+ * - `align: true` (default) — epoch-anchored: `floor(at / every) * every`.
15
+ * - `align: false` — anchored to `anchorMs` (the scheduler's first-run time).
16
+ */
17
+ declare function slotStartFor(atMs: number, everyMs: number, align: boolean, anchorMs: number): number;
18
+ /** The start of the slot AFTER the one containing `atMs`. */
19
+ declare function nextSlotStart(atMs: number, everyMs: number, align: boolean, anchorMs: number): number;
20
+ /** Prefix every scheduler-materialised `metadata.scheduleSlot` carries — the
21
+ * partial UNIQUE index is scoped to non-null slot keys; this prefix keeps the
22
+ * key namespace unambiguous and greppable. */
23
+ declare const SCHEDULE_KEY_PREFIX = "@schedule/";
24
+ /** Deterministic slot key. Pure function of (type, slotStart) — every instance
25
+ * computes the same value, which is what makes the idempotent insert
26
+ * exactly-once. */
27
+ declare function slotKeyFor(type: string, slotStartMs: number): string;
28
+ /** Below this floor (== the default outbox poll interval) materialise/drain
29
+ * latency dominates the cadence; allowed but warned once at boot. */
30
+ declare const SCHEDULE_FLOOR_MS = 1000;
31
+ /** One scheduled event the scheduler will materialise. Built from the generated
32
+ * event registry (`schedule` block + direction/pool routing metadata). */
33
+ interface ScheduledEvent {
34
+ type: string;
35
+ everyMs: number;
36
+ align: boolean;
37
+ catchUp: boolean;
38
+ maxCatchUpSlots: number;
39
+ /** Routing — from the event's registry metadata (a scheduled event is
40
+ * domain-tier, so both are always present). */
41
+ direction: string;
42
+ pool: string;
43
+ }
44
+ /** The raw `schedule` block as it appears in the generated registry entry. */
45
+ interface RegistrySchedule {
46
+ every: string | number;
47
+ align?: boolean;
48
+ catchUp?: boolean;
49
+ maxCatchUpSlots?: number;
50
+ }
51
+ /** Validate + normalise one registry entry's `schedule` into a `ScheduledEvent`.
52
+ * Throws `ScheduleConfigError` on a malformed `every` (boot backstop — codegen
53
+ * already validated, this catches hand-edits / version skew). */
54
+ declare function resolveScheduledEvent(type: string, schedule: RegistrySchedule, direction: string | null, pool: string | null): ScheduledEvent;
55
+ /**
56
+ * Read the scheduled-event set from a generated `eventRegistry`. The registry
57
+ * value shape is structural (`{ schedule?, direction, pool }`) so this stays
58
+ * decoupled from the generated `EventMetadata` type. Returns `[]` when nothing
59
+ * declared `schedule:`.
60
+ */
61
+ declare function scheduledEventsFromRegistry(registry: Record<string, {
62
+ schedule?: RegistrySchedule;
63
+ direction: string | null;
64
+ pool: string | null;
65
+ }>): ScheduledEvent[];
66
+ interface EventSchedulerOptions {
67
+ /** Tick cadence (ms). Default = smallest scheduled `every`, floored. Test override. */
68
+ tickIntervalMs?: number;
69
+ /** Injectable clock for deterministic tests. Default `Date.now`. */
70
+ now?: () => number;
71
+ }
72
+ declare class EventScheduler {
73
+ private readonly bus;
74
+ private readonly schedules;
75
+ private readonly logger;
76
+ private readonly now;
77
+ private timer;
78
+ private readonly anchorMs;
79
+ private readonly tickIntervalMs;
80
+ constructor(bus: IEventBus, schedules: ReadonlyArray<ScheduledEvent>, opts?: EventSchedulerOptions);
81
+ /** Reconcile-on-boot, then start the tick interval. Idempotent. */
82
+ start(): Promise<void>;
83
+ /** Stop the tick interval. Idempotent. */
84
+ stop(): void;
85
+ /** Boot pass — materialise the current slot (or bounded backfill) per event. */
86
+ materializeBoot(): Promise<void>;
87
+ /** Tick pass — materialise the current + next slot per event (current covers a
88
+ * tick landing in a fresh slot the boot pass missed). */
89
+ materializeTick(): Promise<void>;
90
+ private materializeOne;
91
+ /** Backfill missed slots from the last emitted slot to the current one,
92
+ * bounded by `maxCatchUpSlots`. */
93
+ private backfill;
94
+ }
95
+
96
+ export { EventScheduler, type EventSchedulerOptions, type RegistrySchedule, SCHEDULE_FLOOR_MS, SCHEDULE_KEY_PREFIX, type ScheduledEvent, nextSlotStart, parseEvery, resolveScheduledEvent, scheduledEventsFromRegistry, slotKeyFor, slotStartFor };
@@ -0,0 +1,25 @@
1
+ import {
2
+ EventScheduler,
3
+ SCHEDULE_FLOOR_MS,
4
+ SCHEDULE_KEY_PREFIX,
5
+ nextSlotStart,
6
+ parseEvery,
7
+ resolveScheduledEvent,
8
+ scheduledEventsFromRegistry,
9
+ slotKeyFor,
10
+ slotStartFor
11
+ } from "../../../chunk-DUUCU77W.js";
12
+ import "../../../chunk-DUBZOXJC.js";
13
+ import "../../../chunk-2E224ZSN.js";
14
+ export {
15
+ EventScheduler,
16
+ SCHEDULE_FLOOR_MS,
17
+ SCHEDULE_KEY_PREFIX,
18
+ nextSlotStart,
19
+ parseEvery,
20
+ resolveScheduledEvent,
21
+ scheduledEventsFromRegistry,
22
+ slotKeyFor,
23
+ slotStartFor
24
+ };
25
+ //# sourceMappingURL=event-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -21,5 +21,16 @@ declare class MissingTenantIdError extends Error {
21
21
  readonly name = "MissingTenantIdError";
22
22
  constructor(eventType: string);
23
23
  }
24
+ /**
25
+ * Thrown (ADR-039) when a scheduled event's `schedule` config is invalid at
26
+ * runtime — a malformed `schedule.every` duration, or a scheduled event with no
27
+ * direction/pool to route by. Codegen validates `schedule` at `gen-all` time;
28
+ * this is the boot backstop for a hand-edited registry or version skew. Raised
29
+ * by `EventScheduler` construction / `parseEvery`.
30
+ */
31
+ declare class ScheduleConfigError extends Error {
32
+ readonly name = "ScheduleConfigError";
33
+ constructor(message: string);
34
+ }
24
35
 
25
- export { MissingTenantIdError };
36
+ export { MissingTenantIdError, ScheduleConfigError };
@@ -1,8 +1,10 @@
1
1
  import {
2
- MissingTenantIdError
3
- } from "../../../chunk-V4AF6DI4.js";
2
+ MissingTenantIdError,
3
+ ScheduleConfigError
4
+ } from "../../../chunk-DUBZOXJC.js";
4
5
  import "../../../chunk-2E224ZSN.js";
5
6
  export {
6
- MissingTenantIdError
7
+ MissingTenantIdError,
8
+ ScheduleConfigError
7
9
  };
8
10
  //# sourceMappingURL=events-errors.js.map
@@ -1,4 +1,8 @@
1
- import { Type, DynamicModule } from '@nestjs/common';
1
+ import { Type, OnModuleInit, OnModuleDestroy, DynamicModule } from '@nestjs/common';
2
+ import { IEventBus } from './event-bus.protocol.js';
3
+ import { RegistrySchedule } from './event-scheduler.js';
4
+ import '../../types/drizzle.js';
5
+ import 'drizzle-orm/node-postgres';
2
6
 
3
7
  /**
4
8
  * EventsModule — DynamicModule factory for the event bus subsystem.
@@ -120,6 +124,41 @@ interface EventsModuleOptions {
120
124
  * keeps the bundled bus.
121
125
  */
122
126
  typedBus?: Type<unknown>;
127
+ /**
128
+ * ADR-039 — the consumer's generated `eventRegistry`, threaded so the
129
+ * `EventScheduler` can read the `schedule:` block + routing metadata of every
130
+ * scheduled event. Package mode: the generated subsystem barrel passes the
131
+ * consumer's `eventRegistry` (the bundled one is the package's empty/fixture
132
+ * registry, which the package can't see the consumer's events through — same
133
+ * reason `typedBus` is threaded). Omitted ⇒ no scheduler is spawned (vendored
134
+ * tree reads its own bundled registry only if the barrel passes it; tests
135
+ * pass a registry directly).
136
+ *
137
+ * Structural shape: each value needs `schedule?` + `direction` + `pool`. The
138
+ * generated `EventMetadata` satisfies it; typing it loosely here avoids a
139
+ * runtime dependency on the generated types from the module file.
140
+ */
141
+ eventRegistry?: Record<string, {
142
+ schedule?: RegistrySchedule;
143
+ direction: string | null;
144
+ pool: string | null;
145
+ }>;
146
+ }
147
+ /**
148
+ * Lifecycle holder for the `EventScheduler` (ADR-039). Registered as a provider
149
+ * on the drizzle/memory `forRoot` branches; Nest drives `onModuleInit` (after
150
+ * the bus is constructed) and `onModuleDestroy`. Reads the scheduled-event set
151
+ * from the threaded `eventRegistry` and starts the materialiser. No-op when
152
+ * nothing declared `schedule:`, or under the redis backend (no outbox history).
153
+ */
154
+ declare class EventSchedulerLifecycle implements OnModuleInit, OnModuleDestroy {
155
+ private readonly bus;
156
+ private readonly opts;
157
+ private readonly logger;
158
+ private scheduler;
159
+ constructor(bus: IEventBus, opts?: EventsModuleOptions | null);
160
+ onModuleInit(): Promise<void>;
161
+ onModuleDestroy(): Promise<void>;
123
162
  }
124
163
  interface EventsModuleAsyncOptions {
125
164
  useFactory: (...args: unknown[]) => Promise<EventsModuleOptions> | EventsModuleOptions;
@@ -131,4 +170,4 @@ declare class EventsModule {
131
170
  static forRoot(options?: EventsModuleOptions): DynamicModule;
132
171
  }
133
172
 
134
- export { EventsModule, type EventsModuleAsyncOptions, type EventsModuleOptions };
173
+ export { EventSchedulerLifecycle, EventsModule, type EventsModuleAsyncOptions, type EventsModuleOptions };
@@ -1,21 +1,24 @@
1
1
  import {
2
+ EventSchedulerLifecycle,
2
3
  EventsModule
3
- } from "../../../chunk-R4BPUUB5.js";
4
- import "../../../chunk-VHAR2BGH.js";
5
- import "../../../chunk-BGULBWKJ.js";
4
+ } from "../../../chunk-KHQ72A5F.js";
5
+ import "../../../chunk-INO47JXD.js";
6
+ import "../../../chunk-JOBQ6RUU.js";
6
7
  import "../../../chunk-JRQO2IOF.js";
7
- import "../../../chunk-RKNW56RU.js";
8
- import "../../../chunk-Q6LRJ4VI.js";
8
+ import "../../../chunk-DUUCU77W.js";
9
+ import "../../../chunk-DUBZOXJC.js";
10
+ import "../../../chunk-LARB26EI.js";
9
11
  import "../../../chunk-4LH67P4U.js";
10
- import "../../../chunk-LQ6PYFU6.js";
12
+ import "../../../chunk-Q6LRJ4VI.js";
13
+ import "../../../chunk-E2BRT5IB.js";
14
+ import "../../../chunk-4OC5MSHO.js";
11
15
  import "../../../chunk-H5NH7KPE.js";
12
16
  import "../../../chunk-UQ5EHOH2.js";
13
- import "../../../chunk-V4AF6DI4.js";
14
- import "../../../chunk-OFRRBC7M.js";
15
17
  import "../../../chunk-GYGNEQSC.js";
16
18
  import "../../../chunk-U64T4YZE.js";
17
19
  import "../../../chunk-2E224ZSN.js";
18
20
  export {
21
+ EventSchedulerLifecycle,
19
22
  EventsModule
20
23
  };
21
24
  //# sourceMappingURL=events.module.js.map