@zdavison/matador-nest 2.0.1

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 (74) hide show
  1. package/dist/constants.d.ts +13 -0
  2. package/dist/constants.d.ts.map +1 -0
  3. package/dist/constants.js +12 -0
  4. package/dist/decorators/index.d.ts +3 -0
  5. package/dist/decorators/index.d.ts.map +1 -0
  6. package/dist/decorators/index.js +2 -0
  7. package/dist/decorators/matador-subscriber.decorator.d.ts +20 -0
  8. package/dist/decorators/matador-subscriber.decorator.d.ts.map +1 -0
  9. package/dist/decorators/matador-subscriber.decorator.js +26 -0
  10. package/dist/decorators/on-matador-event.decorator.d.ts +24 -0
  11. package/dist/decorators/on-matador-event.decorator.d.ts.map +1 -0
  12. package/dist/decorators/on-matador-event.decorator.js +38 -0
  13. package/dist/discovery/index.d.ts +2 -0
  14. package/dist/discovery/index.d.ts.map +1 -0
  15. package/dist/discovery/index.js +1 -0
  16. package/dist/discovery/subscriber-discovery.service.d.ts +37 -0
  17. package/dist/discovery/subscriber-discovery.service.d.ts.map +1 -0
  18. package/dist/discovery/subscriber-discovery.service.js +144 -0
  19. package/dist/index.cjs +495 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.cts +7 -0
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +10 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/module/index.d.ts +2 -0
  27. package/dist/module/index.d.ts.map +1 -0
  28. package/dist/module/index.js +1 -0
  29. package/dist/module/matador.module.d.ts +71 -0
  30. package/dist/module/matador.module.d.ts.map +1 -0
  31. package/dist/module/matador.module.js +146 -0
  32. package/dist/services/index.d.ts +2 -0
  33. package/dist/services/index.d.ts.map +1 -0
  34. package/dist/services/index.js +1 -0
  35. package/dist/services/matador.service.d.ts +105 -0
  36. package/dist/services/matador.service.d.ts.map +1 -0
  37. package/dist/services/matador.service.js +209 -0
  38. package/dist/testing/index.d.ts +2 -0
  39. package/dist/testing/index.d.ts.map +1 -0
  40. package/dist/testing/index.js +1 -0
  41. package/dist/testing/matador-testing.module.d.ts +53 -0
  42. package/dist/testing/matador-testing.module.d.ts.map +1 -0
  43. package/dist/testing/matador-testing.module.js +77 -0
  44. package/dist/testing.cjs +498 -0
  45. package/dist/testing.cjs.map +1 -0
  46. package/dist/testing.d.cts +2 -0
  47. package/dist/testing.d.ts +2 -0
  48. package/dist/testing.d.ts.map +1 -0
  49. package/dist/testing.js +2 -0
  50. package/dist/testing.js.map +1 -0
  51. package/dist/types.d.ts +88 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +1 -0
  54. package/package.json +61 -0
  55. package/src/constants.ts +14 -0
  56. package/src/decorators/index.ts +2 -0
  57. package/src/decorators/matador-subscriber.decorator.ts +27 -0
  58. package/src/decorators/on-matador-event.decorator.ts +68 -0
  59. package/src/discovery/index.ts +1 -0
  60. package/src/discovery/subscriber-discovery.service.ts +181 -0
  61. package/src/index.ts +28 -0
  62. package/src/module/index.ts +1 -0
  63. package/src/module/matador.module.ts +162 -0
  64. package/src/services/index.ts +1 -0
  65. package/src/services/matador.service.ts +283 -0
  66. package/src/testing/index.ts +1 -0
  67. package/src/testing/matador-testing.module.ts +71 -0
  68. package/src/testing.ts +2 -0
  69. package/src/types.ts +119 -0
  70. package/test/decorators.test.ts +132 -0
  71. package/test/discovery.test.ts +278 -0
  72. package/tsconfig.json +32 -0
  73. package/tsconfig.tsbuildinfo +1 -0
  74. package/tsup.config.ts +23 -0
package/src/index.ts ADDED
@@ -0,0 +1,28 @@
1
+ // Decorators
2
+ export { OnMatadorEvent, MatadorSubscriber } from './decorators/index.js';
3
+
4
+ // Module
5
+ export { MatadorModule } from './module/index.js';
6
+
7
+ // Services
8
+ export { MatadorService } from './services/index.js';
9
+
10
+ // Discovery
11
+ export { SubscriberDiscoveryService } from './discovery/index.js';
12
+
13
+ // Types
14
+ export type {
15
+ OnMatadorEventOptions,
16
+ MatadorEventHandlerMetadata,
17
+ MatadorModuleOptions,
18
+ MatadorModuleAsyncOptions,
19
+ MatadorOptionsFactory,
20
+ NestMatadorOptions,
21
+ } from './types.js';
22
+
23
+ // Constants (for advanced use cases)
24
+ export {
25
+ MATADOR_EVENT_HANDLER,
26
+ MATADOR_EVENT_HANDLERS,
27
+ MATADOR_OPTIONS,
28
+ } from './constants.js';
@@ -0,0 +1 @@
1
+ export { MatadorModule } from './matador.module.js';
@@ -0,0 +1,162 @@
1
+ import {
2
+ type DynamicModule,
3
+ Global,
4
+ type InjectionToken,
5
+ Module,
6
+ type Provider,
7
+ } from '@nestjs/common';
8
+ import { DiscoveryModule, DiscoveryService } from '@nestjs/core';
9
+ import { MATADOR_OPTIONS } from '../constants.js';
10
+ import { SubscriberDiscoveryService } from '../discovery/subscriber-discovery.service.js';
11
+ import { MatadorService } from '../services/matador.service.js';
12
+ import type {
13
+ MatadorModuleAsyncOptions,
14
+ MatadorModuleOptions,
15
+ MatadorOptionsFactory,
16
+ } from '../types.js';
17
+
18
+ /**
19
+ * NestJS module for integrating Matador event processing.
20
+ *
21
+ * Use `MatadorModule.forRoot()` for synchronous configuration or
22
+ * `MatadorModule.forRootAsync()` for async configuration with dependency injection.
23
+ *
24
+ * @example Synchronous configuration
25
+ * ```typescript
26
+ * @Module({
27
+ * imports: [
28
+ * MatadorModule.forRoot({
29
+ * transport: new RabbitMQTransport({ url: 'amqp://localhost' }),
30
+ * topology: TopologyBuilder.create()
31
+ * .withNamespace('myapp')
32
+ * .addQueue('events', { concurrency: 10 })
33
+ * .build(),
34
+ * consumeFrom: ['events'],
35
+ * }),
36
+ * ],
37
+ * })
38
+ * export class AppModule {}
39
+ * ```
40
+ *
41
+ * @example Async configuration with ConfigService
42
+ * ```typescript
43
+ * @Module({
44
+ * imports: [
45
+ * ConfigModule.forRoot(),
46
+ * MatadorModule.forRootAsync({
47
+ * imports: [ConfigModule],
48
+ * inject: [ConfigService],
49
+ * useFactory: (config: ConfigService) => ({
50
+ * transport: new RabbitMQTransport({
51
+ * url: config.get('RABBITMQ_URL'),
52
+ * }),
53
+ * topology: TopologyBuilder.create()
54
+ * .withNamespace(config.get('APP_NAME'))
55
+ * .addQueue('events')
56
+ * .build(),
57
+ * consumeFrom: ['events'],
58
+ * }),
59
+ * }),
60
+ * ],
61
+ * })
62
+ * export class AppModule {}
63
+ * ```
64
+ */
65
+ @Global()
66
+ @Module({})
67
+ export class MatadorModule {
68
+ /**
69
+ * Configures the MatadorModule with static options.
70
+ *
71
+ * @param options - Module configuration options
72
+ * @returns Dynamic module configuration
73
+ */
74
+ static forRoot(options: MatadorModuleOptions): DynamicModule {
75
+ return {
76
+ module: MatadorModule,
77
+ imports: [DiscoveryModule],
78
+ providers: [
79
+ {
80
+ provide: MATADOR_OPTIONS,
81
+ useValue: options,
82
+ },
83
+ DiscoveryService,
84
+ SubscriberDiscoveryService,
85
+ MatadorService,
86
+ ],
87
+ exports: [MatadorService],
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Configures the MatadorModule with async options.
93
+ * Use this when you need to inject dependencies like ConfigService.
94
+ *
95
+ * @param options - Async module configuration options
96
+ * @returns Dynamic module configuration
97
+ */
98
+ static forRootAsync(options: MatadorModuleAsyncOptions): DynamicModule {
99
+ const asyncProviders = this.createAsyncProviders(options);
100
+
101
+ return {
102
+ module: MatadorModule,
103
+ imports: [DiscoveryModule, ...(options.imports ?? [])],
104
+ providers: [
105
+ ...asyncProviders,
106
+ DiscoveryService,
107
+ SubscriberDiscoveryService,
108
+ MatadorService,
109
+ ],
110
+ exports: [MatadorService],
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Creates async providers for the module options.
116
+ */
117
+ private static createAsyncProviders(
118
+ options: MatadorModuleAsyncOptions,
119
+ ): Provider[] {
120
+ if (options.useFactory) {
121
+ return [
122
+ {
123
+ provide: MATADOR_OPTIONS,
124
+ useFactory: options.useFactory,
125
+ inject: (options.inject ?? []) as InjectionToken[],
126
+ },
127
+ ];
128
+ }
129
+
130
+ if (options.useClass) {
131
+ return [
132
+ {
133
+ provide: options.useClass,
134
+ useClass: options.useClass,
135
+ },
136
+ {
137
+ provide: MATADOR_OPTIONS,
138
+ useFactory: async (
139
+ factory: MatadorOptionsFactory,
140
+ ): Promise<MatadorModuleOptions> => factory.createMatadorOptions(),
141
+ inject: [options.useClass],
142
+ },
143
+ ];
144
+ }
145
+
146
+ if (options.useExisting) {
147
+ return [
148
+ {
149
+ provide: MATADOR_OPTIONS,
150
+ useFactory: async (
151
+ factory: MatadorOptionsFactory,
152
+ ): Promise<MatadorModuleOptions> => factory.createMatadorOptions(),
153
+ inject: [options.useExisting],
154
+ },
155
+ ];
156
+ }
157
+
158
+ throw new Error(
159
+ 'MatadorModule.forRootAsync() requires useFactory, useClass, or useExisting',
160
+ );
161
+ }
162
+ }
@@ -0,0 +1 @@
1
+ export { MatadorService } from './matador.service.js';
@@ -0,0 +1,283 @@
1
+ import {
2
+ type BeforeApplicationShutdown,
3
+ Inject,
4
+ Injectable,
5
+ Logger,
6
+ type OnApplicationBootstrap,
7
+ type OnApplicationShutdown,
8
+ type OnModuleDestroy,
9
+ type OnModuleInit,
10
+ } from '@nestjs/common';
11
+ import {
12
+ type Event,
13
+ type EventClass,
14
+ type EventOptions,
15
+ Matador,
16
+ SchemaRegistry,
17
+ type SendResult,
18
+ isSchemaEntryTuple,
19
+ } from '@zdavison/matador';
20
+ import { MATADOR_OPTIONS } from '../constants.js';
21
+ import { SubscriberDiscoveryService } from '../discovery/subscriber-discovery.service.js';
22
+ import type { MatadorModuleOptions } from '../types.js';
23
+
24
+ /**
25
+ * Injectable service that wraps Matador and integrates with NestJS lifecycle.
26
+ *
27
+ * This service handles:
28
+ * - Building the Matador instance with discovered subscribers
29
+ * - Starting Matador at the configured lifecycle hook
30
+ * - Graceful shutdown with drain timeout
31
+ * - Preventing event sending during shutdown
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * @Injectable()
36
+ * export class OrderService {
37
+ * constructor(private readonly matador: MatadorService) {}
38
+ *
39
+ * async createOrder(data: CreateOrderDto) {
40
+ * // ... create order logic ...
41
+ * await this.matador.send(OrderCreatedEvent, { orderId: order.id });
42
+ * }
43
+ * }
44
+ * ```
45
+ */
46
+ @Injectable()
47
+ export class MatadorService
48
+ implements
49
+ OnModuleInit,
50
+ OnApplicationBootstrap,
51
+ OnModuleDestroy,
52
+ BeforeApplicationShutdown,
53
+ OnApplicationShutdown
54
+ {
55
+ private readonly logger = new Logger(MatadorService.name);
56
+ private matador!: Matador;
57
+ private isShuttingDown = false;
58
+ private isStarted = false;
59
+
60
+ constructor(
61
+ @Inject(MATADOR_OPTIONS) private readonly options: MatadorModuleOptions,
62
+ private readonly discoveryService: SubscriberDiscoveryService,
63
+ ) {}
64
+
65
+ /**
66
+ * Called when MatadorModule is initialized.
67
+ * Builds Matador instance and starts if startOn === 'onModuleInit'.
68
+ */
69
+ async onModuleInit(): Promise<void> {
70
+ this.initializeMatador();
71
+
72
+ if (this.shouldAutoStart() && this.options.startOn === 'onModuleInit') {
73
+ await this.doStart();
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Called after all modules are initialized and the app is ready to start.
79
+ * Starts Matador if startOn === 'onApplicationBootstrap' (default).
80
+ */
81
+ async onApplicationBootstrap(): Promise<void> {
82
+ const startOn = this.options.startOn ?? 'onApplicationBootstrap';
83
+ if (this.shouldAutoStart() && startOn === 'onApplicationBootstrap') {
84
+ await this.doStart();
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Called when MatadorModule is destroyed.
90
+ * Shuts down Matador if shutdownOn === 'onModuleDestroy'.
91
+ */
92
+ async onModuleDestroy(): Promise<void> {
93
+ if (this.options.shutdownOn === 'onModuleDestroy') {
94
+ await this.doShutdown();
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Called when the application receives a shutdown signal (SIGTERM, etc).
100
+ * Shuts down Matador if shutdownOn === 'beforeApplicationShutdown' (default).
101
+ */
102
+ async beforeApplicationShutdown(): Promise<void> {
103
+ const shutdownOn = this.options.shutdownOn ?? 'beforeApplicationShutdown';
104
+ if (shutdownOn === 'beforeApplicationShutdown') {
105
+ await this.doShutdown();
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Called after beforeApplicationShutdown completes.
111
+ * Shuts down Matador if shutdownOn === 'onApplicationShutdown'.
112
+ */
113
+ async onApplicationShutdown(): Promise<void> {
114
+ if (this.options.shutdownOn === 'onApplicationShutdown') {
115
+ await this.doShutdown();
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Sends an event to all registered subscribers.
121
+ *
122
+ * @throws Error if called during shutdown
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * // Pass the event class and data directly
127
+ * await matadorService.send(UserCreatedEvent, { userId: '123' });
128
+ *
129
+ * // Or pass an event instance
130
+ * const event = new UserCreatedEvent({ userId: '123' });
131
+ * await matadorService.send(event);
132
+ * ```
133
+ */
134
+ async send<T>(
135
+ eventClass: EventClass<T>,
136
+ data: T,
137
+ options?: EventOptions,
138
+ ): Promise<SendResult>;
139
+ async send<T>(event: Event<T>, options?: EventOptions): Promise<SendResult>;
140
+ async send<T>(
141
+ eventOrClass: EventClass<T> | Event<T>,
142
+ dataOrOptions?: T | EventOptions,
143
+ options?: EventOptions,
144
+ ): Promise<SendResult> {
145
+ if (this.isShuttingDown) {
146
+ throw new Error('Cannot send events during shutdown');
147
+ }
148
+
149
+ // Determine if first arg is an event instance or event class
150
+ const isEventClass =
151
+ typeof eventOrClass === 'function' && 'key' in eventOrClass;
152
+
153
+ if (isEventClass) {
154
+ return this.matador.send(
155
+ eventOrClass as EventClass<T>,
156
+ dataOrOptions as T,
157
+ options,
158
+ );
159
+ }
160
+ return this.matador.send(
161
+ eventOrClass as Event<T>,
162
+ dataOrOptions as EventOptions | undefined,
163
+ );
164
+ }
165
+
166
+ /**
167
+ * Gets the underlying Matador instance for advanced operations.
168
+ */
169
+ getMatador(): Matador {
170
+ return this.matador;
171
+ }
172
+
173
+ /**
174
+ * Starts consuming (if autoStart was false).
175
+ */
176
+ async start(): Promise<void> {
177
+ return this.doStart();
178
+ }
179
+
180
+ /**
181
+ * Checks if connected to transport.
182
+ */
183
+ isConnected(): boolean {
184
+ return this.matador?.isConnected() ?? false;
185
+ }
186
+
187
+ /**
188
+ * Checks if shutdown is in progress.
189
+ */
190
+ isShutdownInProgress(): boolean {
191
+ return this.isShuttingDown;
192
+ }
193
+
194
+ /**
195
+ * Waits for all pending messages to be processed.
196
+ */
197
+ async waitForIdle(timeoutMs?: number): Promise<boolean> {
198
+ return this.matador.waitForIdle(timeoutMs);
199
+ }
200
+
201
+ /**
202
+ * Initializes the Matador instance with discovered schema.
203
+ */
204
+ private initializeMatador(): void {
205
+ const mergedSchema = this.discoveryService.getMergedSchema(this.options);
206
+
207
+ // Validate schema at startup
208
+ const registry = new SchemaRegistry();
209
+ for (const entry of Object.values(mergedSchema)) {
210
+ if (isSchemaEntryTuple(entry)) {
211
+ const [eventClass, subscribers] = entry;
212
+ registry.register(eventClass, subscribers);
213
+ } else {
214
+ registry.register(entry.eventClass, entry.subscribers);
215
+ }
216
+ }
217
+
218
+ const validation = registry.validate();
219
+ if (!validation.valid) {
220
+ const errors = validation.issues.filter((i) => i.severity === 'error');
221
+ if (errors.length > 0) {
222
+ throw new Error(
223
+ `Invalid Matador schema: ${errors.map((i) => i.message).join(', ')}`,
224
+ );
225
+ }
226
+ }
227
+
228
+ this.matador = new Matador(
229
+ {
230
+ transport: this.options.transport,
231
+ topology: this.options.topology,
232
+ schema: mergedSchema,
233
+ consumeFrom: this.options.consumeFrom
234
+ ? [...this.options.consumeFrom]
235
+ : undefined,
236
+ codec: this.options.codec,
237
+ retryPolicy: this.options.retryPolicy,
238
+ checkpointStore: this.options.checkpointStore,
239
+ shutdownConfig: this.options.shutdownConfig,
240
+ },
241
+ this.options.hooks,
242
+ );
243
+
244
+ this.logger.log('Matador instance initialized');
245
+ }
246
+
247
+ private shouldAutoStart(): boolean {
248
+ return this.options.autoStart !== false;
249
+ }
250
+
251
+ private async doStart(): Promise<void> {
252
+ if (this.isStarted) {
253
+ return;
254
+ }
255
+
256
+ await this.matador.start();
257
+ this.isStarted = true;
258
+ this.logger.log('Matador started');
259
+ }
260
+
261
+ private async doShutdown(): Promise<void> {
262
+ if (!this.isStarted || this.isShuttingDown) {
263
+ return;
264
+ }
265
+
266
+ this.isShuttingDown = true;
267
+ this.logger.log('Graceful shutdown initiated, draining in-flight messages');
268
+
269
+ // Wait for in-flight messages to complete (with configurable timeout)
270
+ const timeoutMs =
271
+ this.options.shutdownConfig?.gracefulShutdownTimeout ?? 30000;
272
+ const drained = await this.matador.waitForIdle(timeoutMs);
273
+
274
+ if (!drained) {
275
+ this.logger.warn(
276
+ `Shutdown timeout reached after ${timeoutMs}ms, some messages may not have completed`,
277
+ );
278
+ }
279
+
280
+ await this.matador.shutdown();
281
+ this.logger.log('Matador shutdown complete');
282
+ }
283
+ }
@@ -0,0 +1 @@
1
+ export { MatadorTestingModule } from './matador-testing.module.js';
@@ -0,0 +1,71 @@
1
+ import { type DynamicModule, Module } from '@nestjs/common';
2
+ import { LocalTransport, TopologyBuilder } from '@zdavison/matador';
3
+ import { MatadorModule } from '../module/matador.module.js';
4
+ import type { MatadorModuleOptions } from '../types.js';
5
+
6
+ /**
7
+ * Testing module for Matador with sensible defaults for unit/integration tests.
8
+ *
9
+ * Uses LocalTransport by default, which processes messages synchronously
10
+ * in-memory without requiring external infrastructure.
11
+ *
12
+ * @example Basic usage
13
+ * ```typescript
14
+ * describe('NotificationService', () => {
15
+ * let module: TestingModule;
16
+ * let matadorService: MatadorService;
17
+ *
18
+ * beforeEach(async () => {
19
+ * module = await Test.createTestingModule({
20
+ * imports: [MatadorTestingModule.forTest()],
21
+ * providers: [NotificationService],
22
+ * }).compile();
23
+ *
24
+ * matadorService = module.get(MatadorService);
25
+ * await module.init();
26
+ * });
27
+ *
28
+ * it('processes events', async () => {
29
+ * await matadorService.send(UserCreatedEvent, { userId: '123' });
30
+ * await matadorService.waitForIdle();
31
+ * // Assert expected behavior
32
+ * });
33
+ * });
34
+ * ```
35
+ *
36
+ * @example With custom overrides
37
+ * ```typescript
38
+ * MatadorTestingModule.forTest({
39
+ * topology: TopologyBuilder.create()
40
+ * .withNamespace('custom-test')
41
+ * .addQueue('my-queue')
42
+ * .build(),
43
+ * consumeFrom: ['my-queue'],
44
+ * })
45
+ * ```
46
+ */
47
+ @Module({})
48
+ export class MatadorTestingModule {
49
+ /**
50
+ * Creates a testing module with LocalTransport and default configuration.
51
+ *
52
+ * @param overrides - Optional overrides for the default configuration
53
+ * @returns Dynamic module configuration
54
+ */
55
+ static forTest(overrides?: Partial<MatadorModuleOptions>): DynamicModule {
56
+ const defaultOptions: MatadorModuleOptions = {
57
+ transport: new LocalTransport(),
58
+ topology: TopologyBuilder.create()
59
+ .withNamespace('test')
60
+ .addQueue('events')
61
+ .build(),
62
+ consumeFrom: ['events'],
63
+ autoStart: true,
64
+ };
65
+
66
+ return MatadorModule.forRoot({
67
+ ...defaultOptions,
68
+ ...overrides,
69
+ });
70
+ }
71
+ }
package/src/testing.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Testing utilities
2
+ export { MatadorTestingModule } from './testing/index.js';
package/src/types.ts ADDED
@@ -0,0 +1,119 @@
1
+ import type { ModuleMetadata, Type } from '@nestjs/common';
2
+ import type {
3
+ AnySubscriber,
4
+ BaseSubscriberOptions,
5
+ EventClass,
6
+ Idempotency,
7
+ MatadorConfig,
8
+ MatadorHooks,
9
+ } from '@zdavison/matador';
10
+
11
+ /**
12
+ * Options for the @OnMatadorEvent decorator.
13
+ * Derived from BaseSubscriberOptions with additional NestJS-specific fields.
14
+ */
15
+ export interface OnMatadorEventOptions extends BaseSubscriberOptions {
16
+ /** Optional: override auto-generated name (default: ClassName.methodName) */
17
+ readonly name?: string | undefined;
18
+
19
+ /** Idempotency declaration for retry handling */
20
+ readonly idempotent?: Idempotency | undefined;
21
+ }
22
+
23
+ /**
24
+ * Metadata stored on methods decorated with @OnMatadorEvent.
25
+ */
26
+ export interface MatadorEventHandlerMetadata {
27
+ readonly eventClass: EventClass<unknown>;
28
+ readonly options: OnMatadorEventOptions;
29
+ readonly methodName: string;
30
+ }
31
+
32
+ /**
33
+ * NestJS-specific options that extend MatadorConfig.
34
+ */
35
+ export interface NestMatadorOptions {
36
+ /** Optional lifecycle hooks */
37
+ readonly hooks?: MatadorHooks | undefined;
38
+
39
+ /**
40
+ * Additional events to register (for events without decorated subscribers).
41
+ * Maps event class to subscriber stubs or external subscribers.
42
+ */
43
+ readonly additionalEvents?:
44
+ | Map<EventClass<unknown>, AnySubscriber[]>
45
+ | undefined;
46
+
47
+ /**
48
+ * Whether to auto-start consuming.
49
+ * Default: true
50
+ */
51
+ readonly autoStart?: boolean | undefined;
52
+
53
+ /**
54
+ * Which NestJS lifecycle hook to start Matador on.
55
+ * - 'onModuleInit': Start when MatadorModule initializes (earliest)
56
+ * - 'onApplicationBootstrap': Start after all modules init (default, recommended)
57
+ *
58
+ * Use 'onModuleInit' if other modules depend on Matador being connected
59
+ * during their own onModuleInit/onApplicationBootstrap hooks.
60
+ *
61
+ * Default: 'onApplicationBootstrap'
62
+ */
63
+ readonly startOn?: 'onModuleInit' | 'onApplicationBootstrap' | undefined;
64
+
65
+ /**
66
+ * Which NestJS lifecycle hook to shutdown Matador on.
67
+ * - 'onModuleDestroy': Shutdown when MatadorModule is destroyed (earliest)
68
+ * - 'beforeApplicationShutdown': Shutdown before app closes (default, recommended)
69
+ * - 'onApplicationShutdown': Shutdown when app closes (latest)
70
+ *
71
+ * Use 'beforeApplicationShutdown' to ensure Matador finishes processing
72
+ * before other modules start their shutdown.
73
+ *
74
+ * Default: 'beforeApplicationShutdown'
75
+ */
76
+ readonly shutdownOn?:
77
+ | 'onModuleDestroy'
78
+ | 'beforeApplicationShutdown'
79
+ | 'onApplicationShutdown'
80
+ | undefined;
81
+
82
+ /**
83
+ * Global subscriber options applied to all discovered subscribers.
84
+ */
85
+ readonly globalSubscriberDefaults?:
86
+ | Pick<BaseSubscriberOptions, 'importance'> & {
87
+ readonly idempotent?: Idempotency | undefined;
88
+ }
89
+ | undefined;
90
+ }
91
+
92
+ /**
93
+ * Configuration options for MatadorModule.
94
+ * Derived from MatadorConfig (minus schema which is auto-generated) plus NestJS-specific options.
95
+ */
96
+ export interface MatadorModuleOptions
97
+ extends Omit<MatadorConfig, 'schema'>,
98
+ NestMatadorOptions {}
99
+
100
+ /**
101
+ * Factory interface for creating MatadorModuleOptions.
102
+ */
103
+ export interface MatadorOptionsFactory {
104
+ createMatadorOptions(): Promise<MatadorModuleOptions> | MatadorModuleOptions;
105
+ }
106
+
107
+ /**
108
+ * Async configuration options for MatadorModule.forRootAsync().
109
+ */
110
+ export interface MatadorModuleAsyncOptions
111
+ extends Pick<ModuleMetadata, 'imports'> {
112
+ readonly inject?: readonly unknown[] | undefined;
113
+ readonly useFactory?: (
114
+ // biome-ignore lint/suspicious/noExplicitAny: Factory can receive any injected dependencies
115
+ ...args: any[]
116
+ ) => Promise<MatadorModuleOptions> | MatadorModuleOptions;
117
+ readonly useClass?: Type<MatadorOptionsFactory> | undefined;
118
+ readonly useExisting?: Type<MatadorOptionsFactory> | undefined;
119
+ }