@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
@@ -0,0 +1,407 @@
1
+ # 验证系统
2
+
3
+ Bun Server Framework 提供了强大的验证系统,用于验证请求参数、DTO 和复杂数据结构。
4
+
5
+ ## 目录
6
+
7
+ - [基础验证](#基础验证)
8
+ - [验证规则](#验证规则)
9
+ - [对象规则](#对象规则)
10
+ - [数组规则](#数组规则)
11
+ - [通用规则](#通用规则)
12
+ - [条件规则](#条件规则)
13
+ - [类级别验证](#类级别验证)
14
+ - [嵌套对象验证](#嵌套对象验证)
15
+ - [自定义验证器](#自定义验证器)
16
+ - [内置扩展验证器](#内置扩展验证器)
17
+ - [错误处理](#错误处理)
18
+
19
+ ## 基础验证
20
+
21
+ 在控制器方法参数上使用 `@Validate()` 装饰器:
22
+
23
+ ```typescript
24
+ import { Controller, GET, Query, Validate, IsString, IsEmail, IsOptional, MinLength } from '@dangao/bun-server';
25
+
26
+ @Controller('/api/users')
27
+ class UserController {
28
+ @GET('/search')
29
+ public search(
30
+ @Query('email') @Validate(IsEmail()) email: string,
31
+ @Query('name') @Validate(IsOptional(), IsString(), MinLength(2)) name?: string,
32
+ ) {
33
+ return { email, name };
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## 验证规则
39
+
40
+ ### 对象规则
41
+
42
+ ```typescript
43
+ import { IsObject, IsNotEmpty, IsNotEmptyObject, ValidateNested } from '@dangao/bun-server';
44
+
45
+ // 验证值是否为对象
46
+ IsObject()
47
+
48
+ // 验证值是否非空(null、undefined、空字符串、空数组、空对象)
49
+ IsNotEmpty()
50
+
51
+ // 验证值是否为非空对象
52
+ IsNotEmptyObject()
53
+
54
+ // 标记属性需要嵌套验证
55
+ ValidateNested({ each?: boolean }) // each: true 用于数组元素
56
+ ```
57
+
58
+ ### 数组规则
59
+
60
+ ```typescript
61
+ import {
62
+ IsArray, ArrayMinSize, ArrayMaxSize, ArrayUnique,
63
+ ArrayContains, ArrayNotContains, ArrayNotEmpty
64
+ } from '@dangao/bun-server';
65
+
66
+ // 验证值是否为数组
67
+ IsArray()
68
+
69
+ // 验证数组最小长度
70
+ ArrayMinSize(2)
71
+
72
+ // 验证数组最大长度
73
+ ArrayMaxSize(10)
74
+
75
+ // 验证数组元素是否唯一
76
+ ArrayUnique()
77
+
78
+ // 验证数组是否包含指定值
79
+ ArrayContains([1, 2])
80
+
81
+ // 验证数组是否不包含指定值
82
+ ArrayNotContains(['banned'])
83
+
84
+ // 验证数组是否非空
85
+ ArrayNotEmpty()
86
+ ```
87
+
88
+ ### 通用规则
89
+
90
+ ```typescript
91
+ import {
92
+ IsString, IsNumber, IsBoolean, IsInt, IsPositive, IsNegative,
93
+ Min, Max, IsDate, IsUUID, Length, MaxLength, MinLength,
94
+ Matches, IsIn, IsNotIn, IsUrl, IsJSON, IsEmail,
95
+ Equals, NotEquals, IsDefined, IsAlphanumeric, IsAlpha, IsNumberString
96
+ } from '@dangao/bun-server';
97
+
98
+ // 类型验证
99
+ IsString()
100
+ IsNumber()
101
+ IsBoolean()
102
+ IsInt()
103
+ IsDate()
104
+
105
+ // 数字验证
106
+ IsPositive()
107
+ IsNegative()
108
+ Min(0)
109
+ Max(100)
110
+
111
+ // 字符串验证
112
+ IsEmail()
113
+ IsUUID('4') // '3', '4', '5', 或 'all'
114
+ Length(2, 10)
115
+ MinLength(2)
116
+ MaxLength(10)
117
+ Matches(/^[a-z]+$/)
118
+ IsUrl()
119
+ IsJSON()
120
+ IsAlphanumeric()
121
+ IsAlpha()
122
+ IsNumberString()
123
+
124
+ // 值验证
125
+ IsIn(['a', 'b', 'c'])
126
+ IsNotIn(['x', 'y', 'z'])
127
+ Equals('expected')
128
+ NotEquals('forbidden')
129
+ IsDefined()
130
+ ```
131
+
132
+ ### 条件规则
133
+
134
+ ```typescript
135
+ import { ValidateIf, Transform } from '@dangao/bun-server';
136
+
137
+ // 条件验证 - 仅当条件为 true 时执行验证
138
+ ValidateIf((value, obj) => obj.type === 'premium')
139
+
140
+ // 验证前转换值
141
+ Transform((value) => String(value).trim())
142
+ Transform((value) => Number(value))
143
+ ```
144
+
145
+ 使用示例:
146
+
147
+ ```typescript
148
+ @ValidateClass()
149
+ class UpdateUserDto {
150
+ @Property(ValidateIf((_, obj) => obj.type === 'premium'), IsEmail())
151
+ premiumEmail?: string;
152
+
153
+ @Property(Transform((v) => String(v).trim()), IsString(), MinLength(1))
154
+ name: string;
155
+
156
+ @Property(Transform((v) => Number(v)), IsInt(), Min(0))
157
+ age: number;
158
+ }
159
+ ```
160
+
161
+ ## 类级别验证
162
+
163
+ 对于 DTO 类,使用 `@ValidateClass()` 和 `@Property()` 装饰器:
164
+
165
+ ```typescript
166
+ import { ValidateClass, Property, IsString, IsEmail, IsOptional, IsInt, Min, Max, MinLength } from '@dangao/bun-server';
167
+
168
+ @ValidateClass()
169
+ class CreateUserDto {
170
+ @Property(IsString(), MinLength(2))
171
+ name: string;
172
+
173
+ @Property(IsEmail())
174
+ email: string;
175
+
176
+ @Property(IsOptional(), IsInt(), Min(0), Max(150))
177
+ age?: number;
178
+ }
179
+ ```
180
+
181
+ 手动验证对象:
182
+
183
+ ```typescript
184
+ import { validateObject, validateObjectSync, ValidationError } from '@dangao/bun-server';
185
+
186
+ // 失败时抛出 ValidationError
187
+ try {
188
+ validateObject(data, CreateUserDto);
189
+ } catch (error) {
190
+ if (error instanceof ValidationError) {
191
+ console.log(error.issues);
192
+ }
193
+ }
194
+
195
+ // 返回验证结果而不抛出异常
196
+ const result = validateObjectSync(data, CreateUserDto);
197
+ if (!result.valid) {
198
+ console.log(result.issues);
199
+ }
200
+ ```
201
+
202
+ ## 嵌套对象验证
203
+
204
+ 用于嵌套对象和数组:
205
+
206
+ ```typescript
207
+ import { ValidateClass, Property, NestedProperty, ArrayNestedProperty, IsString, IsNumber, Min, IsArray, ArrayMinSize } from '@dangao/bun-server';
208
+
209
+ @ValidateClass()
210
+ class AddressDto {
211
+ @Property(IsString())
212
+ city: string;
213
+
214
+ @Property(IsString())
215
+ street: string;
216
+ }
217
+
218
+ @ValidateClass()
219
+ class ItemDto {
220
+ @Property(IsString())
221
+ name: string;
222
+
223
+ @Property(IsNumber(), Min(0))
224
+ price: number;
225
+ }
226
+
227
+ @ValidateClass()
228
+ class CreateOrderDto {
229
+ @Property(IsString())
230
+ userId: string;
231
+
232
+ // 嵌套对象验证
233
+ @NestedProperty(AddressDto)
234
+ shippingAddress: AddressDto;
235
+
236
+ // 嵌套对象数组验证
237
+ @Property(IsArray(), ArrayMinSize(1))
238
+ @ArrayNestedProperty(ItemDto)
239
+ items: ItemDto[];
240
+ }
241
+ ```
242
+
243
+ ## 自定义验证器
244
+
245
+ ### 简单自定义验证器
246
+
247
+ ```typescript
248
+ import { createSimpleValidator } from '@dangao/bun-server';
249
+
250
+ const IsEven = createSimpleValidator(
251
+ 'isEven',
252
+ (value) => typeof value === 'number' && value % 2 === 0,
253
+ '必须是偶数'
254
+ );
255
+
256
+ // 使用
257
+ @Property(IsEven())
258
+ count: number;
259
+ ```
260
+
261
+ ### 带参数的自定义验证器
262
+
263
+ ```typescript
264
+ import { createCustomValidator } from '@dangao/bun-server';
265
+
266
+ const IsDivisibleBy = createCustomValidator(
267
+ 'isDivisibleBy',
268
+ (value: unknown, divisor: number) => typeof value === 'number' && value % divisor === 0,
269
+ (divisor: number) => `必须能被 ${divisor} 整除`
270
+ );
271
+
272
+ // 使用
273
+ @Property(IsDivisibleBy(5)())
274
+ value: number;
275
+ ```
276
+
277
+ ### 正则表达式自定义验证器
278
+
279
+ ```typescript
280
+ import { createRegexValidator } from '@dangao/bun-server';
281
+
282
+ const IsSlug = createRegexValidator(
283
+ 'isSlug',
284
+ /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
285
+ '必须是有效的 slug 格式'
286
+ );
287
+
288
+ // 使用
289
+ @Property(IsSlug())
290
+ slug: string;
291
+ ```
292
+
293
+ ## 内置扩展验证器
294
+
295
+ 框架提供了多个预置验证器用于常见场景:
296
+
297
+ ```typescript
298
+ import {
299
+ IsPhoneNumber, // 中国手机号
300
+ IsIdCard, // 中国身份证号
301
+ IsIPv4, // IPv4 地址
302
+ IsPort, // 端口号 (0-65535)
303
+ IsPostalCode, // 中国邮政编码
304
+ IsCreditCard, // 信用卡号(Luhn 算法)
305
+ IsHexColor, // 十六进制颜色值(#fff 或 #ffffff)
306
+ IsMacAddress, // MAC 地址
307
+ IsSemVer, // 语义化版本号
308
+ IsDivisibleBy, // 能被指定数字整除
309
+ IsBetween, // 数字在范围内
310
+ Contains, // 字符串包含子串
311
+ NotContains, // 字符串不包含子串
312
+ } from '@dangao/bun-server';
313
+
314
+ // 使用示例
315
+ @Property(IsPhoneNumber())
316
+ phone: string;
317
+
318
+ @Property(IsIPv4())
319
+ ip: string;
320
+
321
+ @Property(IsBetween(1, 100)())
322
+ percentage: number;
323
+
324
+ @Property(Contains('http')())
325
+ url: string;
326
+ ```
327
+
328
+ ## 错误处理
329
+
330
+ ### ValidationError
331
+
332
+ 验证失败时会抛出 `ValidationError`:
333
+
334
+ ```typescript
335
+ import { ValidationError, ValidationIssue } from '@dangao/bun-server';
336
+
337
+ try {
338
+ validateObject(data, MyDto);
339
+ } catch (error) {
340
+ if (error instanceof ValidationError) {
341
+ // 访问验证问题列表
342
+ console.log(error.issues);
343
+
344
+ // 获取扁平化的错误(对嵌套对象有用)
345
+ console.log(error.getFlattened());
346
+
347
+ // 转换为 JSON
348
+ console.log(error.toJSON());
349
+ }
350
+ }
351
+ ```
352
+
353
+ ### ValidationIssue 结构
354
+
355
+ ```typescript
356
+ interface ValidationIssue {
357
+ index?: number; // 参数索引(用于参数验证)
358
+ property?: string; // 属性路径(如 'user.address.city')
359
+ rule: string; // 失败的规则名称
360
+ message: string; // 错误消息
361
+ value?: unknown; // 验证失败的值
362
+ children?: ValidationIssue[]; // 嵌套错误
363
+ }
364
+ ```
365
+
366
+ ### 控制器集成
367
+
368
+ 验证错误会被自动捕获并返回 400 Bad Request 响应:
369
+
370
+ ```typescript
371
+ @Controller('/api/users')
372
+ class UserController {
373
+ @POST('/')
374
+ public async createUser(@Body() @Validate(IsObject()) body: unknown) {
375
+ const dto = body as CreateUserDto;
376
+ validateObject(dto, CreateUserDto);
377
+ // ... 创建用户
378
+ }
379
+ }
380
+ ```
381
+
382
+ ## 最佳实践
383
+
384
+ 1. **复杂验证使用 DTO**:对于具有多个字段的请求体,使用 `@ValidateClass()` 装饰的 DTO。
385
+
386
+ 2. **组合规则**:链式组合多个规则以进行全面验证:
387
+ ```typescript
388
+ @Property(IsString(), MinLength(2), MaxLength(50), Matches(/^[a-zA-Z\s]+$/))
389
+ name: string;
390
+ ```
391
+
392
+ 3. **IsOptional() 放在最前**:始终将 `IsOptional()` 放在规则链的开头:
393
+ ```typescript
394
+ @Property(IsOptional(), IsString(), MinLength(2))
395
+ nickname?: string;
396
+ ```
397
+
398
+ 4. **先转换后验证**:使用 `Transform()` 在验证前规范化数据:
399
+ ```typescript
400
+ @Property(Transform((v) => String(v).toLowerCase().trim()), IsEmail())
401
+ email: string;
402
+ ```
403
+
404
+ 5. **自定义错误消息**:提供清晰、用户友好的错误消息:
405
+ ```typescript
406
+ IsEmail({ message: '请输入有效的邮箱地址' })
407
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dangao/bun-server",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -9,6 +9,11 @@ import type { Constructor } from "@/core/types";
9
9
  const DEPENDENCY_METADATA_KEY = Symbol("dependency:metadata");
10
10
  const INJECTABLE_METADATA_KEY = Symbol("injectable");
11
11
 
12
+ /**
13
+ * 全局模块元数据键
14
+ */
15
+ export const GLOBAL_MODULE_METADATA_KEY = Symbol("@dangao/bun-server:global-module");
16
+
12
17
  /**
13
18
  * 类型引用映射(用于保存构造函数类型,避免 Reflect.defineMetadata 序列化问题)
14
19
  */
@@ -226,3 +231,44 @@ export function getTypeReference(
226
231
  }
227
232
  return undefined as unknown as Constructor<unknown>;
228
233
  }
234
+
235
+ /**
236
+ * Global 装饰器
237
+ * 标记模块为全局模块,其导出的提供者可在任何模块中使用,无需显式导入
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * @Global()
242
+ * @Module({
243
+ * providers: [ConfigService],
244
+ * exports: [ConfigService],
245
+ * })
246
+ * class GlobalConfigModule {}
247
+ *
248
+ * // 其他模块无需导入 GlobalConfigModule 即可使用 ConfigService
249
+ * @Module({
250
+ * controllers: [UserController],
251
+ * providers: [UserService],
252
+ * })
253
+ * class UserModule {}
254
+ *
255
+ * @Injectable()
256
+ * class UserService {
257
+ * constructor(private readonly config: ConfigService) {}
258
+ * }
259
+ * ```
260
+ */
261
+ export function Global(): ClassDecorator {
262
+ return (target) => {
263
+ Reflect.defineMetadata(GLOBAL_MODULE_METADATA_KEY, true, target);
264
+ };
265
+ }
266
+
267
+ /**
268
+ * 检查模块是否为全局模块
269
+ * @param target - 目标模块类
270
+ * @returns 是否为全局模块
271
+ */
272
+ export function isGlobalModule(target: Constructor<unknown>): boolean {
273
+ return Reflect.getMetadata(GLOBAL_MODULE_METADATA_KEY, target) === true;
274
+ }
package/src/di/index.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  export { Container } from './container';
2
- export { Injectable, Inject, getDependencyMetadata, isInjectable, getLifecycle } from './decorators';
2
+ export {
3
+ Injectable,
4
+ Inject,
5
+ getDependencyMetadata,
6
+ isInjectable,
7
+ getLifecycle,
8
+ Global,
9
+ isGlobalModule,
10
+ GLOBAL_MODULE_METADATA_KEY,
11
+ } from './decorators';
3
12
  export {
4
13
  Lifecycle,
5
14
  INSTANCE_POST_PROCESSOR_TOKEN,
@@ -2,6 +2,7 @@ import { ControllerRegistry } from '../controller/controller';
2
2
  import { Container } from './container';
3
3
  import { Lifecycle } from './types';
4
4
  import { getModuleMetadata, type ModuleClass, type ModuleProvider, type ProviderToken } from './module';
5
+ import { isGlobalModule } from './decorators';
5
6
  import type { Constructor } from '@/core/types';
6
7
  import type { ApplicationExtension } from '../extensions/types';
7
8
  import type { Middleware } from '../middleware';
@@ -14,6 +15,10 @@ interface ModuleRef {
14
15
  attachedParents: Set<Container>;
15
16
  extensions: ApplicationExtension[];
16
17
  middlewares: Middleware[];
18
+ /**
19
+ * 是否为全局模块
20
+ */
21
+ isGlobal: boolean;
17
22
  }
18
23
 
19
24
  export class ModuleRegistry {
@@ -21,6 +26,10 @@ export class ModuleRegistry {
21
26
  private readonly moduleRefs = new Map<ModuleClass, ModuleRef>();
22
27
  private readonly processing = new Set<ModuleClass>();
23
28
  private rootContainer?: Container;
29
+ /**
30
+ * 存储全局模块列表,用于在其他模块注册时自动附加全局 exports
31
+ */
32
+ private readonly globalModules = new Set<ModuleClass>();
24
33
 
25
34
  public static getInstance(): ModuleRegistry {
26
35
  if (!ModuleRegistry.instance) {
@@ -43,9 +52,17 @@ export class ModuleRegistry {
43
52
  public clear(): void {
44
53
  this.moduleRefs.clear();
45
54
  this.processing.clear();
55
+ this.globalModules.clear();
46
56
  this.rootContainer = undefined;
47
57
  }
48
58
 
59
+ /**
60
+ * 获取所有全局模块
61
+ */
62
+ public getGlobalModules(): ModuleClass[] {
63
+ return Array.from(this.globalModules);
64
+ }
65
+
49
66
  private processModule(moduleClass: ModuleClass, parentContainer: Container): ModuleRef {
50
67
  if (this.processing.has(moduleClass)) {
51
68
  throw new Error(`Circular module dependency detected for ${moduleClass.name}`);
@@ -69,6 +86,7 @@ export class ModuleRegistry {
69
86
  throw new Error('ModuleRegistry is not initialized with a root container');
70
87
  }
71
88
  const metadata = getModuleMetadata(moduleClass);
89
+ const isGlobal = isGlobalModule(moduleClass);
72
90
  const container = new Container({ parent: this.rootContainer });
73
91
  this.registerProviders(container, metadata.providers);
74
92
  ref = {
@@ -79,12 +97,33 @@ export class ModuleRegistry {
79
97
  attachedParents: new Set<Container>(),
80
98
  extensions: metadata.extensions ?? [],
81
99
  middlewares: metadata.middlewares ?? [],
100
+ isGlobal,
82
101
  };
83
102
  this.registerControllers(ref);
84
103
  this.moduleRefs.set(moduleClass, ref);
104
+
105
+ // 如果是全局模块,注册到根容器并记录
106
+ if (isGlobal) {
107
+ this.globalModules.add(moduleClass);
108
+ this.registerGlobalExports(ref);
109
+ }
110
+
85
111
  return ref;
86
112
  }
87
113
 
114
+ /**
115
+ * 将全局模块的 exports 注册到根容器
116
+ * 这样所有模块都可以访问全局模块导出的提供者
117
+ */
118
+ private registerGlobalExports(moduleRef: ModuleRef): void {
119
+ if (!this.rootContainer) {
120
+ return;
121
+ }
122
+ for (const exportedToken of moduleRef.metadata.exports) {
123
+ this.registerExport(this.rootContainer, moduleRef, exportedToken);
124
+ }
125
+ }
126
+
88
127
  private registerProviders(container: Container, providers: ModuleProvider[]): void {
89
128
  for (const provider of providers) {
90
129
  if (typeof provider === 'function') {
@@ -0,0 +1,103 @@
1
+ import 'reflect-metadata';
2
+ import {
3
+ ON_EVENT_METADATA_KEY,
4
+ EVENT_LISTENER_CLASS_METADATA_KEY,
5
+ type OnEventMethodMetadata,
6
+ } from './types';
7
+
8
+ /**
9
+ * OnEvent 装饰器选项
10
+ */
11
+ export interface OnEventOptions {
12
+ /**
13
+ * 是否异步处理
14
+ * @default false
15
+ */
16
+ async?: boolean;
17
+
18
+ /**
19
+ * 监听器优先级(数值越大优先级越高)
20
+ * @default 0
21
+ */
22
+ priority?: number;
23
+ }
24
+
25
+ /**
26
+ * 事件监听器装饰器
27
+ * 用于标记方法为事件监听器
28
+ *
29
+ * @param event - 事件名称或标识符
30
+ * @param options - 监听选项
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * @Injectable()
35
+ * class NotificationService {
36
+ * @OnEvent('user.created')
37
+ * handleUserCreated(payload: UserCreatedEvent) {
38
+ * console.log('User created:', payload.userId);
39
+ * }
40
+ *
41
+ * @OnEvent(USER_DELETED, { async: true, priority: 10 })
42
+ * async handleUserDeleted(payload: UserDeletedEvent) {
43
+ * await this.cleanup(payload.userId);
44
+ * }
45
+ * }
46
+ * ```
47
+ */
48
+ export function OnEvent(
49
+ event: string | symbol,
50
+ options: OnEventOptions = {},
51
+ ): MethodDecorator {
52
+ return (
53
+ target: object,
54
+ propertyKey: string | symbol,
55
+ descriptor: PropertyDescriptor,
56
+ ): PropertyDescriptor => {
57
+ const methodName = String(propertyKey);
58
+ const constructor = target.constructor;
59
+
60
+ // 获取类上已有的事件监听器元数据
61
+ const existingMetadata: OnEventMethodMetadata[] =
62
+ Reflect.getMetadata(ON_EVENT_METADATA_KEY, constructor) || [];
63
+
64
+ // 添加新的监听器元数据
65
+ const metadata: OnEventMethodMetadata = {
66
+ methodName,
67
+ event,
68
+ async: options.async ?? false,
69
+ priority: options.priority ?? 0,
70
+ };
71
+
72
+ Reflect.defineMetadata(
73
+ ON_EVENT_METADATA_KEY,
74
+ [...existingMetadata, metadata],
75
+ constructor,
76
+ );
77
+
78
+ // 标记类为事件监听器类
79
+ Reflect.defineMetadata(EVENT_LISTENER_CLASS_METADATA_KEY, true, constructor);
80
+
81
+ return descriptor;
82
+ };
83
+ }
84
+
85
+ /**
86
+ * 获取类的事件监听器元数据
87
+ * @param target - 目标类
88
+ */
89
+ export function getOnEventMetadata(
90
+ target: Function,
91
+ ): OnEventMethodMetadata[] | undefined {
92
+ return Reflect.getMetadata(ON_EVENT_METADATA_KEY, target);
93
+ }
94
+
95
+ /**
96
+ * 检查类是否为事件监听器类
97
+ * @param target - 目标类
98
+ */
99
+ export function isEventListenerClass(target: Function): boolean {
100
+ return (
101
+ Reflect.getMetadata(EVENT_LISTENER_CLASS_METADATA_KEY, target) === true
102
+ );
103
+ }