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
@@ -0,0 +1,8 @@
1
+ # Changesets
2
+
3
+ Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4
+ with multi-package repos, or single-package repos to help you version and publish your code. You can
5
+ find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6
+
7
+ We have a quick list of common questions to get you started engaging with this project in
8
+ [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
3
+ "changelog": "@changesets/cli/changelog",
4
+ "commit": true,
5
+ "fixed": [],
6
+ "linked": [],
7
+ "access": "public",
8
+ "baseBranch": "master",
9
+ "updateInternalDependencies": "patch",
10
+ "ignore": []
11
+ }
@@ -0,0 +1,50 @@
1
+ name: Publish Package
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+
8
+ permissions:
9
+ pull-requests: write
10
+ contents: write
11
+ id-token: write
12
+
13
+ concurrency: ${{ github.workflow }}-${{ github.ref }}
14
+
15
+ jobs:
16
+ build-and-publish:
17
+ runs-on: ubuntu-latest
18
+
19
+ steps:
20
+ - name: Checkout Repo
21
+ uses: actions/checkout@v4
22
+
23
+ - uses: pnpm/action-setup@v4
24
+
25
+ - name: Setup Node.js environment
26
+ uses: actions/setup-node@v4
27
+ with:
28
+ node-version: 20
29
+ cache: pnpm
30
+
31
+ - name: Install pnpm
32
+ run: npm install -g pnpm
33
+
34
+ # 安装依赖
35
+ - name: Install dependencies
36
+ run: pnpm install --no-frozen-lockfile
37
+
38
+ - name: Create Release Pull Request or Publish to npm
39
+ id: changesets
40
+ uses: changesets/action@v1
41
+ with:
42
+ # This expects you to have a script called release which does a build for your packages and calls changeset publish
43
+ publish: pnpm release
44
+ env:
45
+ # this doesn't work but semantic-release works
46
+ # see https://github.com/sonofmagic/npm-lib-rollup-template/blob/main/.github/workflows/release.yml#L46
47
+ # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
48
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
50
+ npm_config_registry: https://registry.npmjs.org
@@ -0,0 +1,20 @@
1
+ {
2
+ // 使用 IntelliSense 了解相关属性。
3
+ // 悬停以查看现有属性的描述。
4
+ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "type": "node",
9
+ "request": "launch",
10
+ "name": "Benchmark",
11
+ "skipFiles": [
12
+ "<node_internals>/**"
13
+ ],
14
+ "program": "${workspaceFolder}\\src\\__benchmarks__\\index.js",
15
+ "outFiles": [
16
+ "${workspaceFolder}/**/*.js"
17
+ ]
18
+ }
19
+ ]
20
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # fastevent
2
+
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - b7d21e2: add prepend option for on
8
+ - 8807aaa: update scope on options
9
+
10
+ ## 1.0.0
11
+
12
+ ### Major Changes
13
+
14
+ - b8b8c13: first release
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 zhangfisher
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/bench.png ADDED
Binary file
@@ -0,0 +1,205 @@
1
+ type FastEventMeta<T = string, M = unknown> = M & {
2
+ type: T;
3
+ };
4
+ type FastEventListener<T = string, P = any, M = unknown> = (payload: P, meta: FastEventMeta<T, M>) => any | Promise<any>;
5
+ type FastListenerNode = {
6
+ __listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[];
7
+ } & {
8
+ [key: string]: FastListenerNode;
9
+ };
10
+ type FastEventSubscriber = {
11
+ off: () => void;
12
+ };
13
+ interface FastEventListenerMeta {
14
+ emitter?: string;
15
+ }
16
+ type FastListeners = FastListenerNode;
17
+ type FastEventOptions<M = unknown> = {
18
+ delimiter?: string;
19
+ context?: any;
20
+ ignoreErrors?: boolean;
21
+ onListenerError?: ((type: string, error: Error) => void);
22
+ meta?: M;
23
+ onAddListener?: (type: string[], listener: FastEventListener) => void;
24
+ onRemoveListener?: (type: string[], listener: FastEventListener) => void;
25
+ };
26
+ type FastEvents = Record<string, any>;
27
+ type ScopeEvents<T extends Record<string, any>, Prefix extends string> = {
28
+ [K in keyof T as K extends `${Prefix}/${infer Rest}` ? Rest : never]: T[K];
29
+ };
30
+ type FastEventListenOptions = {
31
+ count?: number;
32
+ prepend?: boolean;
33
+ };
34
+
35
+ declare class FastEventScope<Events extends FastEvents = FastEvents, Types extends keyof Events = keyof Events, Meta = unknown> {
36
+ emitter: FastEvent<Events, Types, Meta>;
37
+ prefix: string;
38
+ constructor(emitter: FastEvent<Events, Types, Meta>, prefix: string);
39
+ private _getScopeListener;
40
+ private _getScopeType;
41
+ on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
42
+ on<T extends Types = Types>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
43
+ on(type: '**', listener: FastEventListener<any, any, Meta>): FastEventSubscriber;
44
+ once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
45
+ once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
46
+ onAny<P = any>(listener: FastEventListener<Types, P, Meta>, options?: FastEventListenOptions): FastEventSubscriber;
47
+ offAll(): void;
48
+ off(listener: FastEventListener<any, any, any>): void;
49
+ off(type: string, listener: FastEventListener<any, any, any>): void;
50
+ off(type: Types, listener: FastEventListener<any, any, any>): void;
51
+ off(type: string): void;
52
+ off(type: Types): void;
53
+ clear(): void;
54
+ emit<R = any>(type: string, payload?: any, retain?: boolean): R[];
55
+ emit<R = any>(type: Types, payload?: Events[Types], retain?: boolean): R[];
56
+ waitFor<R = any>(type: string, timeout?: number): Promise<R>;
57
+ waitFor<R = any>(type: Types, timeout?: number): Promise<R>;
58
+ scope(prefix: string): FastEventScope<ScopeEvents<Events, string>, keyof ScopeEvents<Events, string>, unknown>;
59
+ }
60
+
61
+ declare class FastEvent<Events extends FastEvents = FastEvents, Types extends keyof Events = keyof Events, Meta = unknown> {
62
+ listeners: FastListeners;
63
+ private _options;
64
+ private _delimiter;
65
+ private _context;
66
+ private _retainedEvents;
67
+ constructor(options?: FastEventOptions<Meta>);
68
+ get options(): FastEventOptions;
69
+ private _addListener;
70
+ /**
71
+ *
72
+ * 根据parts路径遍历侦听器树,并在最后的节点上执行回调函数
73
+ *
74
+ * @param parts
75
+ * @param callback
76
+ * @returns
77
+ */
78
+ private _forEachNodes;
79
+ /**
80
+ * 从监听器节点中移除指定的事件监听器
81
+ * @private
82
+ * @param node - 监听器节点
83
+ * @param listener - 需要移除的事件监听器
84
+ * @description 遍历节点的监听器列表,移除所有匹配的监听器。支持移除普通函数和数组形式的监听器
85
+ */
86
+ private _removeListener;
87
+ on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
88
+ on<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
89
+ on<P = any>(type: '**', listener: FastEventListener<Types, P, Meta>): FastEventSubscriber;
90
+ once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>): FastEventSubscriber;
91
+ once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>): FastEventSubscriber;
92
+ /**
93
+ * 注册一个监听器,用于监听所有事件
94
+ * @param listener 事件监听器函数,可以接收任意类型的事件数据
95
+ * @returns {FastEventSubscriber} 返回一个订阅者对象,包含 off 方法用于取消监听
96
+ * @example
97
+ * ```ts
98
+ * const subscriber = emitter.onAny((eventName, data) => {
99
+ * console.log(eventName, data);
100
+ * });
101
+ *
102
+ * // 取消监听
103
+ * subscriber.off();
104
+ * ```
105
+ */
106
+ onAny<P = any>(listener: FastEventListener<string, P, Meta>, options?: Pick<FastEventListenOptions, 'prepend'>): FastEventSubscriber;
107
+ off(listener: FastEventListener<any, any, any>): void;
108
+ off(type: string, listener: FastEventListener<any, any, any>): void;
109
+ off(type: Types, listener: FastEventListener<any, any, any>): void;
110
+ off(type: string): void;
111
+ off(type: Types): void;
112
+ /**
113
+ * 移除所有事件监听器
114
+ * @param entry - 可选的事件前缀,如果提供则只移除指定前缀下的的监听器
115
+ * @description
116
+ * - 如果提供了prefix参数,则只清除该前缀下的所有监听器
117
+ * - 如果没有提供prefix,则清除所有监听器
118
+ * - 同时会清空保留的事件(_retainedEvents)
119
+ * - 重置监听器对象为空
120
+
121
+ * @example
122
+ *
123
+ * ```ts
124
+ * emitter.offAll(); // 清除所有监听器
125
+ * emitter.offAll('a/b'); // 清除a/b下的所有监听器
126
+ *
127
+ */
128
+ offAll(entry?: string): void;
129
+ private _getListenerNode;
130
+ /**
131
+ * 移除保留的事件
132
+ * @param prefix - 事件前缀。如果不提供,将清除所有保留的事件。
133
+ * 如果提供前缀,将删除所有以该前缀开头的事件。
134
+ * 如果前缀不以分隔符结尾,会自动添加分隔符。
135
+ * @private
136
+ */
137
+ private _removeRetainedEvents;
138
+ clear(): void;
139
+ private _getMeta;
140
+ private _emitForLastEvent;
141
+ /**
142
+ * 遍历监听器节点树
143
+ * @param node 当前遍历的监听器节点
144
+ * @param parts 事件名称按'.'分割的部分数组
145
+ * @param callback 遍历到目标节点时的回调函数
146
+ * @param index 当前遍历的parts数组索引,默认为0
147
+ * @param lastFollowing 当命中**时该值为true, 注意**只能作在路径的最后面,如a.**有效,而a.**.b无效
148
+ * @private
149
+ *
150
+ * 支持三种匹配模式:
151
+ * - 精确匹配: 'a.b.c'
152
+ * - 单层通配: 'a.*.c'
153
+ * - 多层通配: 'a.**'
154
+ */
155
+ private _traverseToPath;
156
+ private _traverseListeners;
157
+ private _executeListener;
158
+ /**
159
+ * 执行监听器节点中的所有监听函数
160
+ * @param node - FastListenerNode类型的监听器节点
161
+ * @param payload - 事件携带的数据
162
+ * @param type - 事件类型
163
+ * @returns 返回所有监听函数的执行结果数组
164
+ * @private
165
+ *
166
+ * @description
167
+ * 遍历执行节点中的所有监听函数:
168
+ * - 对于普通监听器,直接执行并收集结果
169
+ * - 对于带次数限制的监听器(数组形式),执行后递减次数,当次数为0时移除该监听器
170
+ */
171
+ private _executeListeners;
172
+ emit<R = any>(type: string, payload?: any, retain?: boolean, meta?: Meta): R[];
173
+ emit<R = any>(type: Types, payload?: Events[Types], retain?: boolean, meta?: Meta): R[];
174
+ emitAsync<R = any>(type: string, payload?: any, retain?: boolean, meta?: Meta): Promise<[R | Error][]>;
175
+ emitAsync<R = any>(type: Types, payload?: Events[Types], retain?: boolean, meta?: Meta): Promise<[R | Error][]>;
176
+ /**
177
+ * 等待指定事件发生
178
+ *
179
+ * @param type
180
+ * @param timeout 超时时间,单位毫秒,默认为 0,表示无限等待
181
+ */
182
+ waitFor<R = any>(type: string, timeout?: number): Promise<R>;
183
+ waitFor<R = any>(type: Types, timeout?: number): Promise<R>;
184
+ /**
185
+ * 创建事件域
186
+ *
187
+ * 注意:
188
+ *
189
+ * 事件域与当前事件对象共享相同的侦听器表
190
+ * 也就是说,如果在事件域中触发事件,当前事件对象也会触发该事件
191
+ * 两者工不是完全隔离的,仅是事件侦听和触发时的事件类型不同而已
192
+ *
193
+ * const emitter = new FastEvent()
194
+ *
195
+ * const scope= emitter.scope("a/b")
196
+ *
197
+ * scope.on("x",()=>{}) == emitter.on("a/b/x",()=>{})
198
+ * scope.emit("x",1) == emitter.emit("a/b/x",1)
199
+ * scope.offAll() == emitter.offAll("a/b")
200
+ *
201
+ */
202
+ scope<T extends string>(prefix: T): FastEventScope<ScopeEvents<Events, T>, keyof ScopeEvents<Events, T>, unknown>;
203
+ }
204
+
205
+ export { FastEvent, type FastEventListenOptions, type FastEventListener, type FastEventListenerMeta, type FastEventMeta, type FastEventOptions, FastEventScope, type FastEventSubscriber, type FastEvents, type FastListenerNode, type FastListeners, type ScopeEvents };
@@ -0,0 +1,205 @@
1
+ type FastEventMeta<T = string, M = unknown> = M & {
2
+ type: T;
3
+ };
4
+ type FastEventListener<T = string, P = any, M = unknown> = (payload: P, meta: FastEventMeta<T, M>) => any | Promise<any>;
5
+ type FastListenerNode = {
6
+ __listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[];
7
+ } & {
8
+ [key: string]: FastListenerNode;
9
+ };
10
+ type FastEventSubscriber = {
11
+ off: () => void;
12
+ };
13
+ interface FastEventListenerMeta {
14
+ emitter?: string;
15
+ }
16
+ type FastListeners = FastListenerNode;
17
+ type FastEventOptions<M = unknown> = {
18
+ delimiter?: string;
19
+ context?: any;
20
+ ignoreErrors?: boolean;
21
+ onListenerError?: ((type: string, error: Error) => void);
22
+ meta?: M;
23
+ onAddListener?: (type: string[], listener: FastEventListener) => void;
24
+ onRemoveListener?: (type: string[], listener: FastEventListener) => void;
25
+ };
26
+ type FastEvents = Record<string, any>;
27
+ type ScopeEvents<T extends Record<string, any>, Prefix extends string> = {
28
+ [K in keyof T as K extends `${Prefix}/${infer Rest}` ? Rest : never]: T[K];
29
+ };
30
+ type FastEventListenOptions = {
31
+ count?: number;
32
+ prepend?: boolean;
33
+ };
34
+
35
+ declare class FastEventScope<Events extends FastEvents = FastEvents, Types extends keyof Events = keyof Events, Meta = unknown> {
36
+ emitter: FastEvent<Events, Types, Meta>;
37
+ prefix: string;
38
+ constructor(emitter: FastEvent<Events, Types, Meta>, prefix: string);
39
+ private _getScopeListener;
40
+ private _getScopeType;
41
+ on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
42
+ on<T extends Types = Types>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
43
+ on(type: '**', listener: FastEventListener<any, any, Meta>): FastEventSubscriber;
44
+ once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
45
+ once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
46
+ onAny<P = any>(listener: FastEventListener<Types, P, Meta>, options?: FastEventListenOptions): FastEventSubscriber;
47
+ offAll(): void;
48
+ off(listener: FastEventListener<any, any, any>): void;
49
+ off(type: string, listener: FastEventListener<any, any, any>): void;
50
+ off(type: Types, listener: FastEventListener<any, any, any>): void;
51
+ off(type: string): void;
52
+ off(type: Types): void;
53
+ clear(): void;
54
+ emit<R = any>(type: string, payload?: any, retain?: boolean): R[];
55
+ emit<R = any>(type: Types, payload?: Events[Types], retain?: boolean): R[];
56
+ waitFor<R = any>(type: string, timeout?: number): Promise<R>;
57
+ waitFor<R = any>(type: Types, timeout?: number): Promise<R>;
58
+ scope(prefix: string): FastEventScope<ScopeEvents<Events, string>, keyof ScopeEvents<Events, string>, unknown>;
59
+ }
60
+
61
+ declare class FastEvent<Events extends FastEvents = FastEvents, Types extends keyof Events = keyof Events, Meta = unknown> {
62
+ listeners: FastListeners;
63
+ private _options;
64
+ private _delimiter;
65
+ private _context;
66
+ private _retainedEvents;
67
+ constructor(options?: FastEventOptions<Meta>);
68
+ get options(): FastEventOptions;
69
+ private _addListener;
70
+ /**
71
+ *
72
+ * 根据parts路径遍历侦听器树,并在最后的节点上执行回调函数
73
+ *
74
+ * @param parts
75
+ * @param callback
76
+ * @returns
77
+ */
78
+ private _forEachNodes;
79
+ /**
80
+ * 从监听器节点中移除指定的事件监听器
81
+ * @private
82
+ * @param node - 监听器节点
83
+ * @param listener - 需要移除的事件监听器
84
+ * @description 遍历节点的监听器列表,移除所有匹配的监听器。支持移除普通函数和数组形式的监听器
85
+ */
86
+ private _removeListener;
87
+ on<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
88
+ on<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>, options?: FastEventListenOptions): FastEventSubscriber;
89
+ on<P = any>(type: '**', listener: FastEventListener<Types, P, Meta>): FastEventSubscriber;
90
+ once<T extends string>(type: T, listener: FastEventListener<T, Events[T], Meta>): FastEventSubscriber;
91
+ once<T extends Types = Types>(type: T, listener: FastEventListener<Types, Events[T], Meta>): FastEventSubscriber;
92
+ /**
93
+ * 注册一个监听器,用于监听所有事件
94
+ * @param listener 事件监听器函数,可以接收任意类型的事件数据
95
+ * @returns {FastEventSubscriber} 返回一个订阅者对象,包含 off 方法用于取消监听
96
+ * @example
97
+ * ```ts
98
+ * const subscriber = emitter.onAny((eventName, data) => {
99
+ * console.log(eventName, data);
100
+ * });
101
+ *
102
+ * // 取消监听
103
+ * subscriber.off();
104
+ * ```
105
+ */
106
+ onAny<P = any>(listener: FastEventListener<string, P, Meta>, options?: Pick<FastEventListenOptions, 'prepend'>): FastEventSubscriber;
107
+ off(listener: FastEventListener<any, any, any>): void;
108
+ off(type: string, listener: FastEventListener<any, any, any>): void;
109
+ off(type: Types, listener: FastEventListener<any, any, any>): void;
110
+ off(type: string): void;
111
+ off(type: Types): void;
112
+ /**
113
+ * 移除所有事件监听器
114
+ * @param entry - 可选的事件前缀,如果提供则只移除指定前缀下的的监听器
115
+ * @description
116
+ * - 如果提供了prefix参数,则只清除该前缀下的所有监听器
117
+ * - 如果没有提供prefix,则清除所有监听器
118
+ * - 同时会清空保留的事件(_retainedEvents)
119
+ * - 重置监听器对象为空
120
+
121
+ * @example
122
+ *
123
+ * ```ts
124
+ * emitter.offAll(); // 清除所有监听器
125
+ * emitter.offAll('a/b'); // 清除a/b下的所有监听器
126
+ *
127
+ */
128
+ offAll(entry?: string): void;
129
+ private _getListenerNode;
130
+ /**
131
+ * 移除保留的事件
132
+ * @param prefix - 事件前缀。如果不提供,将清除所有保留的事件。
133
+ * 如果提供前缀,将删除所有以该前缀开头的事件。
134
+ * 如果前缀不以分隔符结尾,会自动添加分隔符。
135
+ * @private
136
+ */
137
+ private _removeRetainedEvents;
138
+ clear(): void;
139
+ private _getMeta;
140
+ private _emitForLastEvent;
141
+ /**
142
+ * 遍历监听器节点树
143
+ * @param node 当前遍历的监听器节点
144
+ * @param parts 事件名称按'.'分割的部分数组
145
+ * @param callback 遍历到目标节点时的回调函数
146
+ * @param index 当前遍历的parts数组索引,默认为0
147
+ * @param lastFollowing 当命中**时该值为true, 注意**只能作在路径的最后面,如a.**有效,而a.**.b无效
148
+ * @private
149
+ *
150
+ * 支持三种匹配模式:
151
+ * - 精确匹配: 'a.b.c'
152
+ * - 单层通配: 'a.*.c'
153
+ * - 多层通配: 'a.**'
154
+ */
155
+ private _traverseToPath;
156
+ private _traverseListeners;
157
+ private _executeListener;
158
+ /**
159
+ * 执行监听器节点中的所有监听函数
160
+ * @param node - FastListenerNode类型的监听器节点
161
+ * @param payload - 事件携带的数据
162
+ * @param type - 事件类型
163
+ * @returns 返回所有监听函数的执行结果数组
164
+ * @private
165
+ *
166
+ * @description
167
+ * 遍历执行节点中的所有监听函数:
168
+ * - 对于普通监听器,直接执行并收集结果
169
+ * - 对于带次数限制的监听器(数组形式),执行后递减次数,当次数为0时移除该监听器
170
+ */
171
+ private _executeListeners;
172
+ emit<R = any>(type: string, payload?: any, retain?: boolean, meta?: Meta): R[];
173
+ emit<R = any>(type: Types, payload?: Events[Types], retain?: boolean, meta?: Meta): R[];
174
+ emitAsync<R = any>(type: string, payload?: any, retain?: boolean, meta?: Meta): Promise<[R | Error][]>;
175
+ emitAsync<R = any>(type: Types, payload?: Events[Types], retain?: boolean, meta?: Meta): Promise<[R | Error][]>;
176
+ /**
177
+ * 等待指定事件发生
178
+ *
179
+ * @param type
180
+ * @param timeout 超时时间,单位毫秒,默认为 0,表示无限等待
181
+ */
182
+ waitFor<R = any>(type: string, timeout?: number): Promise<R>;
183
+ waitFor<R = any>(type: Types, timeout?: number): Promise<R>;
184
+ /**
185
+ * 创建事件域
186
+ *
187
+ * 注意:
188
+ *
189
+ * 事件域与当前事件对象共享相同的侦听器表
190
+ * 也就是说,如果在事件域中触发事件,当前事件对象也会触发该事件
191
+ * 两者工不是完全隔离的,仅是事件侦听和触发时的事件类型不同而已
192
+ *
193
+ * const emitter = new FastEvent()
194
+ *
195
+ * const scope= emitter.scope("a/b")
196
+ *
197
+ * scope.on("x",()=>{}) == emitter.on("a/b/x",()=>{})
198
+ * scope.emit("x",1) == emitter.emit("a/b/x",1)
199
+ * scope.offAll() == emitter.offAll("a/b")
200
+ *
201
+ */
202
+ scope<T extends string>(prefix: T): FastEventScope<ScopeEvents<Events, T>, keyof ScopeEvents<Events, T>, unknown>;
203
+ }
204
+
205
+ export { FastEvent, type FastEventListenOptions, type FastEventListener, type FastEventListenerMeta, type FastEventMeta, type FastEventOptions, FastEventScope, type FastEventSubscriber, type FastEvents, type FastListenerNode, type FastListeners, type ScopeEvents };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var p=Object.defineProperty;var y=(h,e,t)=>e in h?p(h,e,{enumerable:true,configurable:true,writable:true,value:t}):h[e]=t;var u=(h,e)=>p(h,"name",{value:e,configurable:true});var l=(h,e,t)=>y(h,typeof e!="symbol"?e+"":e,t);var _=class _{constructor(e,t){l(this,"emitter");l(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,{type:r}){return r.startsWith(t)&&(r=r.substring(t.length)),e(i,{type:r})},"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(_,"FastEventScope");var 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}u(d,"isPathMatched");function v(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(v,"removeItem");var a=class a{constructor(e){l(this,"listeners",{__listeners:[]});l(this,"_options");l(this,"_delimiter","/");l(this,"_context");l(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&&v(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,c)=>{(s!==void 0||r&&d(o,i))&&(s?this._removeListener(c,i,s):c.__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();}_getMeta(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._getMeta({type:e}));}),this._executeListeners(this.listeners,t,this._getMeta({type:e}));}}_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}n in e&&this._traverseToPath(e[n],t,s,i+1),"*"in e&&this._traverseToPath(e["*"],t,s,i+1),"**"in e&&this._traverseToPath(e["**"],t,s,i+1,true);}_traverseListeners(e,t,s){let i=e;t&&t.length>0&&this._traverseToPath(e,t,n=>{i=n;});let r=u((n,o,c)=>{o(c,n);for(let[m,g]of Object.entries(n))m.startsWith("__")||g&&r(g,o,[...c,m]);},"traverseNodes");r(i,s,[]);}_executeListener(e,t,s){try{return typeof e.__wrappedListener=="function"?e.__wrappedListener.call(this._context,t,s):e.call(this._context,t,s)}catch(i){if(i._trigger=s.type,typeof this._options.onListenerError=="function"&&this._options.onListenerError.call(this,s.type,i),this._options.ignoreErrors)return i;throw i}}_executeListeners(e,t,s){if(!e||!e.__listeners)return [];let i=0,r=e.__listeners,n=[];for(;i<r.length;){let o=r[i];Array.isArray(o)?(n.push(this._executeListener(o[0],t,s)),o[1]--,o[1]===0&&(r.splice(i,1),i--)):n.push(this._executeListener(o,t,s)),i++;}return n}emit(){let e=arguments[0],t=arguments[1],s=arguments[2],i=arguments[3]||{},r=e.split(this._delimiter);s&&this._retainedEvents.set(e,t);let n=[];return this._traverseToPath(this.listeners,r,o=>{n.push(...this._executeListeners(o,t,this._getMeta({...i,type:e})));}),n.push(...this._executeListeners(this.listeners,t,this._getMeta({...i,type:e}))),n}async emitAsync(){let e=arguments[0],t=arguments[1],s=arguments[2],i=arguments[3]||{};return (await Promise.allSettled(this.emit(e,t,s,this._getMeta({...i,type:e})))).map(n=>n.status==="fulfilled"?n.value:n.reason)}waitFor(){let e=arguments[0],t=arguments[1];return new Promise((s,i)=>{let r,n,o=u(c=>{clearTimeout(r),n.off(),s(c);},"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(a,"FastEvent");var L=a;exports.FastEvent=L;exports.FastEventScope=f;//# sourceMappingURL=index.js.map
2
+ //# sourceMappingURL=index.js.map