@signaltree/events 7.6.0 → 8.0.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.
@@ -1,218 +0,0 @@
1
- 'use strict';
2
-
3
- var tslib = require('tslib');
4
- var common = require('@nestjs/common');
5
- var dlq_service = require('./dlq.service.cjs');
6
- var eventBus_service = require('./event-bus.service.cjs');
7
- var tokens = require('./tokens.cjs');
8
- var registry = require('../core/registry.cjs');
9
- var idempotency = require('../core/idempotency.cjs');
10
- var errorClassification = require('../core/error-classification.cjs');
11
-
12
- var EventBusModule_1;
13
- /**
14
- * Preset definitions
15
- */
16
- const QUEUE_PRESETS = {
17
- /**
18
- * Full priority-based queues (default) - separate queues per priority
19
- */
20
- 'priority-based': [{
21
- name: 'events-critical',
22
- priorities: ['critical'],
23
- concurrency: 10
24
- }, {
25
- name: 'events-high',
26
- priorities: ['high'],
27
- concurrency: 8
28
- }, {
29
- name: 'events-normal',
30
- priorities: ['normal'],
31
- concurrency: 5
32
- }, {
33
- name: 'events-low',
34
- priorities: ['low'],
35
- concurrency: 3
36
- }, {
37
- name: 'events-bulk',
38
- priorities: ['bulk'],
39
- concurrency: 2,
40
- rateLimit: {
41
- max: 100,
42
- duration: 1000
43
- }
44
- }],
45
- /**
46
- * Single queue for all priorities - simpler setup, uses BullMQ priority numbers
47
- */
48
- 'single-queue': [{
49
- name: 'events',
50
- priorities: ['critical', 'high', 'normal', 'low', 'bulk'],
51
- concurrency: 10
52
- }],
53
- /**
54
- * Minimal setup - one queue, low concurrency, good for development
55
- */
56
- 'minimal': [{
57
- name: 'events',
58
- priorities: ['critical', 'high', 'normal', 'low', 'bulk'],
59
- concurrency: 3
60
- }]
61
- };
62
- /**
63
- * Default queue configuration based on priorities
64
- */
65
- const DEFAULT_QUEUES = [{
66
- name: 'events-critical',
67
- priorities: ['critical'],
68
- concurrency: 10
69
- }, {
70
- name: 'events-high',
71
- priorities: ['high'],
72
- concurrency: 8
73
- }, {
74
- name: 'events-normal',
75
- priorities: ['normal'],
76
- concurrency: 5
77
- }, {
78
- name: 'events-low',
79
- priorities: ['low'],
80
- concurrency: 3
81
- }, {
82
- name: 'events-bulk',
83
- priorities: ['bulk'],
84
- concurrency: 2,
85
- rateLimit: {
86
- max: 100,
87
- duration: 1000
88
- }
89
- }];
90
- exports.EventBusModule = EventBusModule_1 = class EventBusModule {
91
- /**
92
- * Register the EventBus module with configuration
93
- *
94
- * @example
95
- * ```typescript
96
- * @Module({
97
- * imports: [
98
- * EventBusModule.forRoot({
99
- * redis: { host: 'localhost', port: 6379 },
100
- * queues: [
101
- * { name: 'critical', priorities: ['critical'], concurrency: 10 },
102
- * { name: 'normal', priorities: ['high', 'normal'], concurrency: 5 },
103
- * ],
104
- * }),
105
- * ],
106
- * })
107
- * export class AppModule {}
108
- * ```
109
- */
110
- static forRoot(config) {
111
- const providers = EventBusModule_1.createProviders(config);
112
- return {
113
- module: EventBusModule_1,
114
- global: true,
115
- providers,
116
- exports: [eventBus_service.EventBusService, dlq_service.DlqService, tokens.EVENT_BUS_CONFIG, tokens.EVENT_REGISTRY, tokens.IDEMPOTENCY_STORE, tokens.ERROR_CLASSIFIER]
117
- };
118
- }
119
- /**
120
- * Register the EventBus module with async configuration
121
- *
122
- * @example
123
- * ```typescript
124
- * @Module({
125
- * imports: [
126
- * EventBusModule.forRootAsync({
127
- * imports: [ConfigModule],
128
- * useFactory: (configService: ConfigService) => ({
129
- * redis: {
130
- * host: configService.get('REDIS_HOST'),
131
- * port: configService.get('REDIS_PORT'),
132
- * },
133
- * }),
134
- * inject: [ConfigService],
135
- * }),
136
- * ],
137
- * })
138
- * export class AppModule {}
139
- * ```
140
- */
141
- static forRootAsync(asyncConfig) {
142
- const configProvider = {
143
- provide: tokens.EVENT_BUS_CONFIG,
144
- useFactory: asyncConfig.useFactory,
145
- inject: asyncConfig.inject ?? []
146
- };
147
- const registryProvider = {
148
- provide: tokens.EVENT_REGISTRY,
149
- useFactory: config => {
150
- return registry.createEventRegistry(config.registry);
151
- },
152
- inject: [tokens.EVENT_BUS_CONFIG]
153
- };
154
- const idempotencyProvider = {
155
- provide: tokens.IDEMPOTENCY_STORE,
156
- useFactory: config => {
157
- return config.idempotencyStore ?? idempotency.createInMemoryIdempotencyStore();
158
- },
159
- inject: [tokens.EVENT_BUS_CONFIG]
160
- };
161
- const errorClassifierProvider = {
162
- provide: tokens.ERROR_CLASSIFIER,
163
- useFactory: config => {
164
- return errorClassification.createErrorClassifier(config.errorClassifier);
165
- },
166
- inject: [tokens.EVENT_BUS_CONFIG]
167
- };
168
- return {
169
- module: EventBusModule_1,
170
- global: true,
171
- imports: asyncConfig.imports ?? [],
172
- providers: [configProvider, registryProvider, idempotencyProvider, errorClassifierProvider, eventBus_service.EventBusService, dlq_service.DlqService],
173
- exports: [eventBus_service.EventBusService, dlq_service.DlqService, tokens.EVENT_BUS_CONFIG, tokens.EVENT_REGISTRY, tokens.IDEMPOTENCY_STORE, tokens.ERROR_CLASSIFIER]
174
- };
175
- }
176
- /**
177
- * Resolve queue configuration from preset or explicit config
178
- */
179
- static resolveQueues(config) {
180
- // Explicit queues take precedence
181
- if (config.queues && config.queues.length > 0) {
182
- return config.queues;
183
- }
184
- // Use preset if specified
185
- if (config.preset) {
186
- return QUEUE_PRESETS[config.preset];
187
- }
188
- // Default to priority-based
189
- return DEFAULT_QUEUES;
190
- }
191
- static createProviders(config) {
192
- const queues = EventBusModule_1.resolveQueues(config);
193
- const fullConfig = {
194
- ...config,
195
- queues,
196
- enableDlq: config.enableDlq ?? true,
197
- dlqQueueName: config.dlqQueueName ?? 'dead-letter',
198
- enableMetrics: config.enableMetrics ?? true,
199
- metricsPrefix: config.metricsPrefix ?? 'signaltree_events'
200
- };
201
- return [{
202
- provide: tokens.EVENT_BUS_CONFIG,
203
- useValue: fullConfig
204
- }, {
205
- provide: tokens.EVENT_REGISTRY,
206
- useFactory: () => registry.createEventRegistry(config.registry)
207
- }, {
208
- provide: tokens.IDEMPOTENCY_STORE,
209
- useValue: config.idempotencyStore ?? idempotency.createInMemoryIdempotencyStore()
210
- }, {
211
- provide: tokens.ERROR_CLASSIFIER,
212
- useFactory: () => errorClassification.createErrorClassifier(config.errorClassifier)
213
- }, eventBus_service.EventBusService, dlq_service.DlqService];
214
- }
215
- };
216
- exports.EventBusModule = EventBusModule_1 = tslib.__decorate([common.Module({})], exports.EventBusModule);
217
-
218
- exports.QUEUE_PRESETS = QUEUE_PRESETS;
@@ -1,318 +0,0 @@
1
- 'use strict';
2
-
3
- var tslib = require('tslib');
4
- var common = require('@nestjs/common');
5
- var bullmq = require('bullmq');
6
- var factory = require('../core/factory.cjs');
7
- var registry = require('../core/registry.cjs');
8
- var types = require('../core/types.cjs');
9
- var tokens = require('./tokens.cjs');
10
-
11
- var EventBusService_1;
12
- exports.EventBusService = EventBusService_1 = class EventBusService {
13
- config;
14
- registry;
15
- logger = new common.Logger(EventBusService_1.name);
16
- queues = new Map();
17
- priorityToQueue = new Map();
18
- connection;
19
- isReady = false;
20
- constructor(config, registry) {
21
- this.config = config;
22
- this.registry = registry;
23
- this.connection = {
24
- host: config.redis.host,
25
- port: config.redis.port,
26
- password: config.redis.password,
27
- db: config.redis.db,
28
- maxRetriesPerRequest: config.redis.maxRetriesPerRequest ?? 3
29
- };
30
- }
31
- async onModuleInit() {
32
- this.logger.log('Initializing EventBus queues...');
33
- // Create queues
34
- for (const queueConfig of this.config.queues ?? []) {
35
- const queue = new bullmq.Queue(queueConfig.name, {
36
- connection: this.connection,
37
- defaultJobOptions: {
38
- removeOnComplete: 1000,
39
- // Keep last 1000 completed jobs
40
- removeOnFail: 5000,
41
- // Keep last 5000 failed jobs
42
- attempts: 5,
43
- backoff: {
44
- type: 'exponential',
45
- delay: 1000
46
- }
47
- }
48
- });
49
- // Map priorities to this queue
50
- for (const priority of queueConfig.priorities) {
51
- this.priorityToQueue.set(priority, queueConfig.name);
52
- }
53
- const instance = {
54
- config: queueConfig,
55
- queue
56
- };
57
- // Optionally create queue events listener for monitoring
58
- if (this.config.enableMetrics) {
59
- instance.events = new bullmq.QueueEvents(queueConfig.name, {
60
- connection: this.connection
61
- });
62
- this.setupQueueEventListeners(instance);
63
- }
64
- this.queues.set(queueConfig.name, instance);
65
- this.logger.log(`Queue "${queueConfig.name}" initialized for priorities: ${queueConfig.priorities.join(', ')}`);
66
- }
67
- this.isReady = true;
68
- this.logger.log(`EventBus ready with ${this.queues.size} queues`);
69
- }
70
- async onModuleDestroy() {
71
- this.logger.log('Shutting down EventBus...');
72
- const closePromises = [];
73
- for (const [name, instance] of this.queues) {
74
- this.logger.debug(`Closing queue "${name}"...`);
75
- closePromises.push(instance.queue.close());
76
- if (instance.events) {
77
- closePromises.push(instance.events.close());
78
- }
79
- }
80
- await Promise.all(closePromises);
81
- this.queues.clear();
82
- this.isReady = false;
83
- this.logger.log('EventBus shutdown complete');
84
- }
85
- /**
86
- * Publish an event
87
- *
88
- * @example
89
- * ```typescript
90
- * await eventBus.publish({
91
- * type: 'TradeProposalCreated',
92
- * data: {
93
- * tradeId: '123',
94
- * initiatorId: 'user-1',
95
- * recipientId: 'user-2',
96
- * },
97
- * });
98
- * ```
99
- */
100
- async publish(event, options = {}) {
101
- if (!this.isReady) {
102
- throw new Error('EventBus is not ready. Wait for module initialization.');
103
- }
104
- // Build complete event
105
- const eventId = options.id ?? event.id ?? factory.generateEventId();
106
- const correlationId = options.correlationId ?? event.correlationId ?? factory.generateCorrelationId();
107
- const timestamp = event.timestamp ?? new Date().toISOString();
108
- const fullEvent = {
109
- ...event,
110
- id: eventId,
111
- correlationId,
112
- causationId: options.causationId ?? event.causationId,
113
- timestamp,
114
- priority: options.priority ?? event.priority ?? 'normal'
115
- };
116
- // Validate event against registry
117
- const validated = this.registry.validate(fullEvent);
118
- // Determine queue based on priority
119
- const priority = validated.priority ?? 'normal';
120
- const queueName = options.queue ?? this.getQueueForPriority(priority);
121
- const instance = this.queues.get(queueName);
122
- if (!instance) {
123
- throw new Error(`Queue "${queueName}" not found. Available: ${Array.from(this.queues.keys()).join(', ')}`);
124
- }
125
- // Publish to BullMQ
126
- const jobId = options.jobId ?? eventId;
127
- const job = await instance.queue.add(validated.type,
128
- // Job name = event type
129
- validated, {
130
- jobId,
131
- delay: options.delay,
132
- priority: this.getPriorityNumber(priority),
133
- ...options.jobOptions
134
- });
135
- this.logger.debug(`Published event ${validated.type}:${eventId} to queue ${queueName} (job: ${job.id})`);
136
- return {
137
- eventId,
138
- jobId: job.id ?? eventId,
139
- queue: queueName,
140
- correlationId
141
- };
142
- }
143
- /**
144
- * Create an event with defaults from module configuration
145
- *
146
- * This is a convenience method that auto-fills id, timestamp, version,
147
- * correlationId, and metadata.source from the module config.
148
- *
149
- * @example
150
- * ```typescript
151
- * // Instead of constructing the full event manually:
152
- * const event = this.eventBus.createEvent('TradeProposalCreated', {
153
- * tradeId: '123',
154
- * initiatorId: 'user-1',
155
- * recipientId: 'user-2',
156
- * }, {
157
- * actor: { id: userId, type: 'user' },
158
- * priority: 'high',
159
- * });
160
- *
161
- * await this.eventBus.publish(event);
162
- * ```
163
- */
164
- createEvent(type, data, options = {}) {
165
- const id = options.id ?? factory.generateEventId();
166
- const correlationId = options.correlationId ?? factory.generateCorrelationId();
167
- const timestamp = options.timestamp ?? new Date().toISOString();
168
- const actor = options.actor ?? {
169
- id: 'system',
170
- type: 'system'
171
- };
172
- const metadata = {
173
- source: this.config.source ?? 'signaltree',
174
- environment: this.config.environment ?? process.env['NODE_ENV'] ?? 'development',
175
- ...options.metadata
176
- };
177
- return {
178
- id,
179
- type,
180
- version: options.version ?? types.DEFAULT_EVENT_VERSION,
181
- timestamp,
182
- correlationId,
183
- causationId: options.causationId,
184
- actor,
185
- metadata,
186
- data,
187
- priority: options.priority,
188
- aggregate: options.aggregate
189
- };
190
- }
191
- /**
192
- * Convenience method to create and publish an event in one call
193
- *
194
- * Combines createEvent() and publish() for the common case.
195
- *
196
- * @example
197
- * ```typescript
198
- * await this.eventBus.publishEvent('TradeProposalCreated', {
199
- * tradeId: '123',
200
- * initiatorId: 'user-1',
201
- * }, {
202
- * actor: { id: userId, type: 'user' },
203
- * priority: 'high',
204
- * });
205
- * ```
206
- */
207
- async publishEvent(type, data, options = {}) {
208
- const event = this.createEvent(type, data, options);
209
- return this.publish(event, {
210
- delay: options.delay,
211
- queue: options.queue,
212
- jobId: options.jobId,
213
- jobOptions: options.jobOptions,
214
- priority: options.priority
215
- });
216
- }
217
- /**
218
- * Publish multiple events in a batch
219
- */
220
- async publishBatch(events, options = {}) {
221
- // Use same correlation ID for all events in batch
222
- const correlationId = options.correlationId ?? factory.generateCorrelationId();
223
- const results = await Promise.all(events.map((event, index) => this.publish(event, {
224
- ...options,
225
- correlationId,
226
- causationId: index > 0 ? events[index - 1].id : options.causationId
227
- })));
228
- return results;
229
- }
230
- /**
231
- * Get queue for a given priority
232
- */
233
- getQueueForPriority(priority) {
234
- const queueName = this.priorityToQueue.get(priority);
235
- if (!queueName) {
236
- // Fall back to normal queue
237
- return this.priorityToQueue.get('normal') ?? 'events-normal';
238
- }
239
- return queueName;
240
- }
241
- /**
242
- * Get queue stats
243
- */
244
- async getQueueStats(queueName) {
245
- const instance = this.queues.get(queueName);
246
- if (!instance) {
247
- throw new Error(`Queue "${queueName}" not found`);
248
- }
249
- const [waiting, active, completed, failed, delayed] = await Promise.all([instance.queue.getWaitingCount(), instance.queue.getActiveCount(), instance.queue.getCompletedCount(), instance.queue.getFailedCount(), instance.queue.getDelayedCount()]);
250
- return {
251
- waiting,
252
- active,
253
- completed,
254
- failed,
255
- delayed
256
- };
257
- }
258
- /**
259
- * Get all queue names
260
- */
261
- getQueueNames() {
262
- return Array.from(this.queues.keys());
263
- }
264
- /**
265
- * Get underlying BullMQ queue for advanced operations
266
- */
267
- getQueue(name) {
268
- return this.queues.get(name)?.queue;
269
- }
270
- /**
271
- * Check if service is ready
272
- */
273
- isServiceReady() {
274
- return this.isReady;
275
- }
276
- /**
277
- * Convert priority string to number for BullMQ (lower = higher priority)
278
- */
279
- getPriorityNumber(priority) {
280
- switch (priority) {
281
- case 'critical':
282
- return 1;
283
- case 'high':
284
- return 2;
285
- case 'normal':
286
- return 3;
287
- case 'low':
288
- return 4;
289
- case 'bulk':
290
- return 5;
291
- default:
292
- return 3;
293
- }
294
- }
295
- /**
296
- * Setup event listeners for monitoring
297
- */
298
- setupQueueEventListeners(instance) {
299
- if (!instance.events) return;
300
- instance.events.on('completed', ({
301
- jobId
302
- }) => {
303
- this.logger.debug(`Job ${jobId} completed in queue ${instance.config.name}`);
304
- });
305
- instance.events.on('failed', ({
306
- jobId,
307
- failedReason
308
- }) => {
309
- this.logger.warn(`Job ${jobId} failed in queue ${instance.config.name}: ${failedReason}`);
310
- });
311
- instance.events.on('stalled', ({
312
- jobId
313
- }) => {
314
- this.logger.warn(`Job ${jobId} stalled in queue ${instance.config.name}`);
315
- });
316
- }
317
- };
318
- exports.EventBusService = EventBusService_1 = tslib.__decorate([common.Injectable(), tslib.__param(0, common.Inject(tokens.EVENT_BUS_CONFIG)), tslib.__param(1, common.Inject(tokens.EVENT_REGISTRY)), tslib.__metadata("design:paramtypes", [Object, registry.EventRegistry])], exports.EventBusService);
@@ -1,34 +0,0 @@
1
- 'use strict';
2
-
3
- var eventBus_module = require('./event-bus.module.cjs');
4
- var eventBus_service = require('./event-bus.service.cjs');
5
- var base_subscriber = require('./base.subscriber.cjs');
6
- var dlq_service = require('./dlq.service.cjs');
7
- var decorators = require('./decorators.cjs');
8
- var tokens = require('./tokens.cjs');
9
-
10
-
11
-
12
- Object.defineProperty(exports, "EventBusModule", {
13
- enumerable: true,
14
- get: function () { return eventBus_module.EventBusModule; }
15
- });
16
- exports.QUEUE_PRESETS = eventBus_module.QUEUE_PRESETS;
17
- Object.defineProperty(exports, "EventBusService", {
18
- enumerable: true,
19
- get: function () { return eventBus_service.EventBusService; }
20
- });
21
- Object.defineProperty(exports, "BaseSubscriber", {
22
- enumerable: true,
23
- get: function () { return base_subscriber.BaseSubscriber; }
24
- });
25
- Object.defineProperty(exports, "DlqService", {
26
- enumerable: true,
27
- get: function () { return dlq_service.DlqService; }
28
- });
29
- exports.EVENT_HANDLER_METADATA = decorators.EVENT_HANDLER_METADATA;
30
- exports.OnEvent = decorators.OnEvent;
31
- exports.ERROR_CLASSIFIER = tokens.ERROR_CLASSIFIER;
32
- exports.EVENT_BUS_CONFIG = tokens.EVENT_BUS_CONFIG;
33
- exports.EVENT_REGISTRY = tokens.EVENT_REGISTRY;
34
- exports.IDEMPOTENCY_STORE = tokens.IDEMPOTENCY_STORE;
@@ -1,14 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Injection tokens for the EventBus module
5
- */
6
- const EVENT_BUS_CONFIG = Symbol('EVENT_BUS_CONFIG');
7
- const EVENT_REGISTRY = Symbol('EVENT_REGISTRY');
8
- const IDEMPOTENCY_STORE = Symbol('IDEMPOTENCY_STORE');
9
- const ERROR_CLASSIFIER = Symbol('ERROR_CLASSIFIER');
10
-
11
- exports.ERROR_CLASSIFIER = ERROR_CLASSIFIER;
12
- exports.EVENT_BUS_CONFIG = EVENT_BUS_CONFIG;
13
- exports.EVENT_REGISTRY = EVENT_REGISTRY;
14
- exports.IDEMPOTENCY_STORE = IDEMPOTENCY_STORE;