fastevent 1.1.0 → 1.1.2

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 (55) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/devTools.d.mts +543 -0
  3. package/dist/devTools.d.ts +543 -0
  4. package/dist/devTools.js +3 -0
  5. package/dist/devTools.js.map +1 -0
  6. package/dist/devTools.mjs +3 -0
  7. package/dist/devTools.mjs.map +1 -0
  8. package/dist/index.d.mts +282 -37
  9. package/dist/index.d.ts +282 -37
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +1 -1
  13. package/dist/index.mjs.map +1 -1
  14. package/example/README.md +54 -0
  15. package/example/eslint.config.js +28 -0
  16. package/example/index.html +13 -0
  17. package/example/package.json +29 -0
  18. package/example/pnpm-lock.yaml +2047 -0
  19. package/example/public/vite.svg +1 -0
  20. package/example/src/App.css +42 -0
  21. package/example/src/App.tsx +60 -0
  22. package/example/src/assets/react.svg +1 -0
  23. package/example/src/index.css +68 -0
  24. package/example/src/main.tsx +10 -0
  25. package/example/src/myEvent.ts +32 -0
  26. package/example/src/vite-env.d.ts +1 -0
  27. package/example/tsconfig.app.json +26 -0
  28. package/example/tsconfig.json +7 -0
  29. package/example/tsconfig.node.json +24 -0
  30. package/example/vite.config.ts +7 -0
  31. package/package.json +15 -2
  32. package/packages/native/index.ts +1 -0
  33. package/packages/turbo/.zig-cache/h/271c82d991949fd7788fd5451f0ca834.txt +0 -0
  34. package/packages/turbo/.zig-cache/h/timestamp +0 -0
  35. package/packages/turbo/.zig-cache/o/ebd7ddab8ffe003267120d598aecce68/dependencies.zig +2 -0
  36. package/packages/turbo/.zig-cache/z/c8114b040daa461a9e2eabd0357554a4 +0 -0
  37. package/packages/turbo/build.zig +60 -0
  38. package/packages/turbo/examples/basic.zig +107 -0
  39. package/packages/turbo/src/event.zig +251 -0
  40. package/packages/turbo/src/index.zig +70 -0
  41. package/packages/turbo/src/scope.zig +104 -0
  42. package/packages/turbo/src/types.zig +88 -0
  43. package/packages/turbo/src/utils.zig +171 -0
  44. package/readme.md +86 -2
  45. package/readme_cn.md +86 -2
  46. package/src/__tests__/scope.test.ts +2 -2
  47. package/src/__tests__/types.test.ts +23 -12
  48. package/src/__tests__/waitFor.test.ts +1 -0
  49. package/src/devTools.ts +166 -0
  50. package/src/event.ts +272 -38
  51. package/src/scope.ts +52 -11
  52. package/src/types.ts +11 -4
  53. package/src/utils/WeakObjectMap.ts +64 -0
  54. package/tsconfig.json +103 -111
  55. package/tsup.config.ts +17 -6
package/src/event.ts CHANGED
@@ -13,26 +13,65 @@ import {
13
13
  import { isPathMatched } from './utils/isPathMatched';
14
14
  import { removeItem } from './utils/removeItem';
15
15
 
16
+ /**
17
+ * FastEvent 事件发射器类
18
+ *
19
+ * @template Events - 事件类型定义,继承自FastEvents接口
20
+ * @template Meta - 事件元数据类型,默认为任意键值对对象
21
+ * @template Types - 事件类型的键名类型,默认为Events的键名类型
22
+ */
16
23
  export class FastEvent<
17
24
  Events extends FastEvents = FastEvents,
18
25
  Meta extends Record<string, any> = Record<string, any>,
26
+ Context = any,
19
27
  Types extends keyof Events = keyof Events
20
28
  > {
29
+ /** 事件监听器树结构,存储所有注册的事件监听器 */
21
30
  public listeners: FastListeners = { __listeners: [] } as unknown as FastListeners
31
+
32
+ /** 事件发射器的配置选项 */
22
33
  private _options: FastEventOptions
34
+
35
+ /** 事件名称的分隔符,默认为'/' */
23
36
  private _delimiter: string = '/'
24
- private _context: any
25
- private _retainedEvents: Map<string, any> = new Map<string, any>()
37
+
38
+ /** 事件监听器执行时的上下文对象 */
39
+ private _context: Context
40
+
41
+ /** 保留的事件消息映射,用于新订阅者 */
42
+ retainedMessages: Map<string, any> = new Map<string, any>()
43
+
44
+ /** 当前注册的监听器总数 */
45
+ listenerCount: number = 0
46
+ /**
47
+ * 创建FastEvent实例
48
+ * @param options - 事件发射器的配置选项
49
+ *
50
+ * 默认配置:
51
+ * - debug: false - 是否启用调试模式
52
+ * - id: 随机字符串 - 实例唯一标识符
53
+ * - delimiter: '/' - 事件名称分隔符
54
+ * - context: null - 监听器执行上下文
55
+ * - ignoreErrors: true - 是否忽略监听器执行错误
56
+ */
26
57
  constructor(options?: FastEventOptions<Meta>) {
27
58
  this._options = Object.assign({
59
+ debug: false,
60
+ id: Math.random().toString(36).substring(2),
28
61
  delimiter: '/',
29
62
  context: null,
30
63
  ignoreErrors: true
31
64
  }, options)
32
65
  this._delimiter = this._options.delimiter!
33
66
  this._context = this._options.context || this
67
+ this._enableDevTools()
34
68
  }
69
+
70
+ /** 获取事件发射器的配置选项 */
35
71
  get options() { return this._options }
72
+
73
+ /** 获取事件发射器的唯一标识符 */
74
+ get id() { return this._options.id! }
36
75
  private _addListener(parts: string[], listener: FastEventListener<any, any>, options: Required<FastEventListenOptions>): FastListenerNode | undefined {
37
76
  const { count, prepend } = options
38
77
  return this._forEachNodes(parts, (node) => {
@@ -43,10 +82,17 @@ export class FastEvent<
43
82
  node.__listeners.push(newListener)
44
83
  }
45
84
  if (typeof (this._options.onAddListener) === 'function') {
85
+ this.listenerCount++
46
86
  this._options.onAddListener(parts, listener)
47
87
  }
48
88
  })
49
89
  }
90
+ private _enableDevTools() {
91
+ if (this.options.debug) {
92
+ // @ts-ignore
93
+ globalThis.__FLEXEVENT_DEVTOOLS__.add(this)
94
+ }
95
+ }
50
96
  /**
51
97
  *
52
98
  * 根据parts路径遍历侦听器树,并在最后的节点上执行回调函数
@@ -90,14 +136,39 @@ export class FastEvent<
90
136
  item = Array.isArray(item) ? item[0] : item
91
137
  const isRemove = item === listener
92
138
  if (isRemove && typeof (this._options.onRemoveListener) === 'function') {
139
+ this.listenerCount--
93
140
  this._options.onRemoveListener(path, listener)
94
141
  }
95
142
  return isRemove
96
143
  })
97
144
  }
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
145
+ /**
146
+ * 注册事件监听器
147
+ * @param type - 事件类型,支持以下格式:
148
+ * - 普通字符串:'user/login'
149
+ * - 通配符:'user/*'(匹配单层)或'user/**'(匹配多层)
150
+ * - 全局监听:'**'(监听所有事件)
151
+ * @param listener - 事件监听器函数
152
+ * @param options - 监听器配置选项:
153
+ * - count: 触发次数限制,0表示无限制
154
+ * - prepend: 是否将监听器添加到监听器队列开头
155
+ * @returns 返回订阅者对象,包含off方法用于取消监听
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * // 监听特定事件
160
+ * emitter.on('user/login', (data) => console.log(data));
161
+ *
162
+ * // 使用通配符
163
+ * emitter.on('user/*', (data) => console.log(data));
164
+ *
165
+ * // 限制触发次数
166
+ * emitter.on('event', handler, { count: 3 });
167
+ * ```
168
+ */
169
+ public on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber
170
+ public on<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber
171
+ public on<P = any>(type: '**', listener: FastEventListener<Types, P, Meta, Context>): FastEventSubscriber
101
172
  public on(): FastEventSubscriber {
102
173
  const type = arguments[0] as string
103
174
  const listener = arguments[1] as FastEventListener
@@ -123,8 +194,30 @@ export class FastEvent<
123
194
  }
124
195
  }
125
196
 
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
197
+
198
+ /**
199
+ * 注册一次性事件监听器
200
+ * @param type - 事件类型,支持与on方法相同的格式:
201
+ * - 普通字符串:'user/login'
202
+ * - 通配符:'user/*'(匹配单层)或'user/**'(匹配多层)
203
+ * @param listener - 事件监听器函数
204
+ * @returns 返回订阅者对象,包含off方法用于取消监听
205
+ *
206
+ * @description
207
+ * 监听器只会在事件首次触发时被调用一次,之后会自动解除注册。
208
+ * 这是on方法的特例,相当于设置options.count = 1。
209
+ * 如果事件有保留消息,新注册的监听器会立即收到最近一次的保留消息并解除注册。
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * // 只监听一次登录事件
214
+ * emitter.once('user/login', (data) => {
215
+ * console.log('用户登录:', data);
216
+ * });
217
+ * ```
218
+ */
219
+ public once<T extends Types = Types>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>): FastEventSubscriber
220
+ public once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>): FastEventSubscriber
128
221
  public once(): FastEventSubscriber {
129
222
  return this.on(arguments[0], arguments[1], { count: 1 })
130
223
  }
@@ -143,7 +236,7 @@ export class FastEvent<
143
236
  * subscriber.off();
144
237
  * ```
145
238
  */
146
- onAny<P = any>(listener: FastEventListener<string, P, Meta>, options?: Pick<FastEventListenOptions, 'prepend'>): FastEventSubscriber {
239
+ onAny<P = any>(listener: FastEventListener<string, P, Meta, Context>, options?: Pick<FastEventListenOptions, 'prepend'>): FastEventSubscriber {
147
240
  const listeners = this.listeners.__listeners
148
241
  if (options && options.prepend) {
149
242
  listeners.splice(0, 0, listener)
@@ -210,9 +303,10 @@ export class FastEvent<
210
303
  if (entryNode) entryNode.__listeners = []
211
304
  this._removeRetainedEvents(entry)
212
305
  } else {
213
- this._retainedEvents.clear()
306
+ this.retainedMessages.clear()
214
307
  this.listeners = { __listeners: [] } as unknown as FastListeners
215
308
  }
309
+ if (typeof (this._options.onClearListeners) === 'function') this._options.onClearListeners.call(this)
216
310
  }
217
311
 
218
312
  private _getListenerNode(parts: string[]): FastListenerNode | undefined {
@@ -230,14 +324,14 @@ export class FastEvent<
230
324
  * @private
231
325
  */
232
326
  private _removeRetainedEvents(prefix?: string) {
233
- if (!prefix) this._retainedEvents.clear()
327
+ if (!prefix) this.retainedMessages.clear()
234
328
  if (prefix?.endsWith(this._delimiter)) {
235
329
  prefix += this._delimiter
236
330
  }
237
- this._retainedEvents.delete(prefix!)
238
- for (let key of this._retainedEvents.keys()) {
331
+ this.retainedMessages.delete(prefix!)
332
+ for (let key of this.retainedMessages.keys()) {
239
333
  if (key.startsWith(prefix!)) {
240
- this._retainedEvents.delete(key)
334
+ this.retainedMessages.delete(key)
241
335
  }
242
336
  }
243
337
  }
@@ -252,8 +346,8 @@ export class FastEvent<
252
346
 
253
347
 
254
348
  private _emitForLastEvent(type: string) {
255
- if (this._retainedEvents.has(type)) {
256
- const message = this._retainedEvents.get(type)
349
+ if (this.retainedMessages.has(type)) {
350
+ const message = this.retainedMessages.get(type)
257
351
  const parts = type.split(this._delimiter);
258
352
  this._traverseToPath(this.listeners, parts, (node) => {
259
353
  this._executeListeners(node, message)
@@ -324,6 +418,23 @@ export class FastEvent<
324
418
  traverseNodes(entryNode, callback, []);
325
419
  }
326
420
 
421
+ /**
422
+ * 执行单个监听器函数
423
+ * @param listener - 要执行的监听器函数或包装过的监听器对象
424
+ * @param message - 事件消息对象,包含type、payload和meta
425
+ * @returns 监听器的执行结果或错误对象(如果配置了ignoreErrors)
426
+ * @private
427
+ *
428
+ * @description
429
+ * 执行单个监听器函数,处理以下情况:
430
+ * - 如果监听器是包装过的(有__wrappedListener属性),调用包装的函数
431
+ * - 否则直接调用监听器函数
432
+ * - 使用配置的上下文(_context)作为this
433
+ * - 捕获并处理执行过程中的错误:
434
+ * - 如果有onListenerError回调,调用它
435
+ * - 如果配置了ignoreErrors,返回错误对象
436
+ * - 否则抛出错误
437
+ */
327
438
  private _executeListener(listener: any, message: FastEventMessage): any {
328
439
  try {
329
440
  if (typeof (listener.__wrappedListener) === 'function') {
@@ -395,11 +506,48 @@ export class FastEvent<
395
506
  * // 方式2: 对象形式
396
507
  * emit({ type: 'user.login', payload: { id: 1 } ,meta:{...}}}, true)
397
508
  */
509
+ /**
510
+ * 同步触发事件
511
+ * @param type - 事件类型,可以是字符串或预定义的事件类型
512
+ * @param payload - 事件数据负载
513
+ * @param retain - 是否保留该事件,用于后续新的订阅者
514
+ * @param meta - 事件元数据
515
+ * @returns 所有监听器的执行结果数组
516
+ *
517
+ * @description
518
+ * 同步触发指定类型的事件,支持两种调用方式:
519
+ * 1. 参数形式:emit(type, payload, retain, meta)
520
+ * 2. 对象形式:emit({ type, payload, meta }, retain)
521
+ *
522
+ * 特性:
523
+ * - 支持通配符匹配,一个事件可能触发多个监听器
524
+ * - 如果设置了retain为true,会保存最后一次的事件数据
525
+ * - 按照注册顺序同步调用所有匹配的监听器
526
+ * - 如果配置了ignoreErrors,监听器抛出的错误会被捕获并返回
527
+ *
528
+ * @example
529
+ * ```ts
530
+ * // 简单事件触发
531
+ * emitter.emit('user/login', { userId: 123 });
532
+ *
533
+ * // 带保留的事件触发
534
+ * emitter.emit('status/change', { online: true }, true);
535
+ *
536
+ * // 带元数据的事件触发
537
+ * emitter.emit('data/update', newData, false, { timestamp: Date.now() });
538
+ *
539
+ * // 使用对象形式触发
540
+ * emitter.emit({
541
+ * type: 'user/login',
542
+ * payload: { userId: 123 },
543
+ * meta: { time: Date.now() }
544
+ * }, true);
545
+ * ```
546
+ */
398
547
  public emit<R = any>(type: string, payload?: any, retain?: boolean, meta?: Meta): R[]
399
548
  public emit<R = any>(type: Types, payload?: Events[Types], retain?: boolean, meta?: Meta): R[]
400
549
  public emit<R = any>(message: FastEventMessage<Types, Events[Types], Meta>, retain?: boolean): R[]
401
550
  public emit<R = any>(message: FastEventMessage<string, any, Meta>, retain?: boolean): R[]
402
-
403
551
  public emit<R = any>(): R[] {
404
552
  let type: string, payload: any, retain: boolean, meta: any
405
553
  if (typeof (arguments[0]) === 'object') {
@@ -420,17 +568,61 @@ export class FastEvent<
420
568
  }
421
569
  const parts = type.split(this._delimiter);
422
570
  if (retain) {
423
- this._retainedEvents.set(type, message)
571
+ this.retainedMessages.set(type, message)
424
572
  }
425
573
  const results: any[] = []
426
574
  // onAny侦听器保存在根节点中,所以需要执行
427
575
  results.push(...this._executeListeners(this.listeners, message))
428
576
  this._traverseToPath(this.listeners, parts, (node) => {
429
577
  results.push(...this._executeListeners(node, message))
578
+ // 用于调试时使用
579
+ if (this._options.debug && typeof (this._options.onExecuteListener) === 'function') {
580
+ if (retain) {
581
+ if (!message.meta) message.meta = { retain: true }
582
+ }
583
+ this._options.onExecuteListener.call(this, message, results, [...this.listeners.__listeners, ...node.__listeners])
584
+ }
430
585
  })
586
+
431
587
  return results
432
588
  }
433
589
 
590
+ /**
591
+ * 异步触发事件
592
+ * @param type - 事件类型,可以是字符串或预定义的事件类型
593
+ * @param payload - 事件数据负载
594
+ * @param retain - 是否保留该事件,用于后续新的订阅者
595
+ * @param meta - 事件元数据
596
+ * @returns Promise,解析为所有监听器的执行结果数组
597
+ *
598
+ * @description
599
+ * 异步触发指定类型的事件,与emit方法类似,但有以下区别:
600
+ * - 返回Promise,等待所有异步监听器执行完成
601
+ * - 使用Promise.allSettled处理监听器的执行结果
602
+ * - 即使某些监听器失败,也会等待所有监听器执行完成
603
+ * - 返回结果包含成功值或错误信息
604
+ *
605
+ * @example
606
+ * ```ts
607
+ * // 异步事件处理
608
+ * const results = await emitter.emitAsync('data/process', rawData);
609
+ *
610
+ * // 处理结果包含成功和失败的情况
611
+ * results.forEach(result => {
612
+ * if (result instanceof Error) {
613
+ * console.error('处理失败:', result);
614
+ * } else {
615
+ * console.log('处理成功:', result);
616
+ * }
617
+ * });
618
+ *
619
+ * // 带元数据的异步事件
620
+ * await emitter.emitAsync('batch/process', items, false, {
621
+ * batchId: 'batch-001',
622
+ * timestamp: Date.now()
623
+ * });
624
+ * ```
625
+ */
434
626
  public async emitAsync<R = any>(type: string, payload?: any, retain?: boolean, meta?: Meta): Promise<[R | Error][]>
435
627
  public async emitAsync<R = any>(type: Types, payload?: Events[Types], retain?: boolean, meta?: Meta): Promise<[R | Error][]>
436
628
  public async emitAsync<P = any>(): Promise<[P | Error][]> {
@@ -454,20 +646,41 @@ export class FastEvent<
454
646
  }
455
647
 
456
648
  /**
457
- * 等待指定事件发生
649
+ * 等待指定事件发生,返回一个Promise
650
+ * @param type - 要等待的事件类型
651
+ * @param timeout - 超时时间(毫秒),默认为0表示永不超时
652
+ * @returns Promise,解析为事件消息对象,包含type、payload和meta
653
+ *
654
+ * @description
655
+ * 创建一个Promise,在指定事件发生时解析。
656
+ * - 当事件触发时,Promise会解析为事件消息对象
657
+ * - 如果设置了timeout且超时,Promise会被拒绝
658
+ * - 一旦事件发生或超时,会自动取消事件监听
659
+ *
660
+ * @example
661
+ * ```ts
662
+ * try {
663
+ * // 等待登录事件,最多等待5秒
664
+ * const event = await emitter.waitFor('user/login', 5000);
665
+ * console.log('用户登录成功:', event.payload);
666
+ * } catch (error) {
667
+ * console.error('等待登录超时');
668
+ * }
458
669
  *
459
- * @param type
460
- * @param timeout 超时时间,单位毫秒,默认为 0,表示无限等待
670
+ * // 无限等待事件
671
+ * const event = await emitter.waitFor('server/ready');
672
+ * console.log('服务器就绪');
673
+ * ```
461
674
  */
462
- public waitFor<R = any>(type: string, timeout?: number): Promise<R>
463
- public waitFor<R = any>(type: Types, timeout?: number): Promise<R>
464
- public waitFor<R = any>(): Promise<R> {
465
- const type = arguments[0] as string
675
+ public waitFor<T extends Types, P = Events[T], M = Meta>(type: T, timeout?: number): Promise<FastEventMessage<T, P, M>>
676
+ public waitFor<T extends string, P = Events[T], M = Meta>(type: string, timeout?: number): Promise<FastEventMessage<T, P, M>>
677
+ public waitFor<T extends string, P = Events[T], M = Meta>(): Promise<FastEventMessage<T, P, M>> {
678
+ const type = arguments[0] as any
466
679
  const timeout = arguments[1] as number
467
- return new Promise<R>((resolve, reject) => {
680
+ return new Promise<FastEventMessage<T, P, M>>((resolve, reject) => {
468
681
  let tid: any
469
682
  let subscriber: FastEventSubscriber
470
- const listener = (message: any) => {
683
+ const listener = (message: FastEventMessage<T, P, M>) => {
471
684
  clearTimeout(tid)
472
685
  subscriber.off()
473
686
  resolve(message)
@@ -478,30 +691,51 @@ export class FastEvent<
478
691
  reject(new Error('wait for event<' + type + '> is timeout'))
479
692
  }, timeout)
480
693
  }
481
- subscriber = this.on(type, listener)
694
+ subscriber = this.on(type, listener as any)
482
695
  })
483
696
  }
484
697
 
485
698
  /**
486
- * 创建事件域
699
+ * 创建一个新的事件作用域
700
+ * @param prefix - 作用域前缀,将自动添加到该作用域下所有事件名称前
701
+ * @returns 新的FastEventScope实例
702
+ *
703
+ * @description
704
+ * 创建一个新的事件作用域,用于在特定命名空间下管理事件。
705
+ *
706
+ * 重要特性:
707
+ * - 作用域与父事件发射器共享同一个监听器表
708
+ * - 作用域中的事件会自动添加前缀
709
+ * - 作用域的所有操作都会映射到父事件发射器上
710
+ * - 作用域不是完全隔离的,只是提供了事件名称的命名空间
487
711
  *
488
- * 注意:
712
+ * @example
713
+ * ```ts
714
+ * const emitter = new FastEvent();
489
715
  *
490
- * 事件域与当前事件对象共享相同的侦听器表
491
- * 也就是说,如果在事件域中触发事件,当前事件对象也会触发该事件
492
- * 两者工不是完全隔离的,仅是事件侦听和触发时的事件类型不同而已
716
+ * // 创建用户相关事件的作用域
717
+ * const userEvents = emitter.scope('user');
493
718
  *
494
- * const emitter = new FastEvent()
719
+ * // 在作用域中监听事件
720
+ * userEvents.on('login', (data) => {
721
+ * // 实际监听的是 'user/login'
722
+ * console.log('用户登录:', data);
723
+ * });
495
724
  *
496
- * const scope= emitter.scope("a/b")
725
+ * // 在作用域中触发事件
726
+ * userEvents.emit('login', { userId: 123 });
727
+ * // 等同于 emitter.emit('user/login', { userId: 123 })
497
728
  *
498
- * scope.on("x",()=>{}) == emitter.on("a/b/x",()=>{})
499
- * scope.emit("x",1) == emitter.emit("a/b/x",1)
500
- * scope.offAll() == emitter.offAll("a/b")
729
+ * // 创建嵌套作用域
730
+ * const profileEvents = userEvents.scope('profile');
731
+ * profileEvents.emit('update', { name: 'John' });
732
+ * // 等同于 emitter.emit('user/profile/update', { name: 'John' })
501
733
  *
734
+ * // 清理作用域
735
+ * userEvents.offAll(); // 清理 'user' 前缀下的所有事件
736
+ * ```
502
737
  */
503
738
  scope<T extends string>(prefix: T) {
504
739
  return new FastEventScope<ScopeEvents<Events, T>>(this as unknown as FastEvent<ScopeEvents<Events, T>>, prefix)
505
740
  }
506
-
507
741
  }
package/src/scope.ts CHANGED
@@ -4,6 +4,7 @@ import { FastEventListener, FastEventListenOptions, FastEventMessage, FastEvents
4
4
  export class FastEventScope<
5
5
  Events extends FastEvents = FastEvents,
6
6
  Meta extends Record<string, any> = Record<string, any>,
7
+ Context = any,
7
8
  Types extends keyof Events = keyof Events
8
9
  > {
9
10
  constructor(public emitter: FastEvent<Events, Meta, Types>, public prefix: string) {
@@ -29,10 +30,13 @@ export class FastEventScope<
29
30
  private _getScopeType(type: string) {
30
31
  return type === undefined ? undefined : this.prefix + type
31
32
  }
33
+ private _fixScopeType(type: string) {
34
+ return type.startsWith(this.prefix) ? type.substring(this.prefix.length) : type
35
+ }
32
36
 
33
- public on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber
34
- public on<T extends Types = Types>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber
35
- public on(type: '**', listener: FastEventListener<any, any, Meta>): FastEventSubscriber
37
+ public on<T extends Types = Types>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber
38
+ public on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber
39
+ public on(type: '**', listener: FastEventListener<any, any, Meta, Context>): FastEventSubscriber
36
40
  public on(): FastEventSubscriber {
37
41
  const args = [...arguments] as [any, any, any]
38
42
  args[0] = this._getScopeType(args[0])
@@ -40,13 +44,13 @@ export class FastEventScope<
40
44
  return this.emitter.on(...args)
41
45
  }
42
46
 
43
- public once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber
44
- public once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber
47
+ public once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber
48
+ public once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber
45
49
  public once(): FastEventSubscriber {
46
50
  return this.on(arguments[0], arguments[1], Object.assign({}, arguments[2], { count: 1 }))
47
51
  }
48
52
 
49
- onAny<P = any>(listener: FastEventListener<Types, P, Meta>, options?: FastEventListenOptions): FastEventSubscriber {
53
+ onAny<P = any>(listener: FastEventListener<Types, P, Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber {
50
54
  const type = this.prefix + '**'
51
55
  return this.on(type as any, listener, options)
52
56
  }
@@ -68,21 +72,58 @@ export class FastEventScope<
68
72
  clear() {
69
73
  this.offAll()
70
74
  }
71
- public emit<R = any>(type: string, payload?: any, retain?: boolean): R[]
75
+
72
76
  public emit<R = any>(type: Types, payload?: Events[Types], retain?: boolean): R[]
77
+ public emit<R = any>(type: string, payload?: any, retain?: boolean): R[]
73
78
  public emit<R = any>(): R[] {
74
79
  const type = arguments[0] as string
75
80
  const payload = arguments[1]
76
81
  const retain = arguments[2] as boolean
77
82
  return this.emitter.emit(this._getScopeType(type)!, payload, retain)
78
83
  }
79
- public waitFor<R = any>(type: string, timeout?: number): Promise<R>
80
- public waitFor<R = any>(type: Types, timeout?: number): Promise<R>
81
- public waitFor<R = any>(): Promise<R> {
84
+ public async waitFor<T extends Types, P = Events[T], M = Meta>(type: T, timeout?: number): Promise<FastEventMessage<T, P, M>>
85
+ public async waitFor<T extends string, P = Events[T], M = Meta>(type: string, timeout?: number): Promise<FastEventMessage<T, P, M>>
86
+ public async waitFor<T extends string, P = Events[T], M = Meta>(): Promise<FastEventMessage<T, P, M>> {
82
87
  const type = arguments[0] as string
83
88
  const timeout = arguments[1] as number
84
- return this.emitter.waitFor(this._getScopeType(type)!, timeout)
89
+ const message = await this.emitter.waitFor(this._getScopeType(type)!, timeout)
90
+ const scopeMessage = Object.assign({}, message, {
91
+ type: this._fixScopeType(message.type)
92
+ })
93
+ return scopeMessage as unknown as FastEventMessage<T, P, M>
85
94
  }
95
+ /**
96
+ * 创建一个新的作用域实例
97
+ * @param prefix - 作用域前缀
98
+ * @returns 新的FastEventScope实例
99
+ *
100
+ * @description
101
+ * 基于当前作用域创建一个新的子作用域。新作用域会继承当前作用域的所有特性,
102
+ * 并在事件类型前添加额外的前缀。这允许创建层级化的事件命名空间。
103
+ *
104
+ * 作用域的特性:
105
+ * - 自动为所有事件类型添加前缀
106
+ * - 在触发事件时自动添加前缀
107
+ * - 在接收事件时自动移除前缀
108
+ * - 支持多层级的作用域嵌套
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const emitter = new FastEvent();
113
+ * const userScope = emitter.scope('user');
114
+ * const profileScope = userScope.scope('profile');
115
+ *
116
+ * // 在profileScope中监听'update'事件
117
+ * // 实际监听的是'user/profile/update'
118
+ * profileScope.on('update', (data) => {
119
+ * console.log('Profile updated:', data);
120
+ * });
121
+ *
122
+ * // 在profileScope中触发'update'事件
123
+ * // 实际触发的是'user/profile/update'
124
+ * profileScope.emit('update', { name: 'John' });
125
+ * ```
126
+ */
86
127
  public scope(prefix: string) {
87
128
  return this.emitter.scope(this._getScopeType(prefix)!)
88
129
  }
package/src/types.ts CHANGED
@@ -11,8 +11,9 @@ export type FastEventMessage<T = string, P = any, M = unknown> = {
11
11
  export type FastEventListener<
12
12
  T = string,
13
13
  P = any,
14
- M = unknown
15
- > = (message: FastEventMessage<T, P, M>) => any | Promise<any>
14
+ M = unknown,
15
+ C = any
16
+ > = (this: C, message: FastEventMessage<T, P, M>) => any | Promise<any>
16
17
 
17
18
  export type FastListenerNode = {
18
19
  __listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[];
@@ -30,7 +31,9 @@ export interface FastEventListenerMeta {
30
31
 
31
32
  export type FastListeners = FastListenerNode
32
33
 
33
- export type FastEventOptions<M = unknown> = {
34
+ export type FastEventOptions<M = Record<string, any>> = {
35
+ id?: string
36
+ debug?: boolean
34
37
  // 事件分隔符
35
38
  delimiter?: string
36
39
  // 侦听器函数执行上下文
@@ -45,10 +48,14 @@ export type FastEventOptions<M = unknown> = {
45
48
  onAddListener?: (type: string[], listener: FastEventListener) => void
46
49
  // 当移除侦听器时回调
47
50
  onRemoveListener?: (type: string[], listener: FastEventListener) => void
51
+ // 当清空侦听器时回调
52
+ onClearListeners?: () => void
53
+ // 当执行侦听器后时回调
54
+ onExecuteListener?: (message: FastEventMessage, returns: any[], listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[]) => void
48
55
  }
49
56
 
50
- export type FastEvents = Record<string, any>
51
57
 
58
+ export type FastEvents = Record<string, any>
52
59
 
53
60
  export type ScopeEvents<T extends Record<string, any>, Prefix extends string> = {
54
61
  [K in keyof T as K extends `${Prefix}/${infer Rest}` ? Rest : never]: T[K];
@@ -0,0 +1,64 @@
1
+ /**
2
+ * 一个基于弱引用(WeakRef)的键值映射集合
3
+ *
4
+ * @template T - 值的类型,必须是对象类型(继承自 `object`)
5
+ *
6
+ * @example
7
+ * const map = new WeakObjectMap<MyObject>();
8
+ * map.set('key1', obj1);
9
+ * const retrieved = map.get('key1'); // 返回 obj1 或 undefined(如果已被垃圾回收)
10
+ */
11
+ export class WeakObjectMap<T extends object> {
12
+ private map: Map<string, WeakRef<T>>;
13
+ private finalizationRegistry: FinalizationRegistry<string>;
14
+
15
+ /**
16
+ * 构造一个新的 WeakObjectMap 实例
17
+ */
18
+ constructor() {
19
+ this.map = new Map();
20
+ this.finalizationRegistry = new FinalizationRegistry((key) => {
21
+ this.map.delete(key);
22
+ });
23
+ }
24
+
25
+ /**
26
+ * 设置键值对
27
+ * @param key - 字符串键名
28
+ * @param value - 要存储的对象值(会被自动包装为弱引用)
29
+ */
30
+ set(key: string, value: T): void {
31
+ const weakRef = new WeakRef(value);
32
+ this.map.set(key, weakRef);
33
+ this.finalizationRegistry.register(value, key);
34
+ }
35
+
36
+ /**
37
+ * 获取指定键对应的值
38
+ * @param key - 要查找的键名
39
+ * @returns 如果值存在且未被垃圾回收则返回值,否则返回 undefined
40
+ */
41
+ get(key: string): T | undefined {
42
+ const weakRef = this.map.get(key);
43
+ return weakRef ? weakRef.deref() : undefined;
44
+ }
45
+
46
+ /**
47
+ * 删除指定键的映射
48
+ * @param key - 要删除的键名
49
+ * @returns 如果键存在并已删除则返回 true,否则返回 false
50
+ */
51
+ delete(key: string): boolean {
52
+ return this.map.delete(key);
53
+ }
54
+
55
+ /**
56
+ * 检查是否存在指定键的映射(且值未被垃圾回收)
57
+ * @param key - 要检查的键名
58
+ * @returns 如果键存在且值未被回收则返回 true,否则返回 false
59
+ */
60
+ has(key: string): boolean {
61
+ const weakRef = this.map.get(key);
62
+ return weakRef ? weakRef.deref() !== undefined : false;
63
+ }
64
+ }