fastevent 0.0.1 → 1.0.1

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 (41) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.github/workflows/publish.yaml +50 -0
  4. package/.vscode/launch.json +20 -0
  5. package/CHANGELOG.md +14 -0
  6. package/LICENSE +21 -0
  7. package/bench.png +0 -0
  8. package/dist/index.d.mts +205 -0
  9. package/dist/index.d.ts +205 -0
  10. package/dist/index.js +2 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/index.mjs +2 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/package.json +34 -12
  15. package/readme.md +282 -0
  16. package/readme_cn.md +282 -0
  17. package/src/__benchmarks__/index.ts +3 -0
  18. package/src/__benchmarks__/multi-level.ts +40 -0
  19. package/src/__benchmarks__/sample.ts +40 -0
  20. package/src/__benchmarks__/wildcard.ts +41 -0
  21. package/src/__tests__/emit.test.ts +94 -0
  22. package/src/__tests__/emitAsync.test.ts +65 -0
  23. package/src/__tests__/isPathMatched.test.ts +205 -0
  24. package/src/__tests__/many.test.ts +23 -0
  25. package/src/__tests__/meta.test.ts +29 -0
  26. package/src/__tests__/off.test.ts +214 -0
  27. package/src/__tests__/onany.test.ts +173 -0
  28. package/src/__tests__/once.test.ts +71 -0
  29. package/src/__tests__/retain.test.ts +66 -0
  30. package/src/__tests__/scope.test.ts +111 -0
  31. package/src/__tests__/waitFor.test.ts +109 -0
  32. package/src/__tests__/wildcard.test.ts +186 -0
  33. package/src/event.ts +470 -5
  34. package/src/index.ts +3 -1
  35. package/src/scope.ts +88 -0
  36. package/src/types.ts +57 -0
  37. package/src/typestest.ts +102 -0
  38. package/src/utils/isPathMatched.ts +40 -0
  39. package/src/utils/removeItem.ts +16 -0
  40. package/tsconfig.json +112 -0
  41. package/tsup.config.ts +19 -0
package/src/event.ts CHANGED
@@ -1,5 +1,470 @@
1
-
2
-
3
- export class FastEvent{
4
-
5
- }
1
+ import { FastEventScope } from './scope';
2
+ import {
3
+ FastEventListener,
4
+ FastEventOptions,
5
+ FastEvents,
6
+ FastListeners,
7
+ FastListenerNode,
8
+ FastEventSubscriber,
9
+ ScopeEvents,
10
+ FastEventMeta,
11
+ FastEventListenOptions
12
+ } from './types';
13
+ import { isPathMatched } from './utils/isPathMatched';
14
+ import { removeItem } from './utils/removeItem';
15
+
16
+ export class FastEvent<
17
+ Events extends FastEvents = FastEvents,
18
+ Types extends keyof Events = keyof Events,
19
+ Meta = unknown
20
+ >{
21
+ public listeners : FastListeners = { __listeners: [] } as unknown as FastListeners
22
+ private _options : FastEventOptions
23
+ private _delimiter : string = '/'
24
+ private _context : any
25
+ private _retainedEvents : Map<string,any> = new Map<string,any>()
26
+ constructor(options?:FastEventOptions<Meta>) {
27
+ this._options = Object.assign({
28
+ delimiter : '/',
29
+ context : null,
30
+ ignoreErrors : true
31
+ }, options)
32
+ this._delimiter = this._options.delimiter!
33
+ this._context = this._options.context || this
34
+ }
35
+ get options(){ return this._options }
36
+ private _addListener(parts:string[],listener:FastEventListener<any,any>,options:Required<FastEventListenOptions>):FastListenerNode | undefined{
37
+ const { count, prepend } = options
38
+ return this._forEachNodes(parts,(node)=>{
39
+ const newListener = count >0 ? [listener,count]: listener as any
40
+ if(prepend){
41
+ node.__listeners.splice(0,0,newListener)
42
+ }else{
43
+ node.__listeners.push(newListener)
44
+ }
45
+ if(typeof(this._options.onAddListener)==='function'){
46
+ this._options.onAddListener(parts,listener)
47
+ }
48
+ })
49
+ }
50
+ /**
51
+ *
52
+ * 根据parts路径遍历侦听器树,并在最后的节点上执行回调函数
53
+ *
54
+ * @param parts
55
+ * @param callback
56
+ * @returns
57
+ */
58
+ private _forEachNodes(parts:string[],callback:(node:FastListenerNode,parent:FastListenerNode)=>void):FastListenerNode | undefined{
59
+ if(parts.length === 0) return
60
+ let current = this.listeners
61
+ for(let i=0;i<parts.length;i++){
62
+ const part = parts[i]
63
+ if(!(part in current)){
64
+ current[part] = {
65
+ __listeners: []
66
+ } as unknown as FastListeners
67
+ }
68
+ if(i===parts.length-1){
69
+ const node = current[part]
70
+ callback(node,current)
71
+ return node
72
+ }else{
73
+ current = current[part]
74
+ }
75
+ }
76
+ return undefined
77
+ }
78
+
79
+
80
+ /**
81
+ * 从监听器节点中移除指定的事件监听器
82
+ * @private
83
+ * @param node - 监听器节点
84
+ * @param listener - 需要移除的事件监听器
85
+ * @description 遍历节点的监听器列表,移除所有匹配的监听器。支持移除普通函数和数组形式的监听器
86
+ */
87
+ private _removeListener(node: FastListenerNode, path:string[],listener: FastEventListener<any,any,any>): void {
88
+ if(!listener) return
89
+ removeItem(node.__listeners,(item:any)=>{
90
+ item = Array.isArray(item) ? item[0] : item
91
+ const isRemove = item === listener
92
+ if(isRemove && typeof(this._options.onRemoveListener)==='function'){
93
+ this._options.onRemoveListener(path,listener)
94
+ }
95
+ return isRemove
96
+ })
97
+ }
98
+ public on<T extends string>(type: T, listener: FastEventListener<T,Events[T],Meta>, options?:FastEventListenOptions ): FastEventSubscriber
99
+ public on<T extends Types=Types>(type: T, listener: FastEventListener<Types,Events[T],Meta>, options?:FastEventListenOptions): FastEventSubscriber
100
+ public on<P=any>(type: '**', listener: FastEventListener<Types,P,Meta>): FastEventSubscriber
101
+ public on(): FastEventSubscriber{
102
+ const type = arguments[0] as string
103
+ const listener = arguments[1] as FastEventListener
104
+ const options = Object.assign({
105
+ count : 0,
106
+ prepend: false
107
+ },arguments[2]) as Required<FastEventListenOptions>
108
+
109
+ if(type.length===0) throw new Error('event type cannot be empty')
110
+
111
+ if(type==='**'){
112
+ return this.onAny(listener)
113
+ }
114
+
115
+ const parts = type.split(this._delimiter);
116
+ const node = this._addListener(parts,listener,options)
117
+
118
+ // Retain不支持通配符
119
+ if(node && !type.includes('*')) this._emitForLastEvent(type)
120
+
121
+ return {
122
+ off: ()=>node && this._removeListener(node,parts,listener)
123
+ }
124
+ }
125
+
126
+ public once<T extends string>(type: T, listener: FastEventListener<T,Events[T],Meta> ): FastEventSubscriber
127
+ public once<T extends Types=Types>(type: T, listener: FastEventListener<Types,Events[T],Meta> ): FastEventSubscriber
128
+ public once(): FastEventSubscriber{
129
+ return this.on(arguments[0],arguments[1],{count:1})
130
+ }
131
+
132
+ /**
133
+ * 注册一个监听器,用于监听所有事件
134
+ * @param listener 事件监听器函数,可以接收任意类型的事件数据
135
+ * @returns {FastEventSubscriber} 返回一个订阅者对象,包含 off 方法用于取消监听
136
+ * @example
137
+ * ```ts
138
+ * const subscriber = emitter.onAny((eventName, data) => {
139
+ * console.log(eventName, data);
140
+ * });
141
+ *
142
+ * // 取消监听
143
+ * subscriber.off();
144
+ * ```
145
+ */
146
+ onAny<P=any>(listener: FastEventListener<string,P,Meta>, options?:Pick<FastEventListenOptions,'prepend'>): FastEventSubscriber {
147
+ const listeners = this.listeners.__listeners
148
+ if(options && options.prepend){
149
+ listeners.splice(0,0,listener)
150
+ }else{
151
+ listeners.push(listener)
152
+ }
153
+ return {
154
+ off:()=>this._removeListener(this.listeners,[],listener)
155
+ }
156
+ }
157
+
158
+ off(listener: FastEventListener<any, any, any>):void
159
+ off(type: string, listener: FastEventListener<any, any, any>):void
160
+ off(type: Types, listener: FastEventListener<any, any, any>):void
161
+ off(type: string):void
162
+ off(type: Types):void
163
+ off(){
164
+ const args = arguments
165
+ const type = typeof(args[0])==='function' ? undefined : args[0]
166
+ const listener = typeof(args[0])==='function' ? args[0] : args[1]
167
+ const parts = type ? type.split(this._delimiter) : []
168
+ const hasWildcard= type ? type.includes('*') : false
169
+ if(type && !hasWildcard){
170
+ this._traverseToPath(this.listeners,parts,(node)=>{
171
+ if(listener){ // 只删除指定的监听器
172
+ this._removeListener(node,parts,listener)
173
+ }else if(type){
174
+ node.__listeners=[]
175
+ }
176
+ })
177
+ }else{ // 仅删除指定的侦听器
178
+ const entryParts:string[] = hasWildcard ? [] : parts
179
+ this._traverseListeners(this.listeners,entryParts,(path,node)=>{
180
+ if(listener!==undefined || (hasWildcard && isPathMatched(path,parts))){
181
+ if(listener){
182
+ this._removeListener(node,parts,listener)
183
+ }else{
184
+ node.__listeners=[]
185
+ }
186
+ }
187
+ })
188
+ }
189
+ }
190
+
191
+ /**
192
+ * 移除所有事件监听器
193
+ * @param entry - 可选的事件前缀,如果提供则只移除指定前缀下的的监听器
194
+ * @description
195
+ * - 如果提供了prefix参数,则只清除该前缀下的所有监听器
196
+ * - 如果没有提供prefix,则清除所有监听器
197
+ * - 同时会清空保留的事件(_retainedEvents)
198
+ * - 重置监听器对象为空
199
+
200
+ * @example
201
+ *
202
+ * ```ts
203
+ * emitter.offAll(); // 清除所有监听器
204
+ * emitter.offAll('a/b'); // 清除a/b下的所有监听器
205
+ *
206
+ */
207
+ offAll(entry?: string) {
208
+ if(entry){
209
+ const entryNode = this._getListenerNode(entry.split(this._delimiter))
210
+ if(entryNode) entryNode.__listeners = []
211
+ this._removeRetainedEvents(entry)
212
+ }else{
213
+ this._retainedEvents.clear()
214
+ this.listeners = { __listeners: [] } as unknown as FastListeners
215
+ }
216
+ }
217
+
218
+ private _getListenerNode(parts:string[]):FastListenerNode | undefined{
219
+ let entryNode: FastListenerNode | undefined
220
+ this._forEachNodes(parts,(node)=>{
221
+ entryNode = node
222
+ })
223
+ return entryNode
224
+ }
225
+ /**
226
+ * 移除保留的事件
227
+ * @param prefix - 事件前缀。如果不提供,将清除所有保留的事件。
228
+ * 如果提供前缀,将删除所有以该前缀开头的事件。
229
+ * 如果前缀不以分隔符结尾,会自动添加分隔符。
230
+ * @private
231
+ */
232
+ private _removeRetainedEvents(prefix?: string) {
233
+ if (!prefix) this._retainedEvents.clear()
234
+ if(prefix?.endsWith(this._delimiter)){
235
+ prefix+=this._delimiter
236
+ }
237
+ this._retainedEvents.delete(prefix!)
238
+ for(let key of this._retainedEvents.keys()){
239
+ if(key.startsWith(prefix!)){
240
+ this._retainedEvents.delete(key)
241
+ }
242
+ }
243
+ }
244
+ clear(){
245
+ this.offAll()
246
+ }
247
+ private _getMeta(extra:Record<string,any>){
248
+ if(!this._options.meta) return extra
249
+ return Object.assign({},this._options.meta,extra) as FastEventMeta<any,any>
250
+ }
251
+ private _emitForLastEvent(type:string){
252
+ if(this._retainedEvents.has(type)){
253
+ const payload = this._retainedEvents.get(type)
254
+ const parts = type.split(this._delimiter);
255
+ this._traverseToPath(this.listeners,parts,(node)=>{
256
+ this._executeListeners(node,payload,this._getMeta({type}))
257
+ })
258
+ // onAny侦听器保存在根节点中,所以需要执行
259
+ this._executeListeners(this.listeners,payload,this._getMeta({type}))
260
+ }
261
+ }
262
+
263
+ /**
264
+ * 遍历监听器节点树
265
+ * @param node 当前遍历的监听器节点
266
+ * @param parts 事件名称按'.'分割的部分数组
267
+ * @param callback 遍历到目标节点时的回调函数
268
+ * @param index 当前遍历的parts数组索引,默认为0
269
+ * @param lastFollowing 当命中**时该值为true, 注意**只能作在路径的最后面,如a.**有效,而a.**.b无效
270
+ * @private
271
+ *
272
+ * 支持三种匹配模式:
273
+ * - 精确匹配: 'a.b.c'
274
+ * - 单层通配: 'a.*.c'
275
+ * - 多层通配: 'a.**'
276
+ */
277
+ private _traverseToPath(node: FastListenerNode, parts: string[], callback: (node: FastListenerNode) => void, index: number = 0, lastFollowing?:boolean): void {
278
+
279
+ if (index >= parts.length) {
280
+ callback(node)
281
+ return
282
+ }
283
+ const part = parts[index]
284
+
285
+ if(lastFollowing===true){
286
+ this._traverseToPath(node, parts, callback, index + 1,true)
287
+ return
288
+ }
289
+
290
+ if (part in node) {
291
+ this._traverseToPath(node[part], parts, callback, index + 1)
292
+ }
293
+
294
+ if ('*' in node) {
295
+ this._traverseToPath(node['*'], parts, callback, index + 1)
296
+ }
297
+ //
298
+ if ('**' in node) {
299
+ this._traverseToPath(node['**'], parts, callback, index + 1,true)
300
+ }
301
+ }
302
+
303
+ private _traverseListeners(node: FastListenerNode, entry:string[], callback: (path:string[],node: FastListenerNode) => void): void {
304
+ let entryNode: FastListenerNode = node
305
+ // 如果指定了entry路径,则按照路径遍历
306
+ if (entry && entry.length > 0) {
307
+ this._traverseToPath(node, entry,(node)=>{
308
+ entryNode= node
309
+ });
310
+ }
311
+ const traverseNodes = (node: FastListenerNode, callback: (path:string[],node: FastListenerNode) => void,parentPath:string[])=>{
312
+ callback(parentPath, node);
313
+ for(let [key,childNode] of Object.entries(node)){
314
+ if(key.startsWith("__")) continue
315
+ if(childNode){
316
+ traverseNodes(childNode as FastListenerNode, callback,[...parentPath,key]);
317
+ }
318
+ }
319
+ }
320
+ // 如果没有指定entry或entry为空数组,则递归遍历所有节点
321
+ traverseNodes(entryNode, callback,[]);
322
+ }
323
+
324
+ private _executeListener(listener:any, payload: any,meta: FastEventMeta<any,any> ):any{
325
+ try{
326
+ if(typeof(listener.__wrappedListener)==='function'){
327
+ return listener.__wrappedListener.call(this._context,payload,meta)
328
+ }else{
329
+ return listener.call(this._context,payload,meta)
330
+ }
331
+ }catch(e:any){
332
+ e._trigger = meta.type
333
+ if(typeof(this._options.onListenerError)==='function'){
334
+ this._options.onListenerError.call(this,meta.type,e)
335
+ }
336
+ // 如果忽略错误,则返回错误对象
337
+ if(this._options.ignoreErrors){
338
+ return e
339
+ }else{
340
+ throw e
341
+ }
342
+ }
343
+ }
344
+
345
+ /**
346
+ * 执行监听器节点中的所有监听函数
347
+ * @param node - FastListenerNode类型的监听器节点
348
+ * @param payload - 事件携带的数据
349
+ * @param type - 事件类型
350
+ * @returns 返回所有监听函数的执行结果数组
351
+ * @private
352
+ *
353
+ * @description
354
+ * 遍历执行节点中的所有监听函数:
355
+ * - 对于普通监听器,直接执行并收集结果
356
+ * - 对于带次数限制的监听器(数组形式),执行后递减次数,当次数为0时移除该监听器
357
+ */
358
+ private _executeListeners(node: FastListenerNode, payload: any, meta: Meta): any[] {
359
+ if (!node || !node.__listeners) return []
360
+ let i = 0
361
+ const listeners = node.__listeners
362
+ let result:any[] = []
363
+ while(i<listeners.length){
364
+ const listener = listeners[i]
365
+ if(Array.isArray(listener)){
366
+ result.push(this._executeListener(listener[0],payload,meta))
367
+ listener[1]--
368
+ if(listener[1]===0) {
369
+ listeners.splice(i,1)
370
+ i-- // 抵消后面的i++
371
+ }
372
+ }else{
373
+ result.push(this._executeListener(listener,payload,meta))
374
+ }
375
+ i++
376
+ }
377
+ return result
378
+ }
379
+
380
+ public emit<R=any>(type:string,payload?:any,retain?:boolean,meta?:Meta):R[]
381
+ public emit<R=any>(type:Types,payload?:Events[Types],retain?:boolean,meta?:Meta):R[]
382
+ public emit<R=any>():R[]{
383
+ const type = arguments[0] as string
384
+ const payload = arguments[1] as any
385
+ const retain = arguments[2] as boolean
386
+ const meta = (arguments[3] || {}) as Meta
387
+
388
+ const parts = type.split(this._delimiter);
389
+ if(retain) {
390
+ this._retainedEvents.set(type,payload)
391
+ }
392
+ const results:any[] = []
393
+ this._traverseToPath(this.listeners,parts,(node)=>{
394
+ results.push(...this._executeListeners(node,payload,this._getMeta({...meta,type})))
395
+ })
396
+ // onAny侦听器保存在根节点中,所以需要执行
397
+ results.push(...this._executeListeners(this.listeners,payload,this._getMeta({...meta,type})))
398
+ return results
399
+ }
400
+
401
+ public async emitAsync<R=any>(type:string,payload?:any,retain?:boolean,meta?:Meta):Promise<[R|Error][]>
402
+ public async emitAsync<R=any>(type:Types,payload?:Events[Types],retain?:boolean,meta?:Meta):Promise<[R|Error][]>
403
+ public async emitAsync<P=any>():Promise<[P|Error][]>{
404
+ const type = arguments[0] as string
405
+ const payload = arguments[1] as any
406
+ const retain = arguments[2] as boolean
407
+ const meta = (arguments[3] || {}) as Meta
408
+
409
+ const results = await Promise.allSettled(this.emit<P>(type,payload,retain,this._getMeta({...meta,type})))
410
+ return results.map((result)=>{
411
+ if(result.status==='fulfilled'){
412
+ return result.value
413
+ }else{
414
+ return result.reason
415
+ }
416
+ })
417
+ }
418
+
419
+ /**
420
+ * 等待指定事件发生
421
+ *
422
+ * @param type
423
+ * @param timeout 超时时间,单位毫秒,默认为 0,表示无限等待
424
+ */
425
+ public waitFor<R=any>(type:string,timeout?:number):Promise<R>
426
+ public waitFor<R=any>(type:Types,timeout?:number):Promise<R>
427
+ public waitFor<R=any>():Promise<R>{
428
+ const type = arguments[0] as string
429
+ const timeout = arguments[1] as number
430
+ return new Promise<R>((resolve,reject)=>{
431
+ let tid:any
432
+ let subscriber:FastEventSubscriber
433
+ const listener = (payload:any)=>{
434
+ clearTimeout(tid)
435
+ subscriber.off()
436
+ resolve(payload)
437
+ }
438
+ if(timeout && timeout>0){
439
+ tid = setTimeout(()=>{
440
+ subscriber && subscriber.off()
441
+ reject(new Error('wait for event<'+ type +'> is timeout'))
442
+ },timeout)
443
+ }
444
+ subscriber = this.on(type,listener)
445
+ })
446
+ }
447
+
448
+ /**
449
+ * 创建事件域
450
+ *
451
+ * 注意:
452
+ *
453
+ * 事件域与当前事件对象共享相同的侦听器表
454
+ * 也就是说,如果在事件域中触发事件,当前事件对象也会触发该事件
455
+ * 两者工不是完全隔离的,仅是事件侦听和触发时的事件类型不同而已
456
+ *
457
+ * const emitter = new FastEvent()
458
+ *
459
+ * const scope= emitter.scope("a/b")
460
+ *
461
+ * scope.on("x",()=>{}) == emitter.on("a/b/x",()=>{})
462
+ * scope.emit("x",1) == emitter.emit("a/b/x",1)
463
+ * scope.offAll() == emitter.offAll("a/b")
464
+ *
465
+ */
466
+ scope<T extends string>(prefix:T){
467
+ return new FastEventScope<ScopeEvents<Events,T>>(this as unknown as FastEvent<ScopeEvents<Events,T>>,prefix)
468
+ }
469
+
470
+ }
package/src/index.ts CHANGED
@@ -1 +1,3 @@
1
- export * from "./event"
1
+ export * from "./event"
2
+ export * from "./scope"
3
+ export * from "./types"
package/src/scope.ts ADDED
@@ -0,0 +1,88 @@
1
+ import { FastEvent } from "./event";
2
+ import { FastEventListener, FastEventListenOptions, FastEvents, FastEventSubscriber } from "./types";
3
+
4
+ export class FastEventScope<
5
+ Events extends FastEvents = FastEvents,
6
+ Types extends keyof Events = keyof Events,
7
+ Meta = unknown
8
+ >{
9
+ constructor(public emitter:FastEvent<Events,Types,Meta>,public prefix:string){
10
+ if(prefix.length>0 && !prefix.endsWith(emitter.options.delimiter!)){
11
+ this.prefix = prefix + emitter.options.delimiter
12
+ }
13
+ }
14
+ private _getScopeListener(listener:FastEventListener):FastEventListener{
15
+ const scopePrefix = this.prefix
16
+ if(scopePrefix.length===0) return listener
17
+ const scopeListener = function(payload:any,{type}:{type:string}){
18
+ if(type.startsWith(scopePrefix)){
19
+ type = type.substring(scopePrefix.length)
20
+ }
21
+ return listener(payload,{type})
22
+ }
23
+ // 当启用scope时对监听器进行包装
24
+ //@ts-ignore
25
+ listener.__wrappedListener = scopeListener
26
+ return listener
27
+ }
28
+ private _getScopeType(type:string){
29
+ return type===undefined ? undefined : this.prefix + type
30
+ }
31
+
32
+ public on<T extends string>(type: T, listener: FastEventListener<T,Events[T],Meta>, options?:FastEventListenOptions ): FastEventSubscriber
33
+ public on<T extends Types=Types>(type: T, listener: FastEventListener<T,Events[T],Meta>, options?:FastEventListenOptions): FastEventSubscriber
34
+ public on(type: '**', listener: FastEventListener<any,any,Meta>): FastEventSubscriber
35
+ public on(): FastEventSubscriber{
36
+ const args = [...arguments] as [any,any,any]
37
+ args[0] = this._getScopeType(args[0])
38
+ args[1] = this._getScopeListener(args[1])
39
+ return this.emitter.on(...args)
40
+ }
41
+
42
+ public once<T extends string>(type: T, listener: FastEventListener<T,Events[T],Meta>, options?:FastEventListenOptions ): FastEventSubscriber
43
+ public once<T extends Types = Types>(type: T, listener: FastEventListener<Types,Events[T],Meta>, options?:FastEventListenOptions ): FastEventSubscriber
44
+ public once(): FastEventSubscriber{
45
+ return this.on(arguments[0],arguments[1],Object.assign(arguments[2],{},{count:1}))
46
+ }
47
+
48
+ onAny<P=any>(listener: FastEventListener<Types,P,Meta>, options?:FastEventListenOptions): FastEventSubscriber {
49
+ const type = this.prefix + '**'
50
+ return this.on(type as any,listener,options)
51
+ }
52
+ offAll(){
53
+ this.emitter.offAll(this.prefix)
54
+ }
55
+ off(listener: FastEventListener<any, any,any>):void
56
+ off(type: string, listener: FastEventListener<any, any, any>):void
57
+ off(type: Types, listener: FastEventListener<any, any, any>):void
58
+ off(type: string):void
59
+ off(type: Types):void
60
+ off(){
61
+ const args = arguments as unknown as [any,any]
62
+ if(typeof(args[0])==='string'){
63
+ args[0] = this._getScopeType(args[0])
64
+ }
65
+ this.emitter.off(...args)
66
+ }
67
+ clear(){
68
+ this.offAll()
69
+ }
70
+ public emit<R=any>(type:string,payload?:any,retain?:boolean):R[]
71
+ public emit<R=any>(type:Types,payload?:Events[Types],retain?:boolean):R[]
72
+ public emit<R=any>():R[]{
73
+ const type = arguments[0] as string
74
+ const payload = arguments[1]
75
+ const retain = arguments[2] as boolean
76
+ return this.emitter.emit(this._getScopeType(type)!,payload,retain)
77
+ }
78
+ public waitFor<R=any>(type:string,timeout?:number):Promise<R>
79
+ public waitFor<R=any>(type:Types,timeout?:number):Promise<R>
80
+ public waitFor<R=any>():Promise<R>{
81
+ const type = arguments[0] as string
82
+ const timeout = arguments[1] as number
83
+ return this.emitter.waitFor(this._getScopeType(type)!,timeout)
84
+ }
85
+ public scope(prefix:string){
86
+ return this.emitter.scope(this._getScopeType(prefix)!)
87
+ }
88
+ }
package/src/types.ts ADDED
@@ -0,0 +1,57 @@
1
+
2
+ export type FastEventMeta<T=string,M=unknown> = M & {type:T}
3
+
4
+
5
+ export type FastEventListener<
6
+ T = string,
7
+ P = any,
8
+ M = unknown
9
+ > = ( payload:P, meta:FastEventMeta<T,M> ) => any | Promise<any>
10
+
11
+ export type FastListenerNode = {
12
+ __listeners: (FastEventListener<any,any,any> | [FastEventListener<any,any>,number])[];
13
+ } & {
14
+ [key:string]: FastListenerNode
15
+ }
16
+
17
+ export type FastEventSubscriber = {
18
+ off:()=>void
19
+ }
20
+
21
+ export interface FastEventListenerMeta{
22
+ emitter?: string
23
+ }
24
+
25
+ export type FastListeners = FastListenerNode
26
+
27
+ export type FastEventOptions<M=unknown> = {
28
+ // 事件分隔符
29
+ delimiter?: string
30
+ // 侦听器函数执行上下文
31
+ context? : any
32
+ // 当执行侦听器函数出错时是否忽略,默认true
33
+ ignoreErrors?: boolean
34
+ // 当侦听器函数执行出错时的回调,用于诊断时使用,可以打印错误信息
35
+ onListenerError?: ((type:string,error:Error)=>void)
36
+ // 额外的元数据,当触发事件时传递给侦听器
37
+ meta?:M
38
+ // 当创建新侦听器时回调
39
+ onAddListener?:(type:string[],listener:FastEventListener)=>void
40
+ // 当移除侦听器时回调
41
+ onRemoveListener?:(type:string[],listener:FastEventListener)=>void
42
+ }
43
+
44
+ export type FastEvents = Record<string,any>
45
+
46
+ export type ScopeEvents<T extends Record<string, any>, Prefix extends string> = {
47
+ [K in keyof T as K extends `${Prefix}/${infer Rest}` ? Rest : never]: T[K];
48
+ };
49
+
50
+
51
+
52
+ export type FastEventListenOptions={
53
+ // 侦听执行次数,当为1时为单次侦听,为0时为永久侦听,其他值为执行次数,每执行一次减一,减到0时移除侦听器
54
+ count?:number
55
+ // 将侦听器添加到侦听器列表的头部
56
+ prepend?:boolean
57
+ }