@supercat1337/event-emitter 1.0.12 → 2.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.
- package/README.md +118 -166
- package/index.d.ts +2 -2
- package/index.js +2 -1
- package/package.json +42 -17
- package/src/event-emitter-lite.d.ts +52 -0
- package/src/event-emitter-lite.js +104 -0
- package/src/event-emitter.d.ts +61 -0
- package/src/event-emitter.js +289 -0
- package/dist/event-emitter.esm.d.ts +0 -103
- package/dist/event-emitter.esm.d.ts.map +0 -1
- package/dist/event-emitter.esm.js +0 -396
- package/dist/event-emitter.esm.min.js +0 -1
- package/index.d.ts.map +0 -1
- package/jsconfig.json +0 -18
- package/my.tsconfig.types.json +0 -19
- package/src/index.js +0 -396
- package/tests/test.js +0 -508
package/README.md
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# @supercat1337/event-emitter 🐈⚡
|
|
2
2
|
|
|
3
|
-
A modern, feature-rich EventEmitter implementation for JavaScript and TypeScript with advanced capabilities and
|
|
3
|
+
A modern, feature-rich EventEmitter implementation for JavaScript and TypeScript with advanced capabilities and industry-leading type safety.
|
|
4
|
+
|
|
5
|
+
---
|
|
4
6
|
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- ✅ **Dual Implementation** – Choose between lightweight `EventEmitterLite` or full-featured `EventEmitter`.
|
|
10
|
+
- ✅ **First-class TypeScript** – Deep generic support for event names and argument validation.
|
|
11
|
+
- ✅ **Promise-based Waiting** – Native `waitForEvent` and `waitForAnyEvent` with built-in timeout support.
|
|
12
|
+
- ✅ **Lifecycle Tracking** – Monitor when events gain or lose listeners (`onHasEventListeners`, `onNoEventListeners`).
|
|
13
|
+
- ✅ **Centralized Error Handling** – Intercept and handle listener errors globally via `onListenerError`.
|
|
14
|
+
- ✅ **Memory-Efficient** – Automatic cleanup of unused event keys and dedicated `destroy()` lifecycle.
|
|
15
|
+
- ✅ **Immutable Emission** – Listener arrays are snapshotted during emission, making it safe to modify listeners inside callbacks.
|
|
16
|
+
- ✅ **Modern ES2022+** – Leverages native private fields and optimized logic.
|
|
17
|
+
|
|
18
|
+
---
|
|
14
19
|
|
|
15
20
|
## Installation
|
|
16
21
|
|
|
@@ -18,9 +23,13 @@ A modern, feature-rich EventEmitter implementation for JavaScript and TypeScript
|
|
|
18
23
|
npm install @supercat1337/event-emitter
|
|
19
24
|
```
|
|
20
25
|
|
|
26
|
+
---
|
|
27
|
+
|
|
21
28
|
## Quick Start
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
### Using the full-featured `EventEmitter`
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
24
33
|
import { EventEmitter } from '@supercat1337/event-emitter';
|
|
25
34
|
|
|
26
35
|
// Define your event types
|
|
@@ -28,7 +37,7 @@ type AppEvents = 'user:created' | 'user:deleted' | 'notification:sent';
|
|
|
28
37
|
|
|
29
38
|
const emitter = new EventEmitter<AppEvents>();
|
|
30
39
|
|
|
31
|
-
// Subscribe
|
|
40
|
+
// Subscribe with an auto-generated unsubscribe function
|
|
32
41
|
const unsubscribe = emitter.on('user:created', (userData) => {
|
|
33
42
|
console.log('User created:', userData);
|
|
34
43
|
});
|
|
@@ -36,224 +45,167 @@ const unsubscribe = emitter.on('user:created', (userData) => {
|
|
|
36
45
|
// Emit events
|
|
37
46
|
emitter.emit('user:created', { id: 1, name: 'John' });
|
|
38
47
|
|
|
39
|
-
//
|
|
48
|
+
// Clean up
|
|
40
49
|
unsubscribe();
|
|
41
50
|
```
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
### Core Properties
|
|
46
|
-
|
|
47
|
-
#### `isDestroyed`
|
|
52
|
+
### Using the lightweight `EventEmitterLite`
|
|
48
53
|
|
|
49
|
-
|
|
54
|
+
For performance-critical paths where you only need core `on`/`off`/`emit` logic:
|
|
50
55
|
|
|
51
56
|
```javascript
|
|
52
|
-
|
|
53
|
-
emitter.destroy();
|
|
54
|
-
console.log(emitter.isDestroyed); // true
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
#### `events`
|
|
58
|
-
|
|
59
|
-
The list of events and their listeners.
|
|
57
|
+
import { EventEmitterLite } from '@supercat1337/event-emitter';
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
console.log(
|
|
59
|
+
const lite = new EventEmitterLite();
|
|
60
|
+
lite.on('data', (msg) => console.log(msg));
|
|
61
|
+
lite.emit('data', 'Hello, World!');
|
|
63
62
|
```
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Should errors be logged to the console?
|
|
68
|
-
|
|
69
|
-
```javascript
|
|
70
|
-
emitter.logErrors = true;
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Core Methods
|
|
74
|
-
|
|
75
|
-
#### `on(event, listener)`
|
|
64
|
+
---
|
|
76
65
|
|
|
77
|
-
|
|
66
|
+
## API Reference
|
|
78
67
|
|
|
79
|
-
|
|
80
|
-
const unsubscribe = emitter.on("user:created", (data) => {
|
|
81
|
-
console.log(data);
|
|
82
|
-
});
|
|
68
|
+
### Core Classes
|
|
83
69
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
```
|
|
70
|
+
#### `EventEmitterLite<Events>`
|
|
71
|
+
The core implementation focused on performance. Use this when you don't need async waiting or lifecycle hooks.
|
|
87
72
|
|
|
88
|
-
#### `
|
|
73
|
+
#### `EventEmitter<Events>`
|
|
74
|
+
Extends `EventEmitterLite` with the full suite of advanced features: async promises, tracking hooks, and instance destruction.
|
|
89
75
|
|
|
90
|
-
|
|
76
|
+
### Common Properties
|
|
91
77
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
emitter.on("user:created", listener);
|
|
97
|
-
emitter.off("user:created", listener);
|
|
98
|
-
```
|
|
78
|
+
| Property | Type | Description |
|
|
79
|
+
|---------------|-----------|--------------------------------------------------------------------------------------------|
|
|
80
|
+
| `logErrors` | `boolean` | If `true`, errors in listeners are logged to the console. (Default: `true`)<br>Even when `false`, errors can still be caught via `onListenerError`. |
|
|
81
|
+
| `isDestroyed` | `boolean` | (`EventEmitter` only) Returns `true` if the instance has been destroyed. |
|
|
99
82
|
|
|
100
|
-
|
|
83
|
+
### Common Methods
|
|
101
84
|
|
|
102
|
-
|
|
85
|
+
| Method | Description |
|
|
86
|
+
|---------------------------------|---------------------------------------------------------------------------------------|
|
|
87
|
+
| `on(event, listener)` | Subscribes to an event. Returns an `unsubscribe()` function. |
|
|
88
|
+
| `once(event, listener)` | Subscribes for a single invocation, then auto-removes. |
|
|
89
|
+
| `off(event, listener)` | Removes a specific listener. Alias for `removeListener`. |
|
|
90
|
+
| `emit(event, ...args)` | Triggers all listeners for the event with provided arguments. |
|
|
91
|
+
| `removeListener(event, listener)` | Same as `off`. |
|
|
103
92
|
|
|
104
|
-
|
|
105
|
-
emitter.emit("user:created", { id: 1, name: "Alice" });
|
|
106
|
-
```
|
|
93
|
+
### Advanced Methods (`EventEmitter` only)
|
|
107
94
|
|
|
108
|
-
|
|
95
|
+
| Method | Description |
|
|
96
|
+
|-------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
|
|
97
|
+
| `waitForEvent(event, [maxWaitMs])` | Returns a `Promise<boolean>`. Resolves `true` when the event fires. If `maxWaitMs` is reached, resolves `false`. |
|
|
98
|
+
| `waitForAnyEvent(events, [maxWaitMs])` | Waits for the first occurring event from an array of event names. |
|
|
99
|
+
| `clear()` | Removes all listeners from all events. |
|
|
100
|
+
| `clearEventListeners(event)` | Removes all listeners for a specific event. |
|
|
101
|
+
| `destroy()` | Completely wipes the instance. Clears all listeners and prevents further emissions. |
|
|
102
|
+
| `onHasEventListeners(callback)` | Invoked when any event gains its **first** listener. Receives the event name. |
|
|
103
|
+
| `onNoEventListeners(callback)` | Invoked when any event loses its **last** listener. Receives the event name. |
|
|
104
|
+
| `onListenerError(callback)` | Invoked when any listener throws an error. Receives `(error, eventName, ...args)`. |
|
|
109
105
|
|
|
110
|
-
|
|
106
|
+
#### Example: lifecycle tracking
|
|
111
107
|
|
|
112
108
|
```javascript
|
|
113
|
-
emitter.
|
|
114
|
-
console.log(
|
|
109
|
+
emitter.onHasEventListeners((event) => {
|
|
110
|
+
console.log(`First listener added for ${event}`);
|
|
115
111
|
});
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Advanced Methods
|
|
119
|
-
|
|
120
|
-
#### `waitForEvent(event, [maxWaitMs])`
|
|
121
|
-
|
|
122
|
-
Wait for an event using Promises.
|
|
123
|
-
|
|
124
|
-
```javascript
|
|
125
|
-
// Wait indefinitely
|
|
126
|
-
const result = await emitter.waitForEvent("data:loaded");
|
|
127
|
-
|
|
128
|
-
// Wait with timeout (returns false if timeout reached)
|
|
129
|
-
const success = await emitter.waitForEvent("data:loaded", 5000);
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
#### `waitForAnyEvent(events, [maxWaitMs])`
|
|
133
|
-
|
|
134
|
-
Wait for any of multiple events.
|
|
135
|
-
|
|
136
|
-
```javascript
|
|
137
|
-
const events = ['success', 'error', 'timeout'] as const;
|
|
138
|
-
const result = await emitter.waitForAnyEvent(events, 3000);
|
|
139
|
-
```
|
|
140
112
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
Get notified when any event gains its first listener.
|
|
144
|
-
|
|
145
|
-
```javascript
|
|
146
|
-
emitter.onHasEventListeners((eventName) => {
|
|
147
|
-
console.log(`Event ${eventName} now has listeners!`);
|
|
113
|
+
emitter.onNoEventListeners((event) => {
|
|
114
|
+
console.log(`Last listener removed for ${event}`);
|
|
148
115
|
});
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
#### `onNoEventListeners(callback)`
|
|
152
|
-
|
|
153
|
-
Get notified when any event loses its last listener.
|
|
154
116
|
|
|
155
|
-
|
|
156
|
-
emitter.
|
|
157
|
-
console.log(`Event ${eventName} has no more listeners!`);
|
|
158
|
-
});
|
|
117
|
+
emitter.on('foo', () => {}); // logs: First listener added for foo
|
|
118
|
+
emitter.off('foo', () => {}); // logs: Last listener removed for foo
|
|
159
119
|
```
|
|
160
120
|
|
|
161
|
-
####
|
|
162
|
-
|
|
163
|
-
Get notified when any listener throws an error.
|
|
121
|
+
#### Example: error handling
|
|
164
122
|
|
|
165
123
|
```javascript
|
|
166
|
-
emitter.onListenerError((error,
|
|
167
|
-
console.error(`
|
|
124
|
+
emitter.onListenerError((error, event, ...args) => {
|
|
125
|
+
console.error(`Error in ${event}:`, error);
|
|
126
|
+
myLoggingService.report(error);
|
|
168
127
|
});
|
|
128
|
+
|
|
129
|
+
emitter.on('crash', () => { throw new Error('boom'); });
|
|
130
|
+
emitter.emit('crash'); // error is caught, passed to the callback, and (if logErrors=true) also logged.
|
|
169
131
|
```
|
|
170
132
|
|
|
171
|
-
|
|
133
|
+
---
|
|
172
134
|
|
|
173
|
-
|
|
135
|
+
## TypeScript Usage
|
|
174
136
|
|
|
175
|
-
|
|
137
|
+
### 1. Simple string union
|
|
138
|
+
Good for events that don't pass complex data.
|
|
176
139
|
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
|
|
140
|
+
```typescript
|
|
141
|
+
type MyEvents = 'start' | 'stop' | 'tick';
|
|
142
|
+
const emitter = new EventEmitter<MyEvents>();
|
|
143
|
+
emitter.emit('start'); // OK
|
|
144
|
+
emitter.emit('unknown'); // Type error
|
|
180
145
|
```
|
|
181
146
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
Remove all listeners while keeping the emitter functional.
|
|
147
|
+
### 2. Full type safety (recommended)
|
|
148
|
+
Define a record mapping event names to argument tuples. This gives you autocompletion and argument validation.
|
|
185
149
|
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
|
|
150
|
+
```typescript
|
|
151
|
+
type MyEvents = {
|
|
152
|
+
'user:updated': [id: number, name: string];
|
|
153
|
+
'ping': []; // no arguments
|
|
154
|
+
};
|
|
189
155
|
|
|
190
|
-
|
|
156
|
+
const emitter = new EventEmitter<MyEvents>();
|
|
191
157
|
|
|
192
|
-
|
|
158
|
+
// Listener gets correct argument types
|
|
159
|
+
emitter.on('user:updated', (id, name) => {
|
|
160
|
+
console.log(id, name);
|
|
161
|
+
});
|
|
193
162
|
|
|
194
|
-
|
|
195
|
-
emitter.
|
|
163
|
+
// Emit is type-checked
|
|
164
|
+
emitter.emit('user:updated', 1, 'Alice'); // ✅
|
|
165
|
+
emitter.emit('user:updated', '1'); // ❌ Type error
|
|
196
166
|
```
|
|
197
167
|
|
|
198
|
-
|
|
168
|
+
### 3. Using `keyof` with a separate type map
|
|
169
|
+
If you prefer to keep event names as a union but still want argument types, combine a union with a mapped type:
|
|
199
170
|
|
|
200
171
|
```typescript
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
| "user:created"
|
|
206
|
-
| "user:updated"
|
|
207
|
-
| { type: "user:deleted"; payload: { id: string; reason: string } };
|
|
172
|
+
type EventNames = 'start' | 'stop' | 'tick';
|
|
173
|
+
type EventMap = {
|
|
174
|
+
[K in EventNames]: K extends 'tick' ? [counter: number] : [];
|
|
175
|
+
};
|
|
208
176
|
|
|
209
|
-
const emitter = new EventEmitter<
|
|
210
|
-
|
|
211
|
-
// Full type safety!
|
|
212
|
-
emitter.emit("user:created", { id: 1, name: "John" }); // ✅ Correct
|
|
213
|
-
emitter.emit("user:created", "invalid"); // ❌ Type error
|
|
177
|
+
const emitter = new EventEmitter<EventMap>();
|
|
178
|
+
emitter.on('tick', (counter) => console.log(counter)); // counter is number
|
|
214
179
|
```
|
|
215
180
|
|
|
216
|
-
|
|
181
|
+
---
|
|
217
182
|
|
|
218
|
-
|
|
183
|
+
## Error Handling
|
|
219
184
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
});
|
|
185
|
+
By default, all listener errors are caught to prevent the emitter from crashing.
|
|
186
|
+
- If `logErrors` is `true` (default), errors are printed to `console.error`.
|
|
187
|
+
- Even with `logErrors: false`, you can still intercept errors globally using `onListenerError` for custom logging or reporting.
|
|
224
188
|
|
|
225
|
-
|
|
226
|
-
emitter.emit("data:received");
|
|
227
|
-
```
|
|
189
|
+
---
|
|
228
190
|
|
|
229
191
|
## Performance Notes
|
|
230
192
|
|
|
231
|
-
-
|
|
232
|
-
-
|
|
233
|
-
-
|
|
234
|
-
|
|
193
|
+
- **Snapshotted iteration**: Listener arrays are copied before emission. If a listener calls `off()` on itself during emission, the current cycle continues safely without skipping elements.
|
|
194
|
+
- **Zero dependencies**: Ultra-small bundle size.
|
|
195
|
+
- **Memory management**: Event keys are deleted from the internal store when the last listener is removed, reducing memory usage over time.
|
|
196
|
+
|
|
197
|
+
---
|
|
235
198
|
|
|
236
199
|
## Browser Support
|
|
237
200
|
|
|
238
201
|
| Feature | Support |
|
|
239
|
-
|
|
202
|
+
|------------|-----------------|
|
|
240
203
|
| ES2022+ | Modern browsers |
|
|
241
204
|
| TypeScript | 4.0+ |
|
|
242
205
|
| Node.js | 14+ |
|
|
243
206
|
|
|
244
|
-
## License
|
|
245
|
-
|
|
246
|
-
MIT License - feel free to use in commercial projects.
|
|
247
|
-
|
|
248
|
-
## Contributing
|
|
249
|
-
|
|
250
|
-
Contributions welcome! Please ensure:
|
|
251
|
-
|
|
252
|
-
- ✅ All tests pass
|
|
253
|
-
- ✅ TypeScript types are maintained
|
|
254
|
-
- ✅ New features include tests
|
|
255
|
-
- ✅ Code follows existing style
|
|
256
|
-
|
|
257
207
|
---
|
|
258
208
|
|
|
259
|
-
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT © [supercat1337](https://github.com/supercat1337)
|
package/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export { EventEmitterLite } from "./src/event-emitter-lite.js";
|
|
2
|
+
export { EventEmitter } from "./src/event-emitter.js";
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,32 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supercat1337/event-emitter",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Lightweight typed event emitter with Symbol support, one-time listeners, and zero dependencies. Works in Node.js and browsers.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"index.js",
|
|
14
|
+
"index.d.ts",
|
|
15
|
+
"src/**/*.js",
|
|
16
|
+
"src/**/*.d.ts"
|
|
17
|
+
],
|
|
6
18
|
"scripts": {
|
|
7
19
|
"test": "c8 ava",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"build_esm_min": "esbuild --minify --bundle --platform=neutral --legal-comments=none ./src/index.js --outfile=./dist/event-emitter.esm.min.js",
|
|
11
|
-
"create_types": "npx -p typescript tsc --project my.tsconfig.types.json",
|
|
12
|
-
"remove_type_files": "del /q *.d.ts *.d.ts.map && cd dist && del /s /q *.d.ts *.d.ts.map && cd ../src && del /s /q *.d.ts *.d.ts.map && cd .."
|
|
20
|
+
"clean": "shx rm -f *.d.ts *.d.ts.map && shx rm -rf src/**/*.d.ts src/**/*.d.ts.map",
|
|
21
|
+
"build:types": "npm run clean && tsc --project my.tsconfig.types.json"
|
|
13
22
|
},
|
|
14
|
-
"
|
|
23
|
+
"keywords": [
|
|
24
|
+
"event",
|
|
25
|
+
"emitter",
|
|
26
|
+
"eventemitter",
|
|
27
|
+
"event-emitter",
|
|
28
|
+
"typed",
|
|
29
|
+
"typescript",
|
|
30
|
+
"symbols",
|
|
31
|
+
"lightweight",
|
|
32
|
+
"tiny",
|
|
33
|
+
"zero-dependencies",
|
|
34
|
+
"browser",
|
|
35
|
+
"node",
|
|
36
|
+
"events",
|
|
37
|
+
"pubsub",
|
|
38
|
+
"observer"
|
|
39
|
+
],
|
|
40
|
+
"author": "Albert Bazaleev",
|
|
15
41
|
"license": "MIT",
|
|
16
42
|
"devDependencies": {
|
|
43
|
+
"@types/node": "^20.12.2",
|
|
17
44
|
"ava": "^6.1.2",
|
|
18
45
|
"c8": "^9.1.0",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
46
|
+
"shx": "^0.4.0",
|
|
47
|
+
"typescript": "^5.4.3"
|
|
21
48
|
},
|
|
22
|
-
"type": "module",
|
|
23
|
-
"moduleResolution": "nodenext",
|
|
24
|
-
"keywords": [
|
|
25
|
-
"typed event emitter",
|
|
26
|
-
"typed event bus"
|
|
27
|
-
],
|
|
28
49
|
"publishConfig": {
|
|
29
50
|
"registry": "https://registry.npmjs.org"
|
|
30
51
|
},
|
|
31
|
-
"homepage": "https://github.com/supercat1337/event-emitter"
|
|
52
|
+
"homepage": "https://github.com/supercat1337/event-emitter",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/supercat1337/event-emitter.git"
|
|
56
|
+
}
|
|
32
57
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const ORIGINAL: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* @template {string | symbol | Record<string|symbol, any[]>} [Events=string]
|
|
4
|
+
*/
|
|
5
|
+
export class EventEmitterLite<Events extends string | symbol | Record<string | symbol, any[]> = string> {
|
|
6
|
+
/**
|
|
7
|
+
* @type {Object.<Events extends string | symbol ? Events : keyof Events, Function[]>}
|
|
8
|
+
*/
|
|
9
|
+
events: any;
|
|
10
|
+
/**
|
|
11
|
+
* logErrors indicates whether errors thrown by listeners should be logged to the console.
|
|
12
|
+
* @type {boolean}
|
|
13
|
+
*/
|
|
14
|
+
logErrors: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* on is used to add a callback function that's going to be executed when the event is triggered
|
|
17
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
18
|
+
* @param {K} event
|
|
19
|
+
* @param {Function} listener
|
|
20
|
+
* @returns {() => void}
|
|
21
|
+
*/
|
|
22
|
+
on<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Add a one-time listener
|
|
25
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
26
|
+
* @param {K} event
|
|
27
|
+
* @param {Function} listener
|
|
28
|
+
* @returns {()=>void}
|
|
29
|
+
*/
|
|
30
|
+
once<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): () => void;
|
|
31
|
+
/**
|
|
32
|
+
* off is an alias for removeListener
|
|
33
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
34
|
+
* @param {K} event
|
|
35
|
+
* @param {Function} listener
|
|
36
|
+
*/
|
|
37
|
+
off<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): void;
|
|
38
|
+
/**
|
|
39
|
+
* Remove an event listener from an event
|
|
40
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
41
|
+
* @param {K} event
|
|
42
|
+
* @param {Function} listener
|
|
43
|
+
*/
|
|
44
|
+
removeListener<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): void;
|
|
45
|
+
/**
|
|
46
|
+
* emit is used to trigger an event
|
|
47
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
48
|
+
* @param {K} event
|
|
49
|
+
* @param {...any} args
|
|
50
|
+
*/
|
|
51
|
+
emit<K extends Events extends string | symbol ? Events : keyof Events>(event: K, ...args: any[]): void;
|
|
52
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const ORIGINAL = Symbol('original');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @template {string | symbol | Record<string|symbol, any[]>} [Events=string]
|
|
7
|
+
*/
|
|
8
|
+
export class EventEmitterLite {
|
|
9
|
+
/**
|
|
10
|
+
* @type {Object.<Events extends string | symbol ? Events : keyof Events, Function[]>}
|
|
11
|
+
*/
|
|
12
|
+
events = Object.create(null);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* logErrors indicates whether errors thrown by listeners should be logged to the console.
|
|
16
|
+
* @type {boolean}
|
|
17
|
+
*/
|
|
18
|
+
logErrors = true;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* on is used to add a callback function that's going to be executed when the event is triggered
|
|
22
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
23
|
+
* @param {K} event
|
|
24
|
+
* @param {Function} listener
|
|
25
|
+
* @returns {() => void}
|
|
26
|
+
*/
|
|
27
|
+
on(event, listener) {
|
|
28
|
+
if (!this.events[event]) this.events[event] = [];
|
|
29
|
+
|
|
30
|
+
this.events[event].push(listener);
|
|
31
|
+
let unsubscriber = () => this.removeListener(event, listener);
|
|
32
|
+
return unsubscriber;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Add a one-time listener
|
|
37
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
38
|
+
* @param {K} event
|
|
39
|
+
* @param {Function} listener
|
|
40
|
+
* @returns {()=>void}
|
|
41
|
+
*/
|
|
42
|
+
once(event, listener) {
|
|
43
|
+
const wrapper = (/** @type {...any} */ ...args) => {
|
|
44
|
+
this.removeListener(event, wrapper);
|
|
45
|
+
listener.apply(this, args);
|
|
46
|
+
};
|
|
47
|
+
wrapper[ORIGINAL] = listener;
|
|
48
|
+
return this.on(event, wrapper);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* off is an alias for removeListener
|
|
53
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
54
|
+
* @param {K} event
|
|
55
|
+
* @param {Function} listener
|
|
56
|
+
*/
|
|
57
|
+
off(event, listener) {
|
|
58
|
+
return this.removeListener(event, listener);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Remove an event listener from an event
|
|
63
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
64
|
+
* @param {K} event
|
|
65
|
+
* @param {Function} listener
|
|
66
|
+
*/
|
|
67
|
+
removeListener(event, listener) {
|
|
68
|
+
if (typeof listener !== 'function') return;
|
|
69
|
+
|
|
70
|
+
const listeners = this.events[event];
|
|
71
|
+
if (!listeners) return;
|
|
72
|
+
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
const idx = listeners.findIndex(l => l === listener || l[ORIGINAL] === listener);
|
|
75
|
+
if (idx > -1) {
|
|
76
|
+
listeners.splice(idx, 1);
|
|
77
|
+
if (listeners.length === 0) delete this.events[event];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* emit is used to trigger an event
|
|
83
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
84
|
+
* @param {K} event
|
|
85
|
+
* @param {...any} args
|
|
86
|
+
*/
|
|
87
|
+
emit(event, ...args) {
|
|
88
|
+
const listeners = this.events[event];
|
|
89
|
+
if (!listeners) return;
|
|
90
|
+
|
|
91
|
+
const queue = (this.events[event] || []).slice();
|
|
92
|
+
var length = queue.length;
|
|
93
|
+
|
|
94
|
+
for (let i = 0; i < length; i++) {
|
|
95
|
+
try {
|
|
96
|
+
queue[i].apply(this, args);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
if (this.logErrors) {
|
|
99
|
+
console.error(`Error in listener for event "${String(event)}":`, e);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|