@relaybus/nestjs 0.2.0 → 0.3.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/dist/index.d.ts CHANGED
@@ -1,17 +1,17 @@
1
- import { Catalog, SubscribeOptions, EventOf, HandlerContext, CatalogEventSelection, CatalogEntry, RelayBus, RelayBusOptions, EventStoreStatistics, EventClass, EventEnvelope } from '@relaybus/core';
1
+ import { Catalog, SubscribeOptions, EventOf, HandlerContext, CatalogEventSelection, CatalogEntry, RelayBus, RelayBusOptions, EventStoreStatistics, CatalogDeclaration, EventClass, EventEnvelope } from '@relaybus/core';
2
2
  export { RelayBus } from '@relaybus/core';
3
3
  import * as _nestjs_common from '@nestjs/common';
4
- import { OnModuleInit, OnApplicationBootstrap, BeforeApplicationShutdown } from '@nestjs/common';
5
- import { DiscoveryService, MetadataScanner } from '@nestjs/core';
4
+ import { OnModuleInit, OnApplicationBootstrap, BeforeApplicationShutdown, OnApplicationShutdown, DynamicModule } from '@nestjs/common';
5
+ import { DiscoveryService, MetadataScanner, ModulesContainer } from '@nestjs/core';
6
6
 
7
7
  /**
8
- * Options of {@link EventConsumer}: the core {@link SubscribeOptions} applied to
8
+ * Options of {@link RelayBusCatalogConsumer}: the core {@link SubscribeOptions} applied to
9
9
  * every handler of the class, except `id`, which is always
10
10
  * `${ClassName}.on${EventName}`, and optionally the events the class consumes.
11
11
  */
12
- type EventConsumerOptions<TCatalog extends Catalog = Catalog, TName extends keyof TCatalog = keyof TCatalog> = Omit<SubscribeOptions, 'id'> & CatalogEventSelection<TName>;
13
- /** What one `@EventConsumer` decorator recorded on a class. */
14
- interface EventConsumerMetadata {
12
+ type RelayBusCatalogConsumerOptions<TCatalog extends Catalog = Catalog, TName extends keyof TCatalog = keyof TCatalog> = Omit<SubscribeOptions, 'id'> & CatalogEventSelection<TName>;
13
+ /** What one `@RelayBusCatalogConsumer` decorator recorded on a class. */
14
+ interface RelayBusCatalogConsumerMetadata {
15
15
  readonly catalog: Catalog;
16
16
  /** Subscribe options applied to every handler of the class. */
17
17
  readonly options: Omit<SubscribeOptions, 'id'>;
@@ -26,7 +26,7 @@ interface EventConsumerMetadata {
26
26
  * events. The event parameter is checked strictly; the context is typed
27
27
  * `HandlerContext<never>` so a method may declare any transaction type.
28
28
  */
29
- type EventConsumerClass<TCatalog extends Catalog, TName extends keyof TCatalog> = abstract new (...args: never[]) => {
29
+ type RelayBusCatalogConsumerClass<TCatalog extends Catalog, TName extends keyof TCatalog> = abstract new (...args: never[]) => {
30
30
  readonly [TKey in TName & string as `on${TKey}`]: (event: EventOf<TCatalog[TKey]>, context: HandlerContext<never>) => unknown;
31
31
  };
32
32
  /**
@@ -45,35 +45,46 @@ type EventConsumerClass<TCatalog extends Catalog, TName extends keyof TCatalog>
45
45
  *
46
46
  * `catalogs: [consume(Catalog, { events })]` in the module options makes the
47
47
  * application fail to boot if no provider handles a consumed event. Works next
48
- * to `@Subscribe` in the same class (give a method that carries both a
49
- * distinct `@Subscribe` id). The provider must be a singleton. Declarations
48
+ * to `@RelayBusConsumer` in the same class (give a method that carries both a
49
+ * distinct `@RelayBusConsumer` id). The provider must be a singleton. Declarations
50
50
  * are inherited; a subclass that declares the same catalog again replaces the
51
51
  * inherited declaration of that catalog, and declaring one catalog twice on
52
52
  * the same class throws a {@link ConfigurationError}.
53
53
  *
54
54
  * ```ts
55
55
  * @Injectable()
56
- * @EventConsumer(OrderEvents, { events: ['OrderPlaced'], transactional: true })
56
+ * @RelayBusCatalogConsumer(OrderEvents, { events: ['OrderPlaced'], transactional: true })
57
57
  * export class BillingHandlers {
58
58
  * onOrderPlaced(event: EventOf<typeof OrderPlaced>) {}
59
59
  * }
60
60
  * ```
61
61
  */
62
- declare function EventConsumer<TCatalog extends Catalog>(catalog: TCatalog, options?: Omit<SubscribeOptions, 'id'> & {
62
+ declare function RelayBusCatalogConsumer<TCatalog extends Catalog>(catalog: TCatalog, options?: Omit<SubscribeOptions, 'id'> & {
63
63
  readonly events?: undefined;
64
64
  }): ClassDecorator;
65
- declare function EventConsumer<TCatalog extends Catalog, const TName extends keyof TCatalog>(catalog: TCatalog, options: Omit<SubscribeOptions, 'id'> & {
65
+ declare function RelayBusCatalogConsumer<TCatalog extends Catalog, const TName extends keyof TCatalog>(catalog: TCatalog, options: Omit<SubscribeOptions, 'id'> & {
66
66
  readonly events: readonly TName[];
67
- }): (target: EventConsumerClass<TCatalog, TName>) => void;
67
+ }): (target: RelayBusCatalogConsumerClass<TCatalog, TName>) => void;
68
68
  /**
69
69
  * With options whose `events` are not statically known (a variable typed
70
- * {@link EventConsumerOptions}), the selection is checked when the module
70
+ * {@link RelayBusCatalogConsumerOptions}), the selection is checked when the module
71
71
  * initialises instead of at compile time.
72
72
  */
73
- declare function EventConsumer<TCatalog extends Catalog>(catalog: TCatalog, options: EventConsumerOptions<TCatalog>): ClassDecorator;
73
+ declare function RelayBusCatalogConsumer<TCatalog extends Catalog>(catalog: TCatalog, options: RelayBusCatalogConsumerOptions<TCatalog>): ClassDecorator;
74
+ /**
75
+ * @deprecated Renamed to {@link RelayBusCatalogConsumer}, which behaves identically; this alias is
76
+ * removed in 1.0.0.
77
+ */
78
+ declare const EventConsumer: typeof RelayBusCatalogConsumer;
79
+ /** @deprecated Renamed to {@link RelayBusCatalogConsumerOptions}; this alias is removed in 1.0.0. */
80
+ type EventConsumerOptions<TCatalog extends Catalog = Catalog, TName extends keyof TCatalog = keyof TCatalog> = RelayBusCatalogConsumerOptions<TCatalog, TName>;
81
+ /** @deprecated Renamed to {@link RelayBusCatalogConsumerMetadata}; this alias is removed in 1.0.0. */
82
+ type EventConsumerMetadata = RelayBusCatalogConsumerMetadata;
83
+ /** @deprecated Renamed to {@link RelayBusCatalogConsumerClass}; this alias is removed in 1.0.0. */
84
+ type EventConsumerClass<TCatalog extends Catalog, TName extends keyof TCatalog> = RelayBusCatalogConsumerClass<TCatalog, TName>;
74
85
 
75
86
  /**
76
- * Finds every `@Subscribe` method and every `@EventConsumer` class among the
87
+ * Finds every `@RelayBusConsumer` method and every `@RelayBusCatalogConsumer` class among the
77
88
  * singleton providers of the application and registers them on the bus before
78
89
  * the bus starts. Providers that are request-scoped or transient, directly or
79
90
  * through a dependency, and use either decorator fail the boot. A request-scoped provider whose
@@ -84,8 +95,16 @@ declare class RelayBusHandlerExplorer implements OnModuleInit {
84
95
  private readonly discovery;
85
96
  private readonly metadataScanner;
86
97
  private readonly bus;
87
- constructor(discovery: DiscoveryService, metadataScanner: MetadataScanner, bus: RelayBus);
98
+ private readonly modules;
99
+ constructor(discovery: DiscoveryService, metadataScanner: MetadataScanner, bus: RelayBus, modules: ModulesContainer);
88
100
  onModuleInit(): void;
101
+ /** Checks the `RelayBusModule.forFeature` contract of every module that imports one. */
102
+ private assertModuleContracts;
103
+ private importersOf;
104
+ /** Records the event types the provider of `wrapper` consumes under the provider's module. */
105
+ private recordConsumedTypes;
106
+ private registerProvider;
107
+ private registerSubscribedMethod;
89
108
  private registerEventConsumers;
90
109
  private subscribedMethods;
91
110
  /**
@@ -147,19 +166,52 @@ declare class RelayBusHealth {
147
166
 
148
167
  /**
149
168
  * Starts the bus once every handler is registered and the application has
150
- * bootstrapped, and stops it (draining running handlers) on shutdown. A start
151
- * failure rejects the hook, so the application does not boot.
169
+ * bootstrapped, and stops it (draining running handlers, within `dispatcher.shutdownTimeout`) on
170
+ * shutdown: `bus.stop()` resolves at that deadline at the latest, so a hung query or broker cannot
171
+ * hold `app.close()` until the platform kills the process. A start
172
+ * failure rejects the hook, so the application does not boot. `@RelayBusTransaction()`
173
+ * methods find the bus from module initialisation until the application has shut down.
152
174
  */
153
- declare class RelayBusLifecycle implements OnApplicationBootstrap, BeforeApplicationShutdown {
175
+ declare class RelayBusLifecycle implements OnModuleInit, OnApplicationBootstrap, BeforeApplicationShutdown, OnApplicationShutdown {
154
176
  private readonly bus;
155
177
  constructor(bus: RelayBus);
178
+ onModuleInit(): void;
156
179
  onApplicationBootstrap(): Promise<void>;
157
180
  beforeApplicationShutdown(): Promise<void>;
181
+ /** After the bus stopped, so handlers still draining at shutdown can open transactions. */
182
+ onApplicationShutdown(): void;
183
+ }
184
+
185
+ /**
186
+ * What one feature module consumes and produces, given to `RelayBusModule.forFeature`. Each item is
187
+ * a whole catalog or a `consume(...)` / `produce(...)` declaration of some of its events.
188
+ */
189
+ interface RelayBusFeatureOptions {
190
+ /** Events the importing module handles: each needs a `@RelayBusConsumer` method (or a catalog consumer) among its providers. */
191
+ readonly consumes?: readonly (Catalog | CatalogDeclaration)[];
192
+ /** Events the importing module publishes for other services: each needs a route to a transport. */
193
+ readonly produces?: readonly (Catalog | CatalogDeclaration)[];
194
+ }
195
+ /** One event of a feature contract, named for error messages. */
196
+ type ContractEvent = {
197
+ /** `Catalog.EventName`. */
198
+ readonly label: string;
199
+ readonly type: string;
200
+ };
201
+ /**
202
+ * The contract of the module that imports `RelayBusModule.forFeature(...)`, checked when the
203
+ * application initialises.
204
+ */
205
+ declare class RelayBusFeatureContract {
206
+ readonly consumes: readonly ContractEvent[];
207
+ readonly produces: readonly ContractEvent[];
208
+ /** @throws ConfigurationError for a `produce(...)` in `consumes`, a `consume(...)` in `produces`, or something that is not a catalog. */
209
+ constructor(options: RelayBusFeatureOptions);
158
210
  }
159
211
 
160
212
  /**
161
- * Registers one {@link RelayBus} for the application, discovers `@Subscribe`
162
- * handlers in every module, starts the bus after bootstrap and stops it on
213
+ * Registers one {@link RelayBus} for the application, discovers `@RelayBusConsumer` and `@RelayBusCatalogConsumer`
214
+ * handlers in every module, runs `@RelayBusTransaction()` methods on the bus, starts the bus after bootstrap and stops it on
163
215
  * shutdown. Global unless `isGlobal: false`.
164
216
  *
165
217
  * ```ts
@@ -167,28 +219,124 @@ declare class RelayBusLifecycle implements OnApplicationBootstrap, BeforeApplica
167
219
  * ```
168
220
  */
169
221
  declare class RelayBusModule extends RelayBusConfigurableModule {
222
+ /**
223
+ * The relaybus contract of a feature module: import it in the module whose providers consume
224
+ * and produce the events. When the application initialises, every consumed event needs a
225
+ * `@RelayBusConsumer` method (or a `@RelayBusCatalogConsumer` class) among the providers of
226
+ * that module, spread over any classes and method names, and every produced event needs a
227
+ * route to a transport; otherwise the application fails to start naming the module and each
228
+ * missing event. Needs `RelayBusModule.forRoot` in the application.
229
+ *
230
+ * ```ts
231
+ * @Module({
232
+ * imports: [RelayBusModule.forFeature({ consumes: [OrderEvents], produces: [produce(BillingEvents)] })],
233
+ * providers: [OrderPlacedHandlers, OrderCancelledHandlers],
234
+ * })
235
+ * export class BillingModule {}
236
+ * ```
237
+ *
238
+ * @throws ConfigurationError for a `produce(...)` in `consumes` or a `consume(...)` in `produces`.
239
+ */
240
+ static forFeature(options: RelayBusFeatureOptions): DynamicModule;
241
+ }
242
+
243
+ /**
244
+ * Carries the contract of `RelayBusModule.forFeature(...)` into the module that imports it. The
245
+ * contract is checked against that module's providers when the application initialises.
246
+ */
247
+ declare class RelayBusFeatureModule {
170
248
  }
171
249
 
172
250
  /**
173
- * Options of {@link Subscribe}: the core {@link SubscribeOptions}, except that
251
+ * Hands repositories the transaction of the enclosing `@RelayBusTransaction()` method (or
252
+ * `bus.transaction`, transactional handler, `transactionResolver`), typed as the store's
253
+ * transaction handle: `PoolClient` for `pg`, a Kysely `Transaction`, a TypeORM `EntityManager`,
254
+ * a Prisma transaction client, a MongoDB `ClientSession`.
255
+ *
256
+ * ```ts
257
+ * @Injectable()
258
+ * export class OrderRepository {
259
+ * constructor(private readonly transactions: RelayBusTransactionHost<PoolClient>) {}
260
+ *
261
+ * async insert(orderId: string): Promise<void> {
262
+ * await this.transactions.current().query('INSERT INTO orders (id) VALUES ($1)', [orderId]);
263
+ * }
264
+ * }
265
+ * ```
266
+ */
267
+ declare class RelayBusTransactionHost<TTransaction = unknown> {
268
+ private readonly bus;
269
+ constructor(bus: RelayBus<TTransaction>);
270
+ /**
271
+ * The active transaction.
272
+ *
273
+ * @throws TransactionRequiredError when the caller runs outside every transaction.
274
+ */
275
+ current(): TTransaction;
276
+ /** The active transaction, or undefined outside every transaction. */
277
+ find(): TTransaction | undefined;
278
+ }
279
+
280
+ /** The compile error a `@RelayBusTransaction()` on a method that does not return a Promise reads. */
281
+ type RelayBusTransactionNeedsAsyncMethod = '@RelayBusTransaction() needs a method that returns a Promise: make the method async';
282
+ /**
283
+ * Accepts the descriptor of a method that returns a Promise; any other method fails to compile
284
+ * with {@link RelayBusTransactionNeedsAsyncMethod} in the message.
285
+ */
286
+ type AsyncMethodDescriptor<TMethod> = TypedPropertyDescriptor<TMethod> & (TMethod extends (...args: never[]) => PromiseLike<unknown> ? unknown : RelayBusTransactionNeedsAsyncMethod);
287
+ /** What `@RelayBusTransaction()` returns: a method decorator for async methods only. */
288
+ type RelayBusTransactionDecorator = <TMethod extends (...args: never[]) => unknown>(target: object, propertyKey: string | symbol, descriptor: AsyncMethodDescriptor<TMethod>) => void;
289
+ /**
290
+ * Runs the method in a relaybus transaction: `bus.publish(...)` inside it, and in everything it
291
+ * awaits, joins the transaction without passing `{ transaction }`, and a repository reads it with
292
+ * `bus.currentTransaction()` or {@link RelayBusTransactionHost}. The transaction commits when the
293
+ * method resolves and rolls back when it rejects; it is held to the bus `transactionTimeout`.
294
+ *
295
+ * - The method itself is replaced, so `this.other()` calls of decorated methods are transactional
296
+ * too, unlike interceptor-based decorators.
297
+ * - A decorated method called while a transaction is active (another decorated method,
298
+ * `bus.transaction`, a transactional handler, or the `transactionResolver`, for example
299
+ * `@nestjs-cls/transactional`) joins it instead of opening a second one.
300
+ * - The bus is the one of the application's `RelayBusModule`; the class does not inject it.
301
+ * - Only methods that return a Promise compile.
302
+ *
303
+ * ```ts
304
+ * @Injectable()
305
+ * export class OrderService {
306
+ * @RelayBusTransaction()
307
+ * async place(orderId: string): Promise<void> {
308
+ * await this.orders.insert(orderId);
309
+ * await this.bus.publish(new OrderPlaced({ orderId }));
310
+ * }
311
+ * }
312
+ * ```
313
+ */
314
+ declare function RelayBusTransaction(): RelayBusTransactionDecorator;
315
+
316
+ /**
317
+ * Options of {@link RelayBusConsumer}: the core {@link SubscribeOptions}, except that
174
318
  * `id` is optional and defaults to `ClassName.methodName`.
175
319
  */
176
- type SubscribeDecoratorOptions = Omit<SubscribeOptions, 'id'> & {
320
+ type RelayBusConsumerOptions = Omit<SubscribeOptions, 'id'> & {
177
321
  /**
178
322
  * Stable id of the handler. Deliveries are recorded under it, so renaming it
179
323
  * (or the class or method it is derived from) orphans pending deliveries.
180
324
  * Default: `${ClassName}.${methodName}`. Required when one method carries
181
- * several `@Subscribe` decorators.
325
+ * several `@RelayBusConsumer` decorators.
182
326
  */
183
327
  readonly id?: string;
184
328
  };
185
- /** What one `@Subscribe` decorator recorded on a method. */
186
- interface SubscribeMetadata {
329
+ /** What one `@RelayBusConsumer` decorator recorded on a method. */
330
+ interface RelayBusConsumerMetadata {
187
331
  readonly event: EventClass<unknown> | string;
188
- readonly options: SubscribeDecoratorOptions;
332
+ readonly options: RelayBusConsumerOptions;
189
333
  }
334
+ /** @deprecated Renamed to {@link RelayBusConsumerOptions}; this alias is removed in 1.0.0. */
335
+ type SubscribeDecoratorOptions = RelayBusConsumerOptions;
336
+ /** @deprecated Renamed to {@link RelayBusConsumerMetadata}; this alias is removed in 1.0.0. */
337
+ type SubscribeMetadata = RelayBusConsumerMetadata;
190
338
  type HandlerMethod<TPayload> = (event: EventEnvelope<TPayload>, context: HandlerContext<never>) => unknown;
191
- type SubscribeDecorator<TPayload> = <TMethod extends HandlerMethod<TPayload>>(target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<TMethod>) => void;
339
+ type HandlerDecorator<TPayload> = <TMethod extends HandlerMethod<TPayload>>(target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<TMethod>) => void;
192
340
  /**
193
341
  * Subscribes a provider method to an event class or type. The method is
194
342
  * registered with `bus.subscribe` when the module initialises, with `this`
@@ -201,12 +349,17 @@ type SubscribeDecorator<TPayload> = <TMethod extends HandlerMethod<TPayload>>(ta
201
349
  * ```ts
202
350
  * @Injectable()
203
351
  * export class ShippingHandlers {
204
- * @Subscribe(OrderPlaced, { transactional: true })
352
+ * @RelayBusConsumer(OrderPlaced, { transactional: true })
205
353
  * async onOrderPlaced(event: EventEnvelope<{ orderId: string }>, context: HandlerContext) {}
206
354
  * }
207
355
  * ```
208
356
  */
209
- declare function Subscribe<TPayload>(event: EventClass<TPayload>, options?: SubscribeDecoratorOptions): SubscribeDecorator<TPayload>;
210
- declare function Subscribe(eventType: string, options?: SubscribeDecoratorOptions): SubscribeDecorator<never>;
357
+ declare function RelayBusConsumer<TPayload>(event: EventClass<TPayload>, options?: RelayBusConsumerOptions): HandlerDecorator<TPayload>;
358
+ declare function RelayBusConsumer(eventType: string, options?: RelayBusConsumerOptions): HandlerDecorator<never>;
359
+ /**
360
+ * @deprecated Renamed to {@link RelayBusConsumer}, which behaves identically; this alias is
361
+ * removed in 1.0.0.
362
+ */
363
+ declare const Subscribe: typeof RelayBusConsumer;
211
364
 
212
- export { EventConsumer, type EventConsumerClass, type EventConsumerMetadata, type EventConsumerOptions, RelayBusHandlerExplorer, RelayBusHealth, type RelayBusHealthReport, RelayBusLifecycle, RelayBusModule, type RelayBusModuleExtras, type RelayBusModuleOptions, type RelayBusModuleOptionsFactory, Subscribe, type SubscribeDecoratorOptions, type SubscribeMetadata };
365
+ export { type AsyncMethodDescriptor, type ContractEvent, EventConsumer, type EventConsumerClass, type EventConsumerMetadata, type EventConsumerOptions, RelayBusCatalogConsumer, type RelayBusCatalogConsumerClass, type RelayBusCatalogConsumerMetadata, type RelayBusCatalogConsumerOptions, RelayBusConsumer, type RelayBusConsumerMetadata, type RelayBusConsumerOptions, RelayBusFeatureContract, RelayBusFeatureModule, type RelayBusFeatureOptions, RelayBusHandlerExplorer, RelayBusHealth, type RelayBusHealthReport, RelayBusLifecycle, RelayBusModule, type RelayBusModuleExtras, type RelayBusModuleOptions, type RelayBusModuleOptionsFactory, RelayBusTransaction, type RelayBusTransactionDecorator, RelayBusTransactionHost, type RelayBusTransactionNeedsAsyncMethod, Subscribe, type SubscribeDecoratorOptions, type SubscribeMetadata };