@pattern-stack/codegen 0.14.1 → 0.15.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pattern-stack/codegen",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Entity-driven code generation for full-stack TypeScript applications",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -70,6 +70,7 @@ import { BridgeOutboxDrainHook } from './bridge-outbox-drain-hook';
70
70
  import { EventFlowService } from './event-flow.service';
71
71
  import { BridgeDeliveryHandler } from './bridge-delivery-handler';
72
72
  import { bridgeRegistry } from './generated/registry';
73
+ import type { BridgeRegistry } from './bridge.protocol';
73
74
  import { BRIDGE_RESERVED_POOLS } from './reserved-pools';
74
75
 
75
76
  export interface BridgeModuleOptions {
@@ -87,6 +88,26 @@ export interface BridgeModuleOptions {
87
88
  * `null` always passes (cross-tenant work). Defaults to `false`.
88
89
  */
89
90
  multiTenant?: boolean;
91
+ /**
92
+ * The codegen-emitted `Record<EventTypeName, BridgeTriggerEntry[]>` that
93
+ * drives outbox-drain trigger lookup (BRIDGE-4) and the facade's Case B
94
+ * dedup (BRIDGE-7).
95
+ *
96
+ * **Package mode (ADR-037).** When the runtime is imported from
97
+ * `@pattern-stack/codegen` (not vendored), the bundled
98
+ * `./generated/registry` is a frozen empty placeholder (`{}`) — a
99
+ * consumer's `@JobHandler.triggers` are scanned into a registry that lives
100
+ * in THEIR `src/generated/bridge-registry.ts`, which the package can't
101
+ * import. The generated subsystem barrel therefore threads that registry in
102
+ * here: `BridgeModule.forRoot({ ..., registry: bridgeRegistry })`. Omitted
103
+ * (vendored mode / tests) ⇒ falls back to the bundled `./generated/registry`,
104
+ * which in vendored mode IS the consumer's freshly-generated file.
105
+ *
106
+ * Without this, package-mode consumers' triggers never bind and the bridge
107
+ * routes nothing (the "wrappers sit pending" footgun's silent twin —
108
+ * nothing is ever enqueued in the first place).
109
+ */
110
+ registry?: BridgeRegistry;
90
111
  }
91
112
 
92
113
  @Module({})
@@ -108,7 +129,11 @@ export class BridgeModule implements OnModuleInit {
108
129
  providers: [
109
130
  { provide: BRIDGE_MODULE_OPTIONS, useValue: opts },
110
131
  { provide: BRIDGE_MULTI_TENANT, useValue: opts.multiTenant ?? false },
111
- { provide: BRIDGE_REGISTRY, useValue: bridgeRegistry },
132
+ // Package mode threads the consumer's generated registry through
133
+ // `opts.registry`; vendored mode omits it and we fall back to the
134
+ // bundled `./generated/registry` (which IS the consumer's generated
135
+ // file in a vendored tree). See `BridgeModuleOptions.registry`.
136
+ { provide: BRIDGE_REGISTRY, useValue: opts.registry ?? bridgeRegistry },
112
137
  repoProvider,
113
138
  // Drain hook — always wired; `DrizzleEventBus` consumes it via
114
139
  // `@Optional()`, so non-bridge mounts simply see `undefined`.
@@ -44,7 +44,7 @@
44
44
  * `new Class()` which silently left `db` / `redisUrl` undefined
45
45
  * (issue #108).
46
46
  */
47
- import { Module, type DynamicModule, type Provider } from '@nestjs/common';
47
+ import { Module, type DynamicModule, type Provider, type Type } from '@nestjs/common';
48
48
  import {
49
49
  EVENT_BUS,
50
50
  EVENT_READ_PORT,
@@ -112,6 +112,31 @@ export interface EventsModuleOptions {
112
112
  * Defaults to `false`.
113
113
  */
114
114
  multiTenant?: boolean;
115
+ /**
116
+ * The generated `TypedEventBus` class to bind to `TYPED_EVENT_BUS`.
117
+ *
118
+ * **Package mode (ADR-037).** When the runtime is imported from
119
+ * `@pattern-stack/codegen` (not vendored), the bundled `./generated/bus`
120
+ * `TypedEventBus` is typed to an EMPTY event union and reads the bundled
121
+ * empty `eventRegistry` — a consumer's `events/*.yaml` are scanned into
122
+ * `src/generated/events/bus.ts` (typed to THEIR union, reading THEIR
123
+ * registry), which the package can't import. The generated subsystem barrel
124
+ * therefore threads that class in here:
125
+ * `EventsModule.forRoot({ ..., typedBus: TypedEventBus })`. Nest constructs
126
+ * it with this module's `EVENT_BUS` + `EVENTS_MULTI_TENANT` providers (the
127
+ * generated class injects the same string-valued tokens, which match across
128
+ * the package boundary).
129
+ *
130
+ * Omitted (vendored mode / tests) ⇒ falls back to the bundled
131
+ * `./generated/bus`, which in a vendored tree IS the consumer's generated
132
+ * file. Without this, a package-mode consumer's typed `publish<'…'>()` calls
133
+ * resolve against the empty union and their events never get the right
134
+ * `pool` / `direction` stamped.
135
+ *
136
+ * Only consulted by `forRoot` (the path the barrel emits); `forRootAsync`
137
+ * keeps the bundled bus.
138
+ */
139
+ typedBus?: Type<unknown>;
115
140
  }
116
141
 
117
142
  export interface EventsModuleAsyncOptions {
@@ -125,10 +150,18 @@ export interface EventsModuleAsyncOptions {
125
150
  * binding, and the resolved `EVENTS_MULTI_TENANT` flag. Returned from one
126
151
  * place so every `forRoot` branch and `forRootAsync` agree.
127
152
  */
128
- function buildTypedBusProviders(multiTenant: boolean): Provider[] {
153
+ function buildTypedBusProviders(
154
+ multiTenant: boolean,
155
+ typedBus?: Type<unknown>,
156
+ ): Provider[] {
157
+ // Package mode threads the consumer's generated `TypedEventBus` (typed to
158
+ // their event union, reading their registry) via `typedBus`; vendored mode
159
+ // omits it and we fall back to the bundled `./generated/bus` (which IS the
160
+ // consumer's generated file in a vendored tree). See `EventsModuleOptions.typedBus`.
161
+ const BusClass = typedBus ?? TypedEventBus;
129
162
  return [
130
- TypedEventBus,
131
- { provide: TYPED_EVENT_BUS, useExisting: TypedEventBus },
163
+ BusClass,
164
+ { provide: TYPED_EVENT_BUS, useExisting: BusClass },
132
165
  { provide: EVENTS_MULTI_TENANT, useValue: multiTenant },
133
166
  ];
134
167
  }
@@ -249,7 +282,7 @@ export class EventsModule {
249
282
  },
250
283
  inject: [REDIS_URL],
251
284
  },
252
- ...buildTypedBusProviders(multiTenant),
285
+ ...buildTypedBusProviders(multiTenant, options.typedBus),
253
286
  ],
254
287
  exports: [EVENT_BUS, TYPED_EVENT_BUS, EVENTS_MULTI_TENANT],
255
288
  };
@@ -270,7 +303,7 @@ export class EventsModule {
270
303
  // IEventReadPort on the same instance as EVENT_BUS. The redis
271
304
  // backend retains no history and does not provide this token.
272
305
  { provide: EVENT_READ_PORT, useExisting: EVENT_BUS },
273
- ...buildTypedBusProviders(multiTenant),
306
+ ...buildTypedBusProviders(multiTenant, options.typedBus),
274
307
  ],
275
308
  exports: [EVENT_BUS, EVENT_READ_PORT, TYPED_EVENT_BUS, EVENTS_MULTI_TENANT],
276
309
  };