@dangao/bun-server 1.7.0 → 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 +196 -19
- package/dist/cache/cache-module.d.ts +18 -0
- package/dist/cache/cache-module.d.ts.map +1 -1
- package/dist/cache/index.d.ts +3 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/interceptors.d.ts +41 -0
- package/dist/cache/interceptors.d.ts.map +1 -0
- package/dist/cache/service-proxy.d.ts +62 -0
- package/dist/cache/service-proxy.d.ts.map +1 -0
- package/dist/controller/controller.d.ts +8 -0
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts +5 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/di/container.d.ts +18 -1
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +2 -2
- 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/di/types.d.ts +22 -0
- package/dist/di/types.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 +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4641 -2840
- 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/symbol-interface-pattern.md +431 -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/symbol-interface-pattern.md +431 -0
- package/docs/zh/validation.md +407 -0
- package/package.json +1 -1
- package/src/cache/cache-module.ts +37 -0
- package/src/cache/index.ts +16 -1
- package/src/cache/interceptors.ts +295 -0
- package/src/cache/service-proxy.ts +219 -0
- package/src/controller/controller.ts +30 -6
- package/src/core/application.ts +25 -1
- package/src/di/container.ts +57 -7
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +17 -2
- package/src/di/module-registry.ts +39 -0
- package/src/di/types.ts +29 -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 +140 -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/cache/cache-decorators.test.ts +284 -0
- package/tests/controller/path-combination.test.ts +353 -0
- 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/src/di/container.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import {
|
|
3
3
|
type DependencyMetadata,
|
|
4
|
+
type InstancePostProcessor,
|
|
5
|
+
INSTANCE_POST_PROCESSOR_TOKEN,
|
|
4
6
|
Lifecycle,
|
|
5
7
|
type ProviderConfig,
|
|
6
8
|
} from "./types";
|
|
@@ -59,6 +61,11 @@ export class Container {
|
|
|
59
61
|
DependencyPlan
|
|
60
62
|
>();
|
|
61
63
|
|
|
64
|
+
/**
|
|
65
|
+
* 实例后处理器列表
|
|
66
|
+
*/
|
|
67
|
+
private readonly postProcessors: InstancePostProcessor[] = [];
|
|
68
|
+
|
|
62
69
|
/**
|
|
63
70
|
* 注册提供者
|
|
64
71
|
* @param token - 提供者标识符(类构造函数或 token)
|
|
@@ -112,6 +119,45 @@ export class Container {
|
|
|
112
119
|
});
|
|
113
120
|
}
|
|
114
121
|
|
|
122
|
+
/**
|
|
123
|
+
* 注册实例后处理器
|
|
124
|
+
* @param processor - 后处理器实例
|
|
125
|
+
*/
|
|
126
|
+
public registerPostProcessor(processor: InstancePostProcessor): void {
|
|
127
|
+
this.postProcessors.push(processor);
|
|
128
|
+
// 按优先级排序(数字越小优先级越高)
|
|
129
|
+
this.postProcessors.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 应用所有后处理器(包括父容器的后处理器)
|
|
134
|
+
* @param instance - 原始实例
|
|
135
|
+
* @param constructor - 构造函数
|
|
136
|
+
* @param originContainer - 原始调用容器(用于后处理器解析依赖)
|
|
137
|
+
* @returns 处理后的实例
|
|
138
|
+
*/
|
|
139
|
+
private applyPostProcessors<T>(
|
|
140
|
+
instance: T,
|
|
141
|
+
constructor: Constructor<T>,
|
|
142
|
+
originContainer?: Container,
|
|
143
|
+
): T {
|
|
144
|
+
// 使用原始容器或当前容器
|
|
145
|
+
const containerForProcessors = originContainer ?? this;
|
|
146
|
+
let result = instance;
|
|
147
|
+
|
|
148
|
+
// 先应用父容器的后处理器
|
|
149
|
+
if (this.parent) {
|
|
150
|
+
result = (this.parent as Container).applyPostProcessors(result, constructor, containerForProcessors);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 再应用本容器的后处理器
|
|
154
|
+
// 传递原始调用容器,而不是当前容器
|
|
155
|
+
for (const processor of this.postProcessors) {
|
|
156
|
+
result = processor.postProcess(result, constructor, containerForProcessors);
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
115
161
|
/**
|
|
116
162
|
* 解析依赖
|
|
117
163
|
* @param token - 提供者标识符
|
|
@@ -321,16 +367,19 @@ export class Container {
|
|
|
321
367
|
private instantiate<T>(constructor: Constructor<T>): T {
|
|
322
368
|
const plan = this.getDependencyPlan(constructor);
|
|
323
369
|
|
|
370
|
+
let instance: T;
|
|
324
371
|
if (plan.paramLength === 0) {
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
372
|
+
instance = new constructor();
|
|
373
|
+
} else {
|
|
374
|
+
const dependencies = new Array(plan.paramLength);
|
|
375
|
+
for (let index = 0; index < plan.paramLength; index++) {
|
|
376
|
+
dependencies[index] = this.resolveFromPlan(constructor, plan, index);
|
|
377
|
+
}
|
|
378
|
+
instance = new constructor(...dependencies);
|
|
331
379
|
}
|
|
332
380
|
|
|
333
|
-
|
|
381
|
+
// 应用后处理器
|
|
382
|
+
return this.applyPostProcessors(instance, constructor);
|
|
334
383
|
}
|
|
335
384
|
|
|
336
385
|
/**
|
|
@@ -356,6 +405,7 @@ export class Container {
|
|
|
356
405
|
this.singletons.clear();
|
|
357
406
|
this.typeToToken.clear();
|
|
358
407
|
this.dependencyPlans.clear();
|
|
408
|
+
this.postProcessors.length = 0;
|
|
359
409
|
// scopedInstances 使用 WeakMap,当 Context 对象被 GC 时会自动清理
|
|
360
410
|
}
|
|
361
411
|
|
package/src/di/decorators.ts
CHANGED
|
@@ -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,4 +1,19 @@
|
|
|
1
1
|
export { Container } from './container';
|
|
2
|
-
export {
|
|
3
|
-
|
|
2
|
+
export {
|
|
3
|
+
Injectable,
|
|
4
|
+
Inject,
|
|
5
|
+
getDependencyMetadata,
|
|
6
|
+
isInjectable,
|
|
7
|
+
getLifecycle,
|
|
8
|
+
Global,
|
|
9
|
+
isGlobalModule,
|
|
10
|
+
GLOBAL_MODULE_METADATA_KEY,
|
|
11
|
+
} from './decorators';
|
|
12
|
+
export {
|
|
13
|
+
Lifecycle,
|
|
14
|
+
INSTANCE_POST_PROCESSOR_TOKEN,
|
|
15
|
+
type ProviderConfig,
|
|
16
|
+
type DependencyMetadata,
|
|
17
|
+
type InstancePostProcessor,
|
|
18
|
+
} from './types';
|
|
4
19
|
|
|
@@ -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') {
|
package/src/di/types.ts
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import type { Constructor } from '@/core/types'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 实例后处理器接口
|
|
5
|
+
* 用于在 DI 容器创建实例后进行处理(如创建代理、注入额外依赖等)
|
|
6
|
+
*/
|
|
7
|
+
export interface InstancePostProcessor {
|
|
8
|
+
/**
|
|
9
|
+
* 处理新创建的实例
|
|
10
|
+
* @param instance - 原始实例
|
|
11
|
+
* @param constructor - 构造函数
|
|
12
|
+
* @param container - DI 容器引用
|
|
13
|
+
* @returns 处理后的实例(可以是代理)
|
|
14
|
+
*/
|
|
15
|
+
postProcess<T>(
|
|
16
|
+
instance: T,
|
|
17
|
+
constructor: Constructor<T>,
|
|
18
|
+
container: unknown,
|
|
19
|
+
): T;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 优先级(数字越小优先级越高,默认 100)
|
|
23
|
+
*/
|
|
24
|
+
priority?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 实例后处理器注册表 Token
|
|
29
|
+
*/
|
|
30
|
+
export const INSTANCE_POST_PROCESSOR_TOKEN = Symbol('@dangao/bun-server:di:post-processor');
|
|
31
|
+
|
|
3
32
|
/**
|
|
4
33
|
* 依赖生命周期类型
|
|
5
34
|
*/
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { Module, MODULE_METADATA_KEY, type ModuleProvider } from '../di/module';
|
|
3
|
+
import type { Container } from '../di/container';
|
|
4
|
+
import { ModuleRegistry } from '../di/module-registry';
|
|
5
|
+
|
|
6
|
+
import { EventEmitterService } from './service';
|
|
7
|
+
import { getOnEventMetadata, isEventListenerClass } from './decorators';
|
|
8
|
+
import {
|
|
9
|
+
EVENT_EMITTER_TOKEN,
|
|
10
|
+
EVENT_OPTIONS_TOKEN,
|
|
11
|
+
type EventModuleOptions,
|
|
12
|
+
type EventEmitter,
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 事件监听器扫描器
|
|
17
|
+
* 负责扫描和注册带有 @OnEvent() 装饰器的方法
|
|
18
|
+
*/
|
|
19
|
+
export class EventListenerScanner {
|
|
20
|
+
/**
|
|
21
|
+
* 构造函数
|
|
22
|
+
* @param eventEmitter - 事件发射器服务
|
|
23
|
+
* @param container - DI 容器
|
|
24
|
+
*/
|
|
25
|
+
public constructor(
|
|
26
|
+
private readonly eventEmitter: EventEmitter,
|
|
27
|
+
private readonly container: Container,
|
|
28
|
+
) {}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 扫描并注册监听器类
|
|
32
|
+
* @param listenerClasses - 监听器类数组
|
|
33
|
+
*/
|
|
34
|
+
public scanAndRegister(listenerClasses: Function[]): void {
|
|
35
|
+
for (const listenerClass of listenerClasses) {
|
|
36
|
+
this.registerListenerClass(listenerClass);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 注册单个监听器类
|
|
42
|
+
* @param listenerClass - 监听器类
|
|
43
|
+
*/
|
|
44
|
+
public registerListenerClass(listenerClass: Function): void {
|
|
45
|
+
if (!isEventListenerClass(listenerClass)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const metadata = getOnEventMetadata(listenerClass);
|
|
50
|
+
if (!metadata || metadata.length === 0) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 从容器获取监听器实例
|
|
55
|
+
const instance = this.container.resolve<Record<string, Function>>(
|
|
56
|
+
listenerClass as any,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (!instance) {
|
|
60
|
+
console.warn(
|
|
61
|
+
`[EventModule] Failed to resolve listener class: ${listenerClass.name}. ` +
|
|
62
|
+
'Make sure it is registered as a provider.',
|
|
63
|
+
);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 注册所有监听器方法
|
|
68
|
+
for (const listenerMetadata of metadata) {
|
|
69
|
+
const method = instance[listenerMetadata.methodName];
|
|
70
|
+
if (typeof method !== 'function') {
|
|
71
|
+
console.warn(
|
|
72
|
+
`[EventModule] Method "${listenerMetadata.methodName}" not found on ${listenerClass.name}`,
|
|
73
|
+
);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 绑定方法到实例
|
|
78
|
+
const boundMethod = method.bind(instance);
|
|
79
|
+
|
|
80
|
+
// 注册到事件发射器
|
|
81
|
+
this.eventEmitter.on(listenerMetadata.event, boundMethod, {
|
|
82
|
+
async: listenerMetadata.async,
|
|
83
|
+
priority: listenerMetadata.priority,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 事件监听器扫描器 Token
|
|
91
|
+
*/
|
|
92
|
+
export const EVENT_LISTENER_SCANNER_TOKEN = Symbol(
|
|
93
|
+
'@dangao/bun-server:events:scanner',
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
@Module({
|
|
97
|
+
providers: [],
|
|
98
|
+
})
|
|
99
|
+
export class EventModule {
|
|
100
|
+
/**
|
|
101
|
+
* 已注册的监听器类(用于模块初始化后扫描)
|
|
102
|
+
*/
|
|
103
|
+
private static listenerClasses: Function[] = [];
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 创建事件模块
|
|
107
|
+
* @param options - 模块配置
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* @Module({
|
|
112
|
+
* imports: [
|
|
113
|
+
* EventModule.forRoot({
|
|
114
|
+
* wildcard: true,
|
|
115
|
+
* maxListeners: 20,
|
|
116
|
+
* }),
|
|
117
|
+
* ],
|
|
118
|
+
* providers: [NotificationService, AnalyticsService],
|
|
119
|
+
* })
|
|
120
|
+
* class AppModule {}
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
public static forRoot(options: EventModuleOptions = {}): typeof EventModule {
|
|
124
|
+
const providers: ModuleProvider[] = [];
|
|
125
|
+
|
|
126
|
+
// 创建事件发射器服务
|
|
127
|
+
const eventEmitter = new EventEmitterService(options);
|
|
128
|
+
|
|
129
|
+
// 注册选项
|
|
130
|
+
providers.push({
|
|
131
|
+
provide: EVENT_OPTIONS_TOKEN,
|
|
132
|
+
useValue: options,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// 注册事件发射器服务
|
|
136
|
+
providers.push({
|
|
137
|
+
provide: EVENT_EMITTER_TOKEN,
|
|
138
|
+
useValue: eventEmitter,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// 更新模块元数据
|
|
142
|
+
const existingMetadata =
|
|
143
|
+
Reflect.getMetadata(MODULE_METADATA_KEY, EventModule) || {};
|
|
144
|
+
const metadata = {
|
|
145
|
+
...existingMetadata,
|
|
146
|
+
providers: [...(existingMetadata.providers || []), ...providers],
|
|
147
|
+
exports: [
|
|
148
|
+
...(existingMetadata.exports || []),
|
|
149
|
+
EVENT_EMITTER_TOKEN,
|
|
150
|
+
EVENT_OPTIONS_TOKEN,
|
|
151
|
+
],
|
|
152
|
+
};
|
|
153
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, EventModule);
|
|
154
|
+
|
|
155
|
+
// 清空监听器类列表
|
|
156
|
+
EventModule.listenerClasses = [];
|
|
157
|
+
|
|
158
|
+
return EventModule;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 注册监听器类
|
|
163
|
+
* 用于在模块配置后手动注册监听器类
|
|
164
|
+
*
|
|
165
|
+
* @param listenerClasses - 监听器类数组
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* EventModule.registerListeners([NotificationService, AnalyticsService]);
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
public static registerListeners(listenerClasses: Function[]): void {
|
|
173
|
+
EventModule.listenerClasses.push(...listenerClasses);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 初始化事件监听器扫描
|
|
178
|
+
* 在应用启动时调用,扫描并注册所有监听器
|
|
179
|
+
*
|
|
180
|
+
* @param listenerContainer - 用于解析监听器服务的 DI 容器(通常是应用模块的容器)
|
|
181
|
+
* @param additionalListeners - 额外的监听器类
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* // 在 registerModule 之后调用
|
|
186
|
+
* app.registerModule(RootModule);
|
|
187
|
+
*
|
|
188
|
+
* // 方式1:传入监听器所在模块的容器(推荐)
|
|
189
|
+
* const moduleRef = ModuleRegistry.getInstance().getModuleRef(RootModule);
|
|
190
|
+
* EventModule.initializeListeners(moduleRef?.container, [NotificationService]);
|
|
191
|
+
*
|
|
192
|
+
* // 方式2:如果监听器在 EventModule 的 providers 中注册,可以不传容器
|
|
193
|
+
* EventModule.initializeListeners(undefined, [NotificationService]);
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
public static initializeListeners(
|
|
197
|
+
listenerContainer?: Container,
|
|
198
|
+
additionalListeners: Function[] = [],
|
|
199
|
+
): void {
|
|
200
|
+
// 从 EventModule 自身的容器获取 EventEmitter
|
|
201
|
+
const registry = ModuleRegistry.getInstance();
|
|
202
|
+
const eventModuleRef = registry.getModuleRef(EventModule);
|
|
203
|
+
|
|
204
|
+
let eventEmitter: EventEmitter | undefined;
|
|
205
|
+
|
|
206
|
+
if (eventModuleRef) {
|
|
207
|
+
try {
|
|
208
|
+
eventEmitter = eventModuleRef.container.resolve<EventEmitter>(EVENT_EMITTER_TOKEN);
|
|
209
|
+
} catch {
|
|
210
|
+
// 忽略错误
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!eventEmitter) {
|
|
215
|
+
console.warn(
|
|
216
|
+
'[EventModule] EventEmitter not found. Make sure EventModule.forRoot() is called and the module is registered.',
|
|
217
|
+
);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 确定用于解析监听器的容器
|
|
222
|
+
// 优先使用传入的容器,否则使用 EventModule 的容器
|
|
223
|
+
const resolveContainer = listenerContainer ?? eventModuleRef?.container;
|
|
224
|
+
|
|
225
|
+
if (!resolveContainer) {
|
|
226
|
+
console.warn(
|
|
227
|
+
'[EventModule] No container available to resolve listeners.',
|
|
228
|
+
);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const scanner = new EventListenerScanner(eventEmitter, resolveContainer);
|
|
233
|
+
|
|
234
|
+
// 扫描所有注册的监听器类
|
|
235
|
+
const allListeners = [
|
|
236
|
+
...EventModule.listenerClasses,
|
|
237
|
+
...additionalListeners,
|
|
238
|
+
];
|
|
239
|
+
scanner.scanAndRegister(allListeners);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 获取事件发射器服务(静态方法)
|
|
244
|
+
* 用于在模块配置后获取事件发射器
|
|
245
|
+
*
|
|
246
|
+
* @param container - DI 容器(可选,如果不提供则从 EventModule 自身的容器获取)
|
|
247
|
+
*/
|
|
248
|
+
public static getEventEmitter(container?: Container): EventEmitter | undefined {
|
|
249
|
+
// 优先从 EventModule 自身的容器获取
|
|
250
|
+
const registry = ModuleRegistry.getInstance();
|
|
251
|
+
const moduleRef = registry.getModuleRef(EventModule);
|
|
252
|
+
|
|
253
|
+
if (moduleRef) {
|
|
254
|
+
try {
|
|
255
|
+
return moduleRef.container.resolve<EventEmitter>(EVENT_EMITTER_TOKEN);
|
|
256
|
+
} catch {
|
|
257
|
+
// 忽略错误,尝试从传入的容器获取
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// 尝试从传入的容器获取
|
|
262
|
+
if (container) {
|
|
263
|
+
try {
|
|
264
|
+
return container.resolve<EventEmitter>(EVENT_EMITTER_TOKEN);
|
|
265
|
+
} catch {
|
|
266
|
+
// 忽略错误
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// 类型导出
|
|
2
|
+
export {
|
|
3
|
+
EVENT_EMITTER_TOKEN,
|
|
4
|
+
EVENT_OPTIONS_TOKEN,
|
|
5
|
+
ON_EVENT_METADATA_KEY,
|
|
6
|
+
EVENT_LISTENER_CLASS_METADATA_KEY,
|
|
7
|
+
type EventEmitter,
|
|
8
|
+
type EventListener,
|
|
9
|
+
type EventMetadata,
|
|
10
|
+
type EventModuleOptions,
|
|
11
|
+
type ListenerOptions,
|
|
12
|
+
type OnEventMethodMetadata,
|
|
13
|
+
type RegisteredListener,
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
// 服务导出
|
|
17
|
+
export { EventEmitterService } from './service';
|
|
18
|
+
|
|
19
|
+
// 装饰器导出
|
|
20
|
+
export {
|
|
21
|
+
OnEvent,
|
|
22
|
+
getOnEventMetadata,
|
|
23
|
+
isEventListenerClass,
|
|
24
|
+
type OnEventOptions,
|
|
25
|
+
} from './decorators';
|
|
26
|
+
|
|
27
|
+
// 模块导出
|
|
28
|
+
export {
|
|
29
|
+
EventModule,
|
|
30
|
+
EventListenerScanner,
|
|
31
|
+
EVENT_LISTENER_SCANNER_TOKEN,
|
|
32
|
+
} from './event-module';
|