@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.
Files changed (116) hide show
  1. package/README.md +129 -21
  2. package/dist/di/decorators.d.ts +37 -0
  3. package/dist/di/decorators.d.ts.map +1 -1
  4. package/dist/di/index.d.ts +1 -1
  5. package/dist/di/index.d.ts.map +1 -1
  6. package/dist/di/module-registry.d.ts +17 -0
  7. package/dist/di/module-registry.d.ts.map +1 -1
  8. package/dist/events/decorators.d.ts +52 -0
  9. package/dist/events/decorators.d.ts.map +1 -0
  10. package/dist/events/event-module.d.ts +97 -0
  11. package/dist/events/event-module.d.ts.map +1 -0
  12. package/dist/events/index.d.ts +5 -0
  13. package/dist/events/index.d.ts.map +1 -0
  14. package/dist/events/service.d.ts +76 -0
  15. package/dist/events/service.d.ts.map +1 -0
  16. package/dist/events/types.d.ts +184 -0
  17. package/dist/events/types.d.ts.map +1 -0
  18. package/dist/index.d.ts +5 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1511 -11
  21. package/dist/security/filter.d.ts +23 -0
  22. package/dist/security/filter.d.ts.map +1 -1
  23. package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
  24. package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
  25. package/dist/security/guards/builtin/index.d.ts +3 -0
  26. package/dist/security/guards/builtin/index.d.ts.map +1 -0
  27. package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
  28. package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
  29. package/dist/security/guards/decorators.d.ts +50 -0
  30. package/dist/security/guards/decorators.d.ts.map +1 -0
  31. package/dist/security/guards/execution-context.d.ts +56 -0
  32. package/dist/security/guards/execution-context.d.ts.map +1 -0
  33. package/dist/security/guards/guard-registry.d.ts +67 -0
  34. package/dist/security/guards/guard-registry.d.ts.map +1 -0
  35. package/dist/security/guards/index.d.ts +7 -0
  36. package/dist/security/guards/index.d.ts.map +1 -0
  37. package/dist/security/guards/reflector.d.ts +57 -0
  38. package/dist/security/guards/reflector.d.ts.map +1 -0
  39. package/dist/security/guards/types.d.ts +126 -0
  40. package/dist/security/guards/types.d.ts.map +1 -0
  41. package/dist/security/index.d.ts +1 -0
  42. package/dist/security/index.d.ts.map +1 -1
  43. package/dist/security/security-module.d.ts +20 -0
  44. package/dist/security/security-module.d.ts.map +1 -1
  45. package/dist/validation/class-validator.d.ts +108 -0
  46. package/dist/validation/class-validator.d.ts.map +1 -0
  47. package/dist/validation/custom-validator.d.ts +130 -0
  48. package/dist/validation/custom-validator.d.ts.map +1 -0
  49. package/dist/validation/errors.d.ts +22 -2
  50. package/dist/validation/errors.d.ts.map +1 -1
  51. package/dist/validation/index.d.ts +7 -1
  52. package/dist/validation/index.d.ts.map +1 -1
  53. package/dist/validation/rules/array.d.ts +33 -0
  54. package/dist/validation/rules/array.d.ts.map +1 -0
  55. package/dist/validation/rules/common.d.ts +90 -0
  56. package/dist/validation/rules/common.d.ts.map +1 -0
  57. package/dist/validation/rules/conditional.d.ts +30 -0
  58. package/dist/validation/rules/conditional.d.ts.map +1 -0
  59. package/dist/validation/rules/index.d.ts +5 -0
  60. package/dist/validation/rules/index.d.ts.map +1 -0
  61. package/dist/validation/rules/object.d.ts +30 -0
  62. package/dist/validation/rules/object.d.ts.map +1 -0
  63. package/dist/validation/types.d.ts +52 -1
  64. package/dist/validation/types.d.ts.map +1 -1
  65. package/docs/events.md +494 -0
  66. package/docs/guards.md +376 -0
  67. package/docs/guide.md +309 -1
  68. package/docs/request-lifecycle.md +444 -0
  69. package/docs/validation.md +407 -0
  70. package/docs/zh/events.md +494 -0
  71. package/docs/zh/guards.md +376 -0
  72. package/docs/zh/guide.md +309 -1
  73. package/docs/zh/request-lifecycle.md +444 -0
  74. package/docs/zh/validation.md +407 -0
  75. package/package.json +1 -1
  76. package/src/di/decorators.ts +46 -0
  77. package/src/di/index.ts +10 -1
  78. package/src/di/module-registry.ts +39 -0
  79. package/src/events/decorators.ts +103 -0
  80. package/src/events/event-module.ts +272 -0
  81. package/src/events/index.ts +32 -0
  82. package/src/events/service.ts +352 -0
  83. package/src/events/types.ts +223 -0
  84. package/src/index.ts +133 -1
  85. package/src/security/filter.ts +88 -8
  86. package/src/security/guards/builtin/auth-guard.ts +68 -0
  87. package/src/security/guards/builtin/index.ts +3 -0
  88. package/src/security/guards/builtin/roles-guard.ts +165 -0
  89. package/src/security/guards/decorators.ts +124 -0
  90. package/src/security/guards/execution-context.ts +152 -0
  91. package/src/security/guards/guard-registry.ts +164 -0
  92. package/src/security/guards/index.ts +7 -0
  93. package/src/security/guards/reflector.ts +99 -0
  94. package/src/security/guards/types.ts +144 -0
  95. package/src/security/index.ts +1 -0
  96. package/src/security/security-module.ts +72 -2
  97. package/src/validation/class-validator.ts +322 -0
  98. package/src/validation/custom-validator.ts +289 -0
  99. package/src/validation/errors.ts +50 -2
  100. package/src/validation/index.ts +103 -1
  101. package/src/validation/rules/array.ts +118 -0
  102. package/src/validation/rules/common.ts +286 -0
  103. package/src/validation/rules/conditional.ts +52 -0
  104. package/src/validation/rules/index.ts +51 -0
  105. package/src/validation/rules/object.ts +86 -0
  106. package/src/validation/types.ts +61 -1
  107. package/tests/di/global-module.test.ts +487 -0
  108. package/tests/events/event-decorators.test.ts +173 -0
  109. package/tests/events/event-emitter.test.ts +373 -0
  110. package/tests/events/event-module.test.ts +373 -0
  111. package/tests/security/guards/guards-integration.test.ts +371 -0
  112. package/tests/security/guards/guards.test.ts +775 -0
  113. package/tests/security/security-module.test.ts +2 -2
  114. package/tests/validation/class-validator.test.ts +349 -0
  115. package/tests/validation/custom-validator.test.ts +335 -0
  116. package/tests/validation/rules.test.ts +543 -0
package/docs/guards.md ADDED
@@ -0,0 +1,376 @@
1
+ # Guards
2
+
3
+ Guards are a powerful mechanism for controlling access to routes in your application. They execute before the route handler and determine whether a request should be processed.
4
+
5
+ ## Overview
6
+
7
+ Guards implement the `CanActivate` interface and return a boolean value indicating whether the request should be allowed to proceed. Unlike middleware, guards have access to the `ExecutionContext`, which provides rich information about the current request context.
8
+
9
+ ### When to Use Guards
10
+
11
+ - **Authentication**: Verify if a user is authenticated
12
+ - **Authorization**: Check if a user has the required roles or permissions
13
+ - **Rate limiting**: Implement custom rate limiting logic
14
+ - **Feature flags**: Enable/disable features based on conditions
15
+ - **Request validation**: Validate request metadata before processing
16
+
17
+ ## Basic Usage
18
+
19
+ ### Creating a Guard
20
+
21
+ ```typescript
22
+ import { Injectable } from '@dangao/bun-server';
23
+ import type { CanActivate, ExecutionContext } from '@dangao/bun-server';
24
+
25
+ @Injectable()
26
+ class AuthGuard implements CanActivate {
27
+ canActivate(context: ExecutionContext): boolean | Promise<boolean> {
28
+ const request = context.switchToHttp().getRequest();
29
+ const token = request.getHeader('authorization');
30
+
31
+ // Validate token and return true/false
32
+ return !!token;
33
+ }
34
+ }
35
+ ```
36
+
37
+ ### Applying Guards
38
+
39
+ Guards can be applied at different levels:
40
+
41
+ #### Controller Level
42
+
43
+ ```typescript
44
+ import { Controller, GET, UseGuards } from '@dangao/bun-server';
45
+
46
+ @Controller('/api/users')
47
+ @UseGuards(AuthGuard)
48
+ class UserController {
49
+ @GET('/')
50
+ getUsers() {
51
+ return { users: [] };
52
+ }
53
+ }
54
+ ```
55
+
56
+ #### Method Level
57
+
58
+ ```typescript
59
+ @Controller('/api')
60
+ class ApiController {
61
+ @GET('/public')
62
+ publicEndpoint() {
63
+ return { message: 'Public' };
64
+ }
65
+
66
+ @GET('/private')
67
+ @UseGuards(AuthGuard)
68
+ privateEndpoint() {
69
+ return { message: 'Private' };
70
+ }
71
+ }
72
+ ```
73
+
74
+ #### Global Guards
75
+
76
+ ```typescript
77
+ import { SecurityModule } from '@dangao/bun-server';
78
+
79
+ SecurityModule.forRoot({
80
+ jwt: { secret: 'your-secret' },
81
+ globalGuards: [AuthGuard, RolesGuard],
82
+ });
83
+ ```
84
+
85
+ ## Execution Order
86
+
87
+ Guards execute in the following order:
88
+
89
+ 1. **Global guards** (in registration order)
90
+ 2. **Controller guards** (in decoration order)
91
+ 3. **Method guards** (in decoration order)
92
+
93
+ ```
94
+ HTTP Request
95
+
96
+ Middleware Pipeline
97
+
98
+ Security Filter (Token extraction & Authentication)
99
+
100
+ Guards (Global → Controller → Method)
101
+
102
+ Interceptors (Pre)
103
+
104
+ Route Handler
105
+
106
+ Interceptors (Post)
107
+
108
+ HTTP Response
109
+ ```
110
+
111
+ ## Built-in Guards
112
+
113
+ ### AuthGuard
114
+
115
+ Checks if the user is authenticated:
116
+
117
+ ```typescript
118
+ import { Controller, GET, UseGuards, AuthGuard } from '@dangao/bun-server';
119
+
120
+ @Controller('/api/profile')
121
+ @UseGuards(AuthGuard)
122
+ class ProfileController {
123
+ @GET('/')
124
+ getProfile() {
125
+ // Only authenticated users can access
126
+ return { profile: {} };
127
+ }
128
+ }
129
+ ```
130
+
131
+ ### OptionalAuthGuard
132
+
133
+ Allows access without authentication but validates token if present:
134
+
135
+ ```typescript
136
+ import { Controller, GET, UseGuards, OptionalAuthGuard } from '@dangao/bun-server';
137
+
138
+ @Controller('/api/posts')
139
+ class PostController {
140
+ @GET('/')
141
+ @UseGuards(OptionalAuthGuard)
142
+ getPosts() {
143
+ // Access allowed with or without authentication
144
+ return { posts: [] };
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### RolesGuard
150
+
151
+ Checks if the user has required roles:
152
+
153
+ ```typescript
154
+ import { Controller, GET, UseGuards, AuthGuard, RolesGuard, Roles } from '@dangao/bun-server';
155
+
156
+ @Controller('/api/admin')
157
+ @UseGuards(AuthGuard, RolesGuard)
158
+ class AdminController {
159
+ @GET('/dashboard')
160
+ @Roles('admin')
161
+ dashboard() {
162
+ return { message: 'Admin Dashboard' };
163
+ }
164
+
165
+ @GET('/super')
166
+ @Roles('admin', 'superadmin') // Either role grants access
167
+ superAdmin() {
168
+ return { message: 'Super Admin' };
169
+ }
170
+ }
171
+ ```
172
+
173
+ ## Custom Guards
174
+
175
+ ### Basic Custom Guard
176
+
177
+ ```typescript
178
+ import { Injectable } from '@dangao/bun-server';
179
+ import type { CanActivate, ExecutionContext } from '@dangao/bun-server';
180
+
181
+ @Injectable()
182
+ class ApiKeyGuard implements CanActivate {
183
+ private readonly validApiKeys = ['key1', 'key2'];
184
+
185
+ canActivate(context: ExecutionContext): boolean {
186
+ const request = context.switchToHttp().getRequest();
187
+ const apiKey = request.getHeader('x-api-key');
188
+
189
+ return this.validApiKeys.includes(apiKey || '');
190
+ }
191
+ }
192
+ ```
193
+
194
+ ### Async Guard
195
+
196
+ ```typescript
197
+ @Injectable()
198
+ class SubscriptionGuard implements CanActivate {
199
+ constructor(private readonly subscriptionService: SubscriptionService) {}
200
+
201
+ async canActivate(context: ExecutionContext): Promise<boolean> {
202
+ const request = context.switchToHttp().getRequest();
203
+ const userId = request.auth?.user?.id;
204
+
205
+ if (!userId) return false;
206
+
207
+ const subscription = await this.subscriptionService.getSubscription(userId);
208
+ return subscription?.isActive ?? false;
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### Guard with Metadata Access
214
+
215
+ ```typescript
216
+ @Injectable()
217
+ class FeatureFlagGuard implements CanActivate {
218
+ constructor(private readonly reflector: Reflector) {}
219
+
220
+ canActivate(context: ExecutionContext): boolean {
221
+ const feature = this.reflector.getAllAndOverride<string>(
222
+ 'feature',
223
+ context.getClass(),
224
+ context.getMethodName(),
225
+ );
226
+
227
+ if (!feature) return true;
228
+
229
+ return this.isFeatureEnabled(feature);
230
+ }
231
+
232
+ private isFeatureEnabled(feature: string): boolean {
233
+ // Check feature flag status
234
+ return true;
235
+ }
236
+ }
237
+ ```
238
+
239
+ ## ExecutionContext
240
+
241
+ The `ExecutionContext` provides access to:
242
+
243
+ ### HTTP Context
244
+
245
+ ```typescript
246
+ const httpHost = context.switchToHttp();
247
+ const request = httpHost.getRequest(); // Context object
248
+ const response = httpHost.getResponse(); // ResponseBuilder (if available)
249
+ ```
250
+
251
+ ### Controller and Method Info
252
+
253
+ ```typescript
254
+ const controllerClass = context.getClass(); // Controller class
255
+ const handler = context.getHandler(); // Method function
256
+ const methodName = context.getMethodName(); // Method name string
257
+ ```
258
+
259
+ ### Metadata Access
260
+
261
+ ```typescript
262
+ const metadata = context.getMetadata<string[]>('roles');
263
+ // First checks method, then class
264
+ ```
265
+
266
+ ## Reflector Utility
267
+
268
+ The `Reflector` class helps with metadata retrieval:
269
+
270
+ ```typescript
271
+ import { Reflector, REFLECTOR_TOKEN } from '@dangao/bun-server';
272
+
273
+ @Injectable()
274
+ class MyGuard implements CanActivate {
275
+ constructor(@Inject(REFLECTOR_TOKEN) private readonly reflector: Reflector) {}
276
+
277
+ canActivate(context: ExecutionContext): boolean {
278
+ // Get metadata with method priority
279
+ const roles = this.reflector.getAllAndOverride<string[]>(
280
+ ROLES_METADATA_KEY,
281
+ context.getClass(),
282
+ context.getMethodName(),
283
+ );
284
+
285
+ // Merge class and method metadata
286
+ const permissions = this.reflector.getAllAndMerge<string[]>(
287
+ 'permissions',
288
+ context.getClass(),
289
+ context.getMethodName(),
290
+ );
291
+
292
+ return true;
293
+ }
294
+ }
295
+ ```
296
+
297
+ ## Custom Roles Guard Factory
298
+
299
+ Create customized role guards:
300
+
301
+ ```typescript
302
+ import { createRolesGuard } from '@dangao/bun-server';
303
+
304
+ // Require ALL roles instead of ANY
305
+ const AllRolesGuard = createRolesGuard({ matchAll: true });
306
+
307
+ // Custom role extraction
308
+ const CustomRolesGuard = createRolesGuard({
309
+ getRoles: (context) => {
310
+ const request = context.switchToHttp().getRequest();
311
+ return request.auth?.user?.permissions || [];
312
+ },
313
+ });
314
+ ```
315
+
316
+ ## Error Handling
317
+
318
+ Guards can throw exceptions to reject requests:
319
+
320
+ ```typescript
321
+ import { ForbiddenException, UnauthorizedException } from '@dangao/bun-server';
322
+
323
+ @Injectable()
324
+ class StrictAuthGuard implements CanActivate {
325
+ canActivate(context: ExecutionContext): boolean {
326
+ const request = context.switchToHttp().getRequest();
327
+
328
+ if (!request.auth?.isAuthenticated) {
329
+ throw new UnauthorizedException('Authentication required');
330
+ }
331
+
332
+ if (!this.hasPermission(request.auth.user)) {
333
+ throw new ForbiddenException('Insufficient permissions');
334
+ }
335
+
336
+ return true;
337
+ }
338
+ }
339
+ ```
340
+
341
+ ## Best Practices
342
+
343
+ 1. **Keep guards focused**: Each guard should handle one concern
344
+ 2. **Use dependency injection**: Inject services for complex logic
345
+ 3. **Prefer throwing exceptions**: Provides better error messages than returning false
346
+ 4. **Order matters**: Apply AuthGuard before RolesGuard
347
+ 5. **Use built-in guards**: Leverage AuthGuard and RolesGuard when possible
348
+ 6. **Test your guards**: Write unit tests for guard logic
349
+
350
+ ## Integration with SecurityModule
351
+
352
+ Guards work seamlessly with the SecurityModule:
353
+
354
+ ```typescript
355
+ import { Application, SecurityModule, AuthGuard, RolesGuard } from '@dangao/bun-server';
356
+
357
+ const app = new Application();
358
+
359
+ app.registerModule(
360
+ SecurityModule.forRoot({
361
+ jwt: {
362
+ secret: 'your-jwt-secret',
363
+ accessTokenExpiresIn: 3600,
364
+ },
365
+ excludePaths: ['/public', '/health'],
366
+ globalGuards: [AuthGuard], // Applied to all routes
367
+ }),
368
+ );
369
+ ```
370
+
371
+ ## See Also
372
+
373
+ - [Request Lifecycle](./request-lifecycle.md)
374
+ - [Security Module](./guide.md#security-module)
375
+ - [Custom Decorators](./custom-decorators.md)
376
+
package/docs/guide.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  Covers key steps for building Bun Server applications from scratch.
4
4
 
5
+ ## Request Lifecycle Overview
6
+
7
+ Before diving into implementation details, it's helpful to understand how Bun Server processes requests:
8
+
9
+ ```
10
+ HTTP Request → Middleware → Security → Router → Interceptors(Pre) → Validation → Handler → Interceptors(Post) → Exception Filter → HTTP Response
11
+ ```
12
+
13
+ For detailed lifecycle documentation, see [Request Lifecycle](./request-lifecycle.md).
14
+
5
15
  ## 1. Initialize Application
6
16
 
7
17
  ```ts
@@ -620,7 +630,305 @@ class UserController {
620
630
  }
621
631
  ```
622
632
 
623
- ## 16. Testing Recommendations
633
+ ## 16. Guards
634
+
635
+ Guards provide fine-grained access control for your routes. They execute after middleware and before interceptors, deciding whether a request should proceed.
636
+
637
+ ### Built-in Guards
638
+
639
+ ```ts
640
+ import {
641
+ AuthGuard,
642
+ Controller,
643
+ GET,
644
+ Roles,
645
+ RolesGuard,
646
+ UseGuards,
647
+ } from "@dangao/bun-server";
648
+
649
+ @Controller("/api/admin")
650
+ @UseGuards(AuthGuard, RolesGuard)
651
+ class AdminController {
652
+ @GET("/dashboard")
653
+ @Roles("admin")
654
+ public dashboard() {
655
+ return { message: "Admin Dashboard" };
656
+ }
657
+
658
+ @GET("/users")
659
+ @Roles("admin", "moderator") // Either role grants access
660
+ public listUsers() {
661
+ return { users: [] };
662
+ }
663
+ }
664
+ ```
665
+
666
+ ### Custom Guards
667
+
668
+ ```ts
669
+ import { Injectable } from "@dangao/bun-server";
670
+ import type { CanActivate, ExecutionContext } from "@dangao/bun-server";
671
+
672
+ @Injectable()
673
+ class ApiKeyGuard implements CanActivate {
674
+ canActivate(context: ExecutionContext): boolean {
675
+ const request = context.switchToHttp().getRequest();
676
+ const apiKey = request.getHeader("x-api-key");
677
+ return apiKey === "valid-api-key";
678
+ }
679
+ }
680
+
681
+ @Controller("/api/external")
682
+ @UseGuards(ApiKeyGuard)
683
+ class ExternalApiController {
684
+ @GET("/data")
685
+ public getData() {
686
+ return { data: [] };
687
+ }
688
+ }
689
+ ```
690
+
691
+ ### Global Guards
692
+
693
+ ```ts
694
+ SecurityModule.forRoot({
695
+ jwt: { secret: "your-secret" },
696
+ globalGuards: [AuthGuard], // Applied to all routes
697
+ });
698
+ ```
699
+
700
+ For detailed documentation, see [Guards](./guards.md).
701
+
702
+ ## 17. Event System
703
+
704
+ The Event Module provides a powerful event-driven architecture for building loosely coupled applications.
705
+
706
+ ### Basic Usage
707
+
708
+ ```ts
709
+ import {
710
+ EventModule,
711
+ Injectable,
712
+ Inject,
713
+ OnEvent,
714
+ EVENT_EMITTER_TOKEN,
715
+ } from "@dangao/bun-server";
716
+ import type { EventEmitter } from "@dangao/bun-server";
717
+
718
+ // Define events
719
+ const USER_CREATED = Symbol("user.created");
720
+
721
+ interface UserCreatedEvent {
722
+ userId: string;
723
+ email: string;
724
+ }
725
+
726
+ // Service that publishes events
727
+ @Injectable()
728
+ class UserService {
729
+ public constructor(
730
+ @Inject(EVENT_EMITTER_TOKEN) private readonly eventEmitter: EventEmitter,
731
+ ) {}
732
+
733
+ public async createUser(email: string) {
734
+ const userId = "user-123";
735
+
736
+ // Publish event
737
+ this.eventEmitter.emit<UserCreatedEvent>(USER_CREATED, {
738
+ userId,
739
+ email,
740
+ });
741
+
742
+ return { userId, email };
743
+ }
744
+ }
745
+
746
+ // Service that listens to events
747
+ @Injectable()
748
+ class NotificationService {
749
+ @OnEvent(USER_CREATED)
750
+ public handleUserCreated(payload: UserCreatedEvent) {
751
+ console.log(`Welcome email sent to ${payload.email}`);
752
+ }
753
+
754
+ @OnEvent(USER_CREATED, { async: true, priority: 10 })
755
+ public async trackUserCreation(payload: UserCreatedEvent) {
756
+ await this.analytics.track("user_created", payload);
757
+ }
758
+ }
759
+ ```
760
+
761
+ ### Module Configuration
762
+
763
+ ```ts
764
+ EventModule.forRoot({
765
+ wildcard: true, // Enable wildcard matching
766
+ maxListeners: 20, // Max listeners per event
767
+ onError: (error, event, payload) => {
768
+ console.error(`Error in event ${String(event)}:`, error);
769
+ },
770
+ });
771
+
772
+ // Register listener classes
773
+ EventModule.registerListeners([NotificationService, AnalyticsService]);
774
+
775
+ @Module({
776
+ imports: [EventModule],
777
+ providers: [UserService, NotificationService, AnalyticsService],
778
+ })
779
+ class AppModule {}
780
+
781
+ const app = new Application();
782
+ app.registerModule(AppModule);
783
+
784
+ // Initialize event listeners after module registration
785
+ EventModule.initializeListeners(app.getContainer());
786
+ ```
787
+
788
+ ### Wildcard Events
789
+
790
+ ```ts
791
+ // Match any user event: user.created, user.updated, user.deleted
792
+ @OnEvent("user.*")
793
+ handleAnyUserEvent(payload: unknown) {}
794
+
795
+ // Match nested events: order.created, order.item.added, order.payment.completed
796
+ @OnEvent("order.**")
797
+ handleAllOrderEvents(payload: unknown) {}
798
+ ```
799
+
800
+ ### Async Event Publishing
801
+
802
+ ```ts
803
+ // Fire and forget (async listeners are triggered but not awaited)
804
+ this.eventEmitter.emit("order.created", orderData);
805
+
806
+ // Wait for all listeners to complete
807
+ await this.eventEmitter.emitAsync("order.created", orderData);
808
+ ```
809
+
810
+ For detailed documentation, see [Event System](./events.md).
811
+
812
+ ## 18. Global Modules
813
+
814
+ Global modules allow you to share providers across all modules without explicit imports. This is useful for commonly used services like configuration, logging, or caching.
815
+
816
+ ### Creating a Global Module
817
+
818
+ Use the `@Global()` decorator to mark a module as global:
819
+
820
+ ```ts
821
+ import { Global, Injectable, Module } from "@dangao/bun-server";
822
+
823
+ const CONFIG_TOKEN = Symbol("config");
824
+
825
+ @Injectable()
826
+ class ConfigService {
827
+ public get(key: string): string {
828
+ return `config:${key}`;
829
+ }
830
+ }
831
+
832
+ @Global()
833
+ @Module({
834
+ providers: [
835
+ {
836
+ provide: CONFIG_TOKEN,
837
+ useClass: ConfigService,
838
+ },
839
+ ],
840
+ exports: [CONFIG_TOKEN],
841
+ })
842
+ class GlobalConfigModule {}
843
+ ```
844
+
845
+ ### Using Global Module Exports
846
+
847
+ Other modules can use the exported providers without importing the global module:
848
+
849
+ ```ts
850
+ @Injectable()
851
+ class UserService {
852
+ public constructor(
853
+ @Inject(CONFIG_TOKEN) private readonly config: ConfigService,
854
+ ) {}
855
+
856
+ public getAppName(): string {
857
+ return this.config.get("app.name");
858
+ }
859
+ }
860
+
861
+ // UserModule does NOT need to import GlobalConfigModule
862
+ @Module({
863
+ providers: [UserService],
864
+ })
865
+ class UserModule {}
866
+ ```
867
+
868
+ ### Registering Global Modules
869
+
870
+ Global modules must be registered with the application, typically in your root module:
871
+
872
+ ```ts
873
+ @Module({
874
+ imports: [
875
+ GlobalConfigModule, // Register the global module once
876
+ GlobalLoggerModule,
877
+ UserModule, // UserModule can use ConfigService without importing it
878
+ ProductModule,
879
+ ],
880
+ })
881
+ class AppModule {}
882
+
883
+ const app = new Application();
884
+ app.registerModule(AppModule);
885
+ ```
886
+
887
+ ### Key Points
888
+
889
+ - **Single Registration**: Global modules only need to be registered once (usually in the root module)
890
+ - **Automatic Availability**: Exports from global modules are available to all other modules
891
+ - **Singleton Sharing**: Global module providers maintain singleton behavior across the application
892
+ - **No Import Required**: Other modules don't need to add global modules to their `imports` array
893
+
894
+ ### Use Cases
895
+
896
+ Global modules are ideal for:
897
+
898
+ - **Configuration Services**: App-wide configuration access
899
+ - **Logging Services**: Centralized logging
900
+ - **Cache Services**: Shared caching layer
901
+ - **Database Connections**: Shared database access
902
+ - **Event Emitters**: Application-wide event bus
903
+
904
+ ### Example: Multiple Global Modules
905
+
906
+ ```ts
907
+ @Global()
908
+ @Module({
909
+ providers: [{ provide: LOGGER_TOKEN, useClass: LoggerService }],
910
+ exports: [LOGGER_TOKEN],
911
+ })
912
+ class GlobalLoggerModule {}
913
+
914
+ @Global()
915
+ @Module({
916
+ providers: [{ provide: CACHE_TOKEN, useClass: CacheService }],
917
+ exports: [CACHE_TOKEN],
918
+ })
919
+ class GlobalCacheModule {}
920
+
921
+ // App service can use both without explicit imports
922
+ @Injectable()
923
+ class AppService {
924
+ public constructor(
925
+ @Inject(LOGGER_TOKEN) private readonly logger: LoggerService,
926
+ @Inject(CACHE_TOKEN) private readonly cache: CacheService,
927
+ ) {}
928
+ }
929
+ ```
930
+
931
+ ## 19. Testing Recommendations
624
932
 
625
933
  - Use `tests/utils/test-port.ts` to get auto-incrementing ports, avoiding local
626
934
  conflicts.