emittery 1.2.0 → 2.0.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/index.d.ts +307 -153
- package/index.js +628 -144
- package/maps.js +1 -0
- package/package.json +13 -19
- package/readme.md +293 -122
package/index.d.ts
CHANGED
|
@@ -5,6 +5,31 @@ Symbol event names are preferred given that they can be used to avoid name colli
|
|
|
5
5
|
*/
|
|
6
6
|
export type EventName = PropertyKey;
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
The object passed to every event listener. Always includes `name`. Includes `data` only when the event was emitted with data.
|
|
10
|
+
|
|
11
|
+
@example
|
|
12
|
+
```
|
|
13
|
+
import Emittery from 'emittery';
|
|
14
|
+
|
|
15
|
+
const emitter = new Emittery<{unicorn: string; close: undefined}>();
|
|
16
|
+
|
|
17
|
+
emitter.on('unicorn', ({name, data}) => {
|
|
18
|
+
console.log(name); //=> 'unicorn'
|
|
19
|
+
console.log(data); //=> '🌈'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
emitter.on('close', ({name}) => {
|
|
23
|
+
console.log(name); //=> 'close'
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
*/
|
|
27
|
+
export type EmitteryEvent<Name extends EventName, Data> = [Data] extends [undefined]
|
|
28
|
+
? {readonly name: Name; readonly data?: undefined}
|
|
29
|
+
: {readonly name: Name; readonly data: Data};
|
|
30
|
+
|
|
31
|
+
type EventDataPair<EventData, Name extends keyof EventData> = Name extends keyof EventData ? EmitteryEvent<Name, EventData[Name]> : never;
|
|
32
|
+
|
|
8
33
|
// Helper type for turning the passed `EventData` type map into a list of string keys that don't require data alongside the event name when emitting. Uses the same trick that `Omit` does internally to filter keys by building a map of keys to keys we want to keep, and then accessing all the keys to return just the list of keys we want to keep.
|
|
9
34
|
type DatalessEventNames<EventData> = {
|
|
10
35
|
[Key in keyof EventData]: EventData[Key] extends undefined ? Key : never;
|
|
@@ -21,7 +46,7 @@ To enable this feature set the `DEBUG` environment variable to `emittery` or `*`
|
|
|
21
46
|
|
|
22
47
|
See API for more information on how debugging works.
|
|
23
48
|
*/
|
|
24
|
-
export type DebugLogger<EventData, Name extends keyof EventData> = (type: string, debugName
|
|
49
|
+
export type DebugLogger<EventData, Name extends keyof EventData> = (type: string, debugName?: string, eventName?: Name, eventData?: EventData[Name]) => void;
|
|
25
50
|
|
|
26
51
|
/**
|
|
27
52
|
Configure debug options of an instance.
|
|
@@ -40,7 +65,7 @@ export type DebugOptions<EventData> = {
|
|
|
40
65
|
|
|
41
66
|
const emitter = new Emittery({debug: {name: 'myEmitter'}});
|
|
42
67
|
|
|
43
|
-
emitter.on('test',
|
|
68
|
+
emitter.on('test', () => {
|
|
44
69
|
// …
|
|
45
70
|
});
|
|
46
71
|
|
|
@@ -49,7 +74,7 @@ export type DebugOptions<EventData> = {
|
|
|
49
74
|
// data: undefined
|
|
50
75
|
```
|
|
51
76
|
*/
|
|
52
|
-
readonly name
|
|
77
|
+
readonly name?: string;
|
|
53
78
|
|
|
54
79
|
/**
|
|
55
80
|
Toggle debug logging just for this instance.
|
|
@@ -63,11 +88,11 @@ export type DebugOptions<EventData> = {
|
|
|
63
88
|
const emitter1 = new Emittery({debug: {name: 'emitter1', enabled: true}});
|
|
64
89
|
const emitter2 = new Emittery({debug: {name: 'emitter2'}});
|
|
65
90
|
|
|
66
|
-
emitter1.on('test',
|
|
91
|
+
emitter1.on('test', () => {
|
|
67
92
|
// …
|
|
68
93
|
});
|
|
69
94
|
|
|
70
|
-
emitter2.on('test',
|
|
95
|
+
emitter2.on('test', () => {
|
|
71
96
|
// …
|
|
72
97
|
});
|
|
73
98
|
|
|
@@ -86,7 +111,11 @@ export type DebugOptions<EventData> = {
|
|
|
86
111
|
@default
|
|
87
112
|
```
|
|
88
113
|
(type, debugName, eventName, eventData) => {
|
|
89
|
-
|
|
114
|
+
try {
|
|
115
|
+
eventData = JSON.stringify(eventData);
|
|
116
|
+
} catch {
|
|
117
|
+
eventData = `Object with the following keys failed to stringify: ${Object.keys(eventData).join(',')}`;
|
|
118
|
+
}
|
|
90
119
|
|
|
91
120
|
if (typeof eventName === 'symbol' || typeof eventName === 'number') {
|
|
92
121
|
eventName = eventName.toString();
|
|
@@ -114,7 +143,7 @@ export type DebugOptions<EventData> = {
|
|
|
114
143
|
}
|
|
115
144
|
});
|
|
116
145
|
|
|
117
|
-
emitter.on('test',
|
|
146
|
+
emitter.on('test', () => {
|
|
118
147
|
// …
|
|
119
148
|
});
|
|
120
149
|
|
|
@@ -141,8 +170,24 @@ export type EmitteryOncePromise<T> = {
|
|
|
141
170
|
|
|
142
171
|
/**
|
|
143
172
|
Removes an event subscription.
|
|
173
|
+
|
|
174
|
+
Can be used with the `using` keyword for automatic cleanup:
|
|
175
|
+
|
|
176
|
+
@example
|
|
177
|
+
```
|
|
178
|
+
import Emittery from 'emittery';
|
|
179
|
+
|
|
180
|
+
const emitter = new Emittery();
|
|
181
|
+
|
|
182
|
+
{
|
|
183
|
+
using off = emitter.on('event', ({data}) => {
|
|
184
|
+
console.log(data);
|
|
185
|
+
});
|
|
186
|
+
// auto-unsubscribes when leaving scope
|
|
187
|
+
}
|
|
188
|
+
```
|
|
144
189
|
*/
|
|
145
|
-
export type UnsubscribeFunction = () => void;
|
|
190
|
+
export type UnsubscribeFunction = (() => void) & Disposable;
|
|
146
191
|
|
|
147
192
|
/**
|
|
148
193
|
The data provided as `eventData` when listening for `Emittery.listenerAdded` or `Emittery.listenerRemoved`.
|
|
@@ -151,7 +196,7 @@ export type ListenerChangedData = {
|
|
|
151
196
|
/**
|
|
152
197
|
The listener that was added or removed.
|
|
153
198
|
*/
|
|
154
|
-
listener: (
|
|
199
|
+
listener: (event: unknown) => (void | Promise<void>);
|
|
155
200
|
|
|
156
201
|
/**
|
|
157
202
|
The name of the event that was added or removed if `.on()` or `.off()` was used, or `undefined` if `.onAny()` or `.offAny()` was used.
|
|
@@ -191,8 +236,7 @@ emitter.emit('other');
|
|
|
191
236
|
```
|
|
192
237
|
*/
|
|
193
238
|
export default class Emittery<
|
|
194
|
-
EventData = Record<EventName,
|
|
195
|
-
AllEventData = EventData & OmnipresentEventData,
|
|
239
|
+
EventData = Record<EventName, unknown>, AllEventData = EventData & OmnipresentEventData,
|
|
196
240
|
DatalessEvents = DatalessEventNames<EventData>,
|
|
197
241
|
> {
|
|
198
242
|
/**
|
|
@@ -209,11 +253,11 @@ export default class Emittery<
|
|
|
209
253
|
const emitter1 = new Emittery({debug: {name: 'myEmitter1'}});
|
|
210
254
|
const emitter2 = new Emittery({debug: {name: 'myEmitter2'}});
|
|
211
255
|
|
|
212
|
-
emitter1.on('test',
|
|
256
|
+
emitter1.on('test', () => {
|
|
213
257
|
// …
|
|
214
258
|
});
|
|
215
259
|
|
|
216
|
-
emitter2.on('otherTest',
|
|
260
|
+
emitter2.on('otherTest', () => {
|
|
217
261
|
// …
|
|
218
262
|
});
|
|
219
263
|
|
|
@@ -239,15 +283,15 @@ export default class Emittery<
|
|
|
239
283
|
|
|
240
284
|
const emitter = new Emittery();
|
|
241
285
|
|
|
242
|
-
emitter.on(Emittery.listenerAdded, ({listener, eventName}) => {
|
|
286
|
+
emitter.on(Emittery.listenerAdded, ({data: {listener, eventName}}) => {
|
|
243
287
|
console.log(listener);
|
|
244
|
-
//=> data => {}
|
|
288
|
+
//=> ({data}) => {}
|
|
245
289
|
|
|
246
290
|
console.log(eventName);
|
|
247
291
|
//=> '🦄'
|
|
248
292
|
});
|
|
249
293
|
|
|
250
|
-
emitter.on('🦄', data => {
|
|
294
|
+
emitter.on('🦄', ({data}) => {
|
|
251
295
|
// Handle data
|
|
252
296
|
});
|
|
253
297
|
```
|
|
@@ -265,13 +309,13 @@ export default class Emittery<
|
|
|
265
309
|
|
|
266
310
|
const emitter = new Emittery();
|
|
267
311
|
|
|
268
|
-
const off = emitter.on('🦄', data => {
|
|
312
|
+
const off = emitter.on('🦄', ({data}) => {
|
|
269
313
|
// Handle data
|
|
270
314
|
});
|
|
271
315
|
|
|
272
|
-
emitter.on(Emittery.listenerRemoved, ({listener, eventName}) => {
|
|
316
|
+
emitter.on(Emittery.listenerRemoved, ({data: {listener, eventName}}) => {
|
|
273
317
|
console.log(listener);
|
|
274
|
-
//=> data => {}
|
|
318
|
+
//=> ({data}) => {}
|
|
275
319
|
|
|
276
320
|
console.log(eventName);
|
|
277
321
|
//=> '🦄'
|
|
@@ -300,7 +344,7 @@ export default class Emittery<
|
|
|
300
344
|
static mixin(
|
|
301
345
|
emitteryPropertyName: string | symbol,
|
|
302
346
|
methodNames?: readonly string[]
|
|
303
|
-
): <T extends
|
|
347
|
+
): <T extends abstract new (...arguments_: readonly any[]) => any>(klass: T, context?: ClassDecoratorContext<T>) => T;
|
|
304
348
|
|
|
305
349
|
/**
|
|
306
350
|
Debugging options for the current instance.
|
|
@@ -319,7 +363,7 @@ export default class Emittery<
|
|
|
319
363
|
|
|
320
364
|
Using the same listener multiple times for the same event will result in only one method call per emitted event.
|
|
321
365
|
|
|
322
|
-
@returns An unsubscribe method.
|
|
366
|
+
@returns An unsubscribe method, which is also {@link Disposable} (can be used with `using`).
|
|
323
367
|
|
|
324
368
|
@example
|
|
325
369
|
```
|
|
@@ -327,110 +371,92 @@ export default class Emittery<
|
|
|
327
371
|
|
|
328
372
|
const emitter = new Emittery();
|
|
329
373
|
|
|
330
|
-
emitter.on('🦄', data => {
|
|
374
|
+
emitter.on('🦄', ({data}) => {
|
|
331
375
|
console.log(data);
|
|
332
376
|
});
|
|
333
377
|
|
|
334
|
-
emitter.on(['🦄', '🐶'], data => {
|
|
335
|
-
console.log(data);
|
|
378
|
+
emitter.on(['🦄', '🐶'], ({name, data}) => {
|
|
379
|
+
console.log(name, data);
|
|
336
380
|
});
|
|
337
381
|
|
|
338
|
-
emitter.emit('🦄', '🌈'); // log => '🌈'
|
|
339
|
-
emitter.emit('🐶', '🍖'); // log => '🍖'
|
|
382
|
+
emitter.emit('🦄', '🌈'); // log => '🌈' and '🦄 🌈'
|
|
383
|
+
emitter.emit('🐶', '🍖'); // log => '🐶 🍖'
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
@example
|
|
387
|
+
```
|
|
388
|
+
// With AbortSignal
|
|
389
|
+
const abortController = new AbortController();
|
|
390
|
+
|
|
391
|
+
emitter.on('🐗', ({data}) => {
|
|
392
|
+
console.log(data);
|
|
393
|
+
}, {signal: abortController.signal});
|
|
394
|
+
|
|
395
|
+
abortController.abort();
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
@example
|
|
399
|
+
```
|
|
400
|
+
// With `using` for automatic cleanup
|
|
401
|
+
{
|
|
402
|
+
using off = emitter.on('🦄', ({data}) => {
|
|
403
|
+
console.log(data);
|
|
404
|
+
});
|
|
405
|
+
await emitter.emit('🦄', '🌈'); // Logs '🌈'
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
await emitter.emit('🦄', '🌈'); // Nothing happens
|
|
340
409
|
```
|
|
341
410
|
*/
|
|
342
411
|
on<Name extends keyof AllEventData>(
|
|
343
412
|
eventName: Name | readonly Name[],
|
|
344
|
-
listener: (
|
|
413
|
+
listener: (event: EventDataPair<AllEventData, Name>) => void | Promise<void>,
|
|
345
414
|
options?: {signal?: AbortSignal}
|
|
346
415
|
): UnsubscribeFunction;
|
|
347
416
|
|
|
348
417
|
/**
|
|
349
418
|
Get an async iterator which buffers data each time an event is emitted.
|
|
350
419
|
|
|
351
|
-
Call `return()` on the iterator to remove the subscription.
|
|
420
|
+
Call `return()` on the iterator to remove the subscription. You can also pass an {@link AbortSignal} to cancel the subscription externally, or use `await using` for automatic cleanup.
|
|
352
421
|
|
|
353
422
|
@example
|
|
354
423
|
```
|
|
355
424
|
import Emittery from 'emittery';
|
|
356
425
|
|
|
357
426
|
const emitter = new Emittery();
|
|
358
|
-
const iterator = emitter.events('🦄');
|
|
359
|
-
|
|
360
|
-
emitter.emit('🦄', '🌈1'); // Buffered
|
|
361
|
-
emitter.emit('🦄', '🌈2'); // Buffered
|
|
362
|
-
|
|
363
|
-
iterator
|
|
364
|
-
.next()
|
|
365
|
-
.then(({value, done}) => {
|
|
366
|
-
// done === false
|
|
367
|
-
// value === '🌈1'
|
|
368
|
-
return iterator.next();
|
|
369
|
-
})
|
|
370
|
-
.then(({value, done}) => {
|
|
371
|
-
// done === false
|
|
372
|
-
// value === '🌈2'
|
|
373
|
-
// Revoke subscription
|
|
374
|
-
return iterator.return();
|
|
375
|
-
})
|
|
376
|
-
.then(({done}) => {
|
|
377
|
-
// done === true
|
|
378
|
-
});
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
In practice you would usually consume the events using the [for await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) statement. In that case, to revoke the subscription simply break the loop.
|
|
382
427
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
import Emittery from 'emittery';
|
|
386
|
-
|
|
387
|
-
const emitter = new Emittery();
|
|
388
|
-
const iterator = emitter.events('🦄');
|
|
389
|
-
|
|
390
|
-
emitter.emit('🦄', '🌈1'); // Buffered
|
|
391
|
-
emitter.emit('🦄', '🌈2'); // Buffered
|
|
428
|
+
for await (const {data} of emitter.events('🦄')) {
|
|
429
|
+
console.log(data);
|
|
392
430
|
|
|
393
|
-
// In an async context.
|
|
394
|
-
for await (const data of iterator) {
|
|
395
431
|
if (data === '🌈2') {
|
|
396
|
-
break; // Revoke the subscription when we see the value
|
|
432
|
+
break; // Revoke the subscription when we see the value '🌈2'.
|
|
397
433
|
}
|
|
398
434
|
}
|
|
399
435
|
```
|
|
400
436
|
|
|
401
|
-
It accepts multiple event names.
|
|
402
|
-
|
|
403
437
|
@example
|
|
404
438
|
```
|
|
405
|
-
|
|
439
|
+
// With multiple event names
|
|
440
|
+
for await (const {name, data} of emitter.events(['🦄', '🦊'])) {
|
|
441
|
+
console.log(name, data);
|
|
442
|
+
}
|
|
443
|
+
```
|
|
406
444
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
// done === false
|
|
417
|
-
// value === '🌈1'
|
|
418
|
-
return iterator.next();
|
|
419
|
-
})
|
|
420
|
-
.then(({value, done}) => {
|
|
421
|
-
// done === false
|
|
422
|
-
// value === '🌈2'
|
|
423
|
-
// Revoke subscription
|
|
424
|
-
return iterator.return();
|
|
425
|
-
})
|
|
426
|
-
.then(({done}) => {
|
|
427
|
-
// done === true
|
|
428
|
-
});
|
|
445
|
+
@example
|
|
446
|
+
```
|
|
447
|
+
// With `await using` for automatic cleanup
|
|
448
|
+
{
|
|
449
|
+
await using iterator = emitter.events('🦄');
|
|
450
|
+
for await (const {data} of iterator) {
|
|
451
|
+
console.log(data);
|
|
452
|
+
}
|
|
453
|
+
} // Subscription is automatically revoked
|
|
429
454
|
```
|
|
430
455
|
*/
|
|
431
456
|
events<Name extends keyof EventData>(
|
|
432
|
-
eventName: Name | readonly Name[]
|
|
433
|
-
|
|
457
|
+
eventName: Name | readonly Name[],
|
|
458
|
+
options?: {signal?: AbortSignal}
|
|
459
|
+
): AsyncIterableIterator<EventDataPair<EventData, Name>> & AsyncDisposable;
|
|
434
460
|
|
|
435
461
|
/**
|
|
436
462
|
Remove one or more event subscriptions.
|
|
@@ -441,7 +467,7 @@ export default class Emittery<
|
|
|
441
467
|
|
|
442
468
|
const emitter = new Emittery();
|
|
443
469
|
|
|
444
|
-
const listener = data => {
|
|
470
|
+
const listener = ({data}) => {
|
|
445
471
|
console.log(data);
|
|
446
472
|
};
|
|
447
473
|
|
|
@@ -451,23 +477,22 @@ export default class Emittery<
|
|
|
451
477
|
await emitter.emit('🦊', 'c');
|
|
452
478
|
emitter.off('🦄', listener);
|
|
453
479
|
emitter.off(['🐶', '🦊'], listener);
|
|
454
|
-
await emitter.emit('🦄', 'a'); //
|
|
455
|
-
await emitter.emit('🐶', 'b'); //
|
|
456
|
-
await emitter.emit('🦊', 'c'); //
|
|
480
|
+
await emitter.emit('🦄', 'a'); // Nothing happens
|
|
481
|
+
await emitter.emit('🐶', 'b'); // Nothing happens
|
|
482
|
+
await emitter.emit('🦊', 'c'); // Nothing happens
|
|
457
483
|
```
|
|
458
484
|
*/
|
|
459
485
|
off<Name extends keyof AllEventData>(
|
|
460
486
|
eventName: Name | readonly Name[],
|
|
461
|
-
listener: (
|
|
487
|
+
listener: (event: EventDataPair<AllEventData, Name>) => void | Promise<void>
|
|
462
488
|
): void;
|
|
463
489
|
|
|
464
490
|
/**
|
|
465
491
|
Subscribe to one or more events only once. It will be unsubscribed after the first event that matches the predicate (if provided).
|
|
466
492
|
|
|
467
|
-
|
|
468
|
-
@param predicate - Optional predicate function to filter event data. The event will only be emitted if the predicate returns true.
|
|
493
|
+
The second argument can be a predicate function or an options object with `predicate` and/or `signal`.
|
|
469
494
|
|
|
470
|
-
@returns The promise of event data when `eventName` is emitted and predicate matches (if provided).
|
|
495
|
+
@returns The promise of event data when `eventName` is emitted and predicate matches (if provided). The promise has an `off` method to cancel the subscription.
|
|
471
496
|
|
|
472
497
|
@example
|
|
473
498
|
```
|
|
@@ -475,33 +500,55 @@ export default class Emittery<
|
|
|
475
500
|
|
|
476
501
|
const emitter = new Emittery();
|
|
477
502
|
|
|
478
|
-
emitter.once('🦄')
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
503
|
+
const {data} = await emitter.once('🦄');
|
|
504
|
+
console.log(data);
|
|
505
|
+
//=> '🌈'
|
|
506
|
+
```
|
|
482
507
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
508
|
+
@example
|
|
509
|
+
```
|
|
510
|
+
// With multiple event names
|
|
511
|
+
const {name, data} = await emitter.once(['🦄', '🐶']);
|
|
512
|
+
console.log(name, data);
|
|
513
|
+
```
|
|
486
514
|
|
|
515
|
+
@example
|
|
516
|
+
```
|
|
487
517
|
// With predicate
|
|
488
|
-
emitter.once('data', data => data.ok === true)
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
518
|
+
const event = await emitter.once('data', ({data}) => data.ok === true);
|
|
519
|
+
console.log(event.data);
|
|
520
|
+
//=> {ok: true, value: 42}
|
|
521
|
+
```
|
|
492
522
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
emitter.
|
|
523
|
+
@example
|
|
524
|
+
```
|
|
525
|
+
// With AbortSignal for timeout
|
|
526
|
+
await emitter.once('ready', {signal: AbortSignal.timeout(5000)});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
@example
|
|
530
|
+
```
|
|
531
|
+
// Cancel with .off()
|
|
532
|
+
const promise = emitter.once('🦄');
|
|
533
|
+
promise.off();
|
|
497
534
|
```
|
|
498
535
|
*/
|
|
499
|
-
once<Name extends keyof AllEventData>(
|
|
536
|
+
once<Name extends keyof AllEventData>(
|
|
537
|
+
eventName: Name | readonly Name[],
|
|
538
|
+
predicate?: (event: EventDataPair<AllEventData, Name>) => boolean
|
|
539
|
+
): EmitteryOncePromise<EventDataPair<AllEventData, Name>>;
|
|
540
|
+
once<Name extends keyof AllEventData>(
|
|
541
|
+
eventName: Name | readonly Name[],
|
|
542
|
+
options?: {
|
|
543
|
+
predicate?: (event: EventDataPair<AllEventData, Name>) => boolean;
|
|
544
|
+
signal?: AbortSignal;
|
|
545
|
+
}
|
|
546
|
+
): EmitteryOncePromise<EventDataPair<AllEventData, Name>>;
|
|
500
547
|
|
|
501
548
|
/**
|
|
502
549
|
Trigger an event asynchronously, optionally with some data. Listeners are called in the order they were added, but executed concurrently.
|
|
503
550
|
|
|
504
|
-
@returns A promise that resolves when all the event listeners are done. *Done* meaning executed if synchronous or resolved when an async/promise-returning function. You usually wouldn't want to wait for this, but you could for example catch possible errors. If any
|
|
551
|
+
@returns A promise that resolves when all the event listeners are done. *Done* meaning executed if synchronous or resolved when an async/promise-returning function. You usually wouldn't want to wait for this, but you could for example catch possible errors. If any listeners throw/reject, the returned promise rejects with an `AggregateError` — all listener errors are collected in `error.errors`, so no errors are silently lost. All listeners always run to completion, even if some throw or reject.
|
|
505
552
|
*/
|
|
506
553
|
emit<Name extends DatalessEvents>(eventName: Name): Promise<void>;
|
|
507
554
|
emit<Name extends keyof EventData>(
|
|
@@ -515,6 +562,25 @@ export default class Emittery<
|
|
|
515
562
|
If any of the listeners throw/reject, the returned promise will be rejected with the error and the remaining listeners will *not* be called.
|
|
516
563
|
|
|
517
564
|
@returns A promise that resolves when all the event listeners are done.
|
|
565
|
+
|
|
566
|
+
@example
|
|
567
|
+
```
|
|
568
|
+
import Emittery from 'emittery';
|
|
569
|
+
|
|
570
|
+
const emitter = new Emittery();
|
|
571
|
+
|
|
572
|
+
emitter.on('🦄', async () => {
|
|
573
|
+
console.log('listener 1 start');
|
|
574
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
575
|
+
console.log('listener 1 done');
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
emitter.on('🦄', () => {
|
|
579
|
+
console.log('listener 2'); // Only runs after listener 1 is done
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
await emitter.emitSerial('🦄');
|
|
583
|
+
```
|
|
518
584
|
*/
|
|
519
585
|
emitSerial<Name extends DatalessEvents>(eventName: Name): Promise<void>;
|
|
520
586
|
emitSerial<Name extends keyof EventData>(
|
|
@@ -525,76 +591,164 @@ export default class Emittery<
|
|
|
525
591
|
/**
|
|
526
592
|
Subscribe to be notified about any event.
|
|
527
593
|
|
|
528
|
-
@returns A method to unsubscribe.
|
|
594
|
+
@returns A method to unsubscribe, which is also {@link Disposable}.
|
|
595
|
+
|
|
596
|
+
@example
|
|
597
|
+
```
|
|
598
|
+
import Emittery from 'emittery';
|
|
599
|
+
|
|
600
|
+
const emitter = new Emittery();
|
|
601
|
+
|
|
602
|
+
const off = emitter.onAny(({name, data}) => {
|
|
603
|
+
console.log(name, data);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
emitter.emit('🦄', '🌈'); // log => '🦄 🌈'
|
|
607
|
+
|
|
608
|
+
off();
|
|
609
|
+
```
|
|
529
610
|
*/
|
|
530
611
|
onAny(
|
|
531
|
-
listener: (
|
|
532
|
-
eventName: keyof EventData,
|
|
533
|
-
eventData: EventData[keyof EventData]
|
|
534
|
-
) => void | Promise<void>,
|
|
612
|
+
listener: (event: EventDataPair<EventData, keyof EventData>) => void | Promise<void>,
|
|
535
613
|
options?: {signal?: AbortSignal}
|
|
536
614
|
): UnsubscribeFunction;
|
|
537
615
|
|
|
538
616
|
/**
|
|
539
|
-
Get an async iterator which buffers
|
|
540
|
-
|
|
541
|
-
Call `return()` on the iterator to remove the subscription.
|
|
617
|
+
Get an async iterator which buffers an event object each time an event is emitted.
|
|
542
618
|
|
|
543
|
-
|
|
619
|
+
Call `return()` on the iterator to remove the subscription. You can also pass an {@link AbortSignal} to cancel the subscription externally, or use `await using` for automatic cleanup.
|
|
544
620
|
|
|
545
621
|
@example
|
|
546
622
|
```
|
|
547
623
|
import Emittery from 'emittery';
|
|
548
624
|
|
|
549
625
|
const emitter = new Emittery();
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
return iterator.return();
|
|
566
|
-
})
|
|
567
|
-
.then(({done}) => {
|
|
568
|
-
// done is true
|
|
569
|
-
});
|
|
626
|
+
|
|
627
|
+
for await (const {name, data} of emitter.anyEvent()) {
|
|
628
|
+
console.log(name, data);
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
@example
|
|
633
|
+
```
|
|
634
|
+
// With `await using` for automatic cleanup
|
|
635
|
+
{
|
|
636
|
+
await using iterator = emitter.anyEvent();
|
|
637
|
+
for await (const {name, data} of iterator) {
|
|
638
|
+
console.log(name, data);
|
|
639
|
+
}
|
|
640
|
+
} // Subscription is automatically revoked
|
|
570
641
|
```
|
|
571
642
|
*/
|
|
572
|
-
anyEvent(): AsyncIterableIterator<
|
|
573
|
-
[keyof EventData, EventData[keyof EventData]]
|
|
574
|
-
>;
|
|
643
|
+
anyEvent(options?: {signal?: AbortSignal}): AsyncIterableIterator<EventDataPair<EventData, keyof EventData>> & AsyncDisposable;
|
|
575
644
|
|
|
576
645
|
/**
|
|
577
646
|
Remove an `onAny` subscription.
|
|
578
647
|
*/
|
|
579
648
|
offAny(
|
|
580
|
-
listener: (
|
|
581
|
-
eventName: keyof EventData,
|
|
582
|
-
eventData: EventData[keyof EventData]
|
|
583
|
-
) => void | Promise<void>
|
|
649
|
+
listener: (event: EventDataPair<EventData, keyof EventData>) => void | Promise<void>
|
|
584
650
|
): void;
|
|
585
651
|
|
|
586
652
|
/**
|
|
587
653
|
Clear all event listeners on the instance.
|
|
588
654
|
|
|
589
|
-
If `
|
|
655
|
+
If `eventNames` is given, only the listeners for those events are cleared. Accepts a single event name or an array.
|
|
656
|
+
|
|
657
|
+
@example
|
|
658
|
+
```
|
|
659
|
+
import Emittery from 'emittery';
|
|
660
|
+
|
|
661
|
+
const emitter = new Emittery();
|
|
662
|
+
|
|
663
|
+
emitter.on('🦄', listener);
|
|
664
|
+
emitter.on('🐶', listener);
|
|
665
|
+
|
|
666
|
+
emitter.clearListeners('🦄'); // Clear a single event
|
|
667
|
+
emitter.clearListeners(['🐶', '🦊']); // Clear multiple events
|
|
668
|
+
emitter.clearListeners(); // Clear all events
|
|
669
|
+
```
|
|
590
670
|
*/
|
|
591
671
|
clearListeners<Name extends keyof EventData>(eventName?: Name | readonly Name[]): void;
|
|
592
672
|
|
|
673
|
+
/**
|
|
674
|
+
Register a function to be called when the first `.on()` listener subscribes to `eventName`. The `initFn` can optionally return a cleanup (deinit) function, which is called when the last `.on()` listener unsubscribes (or when `clearListeners()` removes all listeners for that event).
|
|
675
|
+
|
|
676
|
+
If `.on()` listeners already exist when `init()` is called, `initFn` is called immediately.
|
|
677
|
+
|
|
678
|
+
Note: Lifecycle hooks only apply to `.on()` listeners. Subscriptions via `.events()` async iterators do not trigger the init or deinit functions.
|
|
679
|
+
|
|
680
|
+
@returns An unsubscribe function. Calling it removes the init/deinit hooks, and if the init is currently active, it calls deinit immediately.
|
|
681
|
+
|
|
682
|
+
@example
|
|
683
|
+
```
|
|
684
|
+
import Emittery from 'emittery';
|
|
685
|
+
|
|
686
|
+
const emitter = new Emittery();
|
|
687
|
+
|
|
688
|
+
emitter.init('mouse', () => {
|
|
689
|
+
terminal.grabInput({mouse: 'button'});
|
|
690
|
+
|
|
691
|
+
terminal.on('mouse', (name, data) => {
|
|
692
|
+
emitter.emit('mouse', data);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
return () => {
|
|
696
|
+
terminal.releaseInput();
|
|
697
|
+
};
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// Init is called when the first listener subscribes
|
|
701
|
+
const off = emitter.on('mouse', handler);
|
|
702
|
+
|
|
703
|
+
// Adding more listeners does not call init again
|
|
704
|
+
emitter.on('mouse', anotherHandler);
|
|
705
|
+
|
|
706
|
+
// Removing one listener does not call deinit yet
|
|
707
|
+
off();
|
|
708
|
+
|
|
709
|
+
// Deinit is called when the last listener unsubscribes
|
|
710
|
+
emitter.off('mouse', anotherHandler);
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
@example
|
|
714
|
+
```
|
|
715
|
+
// With `using` for automatic cleanup of hooks
|
|
716
|
+
{
|
|
717
|
+
using removeInit = emitter.init('mouse', () => {
|
|
718
|
+
startListening();
|
|
719
|
+
return () => stopListening();
|
|
720
|
+
});
|
|
721
|
+
} // init/deinit hooks are automatically removed
|
|
722
|
+
```
|
|
723
|
+
*/
|
|
724
|
+
init<Name extends keyof EventData>(
|
|
725
|
+
eventName: Name,
|
|
726
|
+
initFn: () => (() => void) | void
|
|
727
|
+
): UnsubscribeFunction;
|
|
728
|
+
|
|
593
729
|
/**
|
|
594
730
|
The number of listeners for the `eventName` or all events if not specified.
|
|
731
|
+
|
|
732
|
+
@example
|
|
733
|
+
```
|
|
734
|
+
import Emittery from 'emittery';
|
|
735
|
+
|
|
736
|
+
const emitter = new Emittery();
|
|
737
|
+
|
|
738
|
+
emitter.on('🦄', listener);
|
|
739
|
+
emitter.on('🐶', listener);
|
|
740
|
+
|
|
741
|
+
emitter.listenerCount('🦄'); // 1
|
|
742
|
+
emitter.listenerCount(); // 2
|
|
743
|
+
```
|
|
595
744
|
*/
|
|
596
745
|
listenerCount<Name extends keyof EventData>(eventName?: Name | readonly Name[]): number;
|
|
597
746
|
|
|
747
|
+
/**
|
|
748
|
+
Log debug information if debug mode is enabled (either globally via `Emittery.isDebugEnabled` or per-instance via `debug.enabled`).
|
|
749
|
+
*/
|
|
750
|
+
logIfDebugEnabled<Name extends keyof EventData>(type: string, eventName?: Name, eventData?: EventData[Name]): void;
|
|
751
|
+
|
|
598
752
|
/**
|
|
599
753
|
Bind the given `methodNames`, or all `Emittery` methods if `methodNames` is not defined, into the `target` object.
|
|
600
754
|
|