fastevent 2.0.2 → 2.1.0
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/dist/devTools.js +1 -1
- package/dist/devTools.js.map +1 -1
- package/dist/devTools.mjs +1 -1
- package/dist/devTools.mjs.map +1 -1
- package/dist/eventbus/index.d.mts +1005 -0
- package/dist/eventbus/index.d.ts +1005 -0
- package/dist/eventbus/index.js +2 -0
- package/dist/eventbus/index.js.map +1 -0
- package/dist/eventbus/index.mjs +2 -0
- package/dist/eventbus/index.mjs.map +1 -0
- package/dist/executors/index.d.mts +147 -0
- package/dist/executors/index.d.ts +147 -0
- package/dist/executors/index.js +2 -0
- package/dist/executors/index.js.map +1 -0
- package/dist/executors/index.mjs +2 -0
- package/dist/executors/index.mjs.map +1 -0
- package/dist/index.d.mts +255 -144
- package/dist/index.d.ts +255 -144
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/pipes/index.d.mts +115 -0
- package/dist/pipes/index.d.ts +115 -0
- package/dist/pipes/index.js +2 -0
- package/dist/pipes/index.js.map +1 -0
- package/dist/pipes/index.mjs +2 -0
- package/dist/pipes/index.mjs.map +1 -0
- package/package.json +18 -3
- package/readme.md +217 -772
package/readme.md
CHANGED
|
@@ -1,362 +1,169 @@
|
|
|
1
1
|
# FastEvent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[WebSite](https://zhangfisher.github.io/fastevent/)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- `FastEvent` performs about `1+` times better than `EventEmitter2` when publishing and subscribing with wildcards.
|
|
8
|
-
- `FastEvent` has a package size of `6.3kb`, while `EventEmitter2` is `43.4kb`.
|
|
9
|
-
- `FastEvent` offers more comprehensive features.
|
|
5
|
+
`FastEvent` is a well-designed, powerful, type-safe, and thoroughly tested event emitter that provides robust event subscription and publishing mechanisms, suitable for both `nodejs/browser` environments.
|
|
10
6
|
|
|
11
7
|
# Installation
|
|
12
8
|
|
|
13
|
-
Install using npm:
|
|
14
|
-
|
|
15
9
|
```bash
|
|
16
10
|
npm install fastevent
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Or using yarn:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
11
|
yarn add fastevent
|
|
12
|
+
pnpm add fastevent
|
|
13
|
+
bun add fastevent
|
|
23
14
|
```
|
|
24
15
|
|
|
25
|
-
#
|
|
16
|
+
# Guide
|
|
17
|
+
|
|
18
|
+
## Event Publishing and Subscription
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
`FastEvent` provides complete event emission and subscription functionality, with an `API` design inspired by `eventemitter2`.
|
|
28
21
|
|
|
29
22
|
```typescript
|
|
30
23
|
import { FastEvent } from 'fastevent';
|
|
31
|
-
|
|
32
|
-
// Create event instance
|
|
33
24
|
const events = new FastEvent();
|
|
34
25
|
|
|
35
|
-
//
|
|
26
|
+
// Basic event publishing
|
|
27
|
+
const results = events.emit('user/login', { id: 1 });
|
|
28
|
+
|
|
29
|
+
// Asynchronous event emission
|
|
30
|
+
const results = await events.emitAsync('data/process', { items: [...] });
|
|
31
|
+
|
|
32
|
+
// Event subscription
|
|
36
33
|
events.on('user/login', (message) => {
|
|
37
34
|
console.log('User login:', message.payload);
|
|
38
|
-
console.log('Event type:', message.type);
|
|
39
|
-
console.log('Metadata:', message.meta);
|
|
40
35
|
});
|
|
41
36
|
|
|
42
|
-
//
|
|
43
|
-
events.
|
|
37
|
+
// One-time listener
|
|
38
|
+
events.once('startup', () => console.log('Application has started'));
|
|
44
39
|
|
|
45
|
-
//
|
|
46
|
-
events.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
// Listener with options
|
|
41
|
+
events.on('data/update', handler, {
|
|
42
|
+
count: 3, // Maximum trigger count
|
|
43
|
+
prepend: true, // Add to the beginning of the queue
|
|
44
|
+
filter: (msg) => msg.payload.important // Only process important updates
|
|
50
45
|
});
|
|
51
46
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
'
|
|
55
|
-
'user/logout': { id: number };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const typedEvents = new FastEvent<MyEvents>();
|
|
59
|
-
|
|
60
|
-
// TypeScript will enforce correct event types and payloads
|
|
61
|
-
typedEvents.on('user/login', (message) => {
|
|
62
|
-
const { id, name } = message.payload; // Properly typed
|
|
47
|
+
// Global listener
|
|
48
|
+
events.onAny((message) => {
|
|
49
|
+
console.log('Event occurred:', message.type);
|
|
63
50
|
});
|
|
64
51
|
```
|
|
65
52
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
## Event Triggering
|
|
69
|
-
|
|
70
|
-
FastEvent provides flexible ways to trigger events with different parameter combinations:
|
|
53
|
+
## Event Messages
|
|
71
54
|
|
|
72
|
-
|
|
55
|
+
Listener functions receive a `Message` object that contains the following properties:
|
|
73
56
|
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
events.emit({
|
|
82
|
-
type: 'user/login',
|
|
83
|
-
payload: { id: 1, name: 'Alice' },
|
|
84
|
-
meta: { timestamp: Date.now() },
|
|
57
|
+
```ts
|
|
58
|
+
events.on('user/login', (message) => {
|
|
59
|
+
// {
|
|
60
|
+
// type: 'user/login', // Event name
|
|
61
|
+
// payload: { id: 1 }, // Event data
|
|
62
|
+
// meta: {...} // Event metadata
|
|
63
|
+
// }
|
|
85
64
|
});
|
|
86
65
|
```
|
|
87
66
|
|
|
88
|
-
|
|
67
|
+
## Retained Events
|
|
89
68
|
|
|
90
|
-
|
|
69
|
+
Retain the last event data, so subsequent subscribers can immediately receive the event value upon subscription:
|
|
91
70
|
|
|
92
71
|
```typescript
|
|
93
|
-
|
|
94
|
-
events.emit('config/update', { theme: 'dark' }, true);
|
|
95
|
-
|
|
96
|
-
// Later subscribers will immediately receive the retained event
|
|
97
|
-
events.on('config/update', (message) => {
|
|
98
|
-
console.log('Config:', message.payload); // { theme: 'dark' }
|
|
99
|
-
});
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Event Metadata
|
|
103
|
-
|
|
104
|
-
Metadata can be provided at different levels and will be merged:
|
|
72
|
+
const events = new FastEvent();
|
|
105
73
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
});
|
|
74
|
+
// Publish and retain event
|
|
75
|
+
events.emit('config/theme', { dark: true }, true);
|
|
76
|
+
// Equivalent to
|
|
77
|
+
events.emit('config/theme', { dark: true }, { retain: true });
|
|
110
78
|
|
|
111
|
-
//
|
|
112
|
-
events.
|
|
113
|
-
|
|
79
|
+
// Subsequent subscribers immediately receive the retained value
|
|
80
|
+
events.on('config/theme', (message) => {
|
|
81
|
+
console.log('Theme:', message.payload); // Immediately outputs: { dark: true }
|
|
114
82
|
});
|
|
115
|
-
|
|
116
|
-
// Listener receives merged metadata:
|
|
117
|
-
// { type: 'order/create', app: 'MyApp', timestamp: ... }
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Return Values
|
|
121
|
-
|
|
122
|
-
`emit()` returns an array of listener results:
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
events.on('calculate', () => 1);
|
|
126
|
-
events.on('calculate', () => 2);
|
|
127
|
-
|
|
128
|
-
const results = events.emit('calculate');
|
|
129
|
-
console.log(results); // [1, 2]
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### Type-safe Event Triggering
|
|
133
|
-
|
|
134
|
-
With TypeScript, event payloads are type-checked:
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
interface MyEvents {
|
|
138
|
-
'user/login': { id: number; name: string };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const events = new FastEvent<MyEvents>();
|
|
142
|
-
|
|
143
|
-
// Valid - payload matches type
|
|
144
|
-
events.emit('user/login', { id: 1, name: 'Alice' });
|
|
145
|
-
|
|
146
|
-
// Error - payload type mismatch
|
|
147
|
-
events.emit('user/login', { id: '1' }); // TypeScript error
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
## Event Message Format
|
|
151
|
-
|
|
152
|
-
FastEvent uses a standardized message format for all events:
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
type FastEventMessage<T = string, P = any, M = unknown> = {
|
|
156
|
-
type: T; // Event type
|
|
157
|
-
payload: P; // Event data
|
|
158
|
-
meta: M; // Event metadata
|
|
159
|
-
};
|
|
160
83
|
```
|
|
161
84
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
## Event Wildcards
|
|
85
|
+
## Hierarchical Event Publishing
|
|
165
86
|
|
|
166
|
-
FastEvent supports
|
|
87
|
+
`FastEvent` supports hierarchical event publishing and subscription.
|
|
167
88
|
|
|
168
|
-
-
|
|
169
|
-
-
|
|
89
|
+
- The default event hierarchy delimiter is `/`, which can be modified via `options.delimiter`
|
|
90
|
+
- Two types of wildcards are supported when subscribing to events: `*` matches a single path level, `**` matches multiple path levels (only used at the end of event names)
|
|
170
91
|
|
|
171
92
|
```typescript
|
|
172
93
|
const events = new FastEvent();
|
|
173
94
|
|
|
174
|
-
//
|
|
95
|
+
// Match user/*/login
|
|
175
96
|
events.on('user/*/login', (message) => {
|
|
176
97
|
console.log('Any user type login:', message.payload);
|
|
177
98
|
});
|
|
178
99
|
|
|
179
|
-
//
|
|
100
|
+
// Match all events under user
|
|
180
101
|
events.on('user/**', (message) => {
|
|
181
102
|
console.log('All user-related events:', message.payload);
|
|
182
103
|
});
|
|
183
104
|
|
|
184
105
|
// Trigger events
|
|
185
106
|
events.emit('user/admin/login', { id: 1 }); // Both handlers will be called
|
|
186
|
-
events.emit('user/admin/profile/update', { name: 'New' }); // Only ** handler will be called
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## Event Scoping
|
|
190
|
-
|
|
191
|
-
Scopes allow you to handle events within specific namespaces. Note that scopes share the same listener table with the parent emitter:
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
const events = new FastEvent();
|
|
195
|
-
|
|
196
|
-
// Create user-related scope
|
|
197
|
-
const userScope = events.scope('user');
|
|
198
|
-
|
|
199
|
-
// These are equivalent:
|
|
200
|
-
userScope.on('login', handler);
|
|
201
|
-
events.on('user/login', handler);
|
|
202
|
-
|
|
203
|
-
// These are also equivalent:
|
|
204
|
-
userScope.emit('login', data);
|
|
205
|
-
events.emit('user/login', data);
|
|
206
|
-
|
|
207
|
-
// Clear all listeners in the scope
|
|
208
|
-
userScope.offAll(); // Equivalent to events.offAll('user')
|
|
209
|
-
|
|
210
|
-
// Nested scopes
|
|
211
|
-
const profileScope = userScope.scope('profile');
|
|
212
|
-
profileScope.on('update', (message) => {
|
|
213
|
-
// Will receive events emitted as 'user/profile/update'
|
|
214
|
-
console.log('Profile update:', message.payload);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// Scope with metadata
|
|
218
|
-
const adminScope = events.scope('admin', {
|
|
219
|
-
meta: { role: 'admin' },
|
|
220
|
-
context: { adminId: 1 },
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
adminScope.on('action', function (message) {
|
|
224
|
-
console.log('Admin meta:', message.meta); // Contains { role: 'admin' }
|
|
225
|
-
console.log('Context:', this.adminId); // Access to scope context
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Type-safe scopes
|
|
229
|
-
interface UserEvents {
|
|
230
|
-
login: { id: number };
|
|
231
|
-
logout: { id: number };
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const typedUserScope = events.scope<'user', UserEvents>('user');
|
|
235
|
-
typedUserScope.on('login', (message) => {
|
|
236
|
-
const { id } = message.payload; // Properly typed as { id: number }
|
|
237
|
-
});
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
Scopes provide several benefits:
|
|
241
|
-
|
|
242
|
-
1. Namespace organization - Group related events under a common prefix
|
|
243
|
-
2. Code organization - Separate event handling logic by domain
|
|
244
|
-
3. Metadata inheritance - Share common metadata across related events
|
|
245
|
-
4. Context binding - Provide specific execution context for event handlers
|
|
246
|
-
5. Type safety - Enforce type checking for scoped events
|
|
247
|
-
|
|
248
|
-
## Listener Options
|
|
249
|
-
|
|
250
|
-
When subscribing to events, you can specify additional options:
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
interface FastEventListenOptions {
|
|
254
|
-
// Number of times the listener should be called (0 for unlimited, 1 for once)
|
|
255
|
-
count?: number;
|
|
256
|
-
// Add the listener to the beginning of the listeners array
|
|
257
|
-
prepend?: boolean;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Example: Listen for first 3 occurrences
|
|
261
|
-
events.on('data', handler, { count: 3 });
|
|
262
|
-
|
|
263
|
-
// Example: Ensure handler is called before other listeners
|
|
264
|
-
events.on('important', handler, { prepend: true });
|
|
107
|
+
events.emit('user/admin/profile/update', { name: 'New' }); // Only the ** handler will be called
|
|
265
108
|
```
|
|
266
109
|
|
|
267
110
|
## Removing Listeners
|
|
268
111
|
|
|
269
|
-
FastEvent provides multiple ways to remove listeners:
|
|
112
|
+
`FastEvent` provides multiple ways to remove listeners:
|
|
270
113
|
|
|
271
114
|
```typescript
|
|
272
|
-
//
|
|
273
|
-
events.
|
|
115
|
+
// Return a subscriber object to remove the listener, recommended approach
|
|
116
|
+
const subscriber = events.on('user/login', handler);
|
|
117
|
+
subscriber.off();
|
|
274
118
|
|
|
275
|
-
// Remove
|
|
119
|
+
// Remove a specific listener
|
|
120
|
+
events.off(listener);
|
|
121
|
+
// Remove all listeners for a specific event
|
|
276
122
|
events.off('user/login');
|
|
277
|
-
|
|
278
|
-
// Remove specific listener for an event
|
|
123
|
+
// Remove a specific listener for a specific event
|
|
279
124
|
events.off('user/login', listener);
|
|
280
|
-
|
|
281
|
-
// Remove all listeners with wildcard pattern
|
|
125
|
+
// Remove listeners using wildcard patterns
|
|
282
126
|
events.off('user/*');
|
|
283
|
-
|
|
284
127
|
// Remove all listeners
|
|
285
128
|
events.offAll();
|
|
286
|
-
|
|
287
|
-
// Remove all listeners under a prefix
|
|
129
|
+
// Remove all listeners under a specific prefix
|
|
288
130
|
events.offAll('user');
|
|
289
131
|
```
|
|
290
132
|
|
|
291
|
-
##
|
|
292
|
-
|
|
293
|
-
Use `once` to subscribe to events that trigger only once:
|
|
294
|
-
|
|
295
|
-
```typescript
|
|
296
|
-
const events = new FastEvent();
|
|
297
|
-
|
|
298
|
-
events.once('startup', () => {
|
|
299
|
-
console.log('Application started');
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
// Equivalent to:
|
|
303
|
-
events.on('startup', handler, { count: 1 });
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## Asynchronous Events
|
|
133
|
+
## Event Scopes
|
|
307
134
|
|
|
308
|
-
|
|
135
|
+
Scopes allow you to handle events within a specific namespace.
|
|
309
136
|
|
|
310
|
-
|
|
311
|
-
const events = new FastEvent();
|
|
312
|
-
|
|
313
|
-
events.on('data/fetch', async () => {
|
|
314
|
-
const response = await fetch('https://api.example.com/data');
|
|
315
|
-
return await response.json();
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// Async event publishing returns array of results/errors
|
|
319
|
-
const results = await events.emitAsync('data/fetch');
|
|
320
|
-
console.log('Results from all handlers:', results);
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
## Listener Return Values
|
|
324
|
-
|
|
325
|
-
Both `emit` and `emitAsync` methods return the results from all event listeners:
|
|
137
|
+
**Note** that scopes share the same listener table with the parent event emitter:
|
|
326
138
|
|
|
327
139
|
```typescript
|
|
328
140
|
const events = new FastEvent();
|
|
329
141
|
|
|
330
|
-
//
|
|
331
|
-
events.
|
|
332
|
-
events.on('calculate', () => 2);
|
|
333
|
-
events.on('calculate', () => 3);
|
|
142
|
+
// Create a user-related scope
|
|
143
|
+
const userScope = events.scope('user');
|
|
334
144
|
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
145
|
+
// The following two approaches are equivalent:
|
|
146
|
+
userScope.on('login', handler);
|
|
147
|
+
events.on('user/login', handler);
|
|
338
148
|
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
events.
|
|
149
|
+
// The following two approaches are also equivalent:
|
|
150
|
+
userScope.emit('login', data);
|
|
151
|
+
events.emit('user/login', data);
|
|
342
152
|
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
console.log('Async results:', asyncResults); // ['result 1', 'result 2']
|
|
153
|
+
// Clear all listeners in the scope
|
|
154
|
+
userScope.offAll(); // Equivalent to events.offAll('user')
|
|
346
155
|
```
|
|
347
156
|
|
|
348
|
-
|
|
157
|
+
## Waiting for Events
|
|
349
158
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
Use `waitFor` to wait for specific events:
|
|
159
|
+
Use `waitFor` to wait for a specific event to occur, with timeout support.
|
|
353
160
|
|
|
354
161
|
```typescript
|
|
355
162
|
const events = new FastEvent();
|
|
356
163
|
|
|
357
164
|
async function waitForLogin() {
|
|
358
165
|
try {
|
|
359
|
-
// Wait for login event with 5
|
|
166
|
+
// Wait for login event with a 5-second timeout
|
|
360
167
|
const userData = await events.waitFor('user/login', 5000);
|
|
361
168
|
console.log('User logged in:', userData);
|
|
362
169
|
} catch (error) {
|
|
@@ -365,188 +172,168 @@ async function waitForLogin() {
|
|
|
365
172
|
}
|
|
366
173
|
|
|
367
174
|
waitForLogin();
|
|
368
|
-
// Later trigger login event
|
|
175
|
+
// Later trigger the login event
|
|
369
176
|
events.emit('user/login', { id: 1, name: 'Alice' });
|
|
370
177
|
```
|
|
371
178
|
|
|
372
|
-
##
|
|
179
|
+
## Event Hooks
|
|
373
180
|
|
|
374
|
-
|
|
181
|
+
`FastEvent` provides multiple hook functions for operations at different stages of the event emitter lifecycle.
|
|
375
182
|
|
|
376
183
|
```typescript
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
184
|
+
const otherEvents = new FastEvent();
|
|
185
|
+
const events = new FastEvent({
|
|
186
|
+
// Called when a new listener is added
|
|
187
|
+
onAddListener: (type, listener, options) => {
|
|
188
|
+
console.log('Added new listener:', type);
|
|
189
|
+
// Return false to prevent the listener from being added
|
|
190
|
+
return false;
|
|
191
|
+
// Can directly return a FastEventSubscriber
|
|
192
|
+
// For example: transfer events starting with `@` to another FastEvent
|
|
193
|
+
if (type.startsWith('@')) {
|
|
194
|
+
return otherEvents.on(type, listener, options);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
// Called when a listener is removed
|
|
198
|
+
onRemoveListener: (type, listener) => {
|
|
199
|
+
console.log('Removed listener:', type);
|
|
200
|
+
},
|
|
201
|
+
// Called when listeners are cleared
|
|
202
|
+
onClearListeners: () => {
|
|
203
|
+
console.log('All listeners cleared');
|
|
204
|
+
},
|
|
205
|
+
// Called when a listener throws an error
|
|
206
|
+
onListenerError: (error, listener, message, args) => {
|
|
207
|
+
console.error(`Error in listener for event ${message.type}:`, error);
|
|
208
|
+
},
|
|
209
|
+
// Called before a listener executes
|
|
210
|
+
onBeforeExecuteListener: (message, args) => {
|
|
211
|
+
console.log('Before executing event listener');
|
|
212
|
+
// Return false to prevent listener execution
|
|
213
|
+
return false;
|
|
214
|
+
|
|
215
|
+
// Forward events to another FastEvent
|
|
216
|
+
// For example: forward events starting with `@` to another FastEvent
|
|
217
|
+
if (type.startsWith('@')) {
|
|
218
|
+
return otherEvents.emit(message.type);
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
// Called after a listener executes
|
|
222
|
+
onAfterExecuteListener: (message, returns, listeners) => {
|
|
223
|
+
console.log('After executing event listener');
|
|
224
|
+
// Can intercept and modify return values here
|
|
225
|
+
},
|
|
385
226
|
});
|
|
386
227
|
```
|
|
387
228
|
|
|
388
|
-
##
|
|
229
|
+
## Executors
|
|
389
230
|
|
|
390
|
-
|
|
231
|
+
By default, all listeners are executed in parallel when an event is triggered.
|
|
391
232
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
Events can be organized in a hierarchical structure using path delimiters (default is '/'):
|
|
233
|
+
`FastEvent` provides powerful listener execution mechanisms that allow developers to control how listeners are executed.
|
|
395
234
|
|
|
396
235
|
```typescript
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
events.on('user/profile/update', handler);
|
|
401
|
-
events.on('user/settings/theme/change', handler);
|
|
402
|
-
|
|
403
|
-
// Custom delimiter
|
|
404
|
-
const customEvents = new FastEvent({
|
|
405
|
-
delimiter: '.',
|
|
236
|
+
import { race } from 'fastevent/executors';
|
|
237
|
+
const events = new FastEvent({
|
|
238
|
+
executor: race(),
|
|
406
239
|
});
|
|
407
|
-
customEvents.on('user.profile.update', handler);
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
### Wildcard Patterns
|
|
411
240
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
1. Single-level wildcard (`*`):
|
|
415
|
-
- Matches exactly one level in the event path
|
|
416
|
-
- Can be used at any level in the path
|
|
417
|
-
|
|
418
|
-
```typescript
|
|
419
|
-
// Match any user type
|
|
420
|
-
events.on('user/*/login', (message) => {
|
|
421
|
-
console.log('User type:', message.type.split('/')[1]);
|
|
422
|
-
// Matches: user/admin/login, user/guest/login, etc.
|
|
241
|
+
events.on('task/start', async () => {
|
|
242
|
+
/* Time-consuming operation 1 */
|
|
423
243
|
});
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
events.on('api/users/*/action/*', (message) => {
|
|
427
|
-
const [, , userId, , action] = message.type.split('/');
|
|
428
|
-
console.log(`User ${userId} performed ${action}`);
|
|
429
|
-
// Matches: api/users/123/action/update, api/users/456/action/delete, etc.
|
|
244
|
+
events.on('task/start', async () => {
|
|
245
|
+
/* Time-consuming operation 2 */
|
|
430
246
|
});
|
|
247
|
+
|
|
248
|
+
// The two listeners will execute in parallel, returning the fastest result
|
|
249
|
+
await events.emitAsync('task/start');
|
|
431
250
|
```
|
|
432
251
|
|
|
433
|
-
|
|
434
|
-
- Matches zero or more levels in the event path
|
|
435
|
-
- Must be used at the end of the path pattern
|
|
252
|
+
**Built-in Support**:
|
|
436
253
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
254
|
+
| Executor | Description |
|
|
255
|
+
| ----------------------------------------- | ------------------------------------------------------------------------- |
|
|
256
|
+
| `parallel` | Default, concurrent execution |
|
|
257
|
+
| `race` | Parallel executor, uses `Promise.race` for parallel execution |
|
|
258
|
+
| `balance` | Evenly distributed executor |
|
|
259
|
+
| `first` | Execute only the first listener |
|
|
260
|
+
| `last` | Execute only the last listener |
|
|
261
|
+
| `random` | Randomly select a listener |
|
|
262
|
+
| `series` | Serial executor, execute listeners in sequence and return the last result |
|
|
263
|
+
| `waterfall` | Execute listeners in sequence and return the last result, abort on error |
|
|
264
|
+
| `(listeners,message,args,execute)=>any[]` | Custom executor |
|
|
443
265
|
|
|
444
|
-
|
|
445
|
-
events.on('api/**', (message) => {
|
|
446
|
-
console.log('API event:', message.type, message.payload);
|
|
447
|
-
// Matches: api/get, api/users/create, api/posts/123/comments/add, etc.
|
|
448
|
-
});
|
|
449
|
-
```
|
|
266
|
+
## Listener Pipes
|
|
450
267
|
|
|
451
|
-
|
|
268
|
+
Listener pipes are used to wrap listener functions during event subscription to implement various common advanced features.
|
|
452
269
|
|
|
453
270
|
```typescript
|
|
271
|
+
import { queue } from 'fastevent/pipes';
|
|
454
272
|
const events = new FastEvent();
|
|
455
273
|
|
|
456
|
-
//
|
|
457
|
-
events.on(
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
274
|
+
// default queue size is 10
|
|
275
|
+
events.on(
|
|
276
|
+
'data/update',
|
|
277
|
+
(data) => {
|
|
278
|
+
console.log('Processing data:', data);
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
pipes: [queue({ size: 10 })],
|
|
282
|
+
},
|
|
283
|
+
);
|
|
284
|
+
```
|
|
465
285
|
|
|
466
|
-
|
|
467
|
-
events.on('service/auth/**', (message) => {
|
|
468
|
-
// Matches patterns like:
|
|
469
|
-
// service/auth/user/update
|
|
470
|
-
// service/auth/user/profile/update
|
|
471
|
-
// service/auth/settings/theme/change
|
|
472
|
-
console.log('Auth service event:', message.type, message.payload);
|
|
473
|
-
});
|
|
286
|
+
**Built-in Support:**
|
|
474
287
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
288
|
+
| Pipe | Description |
|
|
289
|
+
| ---------- | -------------------------------------------------------------------------------- |
|
|
290
|
+
| `queue` | Queue listener, process messages in queue, supports priority and timeout control |
|
|
291
|
+
| `throttle` | Throttle listener |
|
|
292
|
+
| `debounce` | Debounce listener |
|
|
293
|
+
| `timeout` | Timeout listener |
|
|
294
|
+
| `retry` | Retry listener, for controlling retries after listener execution failure |
|
|
295
|
+
| `memorize` | Cache listener, cache listener execution results |
|
|
480
296
|
|
|
481
|
-
|
|
297
|
+
## Forwarding Publishing and Subscription
|
|
482
298
|
|
|
483
|
-
|
|
484
|
-
typedEvents.on('api/users/profile', (message) => {
|
|
485
|
-
const { userId, data } = message.payload; // Properly typed
|
|
486
|
-
});
|
|
299
|
+
`FastEvent` can elegantly forward publishing and subscription to another `FastEvent` instance.
|
|
487
300
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
301
|
+
```ts
|
|
302
|
+
const otherEmitter = new FastEvent();
|
|
303
|
+
const emitter = new FastEvent({
|
|
304
|
+
onAddListener: (type, listener, options) => {
|
|
305
|
+
// Subscription forwarding rule: when event name starts with `@/`, forward subscription to another `FastEvent` instance
|
|
306
|
+
if (type.startsWith('@/')) {
|
|
307
|
+
return otherEmitter.on(type.substring(2), listener, options);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
onBeforeExecuteListener: (message, args) => {
|
|
311
|
+
// Event forwarding rule: when event name starts with `@/`, publish to another `FastEvent` instance
|
|
312
|
+
if (message.type.startsWith('@/')) {
|
|
313
|
+
message.type = message.type.substring(2);
|
|
314
|
+
return otherEmitter.emit(message, args);
|
|
315
|
+
}
|
|
316
|
+
},
|
|
492
317
|
});
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
events.
|
|
496
|
-
console.log('Event intercepted:', {
|
|
497
|
-
type: message.type,
|
|
498
|
-
timestamp: new Date(),
|
|
499
|
-
payload: message.payload,
|
|
500
|
-
});
|
|
318
|
+
const events: any[] = [];
|
|
319
|
+
otherEmitter.on('data', ({ payload }) => {
|
|
320
|
+
events.push(payload);
|
|
501
321
|
});
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
events.
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
### Important Notes
|
|
509
|
-
|
|
510
|
-
1. Wildcard Limitations:
|
|
511
|
-
|
|
512
|
-
- `**` wildcard must be at the end of the path
|
|
513
|
-
- `*` can be used multiple times in a path
|
|
514
|
-
- Wildcards cannot be combined in a single segment (e.g., 'a/\*\*/b' is invalid)
|
|
515
|
-
|
|
516
|
-
2. Performance Considerations:
|
|
517
|
-
|
|
518
|
-
- Specific patterns (without wildcards) are matched faster
|
|
519
|
-
- `*` wildcards are more efficient than `**`
|
|
520
|
-
- Excessive use of `**` wildcards may impact performance
|
|
521
|
-
|
|
522
|
-
3. Best Practices:
|
|
523
|
-
- Use specific patterns when possible
|
|
524
|
-
- Limit the use of `**` wildcards
|
|
525
|
-
- Consider the event hierarchy carefully
|
|
526
|
-
- Use TypeScript interfaces for type safety
|
|
527
|
-
|
|
528
|
-
## Global Event Listening
|
|
529
|
-
|
|
530
|
-
Use `onAny` to listen to all events:
|
|
531
|
-
|
|
532
|
-
```typescript
|
|
533
|
-
const events = new FastEvent();
|
|
534
|
-
|
|
535
|
-
events.onAny((message) => {
|
|
536
|
-
console.log(`Event ${message.type} triggered:`, message.payload);
|
|
322
|
+
// Subscribe to otherEmitter's data event
|
|
323
|
+
emitter.on('@/data', ({ payload }) => {
|
|
324
|
+
expect(payload).toBe(1);
|
|
325
|
+
events.push(payload);
|
|
537
326
|
});
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
327
|
+
// Publish data event to otherEmitter
|
|
328
|
+
const subscriber = emitter.emit('@/data', 1);
|
|
329
|
+
subscriber.off();
|
|
541
330
|
```
|
|
542
331
|
|
|
543
332
|
## Metadata (Meta)
|
|
544
333
|
|
|
545
|
-
Metadata is a mechanism for providing additional
|
|
334
|
+
Metadata is a mechanism for providing additional contextual information for events.
|
|
546
335
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
Set global metadata when creating a FastEvent instance:
|
|
336
|
+
You can set metadata at different levels: global, scope level, or event-specific level.
|
|
550
337
|
|
|
551
338
|
```typescript
|
|
552
339
|
const events = new FastEvent({
|
|
@@ -558,171 +345,31 @@ const events = new FastEvent({
|
|
|
558
345
|
|
|
559
346
|
events.on('user/login', (message) => {
|
|
560
347
|
console.log('Event data:', message.payload);
|
|
561
|
-
console.log('Metadata:', message.meta); //
|
|
562
|
-
});
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
### Scope Metadata
|
|
566
|
-
|
|
567
|
-
When creating a scope, you can provide metadata that will be merged with global metadata:
|
|
568
|
-
|
|
569
|
-
```typescript
|
|
570
|
-
const events = new FastEvent({
|
|
571
|
-
meta: { app: 'MyApp' },
|
|
348
|
+
console.log('Metadata:', message.meta); // Includes type, version, and environment
|
|
572
349
|
});
|
|
573
350
|
|
|
351
|
+
// Using scope-level metadata
|
|
574
352
|
const userScope = events.scope('user', {
|
|
575
353
|
meta: { domain: 'user' },
|
|
576
354
|
});
|
|
577
|
-
|
|
578
|
-
userScope.on('login', (message) => {
|
|
579
|
-
console.log('Metadata:', message.meta);
|
|
580
|
-
// { type: 'user/login', app: 'MyApp', domain: 'user' }
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
// Nested scopes merge metadata recursively
|
|
584
|
-
const profileScope = userScope.scope('profile', {
|
|
585
|
-
meta: { section: 'profile' },
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
profileScope.on('update', (message) => {
|
|
589
|
-
console.log('Metadata:', message.meta);
|
|
590
|
-
// { type: 'user/profile/update', app: 'MyApp', domain: 'user', section: 'profile' }
|
|
591
|
-
});
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
### Event-specific Metadata
|
|
595
|
-
|
|
596
|
-
Additional metadata can be passed when publishing events, which will be merged with higher-level metadata:
|
|
597
|
-
|
|
598
|
-
```typescript
|
|
599
|
-
const events = new FastEvent({
|
|
600
|
-
meta: { app: 'MyApp' },
|
|
601
|
-
});
|
|
602
|
-
|
|
603
|
-
const userScope = events.scope('user', {
|
|
604
|
-
meta: { domain: 'user' },
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
// Add specific metadata when publishing event
|
|
355
|
+
// Add specific metadata when publishing events
|
|
608
356
|
userScope.emit(
|
|
609
357
|
'login',
|
|
610
|
-
{ userId: '123' },
|
|
611
|
-
|
|
612
|
-
|
|
358
|
+
{ userId: '123' },
|
|
359
|
+
{
|
|
360
|
+
meta: { timestamp: Date.now() }, // Event-specific metadata
|
|
361
|
+
},
|
|
613
362
|
);
|
|
614
363
|
|
|
615
|
-
//
|
|
364
|
+
// Listeners receive merged metadata
|
|
616
365
|
userScope.on('login', (message) => {
|
|
617
366
|
console.log('Metadata:', message.meta);
|
|
618
|
-
// { type: 'user/login', app: 'MyApp', domain: 'user', timestamp: ... }
|
|
619
|
-
});
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
### Metadata Merge Rules
|
|
623
|
-
|
|
624
|
-
1. Priority (highest to lowest):
|
|
625
|
-
|
|
626
|
-
- Event-specific metadata
|
|
627
|
-
- Scope metadata (innermost to outermost)
|
|
628
|
-
- Global metadata
|
|
629
|
-
- System metadata (type is always added)
|
|
630
|
-
|
|
631
|
-
2. Merge behavior:
|
|
632
|
-
|
|
633
|
-
- Shallow merge (top-level properties only)
|
|
634
|
-
- Later values override earlier ones
|
|
635
|
-
- No deep merging of nested objects
|
|
636
|
-
|
|
637
|
-
3. Special cases:
|
|
638
|
-
- `type` is always preserved as the full event path
|
|
639
|
-
- `undefined` values will remove the property from the result
|
|
640
|
-
- Arrays are replaced, not concatenated
|
|
641
|
-
|
|
642
|
-
## Error Handling
|
|
643
|
-
|
|
644
|
-
FastEvent provides error handling mechanisms:
|
|
645
|
-
|
|
646
|
-
```typescript
|
|
647
|
-
const events = new FastEvent({
|
|
648
|
-
ignoreErrors: true, // Default is true, won't throw errors
|
|
649
|
-
onListenerError: (type, error) => {
|
|
650
|
-
console.error(`Error handling event ${type}:`, error);
|
|
651
|
-
},
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
events.on('process', () => {
|
|
655
|
-
throw new Error('Processing failed');
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
// Won't throw error, will trigger onListenerError instead
|
|
659
|
-
events.emit('process');
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
## Generic Parameters
|
|
663
|
-
|
|
664
|
-
FastEvent supports three generic type parameters for precise type control:
|
|
665
|
-
|
|
666
|
-
```typescript
|
|
667
|
-
class FastEvent<
|
|
668
|
-
Events extends Record<string, any> = Record<string, any>,
|
|
669
|
-
Meta extends Record<string, any> = Record<string, any>,
|
|
670
|
-
Types extends keyof Events = keyof Events
|
|
671
|
-
>
|
|
672
|
-
```
|
|
673
|
-
|
|
674
|
-
1. `Events`: Defines the mapping between event types and their payload types
|
|
675
|
-
2. `Meta`: Defines the type of metadata that can be attached to events
|
|
676
|
-
3. `Types`: The union type of all event types (usually inferred from Events)
|
|
677
|
-
|
|
678
|
-
### Basic Type Safety
|
|
679
|
-
|
|
680
|
-
```typescript
|
|
681
|
-
// Define event types
|
|
682
|
-
interface MyEvents {
|
|
683
|
-
'user/login': { id: number; name: string };
|
|
684
|
-
'user/logout': { id: number };
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// Create typed event emitter
|
|
688
|
-
const events = new FastEvent<MyEvents>();
|
|
689
|
-
|
|
690
|
-
// Type checking for event names and payload
|
|
691
|
-
events.on('user/login', (message) => {
|
|
692
|
-
// message.payload is typed as { id: number; name: string }
|
|
693
|
-
const { id, name } = message.payload;
|
|
694
367
|
});
|
|
695
|
-
|
|
696
|
-
// Error: wrong event name
|
|
697
|
-
events.emit('wrong/event', {});
|
|
698
|
-
|
|
699
|
-
// Error: wrong payload type
|
|
700
|
-
events.emit('user/login', { wrong: 'type' });
|
|
701
368
|
```
|
|
702
369
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
```typescript
|
|
706
|
-
// Define metadata structure
|
|
707
|
-
interface MyMeta {
|
|
708
|
-
timestamp: number;
|
|
709
|
-
source: string;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// Define events with custom metadata
|
|
713
|
-
const events = new FastEvent<MyEvents, MyMeta>();
|
|
714
|
-
|
|
715
|
-
events.on('user/login', (message) => {
|
|
716
|
-
// message.meta is typed as MyMeta
|
|
717
|
-
const { timestamp, source } = message.meta;
|
|
718
|
-
console.log(`Login from ${source} at ${timestamp}`);
|
|
719
|
-
});
|
|
370
|
+
## Event Type Definitions
|
|
720
371
|
|
|
721
|
-
|
|
722
|
-
events.emit('user/login', { id: 1, name: 'Alice' }, false, { timestamp: Date.now(), source: 'web' });
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
### Advanced Type Usage
|
|
372
|
+
`FastEvent` has complete `TypeScript` type support.
|
|
726
373
|
|
|
727
374
|
```typescript
|
|
728
375
|
// Define events with different payload types
|
|
@@ -736,223 +383,21 @@ const events = new FastEvent<ComplexEvents>();
|
|
|
736
383
|
|
|
737
384
|
// TypeScript ensures type safety for each event
|
|
738
385
|
events.on('data/number', (message) => {
|
|
739
|
-
const sum = message.payload + 1; // payload is
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
events.on('data/string', (message) => {
|
|
743
|
-
const upper = message.payload.toUpperCase(); // payload is typed as string
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
events.on('data/object', (message) => {
|
|
747
|
-
const value = message.payload.value; // payload is typed as { value: any }
|
|
386
|
+
const sum = message.payload + 1; // payload type is number
|
|
748
387
|
});
|
|
749
388
|
|
|
750
|
-
// All emissions are type
|
|
389
|
+
// All event emissions are type-checked
|
|
751
390
|
events.emit('data/number', 42);
|
|
752
391
|
events.emit('data/string', 'hello');
|
|
753
392
|
events.emit('data/object', { value: true });
|
|
754
393
|
```
|
|
755
394
|
|
|
756
|
-
##
|
|
757
|
-
|
|
758
|
-
FastEvent provides several hooks for monitoring and debugging the event system:
|
|
395
|
+
## Unit Testing
|
|
759
396
|
|
|
760
|
-
|
|
761
|
-
const events = new FastEvent({
|
|
762
|
-
// Called when a new listener is added
|
|
763
|
-
onAddListener: (path: string[], listener: Function) => {
|
|
764
|
-
console.log('New listener added for:', path.join('/'));
|
|
765
|
-
},
|
|
766
|
-
|
|
767
|
-
// Called when a listener is removed
|
|
768
|
-
onRemoveListener: (path: string[], listener: Function) => {
|
|
769
|
-
console.log('Listener removed from:', path.join('/'));
|
|
770
|
-
},
|
|
397
|
+
`FastEvent` has been thoroughly unit tested, with over `280+` cumulative test cases and `99%+` test coverage.
|
|
771
398
|
|
|
772
|
-
|
|
773
|
-
onClearListeners: () => {
|
|
774
|
-
console.log('All listeners cleared');
|
|
775
|
-
},
|
|
776
|
-
|
|
777
|
-
// Called when a listener throws an error
|
|
778
|
-
onListenerError: (type: string, error: Error) => {
|
|
779
|
-
console.error(`Error in listener for ${type}:`, error);
|
|
780
|
-
},
|
|
781
|
-
|
|
782
|
-
// Called after listeners are executed (debug mode only)
|
|
783
|
-
onExecuteListener: (message, returns, listeners) => {
|
|
784
|
-
console.log('Event executed:', {
|
|
785
|
-
type: message.type,
|
|
786
|
-
payload: message.payload,
|
|
787
|
-
results: returns,
|
|
788
|
-
listenerCount: listeners.length,
|
|
789
|
-
});
|
|
790
|
-
},
|
|
791
|
-
});
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
These hooks provide valuable insights into the event system's operation:
|
|
795
|
-
|
|
796
|
-
1. `onAddListener`: Monitor listener registration
|
|
797
|
-
|
|
798
|
-
- Called whenever a new event listener is added
|
|
799
|
-
- Receives the event path array and listener function
|
|
800
|
-
- Useful for tracking event subscriptions
|
|
801
|
-
|
|
802
|
-
2. `onRemoveListener`: Track listener removal
|
|
803
|
-
|
|
804
|
-
- Called when a listener is removed
|
|
805
|
-
- Helps monitor event unsubscription patterns
|
|
806
|
-
- Receives the same parameters as onAddListener
|
|
807
|
-
|
|
808
|
-
3. `onClearListeners`: Notifies of bulk listener removal
|
|
809
|
-
|
|
810
|
-
- Called when offAll() is invoked
|
|
811
|
-
- Useful for cleanup monitoring
|
|
812
|
-
- No parameters provided
|
|
813
|
-
|
|
814
|
-
4. `onListenerError`: Error handling hook
|
|
815
|
-
|
|
816
|
-
- Called when a listener throws an error
|
|
817
|
-
- Receives the event type and error object
|
|
818
|
-
- Enables centralized error handling
|
|
819
|
-
- Only called if ignoreErrors is true
|
|
820
|
-
|
|
821
|
-
5. `onExecuteListener`: Execution monitoring (debug mode)
|
|
822
|
-
- Only active when debug: true is set
|
|
823
|
-
- Provides detailed execution information
|
|
824
|
-
- Includes message, return values, and listener list
|
|
825
|
-
- Useful for debugging and performance monitoring
|
|
826
|
-
|
|
827
|
-
Example usage:
|
|
828
|
-
|
|
829
|
-
```typescript
|
|
830
|
-
const events = new FastEvent({
|
|
831
|
-
debug: true, // Enable debug mode for onExecuteListener
|
|
832
|
-
onAddListener: (path, listener) => {
|
|
833
|
-
console.log(`Listener added for ${path.join('/')}`);
|
|
834
|
-
// Track listener count or patterns
|
|
835
|
-
},
|
|
836
|
-
onListenerError: (type, error) => {
|
|
837
|
-
console.error(`Error in ${type}:`, error);
|
|
838
|
-
// Log to monitoring system
|
|
839
|
-
},
|
|
840
|
-
onExecuteListener: (message, returns, listeners) => {
|
|
841
|
-
console.log(`Event ${message.type} executed:`, {
|
|
842
|
-
executionTime: Date.now(),
|
|
843
|
-
listenerCount: listeners.length,
|
|
844
|
-
results: returns,
|
|
845
|
-
});
|
|
846
|
-
// Monitor event execution patterns
|
|
847
|
-
},
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
// Example events that trigger hooks
|
|
851
|
-
events.on('user/login', () => {
|
|
852
|
-
// onAddListener will be called
|
|
853
|
-
});
|
|
854
|
-
|
|
855
|
-
events.on('data/process', () => {
|
|
856
|
-
throw new Error('Process failed');
|
|
857
|
-
// onListenerError will be called
|
|
858
|
-
});
|
|
859
|
-
|
|
860
|
-
events.emit('user/login', { id: 1 });
|
|
861
|
-
// onExecuteListener will be called (if debug: true)
|
|
862
|
-
|
|
863
|
-
events.offAll();
|
|
864
|
-
// onClearListeners will be called
|
|
865
|
-
```
|
|
866
|
-
|
|
867
|
-
# Parameters
|
|
868
|
-
|
|
869
|
-
FastEvent constructor accepts the following configuration options:
|
|
870
|
-
|
|
871
|
-
````typescript
|
|
872
|
-
interface FastEventOptions<Meta = Record<string, any>, Context = any> {
|
|
873
|
-
/**
|
|
874
|
-
* Unique identifier for the emitter instance
|
|
875
|
-
* @default Randomly generated string
|
|
876
|
-
*/
|
|
877
|
-
id?: string;
|
|
878
|
-
|
|
879
|
-
/**
|
|
880
|
-
* Whether to enable debug mode
|
|
881
|
-
* @default false
|
|
882
|
-
* @remarks When true, events can be viewed in Redux DevTools
|
|
883
|
-
*/
|
|
884
|
-
debug?: boolean;
|
|
885
|
-
|
|
886
|
-
/**
|
|
887
|
-
* Delimiter for event path segments
|
|
888
|
-
* @default '/'
|
|
889
|
-
* @example
|
|
890
|
-
* ```ts
|
|
891
|
-
* new FastEvent({ delimiter: '.' }); // Use dot as delimiter
|
|
892
|
-
* ```
|
|
893
|
-
*/
|
|
894
|
-
delimiter?: string;
|
|
895
|
-
|
|
896
|
-
/**
|
|
897
|
-
* Default execution context for event handlers
|
|
898
|
-
* @default null
|
|
899
|
-
*/
|
|
900
|
-
context?: Context;
|
|
901
|
-
|
|
902
|
-
/**
|
|
903
|
-
* Whether to ignore listener errors
|
|
904
|
-
* @default true
|
|
905
|
-
*/
|
|
906
|
-
ignoreErrors?: boolean;
|
|
907
|
-
|
|
908
|
-
/**
|
|
909
|
-
* Global metadata attached to all events
|
|
910
|
-
* @default undefined
|
|
911
|
-
*/
|
|
912
|
-
meta?: Meta;
|
|
913
|
-
|
|
914
|
-
/**
|
|
915
|
-
* Callback when a listener is added
|
|
916
|
-
* @param path - Array of path segments
|
|
917
|
-
* @param listener - The listener function
|
|
918
|
-
*/
|
|
919
|
-
onAddListener?: (path: string[], listener: Function) => void;
|
|
920
|
-
|
|
921
|
-
/**
|
|
922
|
-
* Callback when a listener is removed
|
|
923
|
-
* @param path - Array of path segments
|
|
924
|
-
* @param listener - The listener function
|
|
925
|
-
*/
|
|
926
|
-
onRemoveListener?: (path: string[], listener: Function) => void;
|
|
927
|
-
|
|
928
|
-
/**
|
|
929
|
-
* Callback when all listeners are cleared
|
|
930
|
-
*/
|
|
931
|
-
onClearListeners?: () => void;
|
|
932
|
-
|
|
933
|
-
/**
|
|
934
|
-
* Callback when a listener throws an error
|
|
935
|
-
* @param type - Event type
|
|
936
|
-
* @param error - The error object
|
|
937
|
-
*/
|
|
938
|
-
onListenerError?: (type: string, error: Error) => void;
|
|
939
|
-
|
|
940
|
-
/**
|
|
941
|
-
* Callback after listeners are executed (debug mode only)
|
|
942
|
-
* @param message - Event message
|
|
943
|
-
* @param returns - Array of listener return values
|
|
944
|
-
* @param listeners - Array of executed listeners
|
|
945
|
-
*/
|
|
946
|
-
onExecuteListener?: (message: FastEventMessage, returns: any[], listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[]) => void;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
// Debug mode usage
|
|
950
|
-
import 'fastevent/devtools';
|
|
951
|
-
const emitter = new FastEvent({
|
|
952
|
-
debug: true, // Enable debug mode to view events in Redux DevTools
|
|
953
|
-
});
|
|
954
|
-
````
|
|
399
|
+
## License
|
|
955
400
|
|
|
956
|
-
|
|
401
|
+
MIT
|
|
957
402
|
|
|
958
|
-
|
|
403
|
+
For more detailed documentation, see [WebSite](https://zhangfisher.github.io/fastevent/)
|