@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
|
@@ -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';
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EventEmitter,
|
|
3
|
+
EventListener,
|
|
4
|
+
EventModuleOptions,
|
|
5
|
+
ListenerOptions,
|
|
6
|
+
RegisteredListener,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 事件发射器服务实现
|
|
11
|
+
*/
|
|
12
|
+
export class EventEmitterService implements EventEmitter {
|
|
13
|
+
/**
|
|
14
|
+
* 事件监听器映射
|
|
15
|
+
*/
|
|
16
|
+
private listeners: Map<string | symbol, RegisteredListener[]> = new Map();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 模块选项
|
|
20
|
+
*/
|
|
21
|
+
private options: EventModuleOptions;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 构造函数
|
|
25
|
+
* @param options - 模块选项
|
|
26
|
+
*/
|
|
27
|
+
public constructor(options: EventModuleOptions = {}) {
|
|
28
|
+
this.options = {
|
|
29
|
+
wildcard: false,
|
|
30
|
+
delimiter: '.',
|
|
31
|
+
maxListeners: 10,
|
|
32
|
+
...options,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 发布事件(同步触发所有监听器,不等待异步监听器完成)
|
|
38
|
+
*/
|
|
39
|
+
public emit<T>(event: string | symbol, payload: T): void {
|
|
40
|
+
const eventName = this.resolveEventName(event);
|
|
41
|
+
const matchedListeners = this.getMatchedListeners(eventName);
|
|
42
|
+
|
|
43
|
+
if (matchedListeners.length === 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 按优先级排序(高优先级先执行)
|
|
48
|
+
const sortedListeners = this.sortListenersByPriority(matchedListeners);
|
|
49
|
+
|
|
50
|
+
for (const { listener, once, async: isAsync } of sortedListeners) {
|
|
51
|
+
try {
|
|
52
|
+
const result = listener(payload);
|
|
53
|
+
// 如果监听器是异步的,不等待完成
|
|
54
|
+
if (isAsync && result instanceof Promise) {
|
|
55
|
+
result.catch((error) => {
|
|
56
|
+
this.handleError(error, eventName, payload);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
this.handleError(error as Error, eventName, payload);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 如果是一次性监听器,移除它
|
|
64
|
+
if (once) {
|
|
65
|
+
this.off(eventName, listener);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 异步发布事件(等待所有监听器完成)
|
|
72
|
+
*/
|
|
73
|
+
public async emitAsync<T>(event: string | symbol, payload: T): Promise<void> {
|
|
74
|
+
const eventName = this.resolveEventName(event);
|
|
75
|
+
const matchedListeners = this.getMatchedListeners(eventName);
|
|
76
|
+
|
|
77
|
+
if (matchedListeners.length === 0) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 按优先级排序(高优先级先执行)
|
|
82
|
+
const sortedListeners = this.sortListenersByPriority(matchedListeners);
|
|
83
|
+
|
|
84
|
+
const promises: Promise<void>[] = [];
|
|
85
|
+
const toRemove: EventListener[] = [];
|
|
86
|
+
|
|
87
|
+
for (const { listener, once } of sortedListeners) {
|
|
88
|
+
try {
|
|
89
|
+
const result = listener(payload);
|
|
90
|
+
if (result instanceof Promise) {
|
|
91
|
+
promises.push(
|
|
92
|
+
result.catch((error) => {
|
|
93
|
+
this.handleError(error, eventName, payload);
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.handleError(error as Error, eventName, payload);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (once) {
|
|
102
|
+
toRemove.push(listener);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 等待所有异步监听器完成
|
|
107
|
+
if (promises.length > 0) {
|
|
108
|
+
await Promise.all(promises);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 移除一次性监听器
|
|
112
|
+
for (const listener of toRemove) {
|
|
113
|
+
this.off(eventName, listener);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 订阅事件
|
|
119
|
+
*/
|
|
120
|
+
public on<T>(
|
|
121
|
+
event: string | symbol,
|
|
122
|
+
listener: EventListener<T>,
|
|
123
|
+
options: ListenerOptions = {},
|
|
124
|
+
): () => void {
|
|
125
|
+
const eventName = this.resolveEventName(event);
|
|
126
|
+
return this.addListener(eventName, listener, false, options);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 一次性订阅
|
|
131
|
+
*/
|
|
132
|
+
public once<T>(
|
|
133
|
+
event: string | symbol,
|
|
134
|
+
listener: EventListener<T>,
|
|
135
|
+
options: ListenerOptions = {},
|
|
136
|
+
): () => void {
|
|
137
|
+
const eventName = this.resolveEventName(event);
|
|
138
|
+
return this.addListener(eventName, listener, true, options);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 取消订阅
|
|
143
|
+
*/
|
|
144
|
+
public off<T>(event: string | symbol, listener: EventListener<T>): void {
|
|
145
|
+
const eventName = this.resolveEventName(event);
|
|
146
|
+
const eventListeners = this.listeners.get(eventName);
|
|
147
|
+
|
|
148
|
+
if (!eventListeners) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const index = eventListeners.findIndex((l) => l.listener === listener);
|
|
153
|
+
if (index !== -1) {
|
|
154
|
+
eventListeners.splice(index, 1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 如果没有监听器了,移除事件
|
|
158
|
+
if (eventListeners.length === 0) {
|
|
159
|
+
this.listeners.delete(eventName);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 移除所有监听器
|
|
165
|
+
*/
|
|
166
|
+
public removeAllListeners(event?: string | symbol): void {
|
|
167
|
+
if (event !== undefined) {
|
|
168
|
+
const eventName = this.resolveEventName(event);
|
|
169
|
+
this.listeners.delete(eventName);
|
|
170
|
+
} else {
|
|
171
|
+
this.listeners.clear();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 获取指定事件的监听器数量
|
|
177
|
+
*/
|
|
178
|
+
public listenerCount(event: string | symbol): number {
|
|
179
|
+
const eventName = this.resolveEventName(event);
|
|
180
|
+
const matchedListeners = this.getMatchedListeners(eventName);
|
|
181
|
+
return matchedListeners.length;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 获取所有已注册的事件名称
|
|
186
|
+
*/
|
|
187
|
+
public eventNames(): (string | symbol)[] {
|
|
188
|
+
return Array.from(this.listeners.keys());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 添加监听器
|
|
193
|
+
*/
|
|
194
|
+
private addListener<T>(
|
|
195
|
+
event: string | symbol,
|
|
196
|
+
listener: EventListener<T>,
|
|
197
|
+
once: boolean,
|
|
198
|
+
options: ListenerOptions,
|
|
199
|
+
): () => void {
|
|
200
|
+
let eventListeners = this.listeners.get(event);
|
|
201
|
+
|
|
202
|
+
if (!eventListeners) {
|
|
203
|
+
eventListeners = [];
|
|
204
|
+
this.listeners.set(event, eventListeners);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 检查监听器数量限制
|
|
208
|
+
if (
|
|
209
|
+
this.options.maxListeners &&
|
|
210
|
+
eventListeners.length >= this.options.maxListeners
|
|
211
|
+
) {
|
|
212
|
+
console.warn(
|
|
213
|
+
`[EventEmitter] Max listeners (${this.options.maxListeners}) exceeded for event: ${String(event)}. ` +
|
|
214
|
+
'This may indicate a memory leak.',
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const registeredListener: RegisteredListener<T> = {
|
|
219
|
+
listener,
|
|
220
|
+
once,
|
|
221
|
+
priority: options.priority ?? 0,
|
|
222
|
+
async: options.async ?? false,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
eventListeners.push(registeredListener as RegisteredListener);
|
|
226
|
+
|
|
227
|
+
// 返回取消订阅函数
|
|
228
|
+
return () => {
|
|
229
|
+
this.off(event, listener);
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 解析事件名称(添加全局前缀)
|
|
235
|
+
*/
|
|
236
|
+
private resolveEventName(event: string | symbol): string | symbol {
|
|
237
|
+
if (typeof event === 'symbol') {
|
|
238
|
+
return event;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (this.options.globalPrefix) {
|
|
242
|
+
return `${this.options.globalPrefix}${this.options.delimiter}${event}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return event;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 获取匹配的监听器(支持通配符)
|
|
250
|
+
*/
|
|
251
|
+
private getMatchedListeners(event: string | symbol): RegisteredListener[] {
|
|
252
|
+
const result: RegisteredListener[] = [];
|
|
253
|
+
|
|
254
|
+
// 精确匹配
|
|
255
|
+
const exactListeners = this.listeners.get(event);
|
|
256
|
+
if (exactListeners) {
|
|
257
|
+
result.push(...exactListeners);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 通配符匹配
|
|
261
|
+
if (this.options.wildcard && typeof event === 'string') {
|
|
262
|
+
const delimiter = this.options.delimiter ?? '.';
|
|
263
|
+
const eventParts = event.split(delimiter);
|
|
264
|
+
|
|
265
|
+
for (const [key, listeners] of this.listeners.entries()) {
|
|
266
|
+
if (typeof key !== 'string' || key === event) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (this.matchWildcard(eventParts, key.split(delimiter))) {
|
|
271
|
+
result.push(...listeners);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* 匹配通配符模式
|
|
281
|
+
*/
|
|
282
|
+
private matchWildcard(eventParts: string[], patternParts: string[]): boolean {
|
|
283
|
+
let eventIndex = 0;
|
|
284
|
+
let patternIndex = 0;
|
|
285
|
+
|
|
286
|
+
while (patternIndex < patternParts.length) {
|
|
287
|
+
const pattern = patternParts[patternIndex];
|
|
288
|
+
|
|
289
|
+
if (pattern === '**') {
|
|
290
|
+
// ** 匹配任意数量的部分
|
|
291
|
+
if (patternIndex === patternParts.length - 1) {
|
|
292
|
+
return true; // ** 在末尾,匹配所有剩余部分
|
|
293
|
+
}
|
|
294
|
+
// 尝试匹配后续部分
|
|
295
|
+
for (let i = eventIndex; i <= eventParts.length; i++) {
|
|
296
|
+
if (
|
|
297
|
+
this.matchWildcard(
|
|
298
|
+
eventParts.slice(i),
|
|
299
|
+
patternParts.slice(patternIndex + 1),
|
|
300
|
+
)
|
|
301
|
+
) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
} else if (pattern === '*') {
|
|
307
|
+
// * 匹配单个部分
|
|
308
|
+
if (eventIndex >= eventParts.length) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
eventIndex++;
|
|
312
|
+
patternIndex++;
|
|
313
|
+
} else {
|
|
314
|
+
// 精确匹配
|
|
315
|
+
if (eventIndex >= eventParts.length || eventParts[eventIndex] !== pattern) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
eventIndex++;
|
|
319
|
+
patternIndex++;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return eventIndex === eventParts.length;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* 按优先级排序监听器
|
|
328
|
+
*/
|
|
329
|
+
private sortListenersByPriority(
|
|
330
|
+
listeners: RegisteredListener[],
|
|
331
|
+
): RegisteredListener[] {
|
|
332
|
+
return [...listeners].sort((a, b) => b.priority - a.priority);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 处理错误
|
|
337
|
+
*/
|
|
338
|
+
private handleError(
|
|
339
|
+
error: Error,
|
|
340
|
+
event: string | symbol,
|
|
341
|
+
payload: unknown,
|
|
342
|
+
): void {
|
|
343
|
+
if (this.options.onError) {
|
|
344
|
+
this.options.onError(error, event, payload);
|
|
345
|
+
} else {
|
|
346
|
+
console.error(
|
|
347
|
+
`[EventEmitter] Error in listener for event "${String(event)}":`,
|
|
348
|
+
error,
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|