@dangao/bun-server 1.7.1 → 1.8.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/README.md +129 -21
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts +17 -0
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/events/decorators.d.ts +52 -0
- package/dist/events/decorators.d.ts.map +1 -0
- package/dist/events/event-module.d.ts +97 -0
- package/dist/events/event-module.d.ts.map +1 -0
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/service.d.ts +76 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/types.d.ts +184 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1511 -11
- package/dist/security/filter.d.ts +23 -0
- package/dist/security/filter.d.ts.map +1 -1
- package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
- package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
- package/dist/security/guards/builtin/index.d.ts +3 -0
- package/dist/security/guards/builtin/index.d.ts.map +1 -0
- package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
- package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
- package/dist/security/guards/decorators.d.ts +50 -0
- package/dist/security/guards/decorators.d.ts.map +1 -0
- package/dist/security/guards/execution-context.d.ts +56 -0
- package/dist/security/guards/execution-context.d.ts.map +1 -0
- package/dist/security/guards/guard-registry.d.ts +67 -0
- package/dist/security/guards/guard-registry.d.ts.map +1 -0
- package/dist/security/guards/index.d.ts +7 -0
- package/dist/security/guards/index.d.ts.map +1 -0
- package/dist/security/guards/reflector.d.ts +57 -0
- package/dist/security/guards/reflector.d.ts.map +1 -0
- package/dist/security/guards/types.d.ts +126 -0
- package/dist/security/guards/types.d.ts.map +1 -0
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/security-module.d.ts +20 -0
- package/dist/security/security-module.d.ts.map +1 -1
- package/dist/validation/class-validator.d.ts +108 -0
- package/dist/validation/class-validator.d.ts.map +1 -0
- package/dist/validation/custom-validator.d.ts +130 -0
- package/dist/validation/custom-validator.d.ts.map +1 -0
- package/dist/validation/errors.d.ts +22 -2
- package/dist/validation/errors.d.ts.map +1 -1
- package/dist/validation/index.d.ts +7 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/rules/array.d.ts +33 -0
- package/dist/validation/rules/array.d.ts.map +1 -0
- package/dist/validation/rules/common.d.ts +90 -0
- package/dist/validation/rules/common.d.ts.map +1 -0
- package/dist/validation/rules/conditional.d.ts +30 -0
- package/dist/validation/rules/conditional.d.ts.map +1 -0
- package/dist/validation/rules/index.d.ts +5 -0
- package/dist/validation/rules/index.d.ts.map +1 -0
- package/dist/validation/rules/object.d.ts +30 -0
- package/dist/validation/rules/object.d.ts.map +1 -0
- package/dist/validation/types.d.ts +52 -1
- package/dist/validation/types.d.ts.map +1 -1
- package/docs/events.md +494 -0
- package/docs/guards.md +376 -0
- package/docs/guide.md +309 -1
- package/docs/request-lifecycle.md +444 -0
- package/docs/validation.md +407 -0
- package/docs/zh/events.md +494 -0
- package/docs/zh/guards.md +376 -0
- package/docs/zh/guide.md +309 -1
- package/docs/zh/request-lifecycle.md +444 -0
- package/docs/zh/validation.md +407 -0
- package/package.json +1 -1
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +10 -1
- package/src/di/module-registry.ts +39 -0
- package/src/events/decorators.ts +103 -0
- package/src/events/event-module.ts +272 -0
- package/src/events/index.ts +32 -0
- package/src/events/service.ts +352 -0
- package/src/events/types.ts +223 -0
- package/src/index.ts +133 -1
- package/src/security/filter.ts +88 -8
- package/src/security/guards/builtin/auth-guard.ts +68 -0
- package/src/security/guards/builtin/index.ts +3 -0
- package/src/security/guards/builtin/roles-guard.ts +165 -0
- package/src/security/guards/decorators.ts +124 -0
- package/src/security/guards/execution-context.ts +152 -0
- package/src/security/guards/guard-registry.ts +164 -0
- package/src/security/guards/index.ts +7 -0
- package/src/security/guards/reflector.ts +99 -0
- package/src/security/guards/types.ts +144 -0
- package/src/security/index.ts +1 -0
- package/src/security/security-module.ts +72 -2
- package/src/validation/class-validator.ts +322 -0
- package/src/validation/custom-validator.ts +289 -0
- package/src/validation/errors.ts +50 -2
- package/src/validation/index.ts +103 -1
- package/src/validation/rules/array.ts +118 -0
- package/src/validation/rules/common.ts +286 -0
- package/src/validation/rules/conditional.ts +52 -0
- package/src/validation/rules/index.ts +51 -0
- package/src/validation/rules/object.ts +86 -0
- package/src/validation/types.ts +61 -1
- package/tests/di/global-module.test.ts +487 -0
- package/tests/events/event-decorators.test.ts +173 -0
- package/tests/events/event-emitter.test.ts +373 -0
- package/tests/events/event-module.test.ts +373 -0
- package/tests/security/guards/guards-integration.test.ts +371 -0
- package/tests/security/guards/guards.test.ts +775 -0
- package/tests/security/security-module.test.ts +2 -2
- package/tests/validation/class-validator.test.ts +349 -0
- package/tests/validation/custom-validator.test.ts +335 -0
- package/tests/validation/rules.test.ts +543 -0
package/docs/events.md
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# Event System
|
|
2
|
+
|
|
3
|
+
The Event Module provides a powerful event-driven architecture for building loosely coupled, highly maintainable applications. It supports synchronous and asynchronous event handling, event priorities, and wildcard event patterns.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Core Concepts](#core-concepts)
|
|
10
|
+
- [Event Module Configuration](#event-module-configuration)
|
|
11
|
+
- [Publishing Events](#publishing-events)
|
|
12
|
+
- [Listening to Events](#listening-to-events)
|
|
13
|
+
- [Event Priority](#event-priority)
|
|
14
|
+
- [Async Event Handling](#async-event-handling)
|
|
15
|
+
- [Wildcard Events](#wildcard-events)
|
|
16
|
+
- [Error Handling](#error-handling)
|
|
17
|
+
- [Best Practices](#best-practices)
|
|
18
|
+
- [API Reference](#api-reference)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
The Event Module is included in `@dangao/bun-server`. No additional installation is required.
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import {
|
|
28
|
+
Application,
|
|
29
|
+
Module,
|
|
30
|
+
Injectable,
|
|
31
|
+
Inject,
|
|
32
|
+
EventModule,
|
|
33
|
+
OnEvent,
|
|
34
|
+
EVENT_EMITTER_TOKEN,
|
|
35
|
+
} from '@dangao/bun-server';
|
|
36
|
+
import type { EventEmitter } from '@dangao/bun-server';
|
|
37
|
+
|
|
38
|
+
// Define event
|
|
39
|
+
const USER_CREATED = Symbol('user.created');
|
|
40
|
+
|
|
41
|
+
interface UserCreatedEvent {
|
|
42
|
+
userId: string;
|
|
43
|
+
email: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Service that publishes events
|
|
47
|
+
@Injectable()
|
|
48
|
+
class UserService {
|
|
49
|
+
constructor(
|
|
50
|
+
@Inject(EVENT_EMITTER_TOKEN) private eventEmitter: EventEmitter,
|
|
51
|
+
) {}
|
|
52
|
+
|
|
53
|
+
async createUser(email: string) {
|
|
54
|
+
const userId = 'user-123';
|
|
55
|
+
|
|
56
|
+
// Publish event
|
|
57
|
+
this.eventEmitter.emit<UserCreatedEvent>(USER_CREATED, {
|
|
58
|
+
userId,
|
|
59
|
+
email,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return { userId, email };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Service that listens to events
|
|
67
|
+
@Injectable()
|
|
68
|
+
class NotificationService {
|
|
69
|
+
@OnEvent(USER_CREATED)
|
|
70
|
+
handleUserCreated(payload: UserCreatedEvent) {
|
|
71
|
+
console.log(`Welcome email sent to ${payload.email}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Configure module
|
|
76
|
+
EventModule.forRoot({
|
|
77
|
+
maxListeners: 20,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
EventModule.registerListeners([NotificationService]);
|
|
81
|
+
|
|
82
|
+
@Module({
|
|
83
|
+
imports: [EventModule],
|
|
84
|
+
providers: [UserService, NotificationService],
|
|
85
|
+
})
|
|
86
|
+
class AppModule {}
|
|
87
|
+
|
|
88
|
+
const app = new Application({ port: 3000 });
|
|
89
|
+
app.registerModule(AppModule);
|
|
90
|
+
|
|
91
|
+
// Initialize event listeners after module registration
|
|
92
|
+
EventModule.initializeListeners(app.getContainer());
|
|
93
|
+
|
|
94
|
+
app.listen(3000);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Core Concepts
|
|
98
|
+
|
|
99
|
+
### Event Names
|
|
100
|
+
|
|
101
|
+
Events can be identified by:
|
|
102
|
+
|
|
103
|
+
- **Symbol**: Recommended for type safety and avoiding naming conflicts
|
|
104
|
+
- **String**: Useful for dynamic events or wildcard matching
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Symbol event (recommended)
|
|
108
|
+
const USER_CREATED = Symbol('user.created');
|
|
109
|
+
|
|
110
|
+
// String event
|
|
111
|
+
const orderEvent = 'order.created';
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Event Payload
|
|
115
|
+
|
|
116
|
+
Events can carry any data as payload. It's recommended to define interfaces for type safety:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
interface UserCreatedEvent {
|
|
120
|
+
userId: string;
|
|
121
|
+
email: string;
|
|
122
|
+
createdAt: Date;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Publishing with typed payload
|
|
126
|
+
eventEmitter.emit<UserCreatedEvent>(USER_CREATED, {
|
|
127
|
+
userId: '123',
|
|
128
|
+
email: 'user@example.com',
|
|
129
|
+
createdAt: new Date(),
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Event Module Configuration
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
EventModule.forRoot({
|
|
137
|
+
// Enable wildcard event matching
|
|
138
|
+
wildcard: true, // default: false
|
|
139
|
+
|
|
140
|
+
// Delimiter for wildcard matching
|
|
141
|
+
delimiter: '.', // default: '.'
|
|
142
|
+
|
|
143
|
+
// Global prefix for all events
|
|
144
|
+
globalPrefix: 'app', // optional
|
|
145
|
+
|
|
146
|
+
// Maximum listeners per event (memory leak warning)
|
|
147
|
+
maxListeners: 10, // default: 10
|
|
148
|
+
|
|
149
|
+
// Custom error handler
|
|
150
|
+
onError: (error, event, payload) => {
|
|
151
|
+
console.error(`Error in event ${String(event)}:`, error);
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Publishing Events
|
|
157
|
+
|
|
158
|
+
### Synchronous Publishing
|
|
159
|
+
|
|
160
|
+
Use `emit()` to publish events synchronously. Async listeners will be triggered but not awaited:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
@Injectable()
|
|
164
|
+
class OrderService {
|
|
165
|
+
constructor(
|
|
166
|
+
@Inject(EVENT_EMITTER_TOKEN) private eventEmitter: EventEmitter,
|
|
167
|
+
) {}
|
|
168
|
+
|
|
169
|
+
createOrder(userId: string, amount: number) {
|
|
170
|
+
const order = { id: 'order-1', userId, amount };
|
|
171
|
+
|
|
172
|
+
// Publish event (doesn't wait for async listeners)
|
|
173
|
+
this.eventEmitter.emit('order.created', {
|
|
174
|
+
orderId: order.id,
|
|
175
|
+
userId,
|
|
176
|
+
amount,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return order;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Asynchronous Publishing
|
|
185
|
+
|
|
186
|
+
Use `emitAsync()` to wait for all listeners (including async ones) to complete:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
async createOrder(userId: string, amount: number) {
|
|
190
|
+
const order = { id: 'order-1', userId, amount };
|
|
191
|
+
|
|
192
|
+
// Wait for all listeners to complete
|
|
193
|
+
await this.eventEmitter.emitAsync('order.created', {
|
|
194
|
+
orderId: order.id,
|
|
195
|
+
userId,
|
|
196
|
+
amount,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return order;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Listening to Events
|
|
204
|
+
|
|
205
|
+
### Using `@OnEvent()` Decorator
|
|
206
|
+
|
|
207
|
+
The recommended way to listen to events:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
@Injectable()
|
|
211
|
+
class NotificationService {
|
|
212
|
+
@OnEvent('user.created')
|
|
213
|
+
handleUserCreated(payload: { email: string }) {
|
|
214
|
+
console.log(`Sending welcome email to ${payload.email}`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@OnEvent(USER_DELETED)
|
|
218
|
+
async handleUserDeleted(payload: { userId: string }) {
|
|
219
|
+
await this.cleanupUserData(payload.userId);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Manual Subscription
|
|
225
|
+
|
|
226
|
+
You can also subscribe manually using the EventEmitter:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
@Injectable()
|
|
230
|
+
class DynamicListener {
|
|
231
|
+
constructor(
|
|
232
|
+
@Inject(EVENT_EMITTER_TOKEN) private eventEmitter: EventEmitter,
|
|
233
|
+
) {
|
|
234
|
+
// Subscribe
|
|
235
|
+
const unsubscribe = this.eventEmitter.on('custom.event', (payload) => {
|
|
236
|
+
console.log('Received:', payload);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Later: unsubscribe
|
|
240
|
+
// unsubscribe();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### One-time Listeners
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
// Using decorator (listener is removed after first call)
|
|
249
|
+
// Note: @OnEvent doesn't support once directly, use manual subscription
|
|
250
|
+
|
|
251
|
+
// Manual one-time subscription
|
|
252
|
+
this.eventEmitter.once('setup.complete', (payload) => {
|
|
253
|
+
console.log('Setup completed!');
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Event Priority
|
|
258
|
+
|
|
259
|
+
Higher priority listeners execute first:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
@Injectable()
|
|
263
|
+
class HighPriorityService {
|
|
264
|
+
@OnEvent('order.created', { priority: 100 })
|
|
265
|
+
validateOrder(payload: OrderCreatedEvent) {
|
|
266
|
+
// Executes first
|
|
267
|
+
console.log('Validating order...');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@Injectable()
|
|
272
|
+
class LowPriorityService {
|
|
273
|
+
@OnEvent('order.created', { priority: 1 })
|
|
274
|
+
logOrder(payload: OrderCreatedEvent) {
|
|
275
|
+
// Executes last
|
|
276
|
+
console.log('Logging order...');
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Default priority is `0`. Listeners with the same priority execute in registration order.
|
|
282
|
+
|
|
283
|
+
## Async Event Handling
|
|
284
|
+
|
|
285
|
+
Mark listeners as async to ensure proper error handling:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
@Injectable()
|
|
289
|
+
class EmailService {
|
|
290
|
+
@OnEvent('user.created', { async: true })
|
|
291
|
+
async sendWelcomeEmail(payload: { email: string }) {
|
|
292
|
+
await this.mailer.send({
|
|
293
|
+
to: payload.email,
|
|
294
|
+
subject: 'Welcome!',
|
|
295
|
+
body: '...',
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
When using `emit()`:
|
|
302
|
+
- Async listeners are triggered but not awaited
|
|
303
|
+
- Errors are caught and passed to the error handler
|
|
304
|
+
|
|
305
|
+
When using `emitAsync()`:
|
|
306
|
+
- All listeners (sync and async) are awaited
|
|
307
|
+
- All errors are caught and handled
|
|
308
|
+
|
|
309
|
+
## Wildcard Events
|
|
310
|
+
|
|
311
|
+
Enable wildcard matching in the module configuration:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
EventModule.forRoot({
|
|
315
|
+
wildcard: true,
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Single Wildcard (`*`)
|
|
320
|
+
|
|
321
|
+
Matches exactly one segment:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
@OnEvent('user.*')
|
|
325
|
+
handleAnyUserEvent(payload: unknown) {
|
|
326
|
+
// Matches: user.created, user.updated, user.deleted
|
|
327
|
+
// Does NOT match: user.profile.updated
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Double Wildcard (`**`)
|
|
332
|
+
|
|
333
|
+
Matches any number of segments:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
@OnEvent('order.**')
|
|
337
|
+
handleAllOrderEvents(payload: unknown) {
|
|
338
|
+
// Matches: order.created, order.item.added, order.payment.completed
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Error Handling
|
|
343
|
+
|
|
344
|
+
### Global Error Handler
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
EventModule.forRoot({
|
|
348
|
+
onError: (error, event, payload) => {
|
|
349
|
+
// Log to monitoring service
|
|
350
|
+
console.error(`Event handler error for ${String(event)}:`, error);
|
|
351
|
+
|
|
352
|
+
// Optionally re-throw or handle
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Try-Catch in Listeners
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
@OnEvent('risky.event', { async: true })
|
|
361
|
+
async handleRiskyEvent(payload: unknown) {
|
|
362
|
+
try {
|
|
363
|
+
await this.riskyOperation(payload);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
// Handle or log error
|
|
366
|
+
console.error('Failed to process event:', error);
|
|
367
|
+
// Optionally re-throw to trigger global error handler
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Best Practices
|
|
374
|
+
|
|
375
|
+
### 1. Use Symbols for Event Names
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// Good: Type-safe, no naming conflicts
|
|
379
|
+
export const USER_CREATED = Symbol('user.created');
|
|
380
|
+
|
|
381
|
+
// Avoid: Can conflict with other events
|
|
382
|
+
const eventName = 'user.created';
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 2. Define Event Payload Interfaces
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
// Good: Clear contract
|
|
389
|
+
export interface UserCreatedEvent {
|
|
390
|
+
userId: string;
|
|
391
|
+
email: string;
|
|
392
|
+
createdAt: Date;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Avoid: Unclear payload structure
|
|
396
|
+
eventEmitter.emit(USER_CREATED, { userId, email, date: new Date() });
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### 3. Keep Listeners Focused
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
// Good: Single responsibility
|
|
403
|
+
@OnEvent(USER_CREATED)
|
|
404
|
+
sendWelcomeEmail(payload: UserCreatedEvent) { ... }
|
|
405
|
+
|
|
406
|
+
@OnEvent(USER_CREATED)
|
|
407
|
+
trackUserSignup(payload: UserCreatedEvent) { ... }
|
|
408
|
+
|
|
409
|
+
// Avoid: Too many responsibilities
|
|
410
|
+
@OnEvent(USER_CREATED)
|
|
411
|
+
handleUserCreated(payload: UserCreatedEvent) {
|
|
412
|
+
this.sendEmail();
|
|
413
|
+
this.trackAnalytics();
|
|
414
|
+
this.notifyAdmin();
|
|
415
|
+
this.createDefaultSettings();
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### 4. Handle Errors Gracefully
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
// Good: Errors don't break other listeners
|
|
423
|
+
@OnEvent('payment.failed', { async: true })
|
|
424
|
+
async handlePaymentFailure(payload: PaymentFailedEvent) {
|
|
425
|
+
try {
|
|
426
|
+
await this.notifyUser(payload.userId);
|
|
427
|
+
} catch (error) {
|
|
428
|
+
this.logger.error('Failed to notify user', error);
|
|
429
|
+
// Don't re-throw unless necessary
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### 5. Use Priority Wisely
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
// Good: Validation before processing
|
|
438
|
+
@OnEvent('order.submitted', { priority: 100 })
|
|
439
|
+
validateOrder(payload: OrderEvent) { ... }
|
|
440
|
+
|
|
441
|
+
@OnEvent('order.submitted', { priority: 50 })
|
|
442
|
+
processOrder(payload: OrderEvent) { ... }
|
|
443
|
+
|
|
444
|
+
@OnEvent('order.submitted', { priority: 1 })
|
|
445
|
+
logOrder(payload: OrderEvent) { ... }
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## API Reference
|
|
449
|
+
|
|
450
|
+
### EventModule
|
|
451
|
+
|
|
452
|
+
| Method | Description |
|
|
453
|
+
|--------|-------------|
|
|
454
|
+
| `forRoot(options?)` | Configure the event module |
|
|
455
|
+
| `registerListeners(classes)` | Register listener classes |
|
|
456
|
+
| `initializeListeners(container, additional?)` | Initialize and scan listeners |
|
|
457
|
+
| `getEventEmitter(container)` | Get the EventEmitter instance |
|
|
458
|
+
|
|
459
|
+
### EventEmitter
|
|
460
|
+
|
|
461
|
+
| Method | Description |
|
|
462
|
+
|--------|-------------|
|
|
463
|
+
| `emit(event, payload)` | Publish event synchronously |
|
|
464
|
+
| `emitAsync(event, payload)` | Publish event and wait for all listeners |
|
|
465
|
+
| `on(event, listener, options?)` | Subscribe to an event |
|
|
466
|
+
| `once(event, listener, options?)` | Subscribe once to an event |
|
|
467
|
+
| `off(event, listener)` | Unsubscribe from an event |
|
|
468
|
+
| `removeAllListeners(event?)` | Remove all listeners |
|
|
469
|
+
| `listenerCount(event)` | Get listener count for an event |
|
|
470
|
+
| `eventNames()` | Get all registered event names |
|
|
471
|
+
|
|
472
|
+
### @OnEvent Decorator
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
@OnEvent(event: string | symbol, options?: {
|
|
476
|
+
async?: boolean; // default: false
|
|
477
|
+
priority?: number; // default: 0
|
|
478
|
+
})
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### EventModuleOptions
|
|
482
|
+
|
|
483
|
+
| Option | Type | Default | Description |
|
|
484
|
+
|--------|------|---------|-------------|
|
|
485
|
+
| `wildcard` | `boolean` | `false` | Enable wildcard matching |
|
|
486
|
+
| `delimiter` | `string` | `'.'` | Wildcard delimiter |
|
|
487
|
+
| `globalPrefix` | `string` | - | Prefix for all events |
|
|
488
|
+
| `maxListeners` | `number` | `10` | Max listeners per event |
|
|
489
|
+
| `onError` | `function` | - | Global error handler |
|
|
490
|
+
|
|
491
|
+
## Related
|
|
492
|
+
|
|
493
|
+
- [Guide](./guide.md) - Framework guide
|
|
494
|
+
- [Request Lifecycle](./request-lifecycle.md) - Understanding the request flow
|