evnty 2.0.0 → 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/build/index.cjs +22 -11
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +23 -21
- package/build/index.js +22 -11
- package/build/index.js.map +1 -1
- package/package.json +12 -13
- package/src/index.ts +44 -33
- package/src/__tests__/index.ts +0 -383
package/build/index.cjs
CHANGED
|
@@ -41,16 +41,24 @@ class Dismiss extends FunctionExt {
|
|
|
41
41
|
constructor(callback){
|
|
42
42
|
super(callback);
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
pre(callback) {
|
|
45
|
+
return new Dismiss(async ()=>{
|
|
46
|
+
await callback();
|
|
47
|
+
await this();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
post(callback) {
|
|
51
|
+
return new Dismiss(async ()=>{
|
|
52
|
+
await this();
|
|
53
|
+
await callback();
|
|
54
|
+
});
|
|
47
55
|
}
|
|
48
|
-
|
|
49
|
-
return async ()=>{
|
|
56
|
+
countdown(count) {
|
|
57
|
+
return new Dismiss(async ()=>{
|
|
50
58
|
if (!--count) {
|
|
51
59
|
await this();
|
|
52
60
|
}
|
|
53
|
-
};
|
|
61
|
+
});
|
|
54
62
|
}
|
|
55
63
|
}
|
|
56
64
|
const eventEmitter = (listeners, event)=>Promise.all(listeners.map((listener)=>listener(event)));
|
|
@@ -62,9 +70,9 @@ class Event extends FunctionExt {
|
|
|
62
70
|
const fn = (event)=>eventEmitter(listeners, event);
|
|
63
71
|
super(fn);
|
|
64
72
|
this.listeners = listeners;
|
|
65
|
-
this.dispose = ()=>{
|
|
73
|
+
this.dispose = async ()=>{
|
|
66
74
|
this.clear();
|
|
67
|
-
dispose?.();
|
|
75
|
+
await dispose?.();
|
|
68
76
|
};
|
|
69
77
|
}
|
|
70
78
|
get size() {
|
|
@@ -94,12 +102,12 @@ class Event extends FunctionExt {
|
|
|
94
102
|
};
|
|
95
103
|
return this.on(oneTimeListener);
|
|
96
104
|
}
|
|
97
|
-
clear() {
|
|
98
|
-
this.listeners.splice(0);
|
|
99
|
-
}
|
|
100
105
|
onceAsync() {
|
|
101
106
|
return new Promise((resolve)=>this.once((event)=>resolve(event)));
|
|
102
107
|
}
|
|
108
|
+
clear() {
|
|
109
|
+
this.listeners.splice(0);
|
|
110
|
+
}
|
|
103
111
|
filter(filter) {
|
|
104
112
|
const dispose = this.on(async (event)=>{
|
|
105
113
|
if (filteredEvent.size > 0 && await filter(event)) {
|
|
@@ -119,6 +127,9 @@ class Event extends FunctionExt {
|
|
|
119
127
|
const filteredEvent = new Event(dispose);
|
|
120
128
|
return filteredEvent;
|
|
121
129
|
}
|
|
130
|
+
firstAsync(filter) {
|
|
131
|
+
return this.first(filter).onceAsync();
|
|
132
|
+
}
|
|
122
133
|
map(mapper) {
|
|
123
134
|
const dispose = this.on(async (event)=>{
|
|
124
135
|
if (mappedEvent.size > 0) {
|
package/build/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;\n\nexport interface Unsubscribe {\n (): void;\n}\n\nexport interface Listener<T, R = unknown> {\n (event: T): MaybePromise<R | void>;\n}\n\nexport interface Dispose {\n (): void;\n}\n\nexport interface Result<T, E> {\n ok: boolean;\n result: T | E;\n}\n\nexport interface Resolver<T, P> {\n (event: T): Promise<P>;\n}\n\nexport interface FilterFunction<T> {\n (event: T): MaybePromise<boolean>;\n}\n\nexport interface Predicate<T, P extends T> {\n (event: T): event is P;\n}\n\nexport type Filter<T, P extends T> = Predicate<T, P> | FilterFunction<T>;\n\nexport interface Mapper<T, R> {\n (event: T): MaybePromise<R>;\n}\n\nexport interface Reducer<T, R> {\n (result: R, event: T): MaybePromise<R>;\n}\n\nexport type Listeners<T, R> = Listener<T, R>[];\n\n/**\n * An abstract class that extends the built-in Function class. It allows instances of the class\n * to be called as functions. When an instance of FunctionExt is called as a function, it will\n * call the function passed to its constructor with the same arguments.\n * @internal\n */\nexport abstract class FunctionExt extends Function {\n constructor(func: Function) {\n super();\n return Object.setPrototypeOf(func, new.target.prototype);\n }\n}\n\nexport interface Dismiss {\n (): Promise<void> | void;\n}\n\nexport interface Task {\n (): MaybePromise<unknown>;\n}\n\n/**\n * @internal\n */\nexport class Dismiss extends FunctionExt {\n constructor(callback: Unsubscribe) {\n super(callback);\n }\n\n async after(task: Task): Promise<void> {\n await task();\n await this();\n }\n\n afterTimes(count: number): () => Promise<void> {\n return async () => {\n if (!--count) {\n await this();\n }\n };\n }\n}\n\nconst eventEmitter = <A, R>(listeners: Listeners<A, R>, event: A): Promise<(void | R)[]> => Promise.all(listeners.map((listener) => listener(event)));\n\ntype EventType<T> = T extends undefined ? void : T;\n\nexport interface Event<T = unknown, R = void> {\n (event?: EventType<T>): Promise<(R | undefined)[]>;\n}\n\nexport type EventParameters<T> = T extends Event<infer P, unknown> ? P : never;\n\nexport type EventResult<T> = T extends Event<unknown, infer R> ? R : never;\n\nexport type AllEventsParameters<T extends Event<unknown, unknown>[]> = { [K in keyof T]: EventParameters<T[K]> }[number];\n\nexport type AllEventsResults<T extends Event<unknown, unknown>[]> = { [K in keyof T]: EventResult<T[K]> }[number];\n\n/**\n * A class representing an anonymous event that can be listened to or triggered.\n *\n * @typeParam T - The tuple of arguments that the event takes.\n * @typeParam R - The return type of the event.\n */\nexport class Event<T, R> extends FunctionExt {\n /**\n * The array of listeners for the event.\n */\n private listeners: Listeners<T, R>;\n\n /**\n * A function that disposes of the event and its listeners.\n */\n readonly dispose: Dispose;\n\n /**\n * Creates a new event.\n * @example\n * // Create a click event.\n * const clickEvent = new Event<[x: number, y: number], void>();\n * clickEvent.on((x, y) => console.log(`Clicked at ${x}, ${y}`));\n *\n * @param dispose - A function to call on the event disposal.\n */\n constructor(dispose?: Dispose) {\n const listeners: Listeners<T, R> = [];\n const fn = (event: T) => eventEmitter(listeners, event);\n\n super(fn);\n this.listeners = listeners;\n this.dispose = () => {\n this.clear();\n dispose?.();\n };\n }\n\n /**\n * The number of listeners for the event.\n */\n get size(): number {\n return this.listeners.length;\n }\n\n /**\n * Checks if a listener is not registered for the event.\n * @param listener - The listener to check.\n * @returns `true` if the listener is not registered, `false` otherwise.\n */\n lacks(listener: Listener<T, R>): boolean {\n return this.listeners.indexOf(listener) === -1;\n }\n\n /**\n * Checks if a listener is registered for the event.\n * @param listener - The listener to check.\n * @returns `true` if the listener is registered, `false` otherwise.\n */\n has(listener: Listener<T, R>): boolean {\n return this.listeners.indexOf(listener) !== -1;\n }\n\n /**\n * Removes a listener from the event.\n * @param listener - The listener to remove.\n */\n off(listener: Listener<T, R>): void {\n let index = this.listeners.indexOf(listener);\n while (~index) {\n this.listeners.splice(index, 1);\n index = this.listeners.indexOf(listener);\n }\n }\n\n /**\n * Adds a listener to the event.\n * @param listener - The listener to add.\n * @returns An object that can be used to remove the listener.\n */\n on(listener: Listener<T, R>): Dismiss {\n this.listeners.push(listener);\n return new Dismiss(() => this.off(listener));\n }\n\n /**\n * Adds a listener to the event that will only be called once.\n * @param listener - The listener to add.\n * @returns An object that can be used to remove the listener.\n */\n once(listener: Listener<T, R>): Dismiss {\n const oneTimeListener = (event: T) => {\n this.off(oneTimeListener);\n return listener(event);\n };\n return this.on(oneTimeListener);\n }\n\n /**\n * Removes all listeners from the event.\n */\n clear(): void {\n this.listeners.splice(0);\n }\n\n /**\n * Returns a Promise that resolves with the first emitted by the event arguments.\n * @returns A Promise that resolves with the first emitted by the event.\n */\n onceAsync(): Promise<T> {\n return new Promise((resolve) => this.once((event) => resolve(event)));\n }\n\n /**\n * Returns a new event that only triggers when the provided filter function returns `true`.\n * @example\n * const spacePressEvent = keyboardEvent.filter((key) => key === 'Space');\n *\n * @param filter The filter function to apply to the event.\n * @returns A new event that only triggers when the provided filter function returns `true`.\n */\n filter<P extends T>(predicate: Predicate<T, P>): Event<P, R>;\n filter<P extends T>(filter: FilterFunction<T>): Event<P, R>;\n filter<P extends T>(filter: Filter<T, P>): Event<P, R> {\n const dispose = this.on(async (event: T) => {\n if (filteredEvent.size > 0 && (await filter(event))) {\n await filteredEvent(event as EventType<P>);\n }\n });\n const filteredEvent = new Event<P, R>(dispose);\n return filteredEvent;\n }\n\n /**\n * Returns a new event that will only be triggered once the provided filter function returns `true`.\n * @example\n * const escPressEvent = keyboardEvent.first((key) => key === 'Esc');\n * await escPressEvent.toPromise();\n *\n * @param filter - The filter function.\n * @returns A new event that will only be triggered once the provided filter function returns `true`.\n */\n first<P extends T>(predicate: Predicate<T, P>): Event<P, R>;\n first<P extends T>(filter: FilterFunction<T>): Event<P, R>;\n first<P extends T>(filter: Filter<T, P>): Event<P, R> {\n const dispose = this.on(async (event: T) => {\n if (filteredEvent.size > 0 && (await filter(event))) {\n await dispose();\n await filteredEvent(event as EventType<P>);\n }\n });\n const filteredEvent = new Event<P, R>(dispose);\n return filteredEvent;\n }\n\n /**\n * Returns a new event that maps the values of this event using the provided mapper function.\n * @example\n * const keyPressEvent = keyboardEvent.map((key) => key.toUpperCase()); // ['a'] -> ['A']\n *\n * @param mapper A function that maps the values of this event to a new value.\n * @returns A new event that emits the mapped values.\n */\n map<M, MR = R>(mapper: Mapper<T, M>): Event<M, MR> {\n const dispose = this.on(async (event) => {\n if (mappedEvent.size > 0) {\n const value = await mapper(event);\n await mappedEvent(value as EventType<M>);\n }\n });\n const mappedEvent = new Event<M, MR>(dispose);\n return mappedEvent;\n }\n\n /**\n * Returns a new event that reduces the emitted values using the provided reducer function.\n * @example\n * const sumEvent = numberEvent.reduce((a, b) => a + b, 0);\n * sumEvent.on((sum) => console.log(sum)); // 1, 3, 6\n * sumEvent(1);\n * sumEvent(2);\n * sumEvent(3);\n *\n * @typeParam A The type of the accumulated value.\n * @typeParam AR The type of the reduced value.\n * @param {Reducer<T, A>} reducer The reducer function that accumulates the values emitted by this `Event`.\n * @param {A} init The initial value of the accumulated value.\n * @returns {Event<[A], AR>} A new `Event` that emits the reduced value.\n */\n reduce<A, AR = R>(reducer: Reducer<T, A>, init: A): Event<A, AR> {\n let value = init;\n const dispose = this.on(async (event) => {\n if (reducedEvent.size > 0) {\n value = await reducer(value, event);\n await reducedEvent(value as EventType<A>);\n }\n });\n const reducedEvent = new Event<A, AR>(dispose);\n return reducedEvent;\n }\n\n /**\n * Returns a new debounced event that will not fire until a certain amount of time has passed\n * since the last time it was triggered.\n * @example\n * const debouncedEvent = textInputEvent.debounce(100);\n * debouncedEvent.on((str) => console.log(str)); // 'test'\n * event('t');\n * event('te');\n * event('tes');\n * event('test');\n *\n * @param interval - The amount of time to wait before firing the debounced event, in milliseconds.\n * @returns A new debounced event.\n */\n debounce(interval: number): Event<T, R> {\n let timer: ReturnType<typeof setTimeout>;\n const dispose = this.on((event) => {\n clearTimeout(timer);\n timer = setTimeout(() => debouncedEvent(event as EventType<T>), interval);\n });\n const debouncedEvent = new Event<T, R>(dispose);\n return debouncedEvent;\n }\n}\n\n/**\n * Merges multiple events into a single event.\n * @example\n * const inputEvent = Event.merge(mouseEvent, keyboardEvent);\n *\n * @param events - The events to merge.\n * @returns The merged event.\n */\nexport const merge = <Events extends Event<any, any>[]>(...events: Events): Event<AllEventsParameters<Events>, AllEventsResults<Events>> => {\n const mergedEvent = new Event<AllEventsParameters<Events>, AllEventsResults<Events>>();\n events.forEach((event) => event.on(mergedEvent));\n return mergedEvent;\n};\n\n/**\n * Creates an event that triggers at a specified interval.\n * @example\n * const tickEvent = Event.interval(1000);\n * tickEvent.on((tickNumber) => console.log(tickNumber));\n *\n * @param interval - The interval at which to trigger the event.\n * @returns The interval event.\n */\nexport const createInterval = <R = void>(interval: number): Event<number, R> => {\n let counter = 0;\n const intervalEvent = new Event<number, R>(() => clearInterval(timerId));\n const timerId: ReturnType<typeof setInterval> = setInterval(() => intervalEvent(counter++), interval);\n return intervalEvent;\n};\n\n/**\n * Creates a new event instance.\n *\n * @typeParam T - An array of argument types that the event will accept.\n * @typeParam R - The return type of the event handler function.\n * @returns A new instance of the `Event` class.\n *\n * @example\n * const myEvent = createEvent<[string], number>();\n * myEvent.on((str: string) => str.length);\n * await myEvent('hello'); // [5]\n */\nexport const createEvent = <T, R = void>(): Event<T, R> => new Event<T, R>();\n\nexport default createEvent;\n\n/**\n * A type helper that extracts the event listener type\n *\n * @typeParam E - The event type.\n */\nexport type EventHandler<E> = E extends Event<infer T, infer R> ? Listener<T, R> : never;\n\n/**\n * A type helper that extracts the event filter type\n *\n * @typeParam E The event type to filter.\n */\nexport type EventFilter<E> = FilterFunction<EventParameters<E>>;\n\n/**\n * A type helper that extracts the event predicate type\n *\n * @typeParam E The event type to predicate.\n */\nexport type EventPredicate<E, P extends EventParameters<E>> = Predicate<EventParameters<E>, P>;\n\n/**\n * A type helper that extracts the event mapper type\n *\n * @typeParam E The event type to map.\n * @typeParam M The new type to map `E` to.\n */\nexport type EventMapper<E, M> = Mapper<EventParameters<E>, M>;\n\n/**\n * A type helper that extracts the event mapper type\n *\n * @typeParam E The type of event to reduce.\n * @typeParam M The type of reduced event.\n */\nexport type EventReducer<E, R> = Reducer<EventParameters<E>, R>;\n"],"names":["FunctionExt","Dismiss","Event","merge","createInterval","createEvent","Function","constructor","func","Object","setPrototypeOf","prototype","callback","after","task","afterTimes","count","eventEmitter","listeners","event","Promise","all","map","listener","dispose","fn","clear","size","length","lacks","indexOf","has","off","index","splice","on","push","once","oneTimeListener","onceAsync","resolve","filter","filteredEvent","first","mapper","mappedEvent","value","reduce","reducer","init","reducedEvent","debounce","interval","timer","clearTimeout","setTimeout","debouncedEvent","events","mergedEvent","forEach","counter","intervalEvent","clearInterval","timerId","setInterval"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;\n\nexport interface Callback<R = void> {\n (): MaybePromise<R>;\n}\n\nexport interface Listener<T, R = unknown> {\n (event: T): MaybePromise<R | void>;\n}\n\nexport interface Result<T, E> {\n ok: boolean;\n result: T | E;\n}\n\nexport interface FilterFunction<T> {\n (event: T): MaybePromise<boolean>;\n}\n\nexport interface Predicate<T, P extends T> {\n (event: T): event is P;\n}\n\nexport type Filter<T, P extends T> = Predicate<T, P> | FilterFunction<T>;\n\nexport interface Mapper<T, R> {\n (event: T): MaybePromise<R>;\n}\n\nexport interface Reducer<T, R> {\n (result: R, event: T): MaybePromise<R>;\n}\n\nexport type Listeners<T, R> = Listener<T, R>[];\n\n/**\n * An abstract class that extends the built-in Function class. It allows instances of the class\n * to be called as functions. When an instance of FunctionExt is called as a function, it will\n * call the function passed to its constructor with the same arguments.\n * @internal\n */\nexport abstract class FunctionExt extends Function {\n constructor(func: Function) {\n super();\n return Object.setPrototypeOf(func, new.target.prototype);\n }\n}\n\nexport interface Dismiss {\n (): MaybePromise<void>;\n}\n\n/**\n * @internal\n */\nexport class Dismiss extends FunctionExt {\n constructor(callback: Callback) {\n super(callback);\n }\n\n pre(callback: Callback): Dismiss {\n return new Dismiss(async () => {\n await callback();\n await this();\n });\n }\n\n post(callback: Callback): Dismiss {\n return new Dismiss(async () => {\n await this();\n await callback();\n });\n }\n\n countdown(count: number): Dismiss {\n return new Dismiss(async () => {\n if (!--count) {\n await this();\n }\n });\n }\n}\n\nconst eventEmitter = <A, R>(listeners: Listeners<A, R>, event: A): Promise<(void | R)[]> => Promise.all(listeners.map((listener) => listener(event)));\n\ntype EventType<T> = T extends undefined ? void : T;\n\nexport interface Event<T = unknown, R = void> {\n (event?: EventType<T>): Promise<(R | undefined)[]>;\n}\n\nexport type EventParameters<T> = T extends Event<infer P, unknown> ? P : never;\n\nexport type EventResult<T> = T extends Event<unknown, infer R> ? R : never;\n\nexport type AllEventsParameters<T extends Event<unknown, unknown>[]> = { [K in keyof T]: EventParameters<T[K]> }[number];\n\nexport type AllEventsResults<T extends Event<unknown, unknown>[]> = { [K in keyof T]: EventResult<T[K]> }[number];\n\n/**\n * A class representing an anonymous event that can be listened to or triggered.\n *\n * @typeParam T - The tuple of arguments that the event takes.\n * @typeParam R - The return type of the event.\n */\nexport class Event<T, R> extends FunctionExt {\n /**\n * The array of listeners for the event.\n */\n private listeners: Listeners<T, R>;\n\n /**\n * A function that disposes of the event and its listeners.\n */\n readonly dispose: Callback;\n\n /**\n * Creates a new event.\n * @example\n * // Create a click event.\n * const clickEvent = new Event<[x: number, y: number], void>();\n * clickEvent.on((x, y) => console.log(`Clicked at ${x}, ${y}`));\n *\n * @param dispose - A function to call on the event disposal.\n */\n constructor(dispose?: Callback) {\n const listeners: Listeners<T, R> = [];\n const fn = (event: T) => eventEmitter(listeners, event);\n\n super(fn);\n this.listeners = listeners;\n this.dispose = async () => {\n this.clear();\n await dispose?.();\n };\n }\n\n /**\n * The number of listeners for the event.\n */\n get size(): number {\n return this.listeners.length;\n }\n\n /**\n * Checks if a listener is not registered for the event.\n * @param listener - The listener to check.\n * @returns `true` if the listener is not registered, `false` otherwise.\n */\n lacks(listener: Listener<T, R>): boolean {\n return this.listeners.indexOf(listener) === -1;\n }\n\n /**\n * Checks if a listener is registered for the event.\n * @param listener - The listener to check.\n * @returns `true` if the listener is registered, `false` otherwise.\n */\n has(listener: Listener<T, R>): boolean {\n return this.listeners.indexOf(listener) !== -1;\n }\n\n /**\n * Removes a listener from the event.\n * @param listener - The listener to remove.\n */\n off(listener: Listener<T, R>): void {\n let index = this.listeners.indexOf(listener);\n while (~index) {\n this.listeners.splice(index, 1);\n index = this.listeners.indexOf(listener);\n }\n }\n\n /**\n * Adds a listener to the event.\n * @param listener - The listener to add.\n * @returns An object that can be used to remove the listener.\n */\n on(listener: Listener<T, R>): Dismiss {\n this.listeners.push(listener);\n return new Dismiss(() => this.off(listener));\n }\n\n /**\n * Adds a listener to the event that will only be called once.\n * @param listener - The listener to add.\n * @returns An object that can be used to remove the listener.\n */\n once(listener: Listener<T, R>): Dismiss {\n const oneTimeListener = (event: T) => {\n this.off(oneTimeListener);\n return listener(event);\n };\n return this.on(oneTimeListener);\n }\n\n /**\n * Returns a Promise that resolves with the first emitted by the event arguments.\n * @returns A Promise that resolves with the first emitted by the event.\n */\n onceAsync(): Promise<T> {\n return new Promise((resolve) => this.once((event) => resolve(event)));\n }\n\n /**\n * Removes all listeners from the event.\n */\n clear(): void {\n this.listeners.splice(0);\n }\n\n /**\n * Returns a new event that only triggers when the provided filter function returns `true`.\n * @example\n * const spacePressEvent = keyboardEvent.filter((key) => key === 'Space');\n *\n * @param filter The filter function to apply to the event.\n * @returns A new event that only triggers when the provided filter function returns `true`.\n */\n filter<P extends T>(predicate: Predicate<T, P>): Event<P, R>;\n filter<P extends T>(filter: FilterFunction<T>): Event<P, R>;\n filter<P extends T>(filter: Filter<T, P>): Event<P, R> {\n const dispose = this.on(async (event: T) => {\n if (filteredEvent.size > 0 && (await filter(event))) {\n await filteredEvent(event as EventType<P>);\n }\n });\n const filteredEvent = new Event<P, R>(dispose);\n return filteredEvent;\n }\n\n /**\n * Returns a new event that will only be triggered once the provided filter function returns `true`.\n * @example\n * const escPressEvent = keyboardEvent.first((key) => key === 'Esc');\n * await escPressEvent.toPromise();\n *\n * @param filter - The filter function.\n * @returns A new event that will only be triggered once the provided filter function returns `true`.\n */\n first<P extends T>(predicate: Predicate<T, P>): Event<P, R>;\n first<P extends T>(filter: FilterFunction<T>): Event<P, R>;\n first<P extends T>(filter: Filter<T, P>): Event<P, R> {\n const dispose = this.on(async (event: T) => {\n if (filteredEvent.size > 0 && (await filter(event))) {\n await dispose();\n await filteredEvent(event as EventType<P>);\n }\n });\n const filteredEvent = new Event<P, R>(dispose);\n return filteredEvent;\n }\n\n /**\n * Returns a new promise that will be resolved once the provided filter function returns `true`.\n * @example\n * const escPressEvent = await keyboardEvent.firstAsync((key) => key === 'Esc');\n *\n * @param filter - The filter function.\n * @returns A new promise that will be resolved once the provided filter function returns `true`.\n */\n firstAsync<P extends T>(predicate: Predicate<T, P>): Promise<P>;\n firstAsync<P extends T>(filter: FilterFunction<T>): Promise<P>;\n firstAsync<P extends T>(filter: Filter<T, P>): Promise<P> {\n return this.first<P>(filter).onceAsync();\n }\n\n /**\n * Returns a new event that maps the values of this event using the provided mapper function.\n * @example\n * const keyPressEvent = keyboardEvent.map((key) => key.toUpperCase()); // ['a'] -> ['A']\n *\n * @param mapper A function that maps the values of this event to a new value.\n * @returns A new event that emits the mapped values.\n */\n map<M, MR = R>(mapper: Mapper<T, M>): Event<M, MR> {\n const dispose = this.on(async (event) => {\n if (mappedEvent.size > 0) {\n const value = await mapper(event);\n await mappedEvent(value as EventType<M>);\n }\n });\n const mappedEvent = new Event<M, MR>(dispose);\n return mappedEvent;\n }\n\n /**\n * Returns a new event that reduces the emitted values using the provided reducer function.\n * @example\n * const sumEvent = numberEvent.reduce((a, b) => a + b, 0);\n * sumEvent.on((sum) => console.log(sum)); // 1, 3, 6\n * sumEvent(1);\n * sumEvent(2);\n * sumEvent(3);\n *\n * @typeParam A The type of the accumulated value.\n * @typeParam AR The type of the reduced value.\n * @param {Reducer<T, A>} reducer The reducer function that accumulates the values emitted by this `Event`.\n * @param {A} init The initial value of the accumulated value.\n * @returns {Event<[A], AR>} A new `Event` that emits the reduced value.\n */\n reduce<A, AR = R>(reducer: Reducer<T, A>, init: A): Event<A, AR> {\n let value = init;\n const dispose = this.on(async (event) => {\n if (reducedEvent.size > 0) {\n value = await reducer(value, event);\n await reducedEvent(value as EventType<A>);\n }\n });\n const reducedEvent = new Event<A, AR>(dispose);\n return reducedEvent;\n }\n\n /**\n * Returns a new debounced event that will not fire until a certain amount of time has passed\n * since the last time it was triggered.\n * @example\n * const debouncedEvent = textInputEvent.debounce(100);\n * debouncedEvent.on((str) => console.log(str)); // 'test'\n * event('t');\n * event('te');\n * event('tes');\n * event('test');\n *\n * @param interval - The amount of time to wait before firing the debounced event, in milliseconds.\n * @returns A new debounced event.\n */\n debounce(interval: number): Event<T, R> {\n let timer: ReturnType<typeof setTimeout>;\n const dispose = this.on((event) => {\n clearTimeout(timer);\n timer = setTimeout(() => debouncedEvent(event as EventType<T>), interval);\n });\n const debouncedEvent = new Event<T, R>(dispose);\n return debouncedEvent;\n }\n}\n\n/**\n * Merges multiple events into a single event.\n * @example\n * const inputEvent = Event.merge(mouseEvent, keyboardEvent);\n *\n * @param events - The events to merge.\n * @returns The merged event.\n */\nexport const merge = <Events extends Event<any, any>[]>(...events: Events): Event<AllEventsParameters<Events>, AllEventsResults<Events>> => {\n const mergedEvent = new Event<AllEventsParameters<Events>, AllEventsResults<Events>>();\n events.forEach((event) => event.on(mergedEvent));\n return mergedEvent;\n};\n\n/**\n * Creates an event that triggers at a specified interval.\n * @example\n * const tickEvent = Event.interval(1000);\n * tickEvent.on((tickNumber) => console.log(tickNumber));\n *\n * @param interval - The interval at which to trigger the event.\n * @returns The interval event.\n */\nexport const createInterval = <R = void>(interval: number): Event<number, R> => {\n let counter = 0;\n const intervalEvent = new Event<number, R>(() => clearInterval(timerId));\n const timerId: ReturnType<typeof setInterval> = setInterval(() => intervalEvent(counter++), interval);\n return intervalEvent;\n};\n\n/**\n * Creates a new event instance.\n *\n * @typeParam T - An array of argument types that the event will accept.\n * @typeParam R - The return type of the event handler function.\n * @returns A new instance of the `Event` class.\n *\n * @example\n * const myEvent = createEvent<[string], number>();\n * myEvent.on((str: string) => str.length);\n * await myEvent('hello'); // [5]\n */\nexport const createEvent = <T, R = void>(): Event<T, R> => new Event<T, R>();\n\nexport default createEvent;\n\n/**\n * A type helper that extracts the event listener type\n *\n * @typeParam E - The event type.\n */\nexport type EventHandler<E> = E extends Event<infer T, infer R> ? Listener<T, R> : never;\n\n/**\n * A type helper that extracts the event filter type\n *\n * @typeParam E The event type to filter.\n */\nexport type EventFilter<E> = FilterFunction<EventParameters<E>>;\n\n/**\n * A type helper that extracts the event predicate type\n *\n * @typeParam E The event type to predicate.\n */\nexport type EventPredicate<E, P extends EventParameters<E>> = Predicate<EventParameters<E>, P>;\n\n/**\n * A type helper that extracts the event mapper type\n *\n * @typeParam E The event type to map.\n * @typeParam M The new type to map `E` to.\n */\nexport type EventMapper<E, M> = Mapper<EventParameters<E>, M>;\n\n/**\n * A type helper that extracts the event mapper type\n *\n * @typeParam E The type of event to reduce.\n * @typeParam M The type of reduced event.\n */\nexport type EventReducer<E, R> = Reducer<EventParameters<E>, R>;\n"],"names":["FunctionExt","Dismiss","Event","merge","createInterval","createEvent","Function","constructor","func","Object","setPrototypeOf","prototype","callback","pre","post","countdown","count","eventEmitter","listeners","event","Promise","all","map","listener","dispose","fn","clear","size","length","lacks","indexOf","has","off","index","splice","on","push","once","oneTimeListener","onceAsync","resolve","filter","filteredEvent","first","firstAsync","mapper","mappedEvent","value","reduce","reducer","init","reducedEvent","debounce","interval","timer","clearTimeout","setTimeout","debouncedEvent","events","mergedEvent","forEach","counter","intervalEvent","clearInterval","timerId","setInterval"],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;
|
|
2
|
-
export interface
|
|
3
|
-
():
|
|
2
|
+
export interface Callback<R = void> {
|
|
3
|
+
(): MaybePromise<R>;
|
|
4
4
|
}
|
|
5
5
|
export interface Listener<T, R = unknown> {
|
|
6
6
|
(event: T): MaybePromise<R | void>;
|
|
7
7
|
}
|
|
8
|
-
export interface Dispose {
|
|
9
|
-
(): void;
|
|
10
|
-
}
|
|
11
8
|
export interface Result<T, E> {
|
|
12
9
|
ok: boolean;
|
|
13
10
|
result: T | E;
|
|
14
11
|
}
|
|
15
|
-
export interface Resolver<T, P> {
|
|
16
|
-
(event: T): Promise<P>;
|
|
17
|
-
}
|
|
18
12
|
export interface FilterFunction<T> {
|
|
19
13
|
(event: T): MaybePromise<boolean>;
|
|
20
14
|
}
|
|
@@ -39,18 +33,16 @@ export declare abstract class FunctionExt extends Function {
|
|
|
39
33
|
constructor(func: Function);
|
|
40
34
|
}
|
|
41
35
|
export interface Dismiss {
|
|
42
|
-
():
|
|
43
|
-
}
|
|
44
|
-
export interface Task {
|
|
45
|
-
(): MaybePromise<unknown>;
|
|
36
|
+
(): MaybePromise<void>;
|
|
46
37
|
}
|
|
47
38
|
/**
|
|
48
39
|
* @internal
|
|
49
40
|
*/
|
|
50
41
|
export declare class Dismiss extends FunctionExt {
|
|
51
|
-
constructor(callback:
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
constructor(callback: Callback);
|
|
43
|
+
pre(callback: Callback): Dismiss;
|
|
44
|
+
post(callback: Callback): Dismiss;
|
|
45
|
+
countdown(count: number): Dismiss;
|
|
54
46
|
}
|
|
55
47
|
type EventType<T> = T extends undefined ? void : T;
|
|
56
48
|
export interface Event<T = unknown, R = void> {
|
|
@@ -78,7 +70,7 @@ export declare class Event<T, R> extends FunctionExt {
|
|
|
78
70
|
/**
|
|
79
71
|
* A function that disposes of the event and its listeners.
|
|
80
72
|
*/
|
|
81
|
-
readonly dispose:
|
|
73
|
+
readonly dispose: Callback;
|
|
82
74
|
/**
|
|
83
75
|
* Creates a new event.
|
|
84
76
|
* @example
|
|
@@ -88,7 +80,7 @@ export declare class Event<T, R> extends FunctionExt {
|
|
|
88
80
|
*
|
|
89
81
|
* @param dispose - A function to call on the event disposal.
|
|
90
82
|
*/
|
|
91
|
-
constructor(dispose?:
|
|
83
|
+
constructor(dispose?: Callback);
|
|
92
84
|
/**
|
|
93
85
|
* The number of listeners for the event.
|
|
94
86
|
*/
|
|
@@ -122,15 +114,15 @@ export declare class Event<T, R> extends FunctionExt {
|
|
|
122
114
|
* @returns An object that can be used to remove the listener.
|
|
123
115
|
*/
|
|
124
116
|
once(listener: Listener<T, R>): Dismiss;
|
|
125
|
-
/**
|
|
126
|
-
* Removes all listeners from the event.
|
|
127
|
-
*/
|
|
128
|
-
clear(): void;
|
|
129
117
|
/**
|
|
130
118
|
* Returns a Promise that resolves with the first emitted by the event arguments.
|
|
131
119
|
* @returns A Promise that resolves with the first emitted by the event.
|
|
132
120
|
*/
|
|
133
121
|
onceAsync(): Promise<T>;
|
|
122
|
+
/**
|
|
123
|
+
* Removes all listeners from the event.
|
|
124
|
+
*/
|
|
125
|
+
clear(): void;
|
|
134
126
|
/**
|
|
135
127
|
* Returns a new event that only triggers when the provided filter function returns `true`.
|
|
136
128
|
* @example
|
|
@@ -152,6 +144,16 @@ export declare class Event<T, R> extends FunctionExt {
|
|
|
152
144
|
*/
|
|
153
145
|
first<P extends T>(predicate: Predicate<T, P>): Event<P, R>;
|
|
154
146
|
first<P extends T>(filter: FilterFunction<T>): Event<P, R>;
|
|
147
|
+
/**
|
|
148
|
+
* Returns a new promise that will be resolved once the provided filter function returns `true`.
|
|
149
|
+
* @example
|
|
150
|
+
* const escPressEvent = await keyboardEvent.firstAsync((key) => key === 'Esc');
|
|
151
|
+
*
|
|
152
|
+
* @param filter - The filter function.
|
|
153
|
+
* @returns A new promise that will be resolved once the provided filter function returns `true`.
|
|
154
|
+
*/
|
|
155
|
+
firstAsync<P extends T>(predicate: Predicate<T, P>): Promise<P>;
|
|
156
|
+
firstAsync<P extends T>(filter: FilterFunction<T>): Promise<P>;
|
|
155
157
|
/**
|
|
156
158
|
* Returns a new event that maps the values of this event using the provided mapper function.
|
|
157
159
|
* @example
|
package/build/index.js
CHANGED
|
@@ -8,16 +8,24 @@ export class Dismiss extends FunctionExt {
|
|
|
8
8
|
constructor(callback){
|
|
9
9
|
super(callback);
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
pre(callback) {
|
|
12
|
+
return new Dismiss(async ()=>{
|
|
13
|
+
await callback();
|
|
14
|
+
await this();
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
post(callback) {
|
|
18
|
+
return new Dismiss(async ()=>{
|
|
19
|
+
await this();
|
|
20
|
+
await callback();
|
|
21
|
+
});
|
|
14
22
|
}
|
|
15
|
-
|
|
16
|
-
return async ()=>{
|
|
23
|
+
countdown(count) {
|
|
24
|
+
return new Dismiss(async ()=>{
|
|
17
25
|
if (!--count) {
|
|
18
26
|
await this();
|
|
19
27
|
}
|
|
20
|
-
};
|
|
28
|
+
});
|
|
21
29
|
}
|
|
22
30
|
}
|
|
23
31
|
const eventEmitter = (listeners, event)=>Promise.all(listeners.map((listener)=>listener(event)));
|
|
@@ -29,9 +37,9 @@ export class Event extends FunctionExt {
|
|
|
29
37
|
const fn = (event)=>eventEmitter(listeners, event);
|
|
30
38
|
super(fn);
|
|
31
39
|
this.listeners = listeners;
|
|
32
|
-
this.dispose = ()=>{
|
|
40
|
+
this.dispose = async ()=>{
|
|
33
41
|
this.clear();
|
|
34
|
-
dispose?.();
|
|
42
|
+
await dispose?.();
|
|
35
43
|
};
|
|
36
44
|
}
|
|
37
45
|
get size() {
|
|
@@ -61,12 +69,12 @@ export class Event extends FunctionExt {
|
|
|
61
69
|
};
|
|
62
70
|
return this.on(oneTimeListener);
|
|
63
71
|
}
|
|
64
|
-
clear() {
|
|
65
|
-
this.listeners.splice(0);
|
|
66
|
-
}
|
|
67
72
|
onceAsync() {
|
|
68
73
|
return new Promise((resolve)=>this.once((event)=>resolve(event)));
|
|
69
74
|
}
|
|
75
|
+
clear() {
|
|
76
|
+
this.listeners.splice(0);
|
|
77
|
+
}
|
|
70
78
|
filter(filter) {
|
|
71
79
|
const dispose = this.on(async (event)=>{
|
|
72
80
|
if (filteredEvent.size > 0 && await filter(event)) {
|
|
@@ -86,6 +94,9 @@ export class Event extends FunctionExt {
|
|
|
86
94
|
const filteredEvent = new Event(dispose);
|
|
87
95
|
return filteredEvent;
|
|
88
96
|
}
|
|
97
|
+
firstAsync(filter) {
|
|
98
|
+
return this.first(filter).onceAsync();
|
|
99
|
+
}
|
|
89
100
|
map(mapper) {
|
|
90
101
|
const dispose = this.on(async (event)=>{
|
|
91
102
|
if (mappedEvent.size > 0) {
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;\n\nexport interface Unsubscribe {\n (): void;\n}\n\nexport interface Listener<T, R = unknown> {\n (event: T): MaybePromise<R | void>;\n}\n\nexport interface Dispose {\n (): void;\n}\n\nexport interface Result<T, E> {\n ok: boolean;\n result: T | E;\n}\n\nexport interface Resolver<T, P> {\n (event: T): Promise<P>;\n}\n\nexport interface FilterFunction<T> {\n (event: T): MaybePromise<boolean>;\n}\n\nexport interface Predicate<T, P extends T> {\n (event: T): event is P;\n}\n\nexport type Filter<T, P extends T> = Predicate<T, P> | FilterFunction<T>;\n\nexport interface Mapper<T, R> {\n (event: T): MaybePromise<R>;\n}\n\nexport interface Reducer<T, R> {\n (result: R, event: T): MaybePromise<R>;\n}\n\nexport type Listeners<T, R> = Listener<T, R>[];\n\n/**\n * An abstract class that extends the built-in Function class. It allows instances of the class\n * to be called as functions. When an instance of FunctionExt is called as a function, it will\n * call the function passed to its constructor with the same arguments.\n * @internal\n */\nexport abstract class FunctionExt extends Function {\n constructor(func: Function) {\n super();\n return Object.setPrototypeOf(func, new.target.prototype);\n }\n}\n\nexport interface Dismiss {\n (): Promise<void> | void;\n}\n\nexport interface Task {\n (): MaybePromise<unknown>;\n}\n\n/**\n * @internal\n */\nexport class Dismiss extends FunctionExt {\n constructor(callback: Unsubscribe) {\n super(callback);\n }\n\n async after(task: Task): Promise<void> {\n await task();\n await this();\n }\n\n afterTimes(count: number): () => Promise<void> {\n return async () => {\n if (!--count) {\n await this();\n }\n };\n }\n}\n\nconst eventEmitter = <A, R>(listeners: Listeners<A, R>, event: A): Promise<(void | R)[]> => Promise.all(listeners.map((listener) => listener(event)));\n\ntype EventType<T> = T extends undefined ? void : T;\n\nexport interface Event<T = unknown, R = void> {\n (event?: EventType<T>): Promise<(R | undefined)[]>;\n}\n\nexport type EventParameters<T> = T extends Event<infer P, unknown> ? P : never;\n\nexport type EventResult<T> = T extends Event<unknown, infer R> ? R : never;\n\nexport type AllEventsParameters<T extends Event<unknown, unknown>[]> = { [K in keyof T]: EventParameters<T[K]> }[number];\n\nexport type AllEventsResults<T extends Event<unknown, unknown>[]> = { [K in keyof T]: EventResult<T[K]> }[number];\n\n/**\n * A class representing an anonymous event that can be listened to or triggered.\n *\n * @typeParam T - The tuple of arguments that the event takes.\n * @typeParam R - The return type of the event.\n */\nexport class Event<T, R> extends FunctionExt {\n /**\n * The array of listeners for the event.\n */\n private listeners: Listeners<T, R>;\n\n /**\n * A function that disposes of the event and its listeners.\n */\n readonly dispose: Dispose;\n\n /**\n * Creates a new event.\n * @example\n * // Create a click event.\n * const clickEvent = new Event<[x: number, y: number], void>();\n * clickEvent.on((x, y) => console.log(`Clicked at ${x}, ${y}`));\n *\n * @param dispose - A function to call on the event disposal.\n */\n constructor(dispose?: Dispose) {\n const listeners: Listeners<T, R> = [];\n const fn = (event: T) => eventEmitter(listeners, event);\n\n super(fn);\n this.listeners = listeners;\n this.dispose = () => {\n this.clear();\n dispose?.();\n };\n }\n\n /**\n * The number of listeners for the event.\n */\n get size(): number {\n return this.listeners.length;\n }\n\n /**\n * Checks if a listener is not registered for the event.\n * @param listener - The listener to check.\n * @returns `true` if the listener is not registered, `false` otherwise.\n */\n lacks(listener: Listener<T, R>): boolean {\n return this.listeners.indexOf(listener) === -1;\n }\n\n /**\n * Checks if a listener is registered for the event.\n * @param listener - The listener to check.\n * @returns `true` if the listener is registered, `false` otherwise.\n */\n has(listener: Listener<T, R>): boolean {\n return this.listeners.indexOf(listener) !== -1;\n }\n\n /**\n * Removes a listener from the event.\n * @param listener - The listener to remove.\n */\n off(listener: Listener<T, R>): void {\n let index = this.listeners.indexOf(listener);\n while (~index) {\n this.listeners.splice(index, 1);\n index = this.listeners.indexOf(listener);\n }\n }\n\n /**\n * Adds a listener to the event.\n * @param listener - The listener to add.\n * @returns An object that can be used to remove the listener.\n */\n on(listener: Listener<T, R>): Dismiss {\n this.listeners.push(listener);\n return new Dismiss(() => this.off(listener));\n }\n\n /**\n * Adds a listener to the event that will only be called once.\n * @param listener - The listener to add.\n * @returns An object that can be used to remove the listener.\n */\n once(listener: Listener<T, R>): Dismiss {\n const oneTimeListener = (event: T) => {\n this.off(oneTimeListener);\n return listener(event);\n };\n return this.on(oneTimeListener);\n }\n\n /**\n * Removes all listeners from the event.\n */\n clear(): void {\n this.listeners.splice(0);\n }\n\n /**\n * Returns a Promise that resolves with the first emitted by the event arguments.\n * @returns A Promise that resolves with the first emitted by the event.\n */\n onceAsync(): Promise<T> {\n return new Promise((resolve) => this.once((event) => resolve(event)));\n }\n\n /**\n * Returns a new event that only triggers when the provided filter function returns `true`.\n * @example\n * const spacePressEvent = keyboardEvent.filter((key) => key === 'Space');\n *\n * @param filter The filter function to apply to the event.\n * @returns A new event that only triggers when the provided filter function returns `true`.\n */\n filter<P extends T>(predicate: Predicate<T, P>): Event<P, R>;\n filter<P extends T>(filter: FilterFunction<T>): Event<P, R>;\n filter<P extends T>(filter: Filter<T, P>): Event<P, R> {\n const dispose = this.on(async (event: T) => {\n if (filteredEvent.size > 0 && (await filter(event))) {\n await filteredEvent(event as EventType<P>);\n }\n });\n const filteredEvent = new Event<P, R>(dispose);\n return filteredEvent;\n }\n\n /**\n * Returns a new event that will only be triggered once the provided filter function returns `true`.\n * @example\n * const escPressEvent = keyboardEvent.first((key) => key === 'Esc');\n * await escPressEvent.toPromise();\n *\n * @param filter - The filter function.\n * @returns A new event that will only be triggered once the provided filter function returns `true`.\n */\n first<P extends T>(predicate: Predicate<T, P>): Event<P, R>;\n first<P extends T>(filter: FilterFunction<T>): Event<P, R>;\n first<P extends T>(filter: Filter<T, P>): Event<P, R> {\n const dispose = this.on(async (event: T) => {\n if (filteredEvent.size > 0 && (await filter(event))) {\n await dispose();\n await filteredEvent(event as EventType<P>);\n }\n });\n const filteredEvent = new Event<P, R>(dispose);\n return filteredEvent;\n }\n\n /**\n * Returns a new event that maps the values of this event using the provided mapper function.\n * @example\n * const keyPressEvent = keyboardEvent.map((key) => key.toUpperCase()); // ['a'] -> ['A']\n *\n * @param mapper A function that maps the values of this event to a new value.\n * @returns A new event that emits the mapped values.\n */\n map<M, MR = R>(mapper: Mapper<T, M>): Event<M, MR> {\n const dispose = this.on(async (event) => {\n if (mappedEvent.size > 0) {\n const value = await mapper(event);\n await mappedEvent(value as EventType<M>);\n }\n });\n const mappedEvent = new Event<M, MR>(dispose);\n return mappedEvent;\n }\n\n /**\n * Returns a new event that reduces the emitted values using the provided reducer function.\n * @example\n * const sumEvent = numberEvent.reduce((a, b) => a + b, 0);\n * sumEvent.on((sum) => console.log(sum)); // 1, 3, 6\n * sumEvent(1);\n * sumEvent(2);\n * sumEvent(3);\n *\n * @typeParam A The type of the accumulated value.\n * @typeParam AR The type of the reduced value.\n * @param {Reducer<T, A>} reducer The reducer function that accumulates the values emitted by this `Event`.\n * @param {A} init The initial value of the accumulated value.\n * @returns {Event<[A], AR>} A new `Event` that emits the reduced value.\n */\n reduce<A, AR = R>(reducer: Reducer<T, A>, init: A): Event<A, AR> {\n let value = init;\n const dispose = this.on(async (event) => {\n if (reducedEvent.size > 0) {\n value = await reducer(value, event);\n await reducedEvent(value as EventType<A>);\n }\n });\n const reducedEvent = new Event<A, AR>(dispose);\n return reducedEvent;\n }\n\n /**\n * Returns a new debounced event that will not fire until a certain amount of time has passed\n * since the last time it was triggered.\n * @example\n * const debouncedEvent = textInputEvent.debounce(100);\n * debouncedEvent.on((str) => console.log(str)); // 'test'\n * event('t');\n * event('te');\n * event('tes');\n * event('test');\n *\n * @param interval - The amount of time to wait before firing the debounced event, in milliseconds.\n * @returns A new debounced event.\n */\n debounce(interval: number): Event<T, R> {\n let timer: ReturnType<typeof setTimeout>;\n const dispose = this.on((event) => {\n clearTimeout(timer);\n timer = setTimeout(() => debouncedEvent(event as EventType<T>), interval);\n });\n const debouncedEvent = new Event<T, R>(dispose);\n return debouncedEvent;\n }\n}\n\n/**\n * Merges multiple events into a single event.\n * @example\n * const inputEvent = Event.merge(mouseEvent, keyboardEvent);\n *\n * @param events - The events to merge.\n * @returns The merged event.\n */\nexport const merge = <Events extends Event<any, any>[]>(...events: Events): Event<AllEventsParameters<Events>, AllEventsResults<Events>> => {\n const mergedEvent = new Event<AllEventsParameters<Events>, AllEventsResults<Events>>();\n events.forEach((event) => event.on(mergedEvent));\n return mergedEvent;\n};\n\n/**\n * Creates an event that triggers at a specified interval.\n * @example\n * const tickEvent = Event.interval(1000);\n * tickEvent.on((tickNumber) => console.log(tickNumber));\n *\n * @param interval - The interval at which to trigger the event.\n * @returns The interval event.\n */\nexport const createInterval = <R = void>(interval: number): Event<number, R> => {\n let counter = 0;\n const intervalEvent = new Event<number, R>(() => clearInterval(timerId));\n const timerId: ReturnType<typeof setInterval> = setInterval(() => intervalEvent(counter++), interval);\n return intervalEvent;\n};\n\n/**\n * Creates a new event instance.\n *\n * @typeParam T - An array of argument types that the event will accept.\n * @typeParam R - The return type of the event handler function.\n * @returns A new instance of the `Event` class.\n *\n * @example\n * const myEvent = createEvent<[string], number>();\n * myEvent.on((str: string) => str.length);\n * await myEvent('hello'); // [5]\n */\nexport const createEvent = <T, R = void>(): Event<T, R> => new Event<T, R>();\n\nexport default createEvent;\n\n/**\n * A type helper that extracts the event listener type\n *\n * @typeParam E - The event type.\n */\nexport type EventHandler<E> = E extends Event<infer T, infer R> ? Listener<T, R> : never;\n\n/**\n * A type helper that extracts the event filter type\n *\n * @typeParam E The event type to filter.\n */\nexport type EventFilter<E> = FilterFunction<EventParameters<E>>;\n\n/**\n * A type helper that extracts the event predicate type\n *\n * @typeParam E The event type to predicate.\n */\nexport type EventPredicate<E, P extends EventParameters<E>> = Predicate<EventParameters<E>, P>;\n\n/**\n * A type helper that extracts the event mapper type\n *\n * @typeParam E The event type to map.\n * @typeParam M The new type to map `E` to.\n */\nexport type EventMapper<E, M> = Mapper<EventParameters<E>, M>;\n\n/**\n * A type helper that extracts the event mapper type\n *\n * @typeParam E The type of event to reduce.\n * @typeParam M The type of reduced event.\n */\nexport type EventReducer<E, R> = Reducer<EventParameters<E>, R>;\n"],"names":["FunctionExt","Function","constructor","func","Object","setPrototypeOf","prototype","Dismiss","callback","after","task","afterTimes","count","eventEmitter","listeners","event","Promise","all","map","listener","Event","dispose","fn","clear","size","length","lacks","indexOf","has","off","index","splice","on","push","once","oneTimeListener","onceAsync","resolve","filter","filteredEvent","first","mapper","mappedEvent","value","reduce","reducer","init","reducedEvent","debounce","interval","timer","clearTimeout","setTimeout","debouncedEvent","merge","events","mergedEvent","forEach","createInterval","counter","intervalEvent","clearInterval","timerId","setInterval","createEvent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;\n\nexport interface Callback<R = void> {\n (): MaybePromise<R>;\n}\n\nexport interface Listener<T, R = unknown> {\n (event: T): MaybePromise<R | void>;\n}\n\nexport interface Result<T, E> {\n ok: boolean;\n result: T | E;\n}\n\nexport interface FilterFunction<T> {\n (event: T): MaybePromise<boolean>;\n}\n\nexport interface Predicate<T, P extends T> {\n (event: T): event is P;\n}\n\nexport type Filter<T, P extends T> = Predicate<T, P> | FilterFunction<T>;\n\nexport interface Mapper<T, R> {\n (event: T): MaybePromise<R>;\n}\n\nexport interface Reducer<T, R> {\n (result: R, event: T): MaybePromise<R>;\n}\n\nexport type Listeners<T, R> = Listener<T, R>[];\n\n/**\n * An abstract class that extends the built-in Function class. It allows instances of the class\n * to be called as functions. When an instance of FunctionExt is called as a function, it will\n * call the function passed to its constructor with the same arguments.\n * @internal\n */\nexport abstract class FunctionExt extends Function {\n constructor(func: Function) {\n super();\n return Object.setPrototypeOf(func, new.target.prototype);\n }\n}\n\nexport interface Dismiss {\n (): MaybePromise<void>;\n}\n\n/**\n * @internal\n */\nexport class Dismiss extends FunctionExt {\n constructor(callback: Callback) {\n super(callback);\n }\n\n pre(callback: Callback): Dismiss {\n return new Dismiss(async () => {\n await callback();\n await this();\n });\n }\n\n post(callback: Callback): Dismiss {\n return new Dismiss(async () => {\n await this();\n await callback();\n });\n }\n\n countdown(count: number): Dismiss {\n return new Dismiss(async () => {\n if (!--count) {\n await this();\n }\n });\n }\n}\n\nconst eventEmitter = <A, R>(listeners: Listeners<A, R>, event: A): Promise<(void | R)[]> => Promise.all(listeners.map((listener) => listener(event)));\n\ntype EventType<T> = T extends undefined ? void : T;\n\nexport interface Event<T = unknown, R = void> {\n (event?: EventType<T>): Promise<(R | undefined)[]>;\n}\n\nexport type EventParameters<T> = T extends Event<infer P, unknown> ? P : never;\n\nexport type EventResult<T> = T extends Event<unknown, infer R> ? R : never;\n\nexport type AllEventsParameters<T extends Event<unknown, unknown>[]> = { [K in keyof T]: EventParameters<T[K]> }[number];\n\nexport type AllEventsResults<T extends Event<unknown, unknown>[]> = { [K in keyof T]: EventResult<T[K]> }[number];\n\n/**\n * A class representing an anonymous event that can be listened to or triggered.\n *\n * @typeParam T - The tuple of arguments that the event takes.\n * @typeParam R - The return type of the event.\n */\nexport class Event<T, R> extends FunctionExt {\n /**\n * The array of listeners for the event.\n */\n private listeners: Listeners<T, R>;\n\n /**\n * A function that disposes of the event and its listeners.\n */\n readonly dispose: Callback;\n\n /**\n * Creates a new event.\n * @example\n * // Create a click event.\n * const clickEvent = new Event<[x: number, y: number], void>();\n * clickEvent.on((x, y) => console.log(`Clicked at ${x}, ${y}`));\n *\n * @param dispose - A function to call on the event disposal.\n */\n constructor(dispose?: Callback) {\n const listeners: Listeners<T, R> = [];\n const fn = (event: T) => eventEmitter(listeners, event);\n\n super(fn);\n this.listeners = listeners;\n this.dispose = async () => {\n this.clear();\n await dispose?.();\n };\n }\n\n /**\n * The number of listeners for the event.\n */\n get size(): number {\n return this.listeners.length;\n }\n\n /**\n * Checks if a listener is not registered for the event.\n * @param listener - The listener to check.\n * @returns `true` if the listener is not registered, `false` otherwise.\n */\n lacks(listener: Listener<T, R>): boolean {\n return this.listeners.indexOf(listener) === -1;\n }\n\n /**\n * Checks if a listener is registered for the event.\n * @param listener - The listener to check.\n * @returns `true` if the listener is registered, `false` otherwise.\n */\n has(listener: Listener<T, R>): boolean {\n return this.listeners.indexOf(listener) !== -1;\n }\n\n /**\n * Removes a listener from the event.\n * @param listener - The listener to remove.\n */\n off(listener: Listener<T, R>): void {\n let index = this.listeners.indexOf(listener);\n while (~index) {\n this.listeners.splice(index, 1);\n index = this.listeners.indexOf(listener);\n }\n }\n\n /**\n * Adds a listener to the event.\n * @param listener - The listener to add.\n * @returns An object that can be used to remove the listener.\n */\n on(listener: Listener<T, R>): Dismiss {\n this.listeners.push(listener);\n return new Dismiss(() => this.off(listener));\n }\n\n /**\n * Adds a listener to the event that will only be called once.\n * @param listener - The listener to add.\n * @returns An object that can be used to remove the listener.\n */\n once(listener: Listener<T, R>): Dismiss {\n const oneTimeListener = (event: T) => {\n this.off(oneTimeListener);\n return listener(event);\n };\n return this.on(oneTimeListener);\n }\n\n /**\n * Returns a Promise that resolves with the first emitted by the event arguments.\n * @returns A Promise that resolves with the first emitted by the event.\n */\n onceAsync(): Promise<T> {\n return new Promise((resolve) => this.once((event) => resolve(event)));\n }\n\n /**\n * Removes all listeners from the event.\n */\n clear(): void {\n this.listeners.splice(0);\n }\n\n /**\n * Returns a new event that only triggers when the provided filter function returns `true`.\n * @example\n * const spacePressEvent = keyboardEvent.filter((key) => key === 'Space');\n *\n * @param filter The filter function to apply to the event.\n * @returns A new event that only triggers when the provided filter function returns `true`.\n */\n filter<P extends T>(predicate: Predicate<T, P>): Event<P, R>;\n filter<P extends T>(filter: FilterFunction<T>): Event<P, R>;\n filter<P extends T>(filter: Filter<T, P>): Event<P, R> {\n const dispose = this.on(async (event: T) => {\n if (filteredEvent.size > 0 && (await filter(event))) {\n await filteredEvent(event as EventType<P>);\n }\n });\n const filteredEvent = new Event<P, R>(dispose);\n return filteredEvent;\n }\n\n /**\n * Returns a new event that will only be triggered once the provided filter function returns `true`.\n * @example\n * const escPressEvent = keyboardEvent.first((key) => key === 'Esc');\n * await escPressEvent.toPromise();\n *\n * @param filter - The filter function.\n * @returns A new event that will only be triggered once the provided filter function returns `true`.\n */\n first<P extends T>(predicate: Predicate<T, P>): Event<P, R>;\n first<P extends T>(filter: FilterFunction<T>): Event<P, R>;\n first<P extends T>(filter: Filter<T, P>): Event<P, R> {\n const dispose = this.on(async (event: T) => {\n if (filteredEvent.size > 0 && (await filter(event))) {\n await dispose();\n await filteredEvent(event as EventType<P>);\n }\n });\n const filteredEvent = new Event<P, R>(dispose);\n return filteredEvent;\n }\n\n /**\n * Returns a new promise that will be resolved once the provided filter function returns `true`.\n * @example\n * const escPressEvent = await keyboardEvent.firstAsync((key) => key === 'Esc');\n *\n * @param filter - The filter function.\n * @returns A new promise that will be resolved once the provided filter function returns `true`.\n */\n firstAsync<P extends T>(predicate: Predicate<T, P>): Promise<P>;\n firstAsync<P extends T>(filter: FilterFunction<T>): Promise<P>;\n firstAsync<P extends T>(filter: Filter<T, P>): Promise<P> {\n return this.first<P>(filter).onceAsync();\n }\n\n /**\n * Returns a new event that maps the values of this event using the provided mapper function.\n * @example\n * const keyPressEvent = keyboardEvent.map((key) => key.toUpperCase()); // ['a'] -> ['A']\n *\n * @param mapper A function that maps the values of this event to a new value.\n * @returns A new event that emits the mapped values.\n */\n map<M, MR = R>(mapper: Mapper<T, M>): Event<M, MR> {\n const dispose = this.on(async (event) => {\n if (mappedEvent.size > 0) {\n const value = await mapper(event);\n await mappedEvent(value as EventType<M>);\n }\n });\n const mappedEvent = new Event<M, MR>(dispose);\n return mappedEvent;\n }\n\n /**\n * Returns a new event that reduces the emitted values using the provided reducer function.\n * @example\n * const sumEvent = numberEvent.reduce((a, b) => a + b, 0);\n * sumEvent.on((sum) => console.log(sum)); // 1, 3, 6\n * sumEvent(1);\n * sumEvent(2);\n * sumEvent(3);\n *\n * @typeParam A The type of the accumulated value.\n * @typeParam AR The type of the reduced value.\n * @param {Reducer<T, A>} reducer The reducer function that accumulates the values emitted by this `Event`.\n * @param {A} init The initial value of the accumulated value.\n * @returns {Event<[A], AR>} A new `Event` that emits the reduced value.\n */\n reduce<A, AR = R>(reducer: Reducer<T, A>, init: A): Event<A, AR> {\n let value = init;\n const dispose = this.on(async (event) => {\n if (reducedEvent.size > 0) {\n value = await reducer(value, event);\n await reducedEvent(value as EventType<A>);\n }\n });\n const reducedEvent = new Event<A, AR>(dispose);\n return reducedEvent;\n }\n\n /**\n * Returns a new debounced event that will not fire until a certain amount of time has passed\n * since the last time it was triggered.\n * @example\n * const debouncedEvent = textInputEvent.debounce(100);\n * debouncedEvent.on((str) => console.log(str)); // 'test'\n * event('t');\n * event('te');\n * event('tes');\n * event('test');\n *\n * @param interval - The amount of time to wait before firing the debounced event, in milliseconds.\n * @returns A new debounced event.\n */\n debounce(interval: number): Event<T, R> {\n let timer: ReturnType<typeof setTimeout>;\n const dispose = this.on((event) => {\n clearTimeout(timer);\n timer = setTimeout(() => debouncedEvent(event as EventType<T>), interval);\n });\n const debouncedEvent = new Event<T, R>(dispose);\n return debouncedEvent;\n }\n}\n\n/**\n * Merges multiple events into a single event.\n * @example\n * const inputEvent = Event.merge(mouseEvent, keyboardEvent);\n *\n * @param events - The events to merge.\n * @returns The merged event.\n */\nexport const merge = <Events extends Event<any, any>[]>(...events: Events): Event<AllEventsParameters<Events>, AllEventsResults<Events>> => {\n const mergedEvent = new Event<AllEventsParameters<Events>, AllEventsResults<Events>>();\n events.forEach((event) => event.on(mergedEvent));\n return mergedEvent;\n};\n\n/**\n * Creates an event that triggers at a specified interval.\n * @example\n * const tickEvent = Event.interval(1000);\n * tickEvent.on((tickNumber) => console.log(tickNumber));\n *\n * @param interval - The interval at which to trigger the event.\n * @returns The interval event.\n */\nexport const createInterval = <R = void>(interval: number): Event<number, R> => {\n let counter = 0;\n const intervalEvent = new Event<number, R>(() => clearInterval(timerId));\n const timerId: ReturnType<typeof setInterval> = setInterval(() => intervalEvent(counter++), interval);\n return intervalEvent;\n};\n\n/**\n * Creates a new event instance.\n *\n * @typeParam T - An array of argument types that the event will accept.\n * @typeParam R - The return type of the event handler function.\n * @returns A new instance of the `Event` class.\n *\n * @example\n * const myEvent = createEvent<[string], number>();\n * myEvent.on((str: string) => str.length);\n * await myEvent('hello'); // [5]\n */\nexport const createEvent = <T, R = void>(): Event<T, R> => new Event<T, R>();\n\nexport default createEvent;\n\n/**\n * A type helper that extracts the event listener type\n *\n * @typeParam E - The event type.\n */\nexport type EventHandler<E> = E extends Event<infer T, infer R> ? Listener<T, R> : never;\n\n/**\n * A type helper that extracts the event filter type\n *\n * @typeParam E The event type to filter.\n */\nexport type EventFilter<E> = FilterFunction<EventParameters<E>>;\n\n/**\n * A type helper that extracts the event predicate type\n *\n * @typeParam E The event type to predicate.\n */\nexport type EventPredicate<E, P extends EventParameters<E>> = Predicate<EventParameters<E>, P>;\n\n/**\n * A type helper that extracts the event mapper type\n *\n * @typeParam E The event type to map.\n * @typeParam M The new type to map `E` to.\n */\nexport type EventMapper<E, M> = Mapper<EventParameters<E>, M>;\n\n/**\n * A type helper that extracts the event mapper type\n *\n * @typeParam E The type of event to reduce.\n * @typeParam M The type of reduced event.\n */\nexport type EventReducer<E, R> = Reducer<EventParameters<E>, R>;\n"],"names":["FunctionExt","Function","constructor","func","Object","setPrototypeOf","prototype","Dismiss","callback","pre","post","countdown","count","eventEmitter","listeners","event","Promise","all","map","listener","Event","dispose","fn","clear","size","length","lacks","indexOf","has","off","index","splice","on","push","once","oneTimeListener","onceAsync","resolve","filter","filteredEvent","first","firstAsync","mapper","mappedEvent","value","reduce","reducer","init","reducedEvent","debounce","interval","timer","clearTimeout","setTimeout","debouncedEvent","merge","events","mergedEvent","forEach","createInterval","counter","intervalEvent","clearInterval","timerId","setInterval","createEvent"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evnty",
|
|
3
3
|
"description": "0-Deps, simple, fast, for browser and node js reactive anonymous event library",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"main": "build/index.cjs",
|
|
@@ -16,13 +16,6 @@
|
|
|
16
16
|
"src/__tests__/example.js"
|
|
17
17
|
],
|
|
18
18
|
"sideEffects": false,
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
|
|
21
|
-
"test": "jest",
|
|
22
|
-
"test:build": "node src/__tests__/index.cjs && node src/__tests__/index.mjs",
|
|
23
|
-
"lint": "eslint src",
|
|
24
|
-
"prepare": "husky install"
|
|
25
|
-
},
|
|
26
19
|
"repository": {
|
|
27
20
|
"type": "git",
|
|
28
21
|
"url": "git+https://github.com/3axap4eHko/evnty.git"
|
|
@@ -58,9 +51,9 @@
|
|
|
58
51
|
"devDependencies": {
|
|
59
52
|
"@swc/jest": "^0.2.29",
|
|
60
53
|
"@types/jest": "^29.5.5",
|
|
61
|
-
"@types/node": "^20.
|
|
62
|
-
"@typescript-eslint/eslint-plugin": "^6.7.
|
|
63
|
-
"@typescript-eslint/parser": "^6.7.
|
|
54
|
+
"@types/node": "^20.8.2",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
|
56
|
+
"@typescript-eslint/parser": "^6.7.4",
|
|
64
57
|
"eslint": "^8.50.0",
|
|
65
58
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
66
59
|
"eslint-config-prettier": "^9.0.0",
|
|
@@ -70,6 +63,12 @@
|
|
|
70
63
|
"inop": "^0.4.2",
|
|
71
64
|
"jest": "^29.7.0",
|
|
72
65
|
"prettier": "^3.0.3",
|
|
73
|
-
"typescript": "^5.
|
|
66
|
+
"typescript": "^5.2.2"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
|
|
70
|
+
"test": "jest",
|
|
71
|
+
"test:build": "node src/__tests__/index.cjs && node src/__tests__/index.mjs",
|
|
72
|
+
"lint": "eslint src"
|
|
74
73
|
}
|
|
75
|
-
}
|
|
74
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;
|
|
2
2
|
|
|
3
|
-
export interface
|
|
4
|
-
():
|
|
3
|
+
export interface Callback<R = void> {
|
|
4
|
+
(): MaybePromise<R>;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export interface Listener<T, R = unknown> {
|
|
8
8
|
(event: T): MaybePromise<R | void>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export interface Dispose {
|
|
12
|
-
(): void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
export interface Result<T, E> {
|
|
16
12
|
ok: boolean;
|
|
17
13
|
result: T | E;
|
|
18
14
|
}
|
|
19
15
|
|
|
20
|
-
export interface Resolver<T, P> {
|
|
21
|
-
(event: T): Promise<P>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
16
|
export interface FilterFunction<T> {
|
|
25
17
|
(event: T): MaybePromise<boolean>;
|
|
26
18
|
}
|
|
@@ -55,32 +47,37 @@ export abstract class FunctionExt extends Function {
|
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
export interface Dismiss {
|
|
58
|
-
():
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface Task {
|
|
62
|
-
(): MaybePromise<unknown>;
|
|
50
|
+
(): MaybePromise<void>;
|
|
63
51
|
}
|
|
64
52
|
|
|
65
53
|
/**
|
|
66
54
|
* @internal
|
|
67
55
|
*/
|
|
68
56
|
export class Dismiss extends FunctionExt {
|
|
69
|
-
constructor(callback:
|
|
57
|
+
constructor(callback: Callback) {
|
|
70
58
|
super(callback);
|
|
71
59
|
}
|
|
72
60
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
61
|
+
pre(callback: Callback): Dismiss {
|
|
62
|
+
return new Dismiss(async () => {
|
|
63
|
+
await callback();
|
|
64
|
+
await this();
|
|
65
|
+
});
|
|
76
66
|
}
|
|
77
67
|
|
|
78
|
-
|
|
79
|
-
return async () => {
|
|
68
|
+
post(callback: Callback): Dismiss {
|
|
69
|
+
return new Dismiss(async () => {
|
|
70
|
+
await this();
|
|
71
|
+
await callback();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
countdown(count: number): Dismiss {
|
|
76
|
+
return new Dismiss(async () => {
|
|
80
77
|
if (!--count) {
|
|
81
78
|
await this();
|
|
82
79
|
}
|
|
83
|
-
};
|
|
80
|
+
});
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
|
|
@@ -115,7 +112,7 @@ export class Event<T, R> extends FunctionExt {
|
|
|
115
112
|
/**
|
|
116
113
|
* A function that disposes of the event and its listeners.
|
|
117
114
|
*/
|
|
118
|
-
readonly dispose:
|
|
115
|
+
readonly dispose: Callback;
|
|
119
116
|
|
|
120
117
|
/**
|
|
121
118
|
* Creates a new event.
|
|
@@ -126,15 +123,15 @@ export class Event<T, R> extends FunctionExt {
|
|
|
126
123
|
*
|
|
127
124
|
* @param dispose - A function to call on the event disposal.
|
|
128
125
|
*/
|
|
129
|
-
constructor(dispose?:
|
|
126
|
+
constructor(dispose?: Callback) {
|
|
130
127
|
const listeners: Listeners<T, R> = [];
|
|
131
128
|
const fn = (event: T) => eventEmitter(listeners, event);
|
|
132
129
|
|
|
133
130
|
super(fn);
|
|
134
131
|
this.listeners = listeners;
|
|
135
|
-
this.dispose = () => {
|
|
132
|
+
this.dispose = async () => {
|
|
136
133
|
this.clear();
|
|
137
|
-
dispose?.();
|
|
134
|
+
await dispose?.();
|
|
138
135
|
};
|
|
139
136
|
}
|
|
140
137
|
|
|
@@ -198,13 +195,6 @@ export class Event<T, R> extends FunctionExt {
|
|
|
198
195
|
return this.on(oneTimeListener);
|
|
199
196
|
}
|
|
200
197
|
|
|
201
|
-
/**
|
|
202
|
-
* Removes all listeners from the event.
|
|
203
|
-
*/
|
|
204
|
-
clear(): void {
|
|
205
|
-
this.listeners.splice(0);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
198
|
/**
|
|
209
199
|
* Returns a Promise that resolves with the first emitted by the event arguments.
|
|
210
200
|
* @returns A Promise that resolves with the first emitted by the event.
|
|
@@ -213,6 +203,13 @@ export class Event<T, R> extends FunctionExt {
|
|
|
213
203
|
return new Promise((resolve) => this.once((event) => resolve(event)));
|
|
214
204
|
}
|
|
215
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Removes all listeners from the event.
|
|
208
|
+
*/
|
|
209
|
+
clear(): void {
|
|
210
|
+
this.listeners.splice(0);
|
|
211
|
+
}
|
|
212
|
+
|
|
216
213
|
/**
|
|
217
214
|
* Returns a new event that only triggers when the provided filter function returns `true`.
|
|
218
215
|
* @example
|
|
@@ -255,6 +252,20 @@ export class Event<T, R> extends FunctionExt {
|
|
|
255
252
|
return filteredEvent;
|
|
256
253
|
}
|
|
257
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Returns a new promise that will be resolved once the provided filter function returns `true`.
|
|
257
|
+
* @example
|
|
258
|
+
* const escPressEvent = await keyboardEvent.firstAsync((key) => key === 'Esc');
|
|
259
|
+
*
|
|
260
|
+
* @param filter - The filter function.
|
|
261
|
+
* @returns A new promise that will be resolved once the provided filter function returns `true`.
|
|
262
|
+
*/
|
|
263
|
+
firstAsync<P extends T>(predicate: Predicate<T, P>): Promise<P>;
|
|
264
|
+
firstAsync<P extends T>(filter: FilterFunction<T>): Promise<P>;
|
|
265
|
+
firstAsync<P extends T>(filter: Filter<T, P>): Promise<P> {
|
|
266
|
+
return this.first<P>(filter).onceAsync();
|
|
267
|
+
}
|
|
268
|
+
|
|
258
269
|
/**
|
|
259
270
|
* Returns a new event that maps the values of this event using the provided mapper function.
|
|
260
271
|
* @example
|
package/src/__tests__/index.ts
DELETED
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
import createEvent, { merge, createInterval, Event, Dismiss, FunctionExt, EventHandler, FilterFunction, Predicate } from '../index';
|
|
2
|
-
|
|
3
|
-
describe('Anonymous Event test suite', () => {
|
|
4
|
-
test('FunctionExt extends from Function', () => {
|
|
5
|
-
expect(FunctionExt.prototype).toBeInstanceOf(Function);
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
test('Dismiss extends from FunctionExt', () => {
|
|
9
|
-
expect(Dismiss.prototype).toBeInstanceOf(FunctionExt);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('Event extends from FunctionExt', () => {
|
|
13
|
-
expect(Event.prototype).toBeInstanceOf(FunctionExt);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('Should be instantiable', () => {
|
|
17
|
-
expect(() => new Event()).not.toThrow();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('Should be instantiable', () => {
|
|
21
|
-
expect(() => createEvent<[number]>()).not.toThrow();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('Should be instantiable', () => {
|
|
25
|
-
expect(() => createEvent<[string], string>()).not.toThrow();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('Should call parent constructor', () => {
|
|
29
|
-
const EventOriginal = Object.getPrototypeOf(Event);
|
|
30
|
-
const EventMock = jest.fn();
|
|
31
|
-
|
|
32
|
-
Object.setPrototypeOf(Event, EventMock);
|
|
33
|
-
|
|
34
|
-
expect(() => new Event()).not.toThrow();
|
|
35
|
-
expect(EventMock).toHaveBeenCalled();
|
|
36
|
-
|
|
37
|
-
Object.setPrototypeOf(Event, EventOriginal);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('Should be callable', () => {
|
|
41
|
-
const event = new Event();
|
|
42
|
-
expect(() => event()).not.toThrow();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('Should check event existence', () => {
|
|
46
|
-
const event = new Event();
|
|
47
|
-
const listener: EventHandler<typeof event> = jest.fn();
|
|
48
|
-
event.on(listener);
|
|
49
|
-
expect(event.has(listener)).toEqual(true);
|
|
50
|
-
event.off(listener);
|
|
51
|
-
expect(event.lacks(listener)).toEqual(true);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('Should add event listener', async () => {
|
|
55
|
-
const event = new Event();
|
|
56
|
-
const listener = jest.fn();
|
|
57
|
-
event.on(listener);
|
|
58
|
-
await event('test');
|
|
59
|
-
expect(event.size).toEqual(1);
|
|
60
|
-
expect(listener).toHaveBeenCalledWith('test');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('Should remove existing event listener', async () => {
|
|
64
|
-
const event = new Event();
|
|
65
|
-
const listener = jest.fn();
|
|
66
|
-
event.on(listener);
|
|
67
|
-
event.off(listener);
|
|
68
|
-
await event('test');
|
|
69
|
-
expect(listener).not.toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('Should remove all existing event listeners', async () => {
|
|
73
|
-
const event = new Event();
|
|
74
|
-
const listener = jest.fn();
|
|
75
|
-
event.on(listener);
|
|
76
|
-
event.on(listener);
|
|
77
|
-
event.off(listener);
|
|
78
|
-
await event('test');
|
|
79
|
-
expect(listener).not.toHaveBeenCalled();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('Should not remove other event listeners', async () => {
|
|
83
|
-
const event = new Event();
|
|
84
|
-
const listener = jest.fn();
|
|
85
|
-
event.on(listener);
|
|
86
|
-
event.off(jest.fn());
|
|
87
|
-
await event('test');
|
|
88
|
-
expect(listener).toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('Should unsubscribe event', async () => {
|
|
92
|
-
const event = new Event();
|
|
93
|
-
const listener = jest.fn();
|
|
94
|
-
const unsubscribe = event.on(listener);
|
|
95
|
-
unsubscribe();
|
|
96
|
-
await event('test');
|
|
97
|
-
expect(listener).not.toHaveBeenCalled();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('Should add one time event listener', async () => {
|
|
101
|
-
const event = new Event();
|
|
102
|
-
const listener = jest.fn();
|
|
103
|
-
event.once(listener);
|
|
104
|
-
await event('test');
|
|
105
|
-
await event('test');
|
|
106
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('Should clear all events', async () => {
|
|
110
|
-
const event = new Event();
|
|
111
|
-
const listener = jest.fn();
|
|
112
|
-
event.on(listener);
|
|
113
|
-
|
|
114
|
-
event.clear();
|
|
115
|
-
expect(event.size).toEqual(0);
|
|
116
|
-
|
|
117
|
-
await event('test');
|
|
118
|
-
expect(listener).not.toHaveBeenCalled();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('Should return event promise', async () => {
|
|
122
|
-
const listener = jest.fn();
|
|
123
|
-
const event = new Event();
|
|
124
|
-
event.on(listener);
|
|
125
|
-
expect(listener).not.toBeCalled();
|
|
126
|
-
const promise = event.onceAsync();
|
|
127
|
-
await event('test');
|
|
128
|
-
const result = await promise;
|
|
129
|
-
expect(result).toEqual('test');
|
|
130
|
-
expect(listener).toBeCalledWith('test');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('Should create predicated and filtered events', async () => {
|
|
134
|
-
const listener = jest.fn();
|
|
135
|
-
|
|
136
|
-
type ClickEvent = { x: number; y: number; button: number };
|
|
137
|
-
type LeftClickEvent = ClickEvent & { button: 1 };
|
|
138
|
-
|
|
139
|
-
const clickEvent = createEvent<ClickEvent>();
|
|
140
|
-
const leftClickPredicate: Predicate<ClickEvent, LeftClickEvent> = (mouseClickEvent): mouseClickEvent is LeftClickEvent => mouseClickEvent.button === 1;
|
|
141
|
-
const leftClickPredicatedEvent = clickEvent.filter(leftClickPredicate);
|
|
142
|
-
leftClickPredicatedEvent.on(listener);
|
|
143
|
-
leftClickPredicatedEvent({ x: 1, y: 1, button: 1 });
|
|
144
|
-
|
|
145
|
-
const leftClickFiltered: FilterFunction<ClickEvent> = ({ button }) => button === 1;
|
|
146
|
-
const leftClickFilteredEvent = clickEvent.filter<LeftClickEvent>(leftClickFiltered);
|
|
147
|
-
leftClickFilteredEvent.on(listener);
|
|
148
|
-
leftClickFilteredEvent({ x: 1, y: 1, button: 1 });
|
|
149
|
-
|
|
150
|
-
await clickEvent({ x: 1, y: 1, button: 1 });
|
|
151
|
-
await clickEvent({ x: 1, y: 1, button: 2 });
|
|
152
|
-
|
|
153
|
-
expect(listener).toHaveBeenCalledTimes(4);
|
|
154
|
-
expect(listener).not.toHaveBeenCalledWith({ x: 1, y: 1, button: 2 });
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('Should create predicated event', async () => {
|
|
158
|
-
type TestEvent = { name: string; value: number };
|
|
159
|
-
const listener = jest.fn();
|
|
160
|
-
|
|
161
|
-
const predicate = (event: TestEvent): event is TestEvent & { name: 'two' } => event.name === 'two';
|
|
162
|
-
const predicateMock = jest.fn(predicate);
|
|
163
|
-
const event = new Event<TestEvent>();
|
|
164
|
-
const predicatedEvent = event.filter(predicateMock as unknown as typeof predicate);
|
|
165
|
-
expect(event.size).toEqual(1);
|
|
166
|
-
|
|
167
|
-
predicatedEvent.on(listener);
|
|
168
|
-
expect(predicatedEvent.size).toEqual(1);
|
|
169
|
-
|
|
170
|
-
await event({ name: 'one', value: 1 });
|
|
171
|
-
await event({ name: 'two', value: 2 });
|
|
172
|
-
await event({ name: 'three', value: 3 });
|
|
173
|
-
|
|
174
|
-
expect(predicateMock).toHaveBeenCalledTimes(3);
|
|
175
|
-
expect(predicateMock).toHaveBeenNthCalledWith(1, { name: 'one', value: 1 });
|
|
176
|
-
expect(predicateMock).toHaveBeenNthCalledWith(2, { name: 'two', value: 2 });
|
|
177
|
-
expect(predicateMock).toHaveBeenNthCalledWith(3, { name: 'three', value: 3 });
|
|
178
|
-
|
|
179
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
180
|
-
expect(listener).toHaveBeenCalledWith({ name: 'two', value: 2 });
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('Should create filtered event', async () => {
|
|
184
|
-
type TestEvent = { name: string; value: number };
|
|
185
|
-
const listener = jest.fn();
|
|
186
|
-
const filter = (event: TestEvent) => event.name === 'two';
|
|
187
|
-
const filterMock = jest.fn(filter);
|
|
188
|
-
const event = new Event<TestEvent>();
|
|
189
|
-
const filteredEvent = event.filter(filterMock as unknown as typeof filter);
|
|
190
|
-
expect(event.size).toEqual(1);
|
|
191
|
-
|
|
192
|
-
filteredEvent.on(listener);
|
|
193
|
-
expect(filteredEvent.size).toEqual(1);
|
|
194
|
-
|
|
195
|
-
await event({ name: 'one', value: 1 });
|
|
196
|
-
await event({ name: 'two', value: 2 });
|
|
197
|
-
await event({ name: 'three', value: 3 });
|
|
198
|
-
|
|
199
|
-
expect(filterMock).toHaveBeenCalledTimes(3);
|
|
200
|
-
expect(filterMock).toHaveBeenNthCalledWith(1, { name: 'one', value: 1 });
|
|
201
|
-
expect(filterMock).toHaveBeenNthCalledWith(2, { name: 'two', value: 2 });
|
|
202
|
-
expect(filterMock).toHaveBeenNthCalledWith(3, { name: 'three', value: 3 });
|
|
203
|
-
|
|
204
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
205
|
-
expect(listener).toHaveBeenCalledWith({ name: 'two', value: 2 });
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('Should create one time filtered event', async () => {
|
|
209
|
-
type TestEvent = { name: string; value: number };
|
|
210
|
-
|
|
211
|
-
const predicate = (event: TestEvent): event is TestEvent & { name: 'two' } => event.name === 'two';
|
|
212
|
-
const predicateMock = jest.fn(predicate);
|
|
213
|
-
const listener = jest.fn();
|
|
214
|
-
|
|
215
|
-
const event = new Event<TestEvent>();
|
|
216
|
-
const filteredEvent = event.first(predicateMock as unknown as typeof predicate);
|
|
217
|
-
expect(event.size).toEqual(1);
|
|
218
|
-
|
|
219
|
-
filteredEvent.on(listener);
|
|
220
|
-
expect(filteredEvent.size).toEqual(1);
|
|
221
|
-
|
|
222
|
-
await event({ name: 'one', value: 1 });
|
|
223
|
-
await event({ name: 'two', value: 2 });
|
|
224
|
-
await event({ name: 'three', value: 3 });
|
|
225
|
-
|
|
226
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
227
|
-
expect(listener).toHaveBeenCalledWith({ name: 'two', value: 2 });
|
|
228
|
-
|
|
229
|
-
expect(predicateMock).toHaveBeenCalledTimes(2);
|
|
230
|
-
expect(predicateMock).toHaveBeenNthCalledWith(1, { name: 'one', value: 1 });
|
|
231
|
-
expect(predicateMock).toHaveBeenNthCalledWith(2, { name: 'two', value: 2 });
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it('Should create mapped event', async () => {
|
|
235
|
-
const listener = jest.fn();
|
|
236
|
-
const mapper = jest.fn((value) => value * 2);
|
|
237
|
-
|
|
238
|
-
const event = new Event();
|
|
239
|
-
const mappedEvent = event.map(mapper);
|
|
240
|
-
expect(event.size).toEqual(1);
|
|
241
|
-
await event(1);
|
|
242
|
-
|
|
243
|
-
mappedEvent.on(listener);
|
|
244
|
-
expect(mappedEvent.size).toEqual(1);
|
|
245
|
-
|
|
246
|
-
await event(1);
|
|
247
|
-
await event(2);
|
|
248
|
-
await event(3);
|
|
249
|
-
|
|
250
|
-
expect(mapper).toHaveBeenCalledTimes(3);
|
|
251
|
-
expect(mapper).toHaveBeenNthCalledWith(1, 1);
|
|
252
|
-
expect(mapper).toHaveBeenNthCalledWith(2, 2);
|
|
253
|
-
expect(mapper).toHaveBeenNthCalledWith(3, 3);
|
|
254
|
-
|
|
255
|
-
expect(listener).toHaveBeenCalledTimes(3);
|
|
256
|
-
expect(listener).toHaveBeenCalledWith(2);
|
|
257
|
-
expect(listener).toHaveBeenCalledWith(4);
|
|
258
|
-
expect(listener).toHaveBeenCalledWith(6);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it('Should create reduced event', async () => {
|
|
262
|
-
const listener = jest.fn();
|
|
263
|
-
const reducer = jest.fn((result, value) => result + value);
|
|
264
|
-
|
|
265
|
-
const event = new Event();
|
|
266
|
-
const reducedEvent = event.reduce(reducer, 0);
|
|
267
|
-
expect(event.size).toEqual(1);
|
|
268
|
-
await event(1);
|
|
269
|
-
|
|
270
|
-
reducedEvent.on(listener);
|
|
271
|
-
expect(reducedEvent.size).toEqual(1);
|
|
272
|
-
|
|
273
|
-
await event(1);
|
|
274
|
-
await event(2);
|
|
275
|
-
await event(3);
|
|
276
|
-
|
|
277
|
-
expect(reducer).toHaveBeenCalledTimes(3);
|
|
278
|
-
expect(reducer).toHaveBeenNthCalledWith(1, 0, 1);
|
|
279
|
-
expect(reducer).toHaveBeenNthCalledWith(2, 0 + 1, 2);
|
|
280
|
-
expect(reducer).toHaveBeenNthCalledWith(3, 0 + 1 + 2, 3);
|
|
281
|
-
|
|
282
|
-
expect(listener).toHaveBeenCalledTimes(3);
|
|
283
|
-
expect(listener).toHaveBeenCalledWith(1);
|
|
284
|
-
expect(listener).toHaveBeenCalledWith(3);
|
|
285
|
-
expect(listener).toHaveBeenCalledWith(6);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('Should merge multiple events', async () => {
|
|
289
|
-
const listener = jest.fn();
|
|
290
|
-
|
|
291
|
-
const event1 = new Event<string, number>();
|
|
292
|
-
const event2 = new Event<number, boolean>();
|
|
293
|
-
const event3 = new Event<boolean, string>();
|
|
294
|
-
const mergedEvent = merge(event1, event2, event3);
|
|
295
|
-
|
|
296
|
-
mergedEvent.on(listener);
|
|
297
|
-
|
|
298
|
-
await event1('a');
|
|
299
|
-
await event2(1);
|
|
300
|
-
await event3(true);
|
|
301
|
-
await mergedEvent('b');
|
|
302
|
-
await mergedEvent(2);
|
|
303
|
-
await mergedEvent(false);
|
|
304
|
-
|
|
305
|
-
expect(listener).toHaveBeenCalledTimes(6);
|
|
306
|
-
expect(listener).toHaveBeenNthCalledWith(1, 'a');
|
|
307
|
-
expect(listener).toHaveBeenNthCalledWith(2, 1);
|
|
308
|
-
expect(listener).toHaveBeenNthCalledWith(3, true);
|
|
309
|
-
expect(listener).toHaveBeenNthCalledWith(4, 'b');
|
|
310
|
-
expect(listener).toHaveBeenNthCalledWith(5, 2);
|
|
311
|
-
expect(listener).toHaveBeenNthCalledWith(6, false);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it('Should create interval events', async () => {
|
|
315
|
-
const listener = jest.fn();
|
|
316
|
-
const event = createInterval(10);
|
|
317
|
-
event.on(listener);
|
|
318
|
-
await event.onceAsync();
|
|
319
|
-
expect(listener).toBeCalledWith(0);
|
|
320
|
-
event.dispose();
|
|
321
|
-
const result = await Promise.race([new Promise((resolve) => setTimeout(resolve, 100, null)), event.onceAsync()]);
|
|
322
|
-
expect(result).toEqual(null);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it('Should dismiss event listener', async () => {
|
|
326
|
-
const listener = jest.fn();
|
|
327
|
-
const event = createInterval(10);
|
|
328
|
-
event.on(listener);
|
|
329
|
-
await event.onceAsync();
|
|
330
|
-
expect(listener).toBeCalledWith(0);
|
|
331
|
-
event.dispose();
|
|
332
|
-
const result = await Promise.race([new Promise(process.nextTick).then(Boolean), event.onceAsync()]);
|
|
333
|
-
expect(result).toEqual(true);
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it('Should dismiss event after task finished', async () => {
|
|
337
|
-
const listener = jest.fn();
|
|
338
|
-
const event = new Event();
|
|
339
|
-
const dismiss = event.on(listener);
|
|
340
|
-
await event();
|
|
341
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
342
|
-
const nextTick = new Promise(process.nextTick);
|
|
343
|
-
dismiss.after(() => nextTick);
|
|
344
|
-
await nextTick;
|
|
345
|
-
await event();
|
|
346
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it('Should dismiss event after certain events', async () => {
|
|
350
|
-
const listener = jest.fn();
|
|
351
|
-
const event = new Event();
|
|
352
|
-
const dismiss = event.on(listener);
|
|
353
|
-
const timesCallback = dismiss.afterTimes(2);
|
|
354
|
-
await event();
|
|
355
|
-
await timesCallback();
|
|
356
|
-
await event();
|
|
357
|
-
await timesCallback();
|
|
358
|
-
await event();
|
|
359
|
-
await timesCallback();
|
|
360
|
-
await event();
|
|
361
|
-
expect(listener).toHaveBeenCalledTimes(2);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
it('Should return listeners values', async () => {
|
|
365
|
-
const event = new Event<string, number | string>();
|
|
366
|
-
event.on(() => 1);
|
|
367
|
-
event.on(() => 'test');
|
|
368
|
-
event.on(() => {});
|
|
369
|
-
const result = (await event('test')) satisfies (number | string | void)[];
|
|
370
|
-
expect(result).toEqual([1, 'test', undefined]);
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
it('Should debounce events', async () => {
|
|
374
|
-
const listener = jest.fn();
|
|
375
|
-
const event = new Event<string, number | string>();
|
|
376
|
-
const debouncedEvent = event.debounce(10);
|
|
377
|
-
debouncedEvent.on(listener);
|
|
378
|
-
await event('test1');
|
|
379
|
-
await event('test2');
|
|
380
|
-
await debouncedEvent.onceAsync();
|
|
381
|
-
expect(listener).toBeCalledTimes(1);
|
|
382
|
-
});
|
|
383
|
-
});
|