Created
August 7, 2025 03:46
-
-
Save forivall/151da77abeeb5f8b8d1d00161eaa048a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface EventEmitterOn { | |
on(event: string, listener: (...args: any) => void): any; | |
} | |
type EventNames<T extends EventEmitterOn> = T extends { | |
on(event: infer N1, listener: any): any; | |
on(event: infer N2, listener: any): any; | |
on(event: infer N3, listener: any): any; | |
on(event: infer N4, listener: any): any; | |
on(event: infer N5, listener: any): any; | |
on(event: infer N6, listener: any): any; | |
on(event: infer N7, listener: any): any; | |
on(event: infer N8, listener: any): any; | |
on(event: infer N9, listener: any): any; | |
on(event: infer N0, listener: any): any; | |
} | |
? | |
| (string extends N1 ? never : N1) | |
| (string extends N2 ? never : N2) | |
| (string extends N3 ? never : N3) | |
| (string extends N4 ? never : N4) | |
| (string extends N5 ? never : N5) | |
| (string extends N6 ? never : N6) | |
| (string extends N7 ? never : N7) | |
| (string extends N8 ? never : N8) | |
| (string extends N9 ? never : N9) | |
| (string extends N0 ? never : N0) | |
: never; | |
type EventListener< | |
T extends EventEmitterOn, | |
N extends EventNames<T>, | |
> = T extends { | |
on(event: infer N1, listener: infer L1): any; | |
on(event: infer N2, listener: infer L2): any; | |
on(event: infer N3, listener: infer L3): any; | |
on(event: infer N4, listener: infer L4): any; | |
on(event: infer N5, listener: infer L5): any; | |
on(event: infer N6, listener: infer L6): any; | |
on(event: infer N7, listener: infer L7): any; | |
on(event: infer N8, listener: infer L8): any; | |
on(event: infer N9, listener: infer L9): any; | |
on(event: infer N0, listener: infer L0): any; | |
} | |
? N1 extends N | |
? L1 | |
: N2 extends N | |
? L2 | |
: N3 extends N | |
? L3 | |
: N4 extends N | |
? L4 | |
: N5 extends N | |
? L5 | |
: N6 extends N | |
? L6 | |
: N7 extends N | |
? L7 | |
: N8 extends N | |
? L8 | |
: N9 extends N | |
? L9 | |
: N0 extends N | |
? L0 | |
: never | |
: never; | |
type EventValue< | |
T extends { on(event: string, listener: (...args: any) => void): any }, | |
N extends EventNames<T>, | |
> = N extends never | |
? never | |
: { event: N; args: Parameters<EventListener<T, N>> }; | |
class EventsIterator<T extends EventEmitterOn, const N extends EventNames<T>> | |
implements AsyncIterableIterator<EventValue<T, N>, void, never> | |
{ | |
private readonly queue: Array<EventValue<T, N>> = []; | |
private resolve!: () => void; | |
private promise: Promise<void> = new Promise((resolve) => { | |
this.resolve = resolve; | |
}); | |
private finished: Promise<void>; | |
constructor( | |
private readonly emitter: T & Readable, | |
private readonly eventNames: N[], | |
) { | |
this.eventNames.forEach((event) => { | |
this.emitter.on(event, (...args: any) => { | |
this.queue.push({ event, args } as EventValue<T, N>); | |
this.emitter.pause(); | |
this.resolve(); | |
}); | |
}); | |
this.finished = finished(this.emitter); | |
} | |
[Symbol.asyncIterator]() { | |
return this; | |
} | |
async next() { | |
if (!this.queue.length) { | |
await Promise.race([this.promise, this.finished]); | |
this.promise = new Promise((resolve) => { | |
this.resolve = resolve; | |
}); | |
} | |
const value = this.queue.shift(); | |
if (!this.queue.length) { | |
this.emitter.resume(); | |
} | |
return { value, done: !value } as IteratorResult<EventValue<T, N>, void>; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment