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/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ type FastEventMessage<T = string, P = any, M = unknown> = {
3
3
  payload: P;
4
4
  meta: M;
5
5
  };
6
- type FastEventListener<T = string, P = any, M = unknown> = (message: FastEventMessage<T, P, M>) => any | Promise<any>;
6
+ type FastEventListener<T = string, P = any, M = unknown, C = any> = (this: C, message: FastEventMessage<T, P, M>) => any | Promise<any>;
7
7
  type FastListenerNode = {
8
8
  __listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[];
9
9
  } & {
@@ -16,7 +16,9 @@ interface FastEventListenerMeta {
16
16
  emitter?: string;
17
17
  }
18
18
  type FastListeners = FastListenerNode;
19
- type FastEventOptions<M = unknown> = {
19
+ type FastEventOptions<M = Record<string, any>> = {
20
+ id?: string;
21
+ debug?: boolean;
20
22
  delimiter?: string;
21
23
  context?: any;
22
24
  ignoreErrors?: boolean;
@@ -24,6 +26,8 @@ type FastEventOptions<M = unknown> = {
24
26
  meta?: M;
25
27
  onAddListener?: (type: string[], listener: FastEventListener) => void;
26
28
  onRemoveListener?: (type: string[], listener: FastEventListener) => void;
29
+ onClearListeners?: () => void;
30
+ onExecuteListener?: (message: FastEventMessage, returns: any[], listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[]) => void;
27
31
  };
28
32
  type FastEvents = Record<string, any>;
29
33
  type ScopeEvents<T extends Record<string, any>, Prefix extends string> = {
@@ -34,18 +38,19 @@ type FastEventListenOptions = {
34
38
  prepend?: boolean;
35
39
  };
36
40
 
37
- declare class FastEventScope<Events extends FastEvents = FastEvents, Meta extends Record<string, any> = Record<string, any>, Types extends keyof Events = keyof Events> {
41
+ declare class FastEventScope<Events extends FastEvents = FastEvents, Meta extends Record<string, any> = Record<string, any>, Context = any, Types extends keyof Events = keyof Events> {
38
42
  emitter: FastEvent<Events, Meta, Types>;
39
43
  prefix: string;
40
44
  constructor(emitter: FastEvent<Events, Meta, Types>, prefix: string);
41
45
  private _getScopeListener;
42
46
  private _getScopeType;
43
- on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
44
- on<T extends Types = Types>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
45
- on(type: '**', listener: FastEventListener<any, any, Meta>): FastEventSubscriber;
46
- once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
47
- once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
48
- onAny<P = any>(listener: FastEventListener<Types, P, Meta>, options?: FastEventListenOptions): FastEventSubscriber;
47
+ private _fixScopeType;
48
+ on<T extends Types = Types>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber;
49
+ on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber;
50
+ on(type: '**', listener: FastEventListener<any, any, Meta, Context>): FastEventSubscriber;
51
+ once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber;
52
+ once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber;
53
+ onAny<P = any>(listener: FastEventListener<Types, P, Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber;
49
54
  offAll(): void;
50
55
  off(listener: FastEventListener<any, any, any>): void;
51
56
  off(type: string, listener: FastEventListener<any, any, any>): void;
@@ -53,22 +58,83 @@ declare class FastEventScope<Events extends FastEvents = FastEvents, Meta extend
53
58
  off(type: string): void;
54
59
  off(type: Types): void;
55
60
  clear(): void;
56
- emit<R = any>(type: string, payload?: any, retain?: boolean): R[];
57
61
  emit<R = any>(type: Types, payload?: Events[Types], retain?: boolean): R[];
58
- waitFor<R = any>(type: string, timeout?: number): Promise<R>;
59
- waitFor<R = any>(type: Types, timeout?: number): Promise<R>;
60
- scope(prefix: string): FastEventScope<ScopeEvents<Events, string>, Record<string, any>, keyof ScopeEvents<Events, string>>;
62
+ emit<R = any>(type: string, payload?: any, retain?: boolean): R[];
63
+ waitFor<T extends Types, P = Events[T], M = Meta>(type: T, timeout?: number): Promise<FastEventMessage<T, P, M>>;
64
+ waitFor<T extends string, P = Events[T], M = Meta>(type: string, timeout?: number): Promise<FastEventMessage<T, P, M>>;
65
+ /**
66
+ * 创建一个新的作用域实例
67
+ * @param prefix - 作用域前缀
68
+ * @returns 新的FastEventScope实例
69
+ *
70
+ * @description
71
+ * 基于当前作用域创建一个新的子作用域。新作用域会继承当前作用域的所有特性,
72
+ * 并在事件类型前添加额外的前缀。这允许创建层级化的事件命名空间。
73
+ *
74
+ * 作用域的特性:
75
+ * - 自动为所有事件类型添加前缀
76
+ * - 在触发事件时自动添加前缀
77
+ * - 在接收事件时自动移除前缀
78
+ * - 支持多层级的作用域嵌套
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const emitter = new FastEvent();
83
+ * const userScope = emitter.scope('user');
84
+ * const profileScope = userScope.scope('profile');
85
+ *
86
+ * // 在profileScope中监听'update'事件
87
+ * // 实际监听的是'user/profile/update'
88
+ * profileScope.on('update', (data) => {
89
+ * console.log('Profile updated:', data);
90
+ * });
91
+ *
92
+ * // 在profileScope中触发'update'事件
93
+ * // 实际触发的是'user/profile/update'
94
+ * profileScope.emit('update', { name: 'John' });
95
+ * ```
96
+ */
97
+ scope(prefix: string): FastEventScope<ScopeEvents<Events, string>, Record<string, any>, any, keyof ScopeEvents<Events, string>>;
61
98
  }
62
99
 
63
- declare class FastEvent<Events extends FastEvents = FastEvents, Meta extends Record<string, any> = Record<string, any>, Types extends keyof Events = keyof Events> {
100
+ /**
101
+ * FastEvent 事件发射器类
102
+ *
103
+ * @template Events - 事件类型定义,继承自FastEvents接口
104
+ * @template Meta - 事件元数据类型,默认为任意键值对对象
105
+ * @template Types - 事件类型的键名类型,默认为Events的键名类型
106
+ */
107
+ declare class FastEvent<Events extends FastEvents = FastEvents, Meta extends Record<string, any> = Record<string, any>, Context = any, Types extends keyof Events = keyof Events> {
108
+ /** 事件监听器树结构,存储所有注册的事件监听器 */
64
109
  listeners: FastListeners;
110
+ /** 事件发射器的配置选项 */
65
111
  private _options;
112
+ /** 事件名称的分隔符,默认为'/' */
66
113
  private _delimiter;
114
+ /** 事件监听器执行时的上下文对象 */
67
115
  private _context;
68
- private _retainedEvents;
116
+ /** 保留的事件消息映射,用于新订阅者 */
117
+ retainedMessages: Map<string, any>;
118
+ /** 当前注册的监听器总数 */
119
+ listenerCount: number;
120
+ /**
121
+ * 创建FastEvent实例
122
+ * @param options - 事件发射器的配置选项
123
+ *
124
+ * 默认配置:
125
+ * - debug: false - 是否启用调试模式
126
+ * - id: 随机字符串 - 实例唯一标识符
127
+ * - delimiter: '/' - 事件名称分隔符
128
+ * - context: null - 监听器执行上下文
129
+ * - ignoreErrors: true - 是否忽略监听器执行错误
130
+ */
69
131
  constructor(options?: FastEventOptions<Meta>);
132
+ /** 获取事件发射器的配置选项 */
70
133
  get options(): FastEventOptions;
134
+ /** 获取事件发射器的唯一标识符 */
135
+ get id(): string;
71
136
  private _addListener;
137
+ private _enableDevTools;
72
138
  /**
73
139
  *
74
140
  * 根据parts路径遍历侦听器树,并在最后的节点上执行回调函数
@@ -86,11 +152,56 @@ declare class FastEvent<Events extends FastEvents = FastEvents, Meta extends Rec
86
152
  * @description 遍历节点的监听器列表,移除所有匹配的监听器。支持移除普通函数和数组形式的监听器
87
153
  */
88
154
  private _removeListener;
89
- on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
90
- on<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
91
- on<P = any>(type: '**', listener: FastEventListener<Types, P, Meta>): FastEventSubscriber;
92
- once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>): FastEventSubscriber;
93
- once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>): FastEventSubscriber;
155
+ /**
156
+ * 注册事件监听器
157
+ * @param type - 事件类型,支持以下格式:
158
+ * - 普通字符串:'user/login'
159
+ * - 通配符:'user/*'(匹配单层)或'user/**'(匹配多层)
160
+ * - 全局监听:'**'(监听所有事件)
161
+ * @param listener - 事件监听器函数
162
+ * @param options - 监听器配置选项:
163
+ * - count: 触发次数限制,0表示无限制
164
+ * - prepend: 是否将监听器添加到监听器队列开头
165
+ * @returns 返回订阅者对象,包含off方法用于取消监听
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * // 监听特定事件
170
+ * emitter.on('user/login', (data) => console.log(data));
171
+ *
172
+ * // 使用通配符
173
+ * emitter.on('user/*', (data) => console.log(data));
174
+ *
175
+ * // 限制触发次数
176
+ * emitter.on('event', handler, { count: 3 });
177
+ * ```
178
+ */
179
+ on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber;
180
+ on<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta, Context>, options?: FastEventListenOptions): FastEventSubscriber;
181
+ on<P = any>(type: '**', listener: FastEventListener<Types, P, Meta, Context>): FastEventSubscriber;
182
+ /**
183
+ * 注册一次性事件监听器
184
+ * @param type - 事件类型,支持与on方法相同的格式:
185
+ * - 普通字符串:'user/login'
186
+ * - 通配符:'user/*'(匹配单层)或'user/**'(匹配多层)
187
+ * @param listener - 事件监听器函数
188
+ * @returns 返回订阅者对象,包含off方法用于取消监听
189
+ *
190
+ * @description
191
+ * 监听器只会在事件首次触发时被调用一次,之后会自动解除注册。
192
+ * 这是on方法的特例,相当于设置options.count = 1。
193
+ * 如果事件有保留消息,新注册的监听器会立即收到最近一次的保留消息并解除注册。
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * // 只监听一次登录事件
198
+ * emitter.once('user/login', (data) => {
199
+ * console.log('用户登录:', data);
200
+ * });
201
+ * ```
202
+ */
203
+ once<T extends Types = Types>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>): FastEventSubscriber;
204
+ once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta, Context>): FastEventSubscriber;
94
205
  /**
95
206
  * 注册一个监听器,用于监听所有事件
96
207
  * @param listener 事件监听器函数,可以接收任意类型的事件数据
@@ -105,7 +216,7 @@ declare class FastEvent<Events extends FastEvents = FastEvents, Meta extends Rec
105
216
  * subscriber.off();
106
217
  * ```
107
218
  */
108
- onAny<P = any>(listener: FastEventListener<string, P, Meta>, options?: Pick<FastEventListenOptions, 'prepend'>): FastEventSubscriber;
219
+ onAny<P = any>(listener: FastEventListener<string, P, Meta, Context>, options?: Pick<FastEventListenOptions, 'prepend'>): FastEventSubscriber;
109
220
  off(listener: FastEventListener<any, any, any>): void;
110
221
  off(type: string, listener: FastEventListener<any, any, any>): void;
111
222
  off(type: Types, listener: FastEventListener<any, any, any>): void;
@@ -156,6 +267,23 @@ declare class FastEvent<Events extends FastEvents = FastEvents, Meta extends Rec
156
267
  */
157
268
  private _traverseToPath;
158
269
  private _traverseListeners;
270
+ /**
271
+ * 执行单个监听器函数
272
+ * @param listener - 要执行的监听器函数或包装过的监听器对象
273
+ * @param message - 事件消息对象,包含type、payload和meta
274
+ * @returns 监听器的执行结果或错误对象(如果配置了ignoreErrors)
275
+ * @private
276
+ *
277
+ * @description
278
+ * 执行单个监听器函数,处理以下情况:
279
+ * - 如果监听器是包装过的(有__wrappedListener属性),调用包装的函数
280
+ * - 否则直接调用监听器函数
281
+ * - 使用配置的上下文(_context)作为this
282
+ * - 捕获并处理执行过程中的错误:
283
+ * - 如果有onListenerError回调,调用它
284
+ * - 如果配置了ignoreErrors,返回错误对象
285
+ * - 否则抛出错误
286
+ */
159
287
  private _executeListener;
160
288
  /**
161
289
  * 执行监听器节点中的所有监听函数
@@ -187,39 +315,156 @@ declare class FastEvent<Events extends FastEvents = FastEvents, Meta extends Rec
187
315
  * // 方式2: 对象形式
188
316
  * emit({ type: 'user.login', payload: { id: 1 } ,meta:{...}}}, true)
189
317
  */
318
+ /**
319
+ * 同步触发事件
320
+ * @param type - 事件类型,可以是字符串或预定义的事件类型
321
+ * @param payload - 事件数据负载
322
+ * @param retain - 是否保留该事件,用于后续新的订阅者
323
+ * @param meta - 事件元数据
324
+ * @returns 所有监听器的执行结果数组
325
+ *
326
+ * @description
327
+ * 同步触发指定类型的事件,支持两种调用方式:
328
+ * 1. 参数形式:emit(type, payload, retain, meta)
329
+ * 2. 对象形式:emit({ type, payload, meta }, retain)
330
+ *
331
+ * 特性:
332
+ * - 支持通配符匹配,一个事件可能触发多个监听器
333
+ * - 如果设置了retain为true,会保存最后一次的事件数据
334
+ * - 按照注册顺序同步调用所有匹配的监听器
335
+ * - 如果配置了ignoreErrors,监听器抛出的错误会被捕获并返回
336
+ *
337
+ * @example
338
+ * ```ts
339
+ * // 简单事件触发
340
+ * emitter.emit('user/login', { userId: 123 });
341
+ *
342
+ * // 带保留的事件触发
343
+ * emitter.emit('status/change', { online: true }, true);
344
+ *
345
+ * // 带元数据的事件触发
346
+ * emitter.emit('data/update', newData, false, { timestamp: Date.now() });
347
+ *
348
+ * // 使用对象形式触发
349
+ * emitter.emit({
350
+ * type: 'user/login',
351
+ * payload: { userId: 123 },
352
+ * meta: { time: Date.now() }
353
+ * }, true);
354
+ * ```
355
+ */
190
356
  emit<R = any>(type: string, payload?: any, retain?: boolean, meta?: Meta): R[];
191
357
  emit<R = any>(type: Types, payload?: Events[Types], retain?: boolean, meta?: Meta): R[];
192
358
  emit<R = any>(message: FastEventMessage<Types, Events[Types], Meta>, retain?: boolean): R[];
193
359
  emit<R = any>(message: FastEventMessage<string, any, Meta>, retain?: boolean): R[];
360
+ /**
361
+ * 异步触发事件
362
+ * @param type - 事件类型,可以是字符串或预定义的事件类型
363
+ * @param payload - 事件数据负载
364
+ * @param retain - 是否保留该事件,用于后续新的订阅者
365
+ * @param meta - 事件元数据
366
+ * @returns Promise,解析为所有监听器的执行结果数组
367
+ *
368
+ * @description
369
+ * 异步触发指定类型的事件,与emit方法类似,但有以下区别:
370
+ * - 返回Promise,等待所有异步监听器执行完成
371
+ * - 使用Promise.allSettled处理监听器的执行结果
372
+ * - 即使某些监听器失败,也会等待所有监听器执行完成
373
+ * - 返回结果包含成功值或错误信息
374
+ *
375
+ * @example
376
+ * ```ts
377
+ * // 异步事件处理
378
+ * const results = await emitter.emitAsync('data/process', rawData);
379
+ *
380
+ * // 处理结果包含成功和失败的情况
381
+ * results.forEach(result => {
382
+ * if (result instanceof Error) {
383
+ * console.error('处理失败:', result);
384
+ * } else {
385
+ * console.log('处理成功:', result);
386
+ * }
387
+ * });
388
+ *
389
+ * // 带元数据的异步事件
390
+ * await emitter.emitAsync('batch/process', items, false, {
391
+ * batchId: 'batch-001',
392
+ * timestamp: Date.now()
393
+ * });
394
+ * ```
395
+ */
194
396
  emitAsync<R = any>(type: string, payload?: any, retain?: boolean, meta?: Meta): Promise<[R | Error][]>;
195
397
  emitAsync<R = any>(type: Types, payload?: Events[Types], retain?: boolean, meta?: Meta): Promise<[R | Error][]>;
196
398
  /**
197
- * 等待指定事件发生
399
+ * 等待指定事件发生,返回一个Promise
400
+ * @param type - 要等待的事件类型
401
+ * @param timeout - 超时时间(毫秒),默认为0表示永不超时
402
+ * @returns Promise,解析为事件消息对象,包含type、payload和meta
403
+ *
404
+ * @description
405
+ * 创建一个Promise,在指定事件发生时解析。
406
+ * - 当事件触发时,Promise会解析为事件消息对象
407
+ * - 如果设置了timeout且超时,Promise会被拒绝
408
+ * - 一旦事件发生或超时,会自动取消事件监听
198
409
  *
199
- * @param type
200
- * @param timeout 超时时间,单位毫秒,默认为 0,表示无限等待
410
+ * @example
411
+ * ```ts
412
+ * try {
413
+ * // 等待登录事件,最多等待5秒
414
+ * const event = await emitter.waitFor('user/login', 5000);
415
+ * console.log('用户登录成功:', event.payload);
416
+ * } catch (error) {
417
+ * console.error('等待登录超时');
418
+ * }
419
+ *
420
+ * // 无限等待事件
421
+ * const event = await emitter.waitFor('server/ready');
422
+ * console.log('服务器就绪');
423
+ * ```
201
424
  */
202
- waitFor<R = any>(type: string, timeout?: number): Promise<R>;
203
- waitFor<R = any>(type: Types, timeout?: number): Promise<R>;
425
+ waitFor<T extends Types, P = Events[T], M = Meta>(type: T, timeout?: number): Promise<FastEventMessage<T, P, M>>;
426
+ waitFor<T extends string, P = Events[T], M = Meta>(type: string, timeout?: number): Promise<FastEventMessage<T, P, M>>;
204
427
  /**
205
- * 创建事件域
428
+ * 创建一个新的事件作用域
429
+ * @param prefix - 作用域前缀,将自动添加到该作用域下所有事件名称前
430
+ * @returns 新的FastEventScope实例
206
431
  *
207
- * 注意:
432
+ * @description
433
+ * 创建一个新的事件作用域,用于在特定命名空间下管理事件。
208
434
  *
209
- * 事件域与当前事件对象共享相同的侦听器表
210
- * 也就是说,如果在事件域中触发事件,当前事件对象也会触发该事件
211
- * 两者工不是完全隔离的,仅是事件侦听和触发时的事件类型不同而已
435
+ * 重要特性:
436
+ * - 作用域与父事件发射器共享同一个监听器表
437
+ * - 作用域中的事件会自动添加前缀
438
+ * - 作用域的所有操作都会映射到父事件发射器上
439
+ * - 作用域不是完全隔离的,只是提供了事件名称的命名空间
212
440
  *
213
- * const emitter = new FastEvent()
441
+ * @example
442
+ * ```ts
443
+ * const emitter = new FastEvent();
214
444
  *
215
- * const scope= emitter.scope("a/b")
445
+ * // 创建用户相关事件的作用域
446
+ * const userEvents = emitter.scope('user');
216
447
  *
217
- * scope.on("x",()=>{}) == emitter.on("a/b/x",()=>{})
218
- * scope.emit("x",1) == emitter.emit("a/b/x",1)
219
- * scope.offAll() == emitter.offAll("a/b")
448
+ * // 在作用域中监听事件
449
+ * userEvents.on('login', (data) => {
450
+ * // 实际监听的是 'user/login'
451
+ * console.log('用户登录:', data);
452
+ * });
453
+ *
454
+ * // 在作用域中触发事件
455
+ * userEvents.emit('login', { userId: 123 });
456
+ * // 等同于 emitter.emit('user/login', { userId: 123 })
220
457
  *
458
+ * // 创建嵌套作用域
459
+ * const profileEvents = userEvents.scope('profile');
460
+ * profileEvents.emit('update', { name: 'John' });
461
+ * // 等同于 emitter.emit('user/profile/update', { name: 'John' })
462
+ *
463
+ * // 清理作用域
464
+ * userEvents.offAll(); // 清理 'user' 前缀下的所有事件
465
+ * ```
221
466
  */
222
- scope<T extends string>(prefix: T): FastEventScope<ScopeEvents<Events, T>, Record<string, any>, keyof ScopeEvents<Events, T>>;
467
+ scope<T extends string>(prefix: T): FastEventScope<ScopeEvents<Events, T>, Record<string, any>, any, keyof ScopeEvents<Events, T>>;
223
468
  }
224
469
 
225
470
  export { FastEvent, type FastEventListenOptions, type FastEventListener, type FastEventListenerMeta, type FastEventMessage, type FastEventOptions, FastEventScope, type FastEventSubscriber, type FastEvents, type FastListenerNode, type FastListeners, type ScopeEvents };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var g=Object.defineProperty;var L=(h,e,t)=>e in h?g(h,e,{enumerable:true,configurable:true,writable:true,value:t}):h[e]=t;var u=(h,e)=>g(h,"name",{value:e,configurable:true});var c=(h,e,t)=>L(h,typeof e!="symbol"?e+"":e,t);var a=class a{constructor(e,t){c(this,"emitter");c(this,"prefix");this.emitter=e,this.prefix=t,t.length>0&&!t.endsWith(e.options.delimiter)&&(this.prefix=t+e.options.delimiter);}_getScopeListener(e){let t=this.prefix;if(t.length===0)return e;let s=u(function(i){if(i.type.startsWith(t))return e(Object.assign({},i,{type:i.type.substring(t.length)}))},"scopeListener");return e.__wrappedListener=s,e}_getScopeType(e){return e===void 0?void 0:this.prefix+e}on(){let e=[...arguments];return e[0]=this._getScopeType(e[0]),e[1]=this._getScopeListener(e[1]),this.emitter.on(...e)}once(){return this.on(arguments[0],arguments[1],Object.assign({},arguments[2],{count:1}))}onAny(e,t){let s=this.prefix+"**";return this.on(s,e,t)}offAll(){this.emitter.offAll(this.prefix);}off(){let e=arguments;typeof e[0]=="string"&&(e[0]=this._getScopeType(e[0])),this.emitter.off(...e);}clear(){this.offAll();}emit(){let e=arguments[0],t=arguments[1],s=arguments[2];return this.emitter.emit(this._getScopeType(e),t,s)}waitFor(){let e=arguments[0],t=arguments[1];return this.emitter.waitFor(this._getScopeType(e),t)}scope(e){return this.emitter.scope(this._getScopeType(e))}};u(a,"FastEventScope");var f=a;function d(h,e){if(h.length!==e.length&&h.length>0&&e[e.length-1]!=="**")return false;let t=[...e];e.length>0&&e[e.length-1]==="**"&&t.splice(e.length-1,1,...Array.from({length:h.length-e.length+1}).fill("*"));for(let s=0;s<h.length;s++)if(t[s]!=="*"&&t[s]!==h[s])return false;return true}u(d,"isPathMatched");function y(h,e){let t=[];for(;;){let s=h.findIndex(i=>e(i));if(s===-1){t.push(s);break}h.splice(s,1);}return t}u(y,"removeItem");var _=class _{constructor(e){c(this,"listeners",{__listeners:[]});c(this,"_options");c(this,"_delimiter","/");c(this,"_context");c(this,"_retainedEvents",new Map);this._options=Object.assign({delimiter:"/",context:null,ignoreErrors:true},e),this._delimiter=this._options.delimiter,this._context=this._options.context||this;}get options(){return this._options}_addListener(e,t,s){let{count:i,prepend:r}=s;return this._forEachNodes(e,n=>{let o=i>0?[t,i]:t;r?n.__listeners.splice(0,0,o):n.__listeners.push(o),typeof this._options.onAddListener=="function"&&this._options.onAddListener(e,t);})}_forEachNodes(e,t){if(e.length===0)return;let s=this.listeners;for(let i=0;i<e.length;i++){let r=e[i];if(r in s||(s[r]={__listeners:[]}),i===e.length-1){let n=s[r];return t(n,s),n}else s=s[r];}}_removeListener(e,t,s){s&&y(e.__listeners,i=>{i=Array.isArray(i)?i[0]:i;let r=i===s;return r&&typeof this._options.onRemoveListener=="function"&&this._options.onRemoveListener(t,s),r});}on(){let e=arguments[0],t=arguments[1],s=Object.assign({count:0,prepend:false},arguments[2]);if(e.length===0)throw new Error("event type cannot be empty");if(e==="**")return this.onAny(t);let i=e.split(this._delimiter),r=this._addListener(i,t,s);return r&&!e.includes("*")&&this._emitForLastEvent(e),{off:u(()=>r&&this._removeListener(r,i,t),"off")}}once(){return this.on(arguments[0],arguments[1],{count:1})}onAny(e,t){let s=this.listeners.__listeners;return t&&t.prepend?s.splice(0,0,e):s.push(e),{off:u(()=>this._removeListener(this.listeners,[],e),"off")}}off(){let e=arguments,t=typeof e[0]=="function"?void 0:e[0],s=typeof e[0]=="function"?e[0]:e[1],i=t?t.split(this._delimiter):[],r=t?t.includes("*"):false;if(t&&!r)this._traverseToPath(this.listeners,i,n=>{s?this._removeListener(n,i,s):t&&(n.__listeners=[]);});else {let n=r?[]:i;this._traverseListeners(this.listeners,n,(o,l)=>{(s!==void 0||r&&d(o,i))&&(s?this._removeListener(l,i,s):l.__listeners=[]);});}}offAll(e){if(e){let t=this._getListenerNode(e.split(this._delimiter));t&&(t.__listeners=[]),this._removeRetainedEvents(e);}else this._retainedEvents.clear(),this.listeners={__listeners:[]};}_getListenerNode(e){let t;return this._forEachNodes(e,s=>{t=s;}),t}_removeRetainedEvents(e){e||this._retainedEvents.clear(),e?.endsWith(this._delimiter)&&(e+=this._delimiter),this._retainedEvents.delete(e);for(let t of this._retainedEvents.keys())t.startsWith(e)&&this._retainedEvents.delete(t);}clear(){this.offAll();}_createMeta(e){return this._options.meta?Object.assign({},this._options.meta,e):e}_emitForLastEvent(e){if(this._retainedEvents.has(e)){let t=this._retainedEvents.get(e),s=e.split(this._delimiter);this._traverseToPath(this.listeners,s,i=>{this._executeListeners(i,t);}),this._executeListeners(this.listeners,t);}}_traverseToPath(e,t,s,i=0,r){if(i>=t.length){s(e);return}let n=t[i];if(r===true){this._traverseToPath(e,t,s,i+1,true);return}"*"in e&&this._traverseToPath(e["*"],t,s,i+1),"**"in e&&this._traverseToPath(e["**"],t,s,i+1,true),n in e&&this._traverseToPath(e[n],t,s,i+1);}_traverseListeners(e,t,s){let i=e;t&&t.length>0&&this._traverseToPath(e,t,n=>{i=n;});let r=u((n,o,l)=>{o(l,n);for(let[m,p]of Object.entries(n))m.startsWith("__")||p&&r(p,o,[...l,m]);},"traverseNodes");r(i,s,[]);}_executeListener(e,t){try{return typeof e.__wrappedListener=="function"?e.__wrappedListener.call(this._context,t):e.call(this._context,t)}catch(s){if(s._emitter=t.type,typeof this._options.onListenerError=="function"&&this._options.onListenerError.call(this,t.type,s),this._options.ignoreErrors)return s;throw s}}_executeListeners(e,t){if(!e||!e.__listeners)return [];let s=0,i=e.__listeners,r=[];for(;s<i.length;){let n=i[s];Array.isArray(n)?(r.push(this._executeListener(n[0],t)),n[1]--,n[1]===0&&(i.splice(s,1),s--)):r.push(this._executeListener(n,t)),s++;}return r}emit(){let e,t,s,i;typeof arguments[0]=="object"?(e=arguments[0].type,t=arguments[0].payload,i=this._createMeta(arguments[0].meta),s=arguments[1]):(e=arguments[0],t=arguments[1],s=arguments[2],i=this._createMeta(arguments[3]));let r={type:e,payload:t,meta:i},n=e.split(this._delimiter);s&&this._retainedEvents.set(e,r);let o=[];return o.push(...this._executeListeners(this.listeners,r)),this._traverseToPath(this.listeners,n,l=>{o.push(...this._executeListeners(l,r));}),o}async emitAsync(){let e=arguments[0],t=arguments[1],s=arguments[2],i=this._createMeta(arguments[3]),r={type:e,payload:t,meta:i};return (await Promise.allSettled(this.emit(r,s))).map(o=>o.status==="fulfilled"?o.value:o.reason)}waitFor(){let e=arguments[0],t=arguments[1];return new Promise((s,i)=>{let r,n,o=u(l=>{clearTimeout(r),n.off(),s(l);},"listener");t&&t>0&&(r=setTimeout(()=>{n&&n.off(),i(new Error("wait for event<"+e+"> is timeout"));},t)),n=this.on(e,o);})}scope(e){return new f(this,e)}};u(_,"FastEvent");var v=_;exports.FastEvent=v;exports.FastEventScope=f;//# sourceMappingURL=index.js.map
1
+ 'use strict';var m=Object.defineProperty;var v=(h,e,t)=>e in h?m(h,e,{enumerable:true,configurable:true,writable:true,value:t}):h[e]=t;var l=(h,e)=>m(h,"name",{value:e,configurable:true});var u=(h,e,t)=>v(h,typeof e!="symbol"?e+"":e,t);var f=class f{constructor(e,t){u(this,"emitter");u(this,"prefix");this.emitter=e,this.prefix=t,t.length>0&&!t.endsWith(e.options.delimiter)&&(this.prefix=t+e.options.delimiter);}_getScopeListener(e){let t=this.prefix;if(t.length===0)return e;let s=l(function(i){if(i.type.startsWith(t))return e(Object.assign({},i,{type:i.type.substring(t.length)}))},"scopeListener");return e.__wrappedListener=s,e}_getScopeType(e){return e===void 0?void 0:this.prefix+e}_fixScopeType(e){return e.startsWith(this.prefix)?e.substring(this.prefix.length):e}on(){let e=[...arguments];return e[0]=this._getScopeType(e[0]),e[1]=this._getScopeListener(e[1]),this.emitter.on(...e)}once(){return this.on(arguments[0],arguments[1],Object.assign({},arguments[2],{count:1}))}onAny(e,t){let s=this.prefix+"**";return this.on(s,e,t)}offAll(){this.emitter.offAll(this.prefix);}off(){let e=arguments;typeof e[0]=="string"&&(e[0]=this._getScopeType(e[0])),this.emitter.off(...e);}clear(){this.offAll();}emit(){let e=arguments[0],t=arguments[1],s=arguments[2];return this.emitter.emit(this._getScopeType(e),t,s)}async waitFor(){let e=arguments[0],t=arguments[1],s=await this.emitter.waitFor(this._getScopeType(e),t);return Object.assign({},s,{type:this._fixScopeType(s.type)})}scope(e){return this.emitter.scope(this._getScopeType(e))}};l(f,"FastEventScope");var c=f;function d(h,e){if(h.length!==e.length&&h.length>0&&e[e.length-1]!=="**")return false;let t=[...e];e.length>0&&e[e.length-1]==="**"&&t.splice(e.length-1,1,...Array.from({length:h.length-e.length+1}).fill("*"));for(let s=0;s<h.length;s++)if(t[s]!=="*"&&t[s]!==h[s])return false;return true}l(d,"isPathMatched");function y(h,e){let t=[];for(;;){let s=h.findIndex(i=>e(i));if(s===-1){t.push(s);break}h.splice(s,1);}return t}l(y,"removeItem");var _=class _{constructor(e){u(this,"listeners",{__listeners:[]});u(this,"_options");u(this,"_delimiter","/");u(this,"_context");u(this,"retainedMessages",new Map);u(this,"listenerCount",0);this._options=Object.assign({debug:false,id:Math.random().toString(36).substring(2),delimiter:"/",context:null,ignoreErrors:true},e),this._delimiter=this._options.delimiter,this._context=this._options.context||this,this._enableDevTools();}get options(){return this._options}get id(){return this._options.id}_addListener(e,t,s){let{count:i,prepend:r}=s;return this._forEachNodes(e,n=>{let o=i>0?[t,i]:t;r?n.__listeners.splice(0,0,o):n.__listeners.push(o),typeof this._options.onAddListener=="function"&&(this.listenerCount++,this._options.onAddListener(e,t));})}_enableDevTools(){this.options.debug&&globalThis.__FLEXEVENT_DEVTOOLS__.add(this);}_forEachNodes(e,t){if(e.length===0)return;let s=this.listeners;for(let i=0;i<e.length;i++){let r=e[i];if(r in s||(s[r]={__listeners:[]}),i===e.length-1){let n=s[r];return t(n,s),n}else s=s[r];}}_removeListener(e,t,s){s&&y(e.__listeners,i=>{i=Array.isArray(i)?i[0]:i;let r=i===s;return r&&typeof this._options.onRemoveListener=="function"&&(this.listenerCount--,this._options.onRemoveListener(t,s)),r});}on(){let e=arguments[0],t=arguments[1],s=Object.assign({count:0,prepend:false},arguments[2]);if(e.length===0)throw new Error("event type cannot be empty");if(e==="**")return this.onAny(t);let i=e.split(this._delimiter),r=this._addListener(i,t,s);return r&&!e.includes("*")&&this._emitForLastEvent(e),{off:l(()=>r&&this._removeListener(r,i,t),"off")}}once(){return this.on(arguments[0],arguments[1],{count:1})}onAny(e,t){let s=this.listeners.__listeners;return t&&t.prepend?s.splice(0,0,e):s.push(e),{off:l(()=>this._removeListener(this.listeners,[],e),"off")}}off(){let e=arguments,t=typeof e[0]=="function"?void 0:e[0],s=typeof e[0]=="function"?e[0]:e[1],i=t?t.split(this._delimiter):[],r=t?t.includes("*"):false;if(t&&!r)this._traverseToPath(this.listeners,i,n=>{s?this._removeListener(n,i,s):t&&(n.__listeners=[]);});else {let n=r?[]:i;this._traverseListeners(this.listeners,n,(o,a)=>{(s!==void 0||r&&d(o,i))&&(s?this._removeListener(a,i,s):a.__listeners=[]);});}}offAll(e){if(e){let t=this._getListenerNode(e.split(this._delimiter));t&&(t.__listeners=[]),this._removeRetainedEvents(e);}else this.retainedMessages.clear(),this.listeners={__listeners:[]};typeof this._options.onClearListeners=="function"&&this._options.onClearListeners.call(this);}_getListenerNode(e){let t;return this._forEachNodes(e,s=>{t=s;}),t}_removeRetainedEvents(e){e||this.retainedMessages.clear(),e?.endsWith(this._delimiter)&&(e+=this._delimiter),this.retainedMessages.delete(e);for(let t of this.retainedMessages.keys())t.startsWith(e)&&this.retainedMessages.delete(t);}clear(){this.offAll();}_createMeta(e){return this._options.meta?Object.assign({},this._options.meta,e):e}_emitForLastEvent(e){if(this.retainedMessages.has(e)){let t=this.retainedMessages.get(e),s=e.split(this._delimiter);this._traverseToPath(this.listeners,s,i=>{this._executeListeners(i,t);}),this._executeListeners(this.listeners,t);}}_traverseToPath(e,t,s,i=0,r){if(i>=t.length){s(e);return}let n=t[i];if(r===true){this._traverseToPath(e,t,s,i+1,true);return}"*"in e&&this._traverseToPath(e["*"],t,s,i+1),"**"in e&&this._traverseToPath(e["**"],t,s,i+1,true),n in e&&this._traverseToPath(e[n],t,s,i+1);}_traverseListeners(e,t,s){let i=e;t&&t.length>0&&this._traverseToPath(e,t,n=>{i=n;});let r=l((n,o,a)=>{o(a,n);for(let[p,g]of Object.entries(n))p.startsWith("__")||g&&r(g,o,[...a,p]);},"traverseNodes");r(i,s,[]);}_executeListener(e,t){try{return typeof e.__wrappedListener=="function"?e.__wrappedListener.call(this._context,t):e.call(this._context,t)}catch(s){if(s._emitter=t.type,typeof this._options.onListenerError=="function"&&this._options.onListenerError.call(this,t.type,s),this._options.ignoreErrors)return s;throw s}}_executeListeners(e,t){if(!e||!e.__listeners)return [];let s=0,i=e.__listeners,r=[];for(;s<i.length;){let n=i[s];Array.isArray(n)?(r.push(this._executeListener(n[0],t)),n[1]--,n[1]===0&&(i.splice(s,1),s--)):r.push(this._executeListener(n,t)),s++;}return r}emit(){let e,t,s,i;typeof arguments[0]=="object"?(e=arguments[0].type,t=arguments[0].payload,i=this._createMeta(arguments[0].meta),s=arguments[1]):(e=arguments[0],t=arguments[1],s=arguments[2],i=this._createMeta(arguments[3]));let r={type:e,payload:t,meta:i},n=e.split(this._delimiter);s&&this.retainedMessages.set(e,r);let o=[];return o.push(...this._executeListeners(this.listeners,r)),this._traverseToPath(this.listeners,n,a=>{o.push(...this._executeListeners(a,r)),this._options.debug&&typeof this._options.onExecuteListener=="function"&&(s&&(r.meta||(r.meta={retain:true})),this._options.onExecuteListener.call(this,r,o,[...this.listeners.__listeners,...a.__listeners]));}),o}async emitAsync(){let e=arguments[0],t=arguments[1],s=arguments[2],i=this._createMeta(arguments[3]),r={type:e,payload:t,meta:i};return (await Promise.allSettled(this.emit(r,s))).map(o=>o.status==="fulfilled"?o.value:o.reason)}waitFor(){let e=arguments[0],t=arguments[1];return new Promise((s,i)=>{let r,n,o=l(a=>{clearTimeout(r),n.off(),s(a);},"listener");t&&t>0&&(r=setTimeout(()=>{n&&n.off(),i(new Error("wait for event<"+e+"> is timeout"));},t)),n=this.on(e,o);})}scope(e){return new c(this,e)}};l(_,"FastEvent");var L=_;exports.FastEvent=L;exports.FastEventScope=c;//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map