@homer0/events-hub 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +129 -0
- package/dist/esm/index.js +154 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.js +180 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Leonardo Apiwan (@homer0)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# 🚚 Events hub
|
|
2
|
+
|
|
3
|
+
A simple implementation of a pubsub service for handling events.
|
|
4
|
+
|
|
5
|
+
It doesn't require any configuration or have customization options: You listen for an event with `on`, emit an event with `emit`, and, you could reduce a variable through an event with `reduce` (or `reduceSync`).
|
|
6
|
+
|
|
7
|
+
## 🍿 Usage
|
|
8
|
+
|
|
9
|
+
- ⚙️ [Examples](#%EF%B8%8F-examples)
|
|
10
|
+
- 🤘 [Development](#-development)
|
|
11
|
+
|
|
12
|
+
### ⚙️ Examples
|
|
13
|
+
|
|
14
|
+
First, let's create an instance of the hub:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { eventsHub } from '@homer0/events-hub';
|
|
18
|
+
const events = eventsHub();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
#### Listen and emit
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// Add the listener
|
|
25
|
+
events.on('my-event', () => {
|
|
26
|
+
console.log('The event listener was called!');
|
|
27
|
+
});
|
|
28
|
+
// Emit the event
|
|
29
|
+
events.emit('my-event');
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Quite simple, right? You can also send any number of parameters to the events:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// Define the listener, to get the parameters' types.
|
|
36
|
+
type LoginListener = (username: string, password: string) => void | Promise<void>;
|
|
37
|
+
// Add the listener
|
|
38
|
+
events.on<LoginListener>('user-login', async (username, password) => {
|
|
39
|
+
try {
|
|
40
|
+
const userInfo = await someAuthService.login(username, password);
|
|
41
|
+
events.emit('user-login-successfull', userInfo);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
events.emit('user-login-failed', error);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
//...
|
|
48
|
+
// Emit the event.
|
|
49
|
+
events.emit('user-login', 'rosario', 'p4ssword');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### Multiple events
|
|
53
|
+
|
|
54
|
+
All methods that support an event name also support an `Array` with a list of them, which means you can set the same listener for multiple events:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// Add the listener
|
|
58
|
+
events.on(['logout-route', 'unauthorized-request'], () => {
|
|
59
|
+
someAuthService.signout();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
//...
|
|
63
|
+
// This would trigger the listener.
|
|
64
|
+
events.emit('logout-route');
|
|
65
|
+
// and this too (if it fails :P).
|
|
66
|
+
someRequest()
|
|
67
|
+
.then(() => ... )
|
|
68
|
+
.catch((error) => {
|
|
69
|
+
if (error.code === 401) {
|
|
70
|
+
events.emit('unauthorized-request');
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Listening only once
|
|
76
|
+
|
|
77
|
+
You can use the `once` method to create a subscription that will be removed after being called one time. Following the previous examples, let's say that the `signout` method makes a redirection to a different URL (because of reasons); that means that there's no need to keep the listener around once executed:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
events.once(['logout-route', 'unauthorized-request'], () => {
|
|
81
|
+
someAuthService.signout();
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
After `.signout()` gets called, the listener will be removed.
|
|
86
|
+
|
|
87
|
+
> Yeah, technically, if `.signout()` does a redirection, it could stop the execution... but you get the point of the example.
|
|
88
|
+
|
|
89
|
+
#### Reducing a variable
|
|
90
|
+
|
|
91
|
+
The service has a `reduce` method that is basically the same as `emit`, but the first parameter may be modified by the listeners.
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
events.on('filter-users-list', async (list) => {
|
|
95
|
+
const fromAPI = await getUsersToFilterFromSomeAPI('...', { list });
|
|
96
|
+
return list.filter((item) => !fromAPI.includes(item));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const usersList = ['Rosario', 'Pilar'];
|
|
100
|
+
const newUsersList = await events.reduce('filter-users-list', usersList);
|
|
101
|
+
// `newUsersList` would now be the users that weren't in the API.
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
> After the `target`, you can send any number of parameters for the listeners/reducers.
|
|
105
|
+
|
|
106
|
+
There's also a synchronous version of the `reduce` method: `reduceSync`.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
events.on('filter-users-list', (list) => {
|
|
110
|
+
list.slice(1, 3);
|
|
111
|
+
return list;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const usersList = ['charito', 'Rosario', 'Pilar', 'pili'];
|
|
115
|
+
const newUsersList = events.reduceSync('filter-users-list', usersList);
|
|
116
|
+
console.log(newUsersList);
|
|
117
|
+
// Will log ['Rosario', 'Pilar']
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 🤘 Development
|
|
121
|
+
|
|
122
|
+
As this project is part of the `packages` monorepo, it requires Yarn, and some of the tooling, like ESLint and Husky, are installed on the root's `package.json`.
|
|
123
|
+
|
|
124
|
+
#### Yarn tasks
|
|
125
|
+
|
|
126
|
+
| Task | Description |
|
|
127
|
+
| ------- | -------------------- |
|
|
128
|
+
| `test` | Runs the unit tests. |
|
|
129
|
+
| `build` | Bundles the project. |
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var EventsHub = class {
|
|
3
|
+
events = {};
|
|
4
|
+
onceWrappers = {};
|
|
5
|
+
getSubscribers(event) {
|
|
6
|
+
if (!this.events[event]) {
|
|
7
|
+
this.events[event] = [];
|
|
8
|
+
}
|
|
9
|
+
return this.events[event];
|
|
10
|
+
}
|
|
11
|
+
on(event, listener) {
|
|
12
|
+
const events = Array.isArray(event) ? event : [event];
|
|
13
|
+
events.forEach((name) => {
|
|
14
|
+
const subscribers = this.getSubscribers(name);
|
|
15
|
+
if (!subscribers.includes(listener)) {
|
|
16
|
+
subscribers.push(listener);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
return () => this.off(event, listener);
|
|
20
|
+
}
|
|
21
|
+
once(event, listener) {
|
|
22
|
+
const events = Array.isArray(event) ? event : [event];
|
|
23
|
+
let wrapper = events.reduce((acc, name) => {
|
|
24
|
+
if (acc)
|
|
25
|
+
return acc;
|
|
26
|
+
const onceWrapper = this.onceWrappers[name];
|
|
27
|
+
if (Array.isArray(onceWrapper)) {
|
|
28
|
+
const existing = onceWrapper.find((item) => item.original === listener);
|
|
29
|
+
if (existing) {
|
|
30
|
+
return existing.wrapper;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
this.onceWrappers[name] = [];
|
|
35
|
+
return null;
|
|
36
|
+
}, null);
|
|
37
|
+
if (!wrapper) {
|
|
38
|
+
const newWrapper = (...args) => listener(...args);
|
|
39
|
+
newWrapper.once = true;
|
|
40
|
+
wrapper = newWrapper;
|
|
41
|
+
events.forEach((name) => {
|
|
42
|
+
this.onceWrappers[name].push({
|
|
43
|
+
wrapper,
|
|
44
|
+
original: listener
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return this.on(event, wrapper);
|
|
49
|
+
}
|
|
50
|
+
off(event, listener) {
|
|
51
|
+
const isArray = Array.isArray(event);
|
|
52
|
+
const events = isArray ? event : [event];
|
|
53
|
+
const result = events.map((name) => {
|
|
54
|
+
const subscribers = this.getSubscribers(name);
|
|
55
|
+
const onceSubscribers = this.onceWrappers[name];
|
|
56
|
+
let found = false;
|
|
57
|
+
let index = subscribers.indexOf(listener);
|
|
58
|
+
if (index > -1) {
|
|
59
|
+
found = true;
|
|
60
|
+
if ("once" in listener && onceSubscribers) {
|
|
61
|
+
const wrapperIndex = onceSubscribers.findIndex((item) => item.wrapper === listener);
|
|
62
|
+
onceSubscribers.splice(wrapperIndex, 1);
|
|
63
|
+
}
|
|
64
|
+
subscribers.splice(index, 1);
|
|
65
|
+
} else if (onceSubscribers) {
|
|
66
|
+
index = onceSubscribers.findIndex((item) => item.original === listener);
|
|
67
|
+
if (index > -1) {
|
|
68
|
+
found = true;
|
|
69
|
+
const originalIndex = subscribers.indexOf(onceSubscribers[index].original);
|
|
70
|
+
subscribers.splice(originalIndex, 1);
|
|
71
|
+
onceSubscribers.splice(index, 1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return found;
|
|
75
|
+
});
|
|
76
|
+
return isArray ? result : result[0];
|
|
77
|
+
}
|
|
78
|
+
emit(event, ...args) {
|
|
79
|
+
const toClean = [];
|
|
80
|
+
const events = Array.isArray(event) ? event : [event];
|
|
81
|
+
events.forEach((name) => {
|
|
82
|
+
this.getSubscribers(name).forEach((subscriber) => {
|
|
83
|
+
subscriber(...args);
|
|
84
|
+
if ("once" in subscriber) {
|
|
85
|
+
toClean.push({
|
|
86
|
+
event: name,
|
|
87
|
+
listener: subscriber
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
toClean.forEach((info) => this.off(info.event, info.listener));
|
|
93
|
+
}
|
|
94
|
+
async reduce(event, target, ...args) {
|
|
95
|
+
const events = Array.isArray(event) ? event : [event];
|
|
96
|
+
const toClean = [];
|
|
97
|
+
const result = await events.reduce((eventAcc, name) => eventAcc.then((eventCurrent) => {
|
|
98
|
+
const subscribers = this.getSubscribers(name);
|
|
99
|
+
return subscribers.reduce((subAcc, subscriber) => subAcc.then((subCurrent) => {
|
|
100
|
+
let useCurrent;
|
|
101
|
+
if (Array.isArray(subCurrent)) {
|
|
102
|
+
useCurrent = subCurrent.slice();
|
|
103
|
+
} else if (typeof subCurrent === "object") {
|
|
104
|
+
useCurrent = { ...subCurrent };
|
|
105
|
+
} else {
|
|
106
|
+
useCurrent = subCurrent;
|
|
107
|
+
}
|
|
108
|
+
const nextStep = subscriber(...[useCurrent, ...args]);
|
|
109
|
+
if ("once" in subscriber) {
|
|
110
|
+
toClean.push({
|
|
111
|
+
event: name,
|
|
112
|
+
listener: subscriber
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return nextStep;
|
|
116
|
+
}), Promise.resolve(eventCurrent));
|
|
117
|
+
}), Promise.resolve(target));
|
|
118
|
+
toClean.forEach((info) => this.off(info.event, info.listener));
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
reduceSync(event, target, ...args) {
|
|
122
|
+
const events = Array.isArray(event) ? event : [event];
|
|
123
|
+
const toClean = [];
|
|
124
|
+
const result = events.reduce((eventAcc, name) => {
|
|
125
|
+
const subscribers = this.getSubscribers(name);
|
|
126
|
+
return subscribers.reduce((subAcc, subscriber) => {
|
|
127
|
+
let useCurrent;
|
|
128
|
+
if (Array.isArray(subAcc)) {
|
|
129
|
+
useCurrent = subAcc.slice();
|
|
130
|
+
} else if (typeof subAcc === "object") {
|
|
131
|
+
useCurrent = { ...subAcc };
|
|
132
|
+
} else {
|
|
133
|
+
useCurrent = subAcc;
|
|
134
|
+
}
|
|
135
|
+
const nextStep = subscriber(...[useCurrent, ...args]);
|
|
136
|
+
if ("once" in subscriber) {
|
|
137
|
+
toClean.push({
|
|
138
|
+
event: name,
|
|
139
|
+
listener: subscriber
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return nextStep;
|
|
143
|
+
}, eventAcc);
|
|
144
|
+
}, target);
|
|
145
|
+
toClean.forEach((info) => this.off(info.event, info.listener));
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var eventsHub = () => new EventsHub();
|
|
150
|
+
export {
|
|
151
|
+
EventsHub,
|
|
152
|
+
eventsHub
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/no-explicit-any -- It's a generic function.\ntype GenericFn = (...args: any[]) => any;\ntype GenericParams = Parameters<GenericFn>;\ntype OnceWrapperFn = GenericFn & {\n once: boolean;\n};\n/**\n * When the service receives a \"once subscription\", it needs to track the listener so it\n * gets removed after being called once, and to avoid modifying the original function, it\n * creates a wrapper function that has the \"once\" property set to true. The wrapper and\n * the original function are stored in case `off` is called before the wrapper gets\n * triggered; it will receive the original function, not the wrapper, so the class needs a\n * way to map them together.\n */\ntype OnceWrapper = {\n /**\n * A simple wrapper for the original listener, with the difference that it has a `once`\n * property set to `true`.\n */\n wrapper: OnceWrapperFn;\n /**\n * The original listener that will be called once.\n */\n original: GenericFn;\n};\n/**\n * A minimal implementation of an events handler service.\n */\nexport class EventsHub {\n /**\n * A dictionary of the events and their listeners.\n */\n protected events: Record<string, GenericFn[]> = {};\n /**\n * A dictionary of wrappers that were created for \"one time subscriptions\". This is\n * used by the {@link EventsHub.off}: if it doesn't find the subscriber as it is, it\n * will look for a wrapper and remove it.\n */\n protected onceWrappers: Record<string, OnceWrapper[]> = {};\n /**\n * Gets all the listeners for a specific event.\n * The list is returned by reference, so it can be modified once obtained.\n *\n * @param event The name of the event.\n */\n protected getSubscribers(event: string): (GenericFn | OnceWrapperFn)[] {\n if (!this.events[event]) {\n this.events[event] = [];\n }\n\n return this.events[event]!;\n }\n on<ListenerFn extends GenericFn = GenericFn>(\n event: string,\n listener: ListenerFn,\n ): () => boolean;\n on<ListenerFn extends GenericFn = GenericFn>(\n event: string[],\n listener: ListenerFn,\n ): () => boolean[];\n on<ListenerFn extends GenericFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): () => boolean | boolean[];\n /**\n * Adds a new event listener.\n *\n * @param event An event name or a list of them.\n * @param listener The listener function.\n * @returns An unsubscribe function to remove the listene(s).\n * @template ListenerFn The type of the listener function.\n * @example\n *\n * const events = new EventsHub();\n * type Listener = (arg0: string) => void;\n * const unsubscribe = events.on<Listener>('event', (arg0) => {\n * console.log(`Event received: ${arg0}`);\n * });\n *\n */\n on<ListenerFn extends GenericFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): () => boolean | boolean[] {\n const events = Array.isArray(event) ? event : [event];\n events.forEach((name) => {\n const subscribers = this.getSubscribers(name);\n if (!subscribers.includes(listener)) {\n subscribers.push(listener);\n }\n });\n\n return () => this.off<ListenerFn>(event, listener);\n }\n once<ListenerFn extends GenericFn = GenericFn>(\n event: string,\n listener: ListenerFn,\n ): () => boolean;\n once<ListenerFn extends GenericFn = GenericFn>(\n event: string[],\n listener: ListenerFn,\n ): () => boolean[];\n once<ListenerFn extends GenericFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): () => boolean | boolean[];\n /**\n * Adds an event listener that will only be executed once.\n *\n * @param event An event name or a list of them.\n * @param listener The listener function.\n * @returns An unsubscribe function to remove the listener(s).\n * @template ListenerFn The type of the listener function.\n * @example\n *\n * const events = new EventsHub();\n * type Listener = (arg0: string) => void;\n * const unsubscribe = events.once<Listener>('event', (arg0) => {\n * console.log(`Event received: ${arg0}`);\n * });\n *\n */\n once<ListenerFn extends GenericFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): () => boolean | boolean[] {\n const events = Array.isArray(event) ? event : [event];\n // Try to find an existing wrapper.\n let wrapper = events.reduce<OnceWrapperFn | null>((acc, name) => {\n // A previous iteration found a wrapper, so `continue`.\n if (acc) return acc;\n\n // A list of wrappers exists for the event, so, let's try an find one for this function.\n const onceWrapper = this.onceWrappers[name];\n if (Array.isArray(onceWrapper)) {\n const existing = onceWrapper.find((item) => item.original === listener);\n if (existing) {\n return existing.wrapper;\n }\n\n return null;\n }\n\n // The list didn't even exists, let's at least create it.\n this.onceWrappers[name] = [];\n return null;\n }, null);\n\n if (!wrapper) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- It's a generic function.\n const newWrapper = (...args: any[]) => listener(...args);\n newWrapper.once = true;\n wrapper = newWrapper;\n events.forEach((name) => {\n this.onceWrappers[name]!.push({\n wrapper: wrapper!,\n original: listener,\n });\n });\n }\n\n return this.on(event, wrapper);\n }\n off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(\n event: string[],\n listener: ListenerFn,\n ): boolean[];\n off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(\n event: string,\n listener: ListenerFn,\n ): boolean;\n off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): boolean | boolean[];\n /**\n * Removes an event listener.\n *\n * @param event An event name or a list of them.\n * @param listener The listener function.\n * @returns If `event` was a `string`, it will return whether or not the listener was\n * found and removed; but if `event`\n * was an `Array`, it will return a list of boolean values.\n * @template ListenerFn The type of the listener function.\n * @example\n *\n * const events = new EventsHub();\n * const listener = (arg0) => {\n * console.log(`Event received: ${arg0}`);\n * };\n * events.on('event', listener); // subscribe.\n * events.off('event', listener); // manually unsubscribe.\n *\n */\n off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): boolean | boolean[] {\n const isArray = Array.isArray(event);\n const events = isArray ? event : [event];\n const result = events.map((name) => {\n const subscribers = this.getSubscribers(name);\n const onceSubscribers = this.onceWrappers[name];\n let found = false;\n let index = subscribers.indexOf(listener);\n if (index > -1) {\n found = true;\n /**\n * If the listener had the `once` flag, then it's a wrapper, so it needs to remove\n * it from the wrappers list too.\n */\n if ('once' in listener && onceSubscribers) {\n const wrapperIndex = onceSubscribers.findIndex(\n (item) => item.wrapper === listener,\n );\n onceSubscribers.splice(wrapperIndex, 1);\n }\n subscribers.splice(index, 1);\n } else if (onceSubscribers) {\n /**\n * If it couldn't found the subscriber, maybe it's because it's the original\n * listener of a wrapper.\n */\n index = onceSubscribers.findIndex((item) => item.original === listener);\n if (index > -1) {\n found = true;\n const originalIndex = subscribers.indexOf(onceSubscribers[index]!.original);\n subscribers.splice(originalIndex, 1);\n onceSubscribers.splice(index, 1);\n }\n }\n\n return found;\n });\n\n return isArray ? result : result[0]!;\n }\n /**\n * Emits an event and call all its listeners.\n *\n * @param event An event name or a list of them.\n * @param args A list of parameters to send to the listeners.\n * @template Args The type of the parameters to send to the listeners.\n * @example\n *\n * const events = new EventsHub();\n * events.on('event', (arg0) => {\n * console.log(`Event received: ${arg0}`);\n * });\n * events.emit('event', 'Hello'); // prints \"Event received: Hello\"\n *\n */\n emit<Args extends GenericParams>(event: string | string[], ...args: Args): void {\n const toClean: { event: string; listener: GenericFn }[] = [];\n const events = Array.isArray(event) ? event : [event];\n events.forEach((name) => {\n this.getSubscribers(name).forEach((subscriber) => {\n subscriber(...args);\n if ('once' in subscriber) {\n toClean.push({\n event: name,\n listener: subscriber,\n });\n }\n });\n });\n\n toClean.forEach((info) => this.off(info.event, info.listener));\n }\n /**\n * Asynchronously reduces a target using an event. It's like emit, but the events\n * listener return a modified (or not) version of the `target`.\n *\n * @param event An event name or a list of them.\n * @param target The variable to reduce with the reducers/listeners.\n * @param args A list of parameters to send to the reducers/listeners.\n * @returns A version of the `target` processed by the listeners.\n * @template Target The type of the target.\n * @template Args The type of the parameters to send to the reducers/listeners.\n * @example\n *\n * const events = new EventsHub();\n * events.on('event', async (target, arg0) => {\n * const data = await fetch(`https://api.example.com/${arg0}`);\n * target.push(data);\n * return target;\n * });\n * const result = await events.reduce('event', [], 'Hello');\n * // result would be a list of data fetched from the API.\n *\n */\n async reduce<Target, Args extends GenericParams>(\n event: string | string[],\n target: Target,\n ...args: Args\n ): Promise<Target> {\n const events = Array.isArray(event) ? event : [event];\n const toClean: { event: string; listener: GenericFn }[] = [];\n const result = await events.reduce<Promise<Target>>(\n (eventAcc, name) =>\n eventAcc.then((eventCurrent) => {\n const subscribers = this.getSubscribers(name);\n return subscribers.reduce(\n (subAcc, subscriber) =>\n subAcc.then((subCurrent) => {\n let useCurrent;\n if (Array.isArray(subCurrent)) {\n useCurrent = subCurrent.slice();\n } else if (typeof subCurrent === 'object') {\n useCurrent = { ...subCurrent };\n } else {\n useCurrent = subCurrent;\n }\n\n const nextStep = subscriber(...[useCurrent, ...args]);\n if ('once' in subscriber) {\n toClean.push({\n event: name,\n listener: subscriber,\n });\n }\n\n return nextStep;\n }),\n Promise.resolve(eventCurrent),\n );\n }),\n Promise.resolve(target),\n );\n\n toClean.forEach((info) => this.off(info.event, info.listener));\n\n return result;\n }\n /**\n * Synchronously reduces a target using an event. It's like emit, but the events\n * listener return a modified (or not) version of the `target`.\n *\n * @param event An event name or a list of them.\n * @param target The variable to reduce with the reducers/listeners.\n * @param args A list of parameters to send to the reducers/listeners.\n * @returns A version of the `target` processed by the listeners.\n * @template Target The type of the target.\n * @template Args The type of the parameters to send to the reducers/listeners.\n * @example\n *\n * const events = new EventsHub();\n * events.on('event', (target, arg0) => {\n * target.push(arg0);\n * return target;\n * });\n * events.reduce('event', [], 'Hello'); // returns ['Hello']\n *\n */\n reduceSync<Target, ReducerArgs extends GenericParams>(\n event: string | string[],\n target: Target,\n ...args: ReducerArgs\n ): Target {\n const events = Array.isArray(event) ? event : [event];\n const toClean: { event: string; listener: GenericFn }[] = [];\n const result = events.reduce((eventAcc, name) => {\n const subscribers = this.getSubscribers(name);\n return subscribers.reduce((subAcc, subscriber) => {\n let useCurrent;\n if (Array.isArray(subAcc)) {\n useCurrent = subAcc.slice();\n } else if (typeof subAcc === 'object') {\n useCurrent = { ...subAcc };\n } else {\n useCurrent = subAcc;\n }\n\n const nextStep = subscriber(...[useCurrent, ...args]);\n if ('once' in subscriber) {\n toClean.push({\n event: name,\n listener: subscriber,\n });\n }\n\n return nextStep;\n }, eventAcc);\n }, target);\n\n toClean.forEach((info) => this.off(info.event, info.listener));\n return result;\n }\n}\n/**\n * Shorthand for `new EventsHub()`.\n *\n * @returns A new instance of {@link EventsHub}.\n */\nexport const eventsHub = () => new EventsHub();\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
declare type GenericFn = (...args: any[]) => any;
|
|
2
|
+
declare type GenericParams = Parameters<GenericFn>;
|
|
3
|
+
declare type OnceWrapperFn = GenericFn & {
|
|
4
|
+
once: boolean;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* When the service receives a "once subscription", it needs to track the listener so it
|
|
8
|
+
* gets removed after being called once, and to avoid modifying the original function, it
|
|
9
|
+
* creates a wrapper function that has the "once" property set to true. The wrapper and
|
|
10
|
+
* the original function are stored in case `off` is called before the wrapper gets
|
|
11
|
+
* triggered; it will receive the original function, not the wrapper, so the class needs a
|
|
12
|
+
* way to map them together.
|
|
13
|
+
*/
|
|
14
|
+
declare type OnceWrapper = {
|
|
15
|
+
/**
|
|
16
|
+
* A simple wrapper for the original listener, with the difference that it has a `once`
|
|
17
|
+
* property set to `true`.
|
|
18
|
+
*/
|
|
19
|
+
wrapper: OnceWrapperFn;
|
|
20
|
+
/**
|
|
21
|
+
* The original listener that will be called once.
|
|
22
|
+
*/
|
|
23
|
+
original: GenericFn;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A minimal implementation of an events handler service.
|
|
27
|
+
*/
|
|
28
|
+
declare class EventsHub {
|
|
29
|
+
/**
|
|
30
|
+
* A dictionary of the events and their listeners.
|
|
31
|
+
*/
|
|
32
|
+
protected events: Record<string, GenericFn[]>;
|
|
33
|
+
/**
|
|
34
|
+
* A dictionary of wrappers that were created for "one time subscriptions". This is
|
|
35
|
+
* used by the {@link EventsHub.off}: if it doesn't find the subscriber as it is, it
|
|
36
|
+
* will look for a wrapper and remove it.
|
|
37
|
+
*/
|
|
38
|
+
protected onceWrappers: Record<string, OnceWrapper[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Gets all the listeners for a specific event.
|
|
41
|
+
* The list is returned by reference, so it can be modified once obtained.
|
|
42
|
+
*
|
|
43
|
+
* @param event The name of the event.
|
|
44
|
+
*/
|
|
45
|
+
protected getSubscribers(event: string): (GenericFn | OnceWrapperFn)[];
|
|
46
|
+
on<ListenerFn extends GenericFn = GenericFn>(event: string, listener: ListenerFn): () => boolean;
|
|
47
|
+
on<ListenerFn extends GenericFn = GenericFn>(event: string[], listener: ListenerFn): () => boolean[];
|
|
48
|
+
on<ListenerFn extends GenericFn = GenericFn>(event: string | string[], listener: ListenerFn): () => boolean | boolean[];
|
|
49
|
+
once<ListenerFn extends GenericFn = GenericFn>(event: string, listener: ListenerFn): () => boolean;
|
|
50
|
+
once<ListenerFn extends GenericFn = GenericFn>(event: string[], listener: ListenerFn): () => boolean[];
|
|
51
|
+
once<ListenerFn extends GenericFn = GenericFn>(event: string | string[], listener: ListenerFn): () => boolean | boolean[];
|
|
52
|
+
off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(event: string[], listener: ListenerFn): boolean[];
|
|
53
|
+
off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(event: string, listener: ListenerFn): boolean;
|
|
54
|
+
off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(event: string | string[], listener: ListenerFn): boolean | boolean[];
|
|
55
|
+
/**
|
|
56
|
+
* Emits an event and call all its listeners.
|
|
57
|
+
*
|
|
58
|
+
* @param event An event name or a list of them.
|
|
59
|
+
* @param args A list of parameters to send to the listeners.
|
|
60
|
+
* @template Args The type of the parameters to send to the listeners.
|
|
61
|
+
* @example
|
|
62
|
+
*
|
|
63
|
+
* const events = new EventsHub();
|
|
64
|
+
* events.on('event', (arg0) => {
|
|
65
|
+
* console.log(`Event received: ${arg0}`);
|
|
66
|
+
* });
|
|
67
|
+
* events.emit('event', 'Hello'); // prints "Event received: Hello"
|
|
68
|
+
*
|
|
69
|
+
*/
|
|
70
|
+
emit<Args extends GenericParams>(event: string | string[], ...args: Args): void;
|
|
71
|
+
/**
|
|
72
|
+
* Asynchronously reduces a target using an event. It's like emit, but the events
|
|
73
|
+
* listener return a modified (or not) version of the `target`.
|
|
74
|
+
*
|
|
75
|
+
* @param event An event name or a list of them.
|
|
76
|
+
* @param target The variable to reduce with the reducers/listeners.
|
|
77
|
+
* @param args A list of parameters to send to the reducers/listeners.
|
|
78
|
+
* @returns A version of the `target` processed by the listeners.
|
|
79
|
+
* @template Target The type of the target.
|
|
80
|
+
* @template Args The type of the parameters to send to the reducers/listeners.
|
|
81
|
+
* @example
|
|
82
|
+
*
|
|
83
|
+
* const events = new EventsHub();
|
|
84
|
+
* events.on('event', async (target, arg0) => {
|
|
85
|
+
* const data = await fetch(`https://api.example.com/${arg0}`);
|
|
86
|
+
* target.push(data);
|
|
87
|
+
* return target;
|
|
88
|
+
* });
|
|
89
|
+
* const result = await events.reduce('event', [], 'Hello');
|
|
90
|
+
* // result would be a list of data fetched from the API.
|
|
91
|
+
*
|
|
92
|
+
*/
|
|
93
|
+
reduce<Target, Args extends GenericParams>(event: string | string[], target: Target, ...args: Args): Promise<Target>;
|
|
94
|
+
/**
|
|
95
|
+
* Synchronously reduces a target using an event. It's like emit, but the events
|
|
96
|
+
* listener return a modified (or not) version of the `target`.
|
|
97
|
+
*
|
|
98
|
+
* @param event An event name or a list of them.
|
|
99
|
+
* @param target The variable to reduce with the reducers/listeners.
|
|
100
|
+
* @param args A list of parameters to send to the reducers/listeners.
|
|
101
|
+
* @returns A version of the `target` processed by the listeners.
|
|
102
|
+
* @template Target The type of the target.
|
|
103
|
+
* @template Args The type of the parameters to send to the reducers/listeners.
|
|
104
|
+
* @example
|
|
105
|
+
*
|
|
106
|
+
* const events = new EventsHub();
|
|
107
|
+
* events.on('event', (target, arg0) => {
|
|
108
|
+
* target.push(arg0);
|
|
109
|
+
* return target;
|
|
110
|
+
* });
|
|
111
|
+
* events.reduce('event', [], 'Hello'); // returns ['Hello']
|
|
112
|
+
*
|
|
113
|
+
*/
|
|
114
|
+
reduceSync<Target, ReducerArgs extends GenericParams>(event: string | string[], target: Target, ...args: ReducerArgs): Target;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Shorthand for `new EventsHub()`.
|
|
118
|
+
*
|
|
119
|
+
* @returns A new instance of {@link EventsHub}.
|
|
120
|
+
*/
|
|
121
|
+
declare const eventsHub: () => EventsHub;
|
|
122
|
+
|
|
123
|
+
export { EventsHub, eventsHub };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
EventsHub: () => EventsHub,
|
|
24
|
+
eventsHub: () => eventsHub
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(src_exports);
|
|
27
|
+
var EventsHub = class {
|
|
28
|
+
events = {};
|
|
29
|
+
onceWrappers = {};
|
|
30
|
+
getSubscribers(event) {
|
|
31
|
+
if (!this.events[event]) {
|
|
32
|
+
this.events[event] = [];
|
|
33
|
+
}
|
|
34
|
+
return this.events[event];
|
|
35
|
+
}
|
|
36
|
+
on(event, listener) {
|
|
37
|
+
const events = Array.isArray(event) ? event : [event];
|
|
38
|
+
events.forEach((name) => {
|
|
39
|
+
const subscribers = this.getSubscribers(name);
|
|
40
|
+
if (!subscribers.includes(listener)) {
|
|
41
|
+
subscribers.push(listener);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return () => this.off(event, listener);
|
|
45
|
+
}
|
|
46
|
+
once(event, listener) {
|
|
47
|
+
const events = Array.isArray(event) ? event : [event];
|
|
48
|
+
let wrapper = events.reduce((acc, name) => {
|
|
49
|
+
if (acc)
|
|
50
|
+
return acc;
|
|
51
|
+
const onceWrapper = this.onceWrappers[name];
|
|
52
|
+
if (Array.isArray(onceWrapper)) {
|
|
53
|
+
const existing = onceWrapper.find((item) => item.original === listener);
|
|
54
|
+
if (existing) {
|
|
55
|
+
return existing.wrapper;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
this.onceWrappers[name] = [];
|
|
60
|
+
return null;
|
|
61
|
+
}, null);
|
|
62
|
+
if (!wrapper) {
|
|
63
|
+
const newWrapper = (...args) => listener(...args);
|
|
64
|
+
newWrapper.once = true;
|
|
65
|
+
wrapper = newWrapper;
|
|
66
|
+
events.forEach((name) => {
|
|
67
|
+
this.onceWrappers[name].push({
|
|
68
|
+
wrapper,
|
|
69
|
+
original: listener
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return this.on(event, wrapper);
|
|
74
|
+
}
|
|
75
|
+
off(event, listener) {
|
|
76
|
+
const isArray = Array.isArray(event);
|
|
77
|
+
const events = isArray ? event : [event];
|
|
78
|
+
const result = events.map((name) => {
|
|
79
|
+
const subscribers = this.getSubscribers(name);
|
|
80
|
+
const onceSubscribers = this.onceWrappers[name];
|
|
81
|
+
let found = false;
|
|
82
|
+
let index = subscribers.indexOf(listener);
|
|
83
|
+
if (index > -1) {
|
|
84
|
+
found = true;
|
|
85
|
+
if ("once" in listener && onceSubscribers) {
|
|
86
|
+
const wrapperIndex = onceSubscribers.findIndex((item) => item.wrapper === listener);
|
|
87
|
+
onceSubscribers.splice(wrapperIndex, 1);
|
|
88
|
+
}
|
|
89
|
+
subscribers.splice(index, 1);
|
|
90
|
+
} else if (onceSubscribers) {
|
|
91
|
+
index = onceSubscribers.findIndex((item) => item.original === listener);
|
|
92
|
+
if (index > -1) {
|
|
93
|
+
found = true;
|
|
94
|
+
const originalIndex = subscribers.indexOf(onceSubscribers[index].original);
|
|
95
|
+
subscribers.splice(originalIndex, 1);
|
|
96
|
+
onceSubscribers.splice(index, 1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return found;
|
|
100
|
+
});
|
|
101
|
+
return isArray ? result : result[0];
|
|
102
|
+
}
|
|
103
|
+
emit(event, ...args) {
|
|
104
|
+
const toClean = [];
|
|
105
|
+
const events = Array.isArray(event) ? event : [event];
|
|
106
|
+
events.forEach((name) => {
|
|
107
|
+
this.getSubscribers(name).forEach((subscriber) => {
|
|
108
|
+
subscriber(...args);
|
|
109
|
+
if ("once" in subscriber) {
|
|
110
|
+
toClean.push({
|
|
111
|
+
event: name,
|
|
112
|
+
listener: subscriber
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
toClean.forEach((info) => this.off(info.event, info.listener));
|
|
118
|
+
}
|
|
119
|
+
async reduce(event, target, ...args) {
|
|
120
|
+
const events = Array.isArray(event) ? event : [event];
|
|
121
|
+
const toClean = [];
|
|
122
|
+
const result = await events.reduce((eventAcc, name) => eventAcc.then((eventCurrent) => {
|
|
123
|
+
const subscribers = this.getSubscribers(name);
|
|
124
|
+
return subscribers.reduce((subAcc, subscriber) => subAcc.then((subCurrent) => {
|
|
125
|
+
let useCurrent;
|
|
126
|
+
if (Array.isArray(subCurrent)) {
|
|
127
|
+
useCurrent = subCurrent.slice();
|
|
128
|
+
} else if (typeof subCurrent === "object") {
|
|
129
|
+
useCurrent = { ...subCurrent };
|
|
130
|
+
} else {
|
|
131
|
+
useCurrent = subCurrent;
|
|
132
|
+
}
|
|
133
|
+
const nextStep = subscriber(...[useCurrent, ...args]);
|
|
134
|
+
if ("once" in subscriber) {
|
|
135
|
+
toClean.push({
|
|
136
|
+
event: name,
|
|
137
|
+
listener: subscriber
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return nextStep;
|
|
141
|
+
}), Promise.resolve(eventCurrent));
|
|
142
|
+
}), Promise.resolve(target));
|
|
143
|
+
toClean.forEach((info) => this.off(info.event, info.listener));
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
reduceSync(event, target, ...args) {
|
|
147
|
+
const events = Array.isArray(event) ? event : [event];
|
|
148
|
+
const toClean = [];
|
|
149
|
+
const result = events.reduce((eventAcc, name) => {
|
|
150
|
+
const subscribers = this.getSubscribers(name);
|
|
151
|
+
return subscribers.reduce((subAcc, subscriber) => {
|
|
152
|
+
let useCurrent;
|
|
153
|
+
if (Array.isArray(subAcc)) {
|
|
154
|
+
useCurrent = subAcc.slice();
|
|
155
|
+
} else if (typeof subAcc === "object") {
|
|
156
|
+
useCurrent = { ...subAcc };
|
|
157
|
+
} else {
|
|
158
|
+
useCurrent = subAcc;
|
|
159
|
+
}
|
|
160
|
+
const nextStep = subscriber(...[useCurrent, ...args]);
|
|
161
|
+
if ("once" in subscriber) {
|
|
162
|
+
toClean.push({
|
|
163
|
+
event: name,
|
|
164
|
+
listener: subscriber
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return nextStep;
|
|
168
|
+
}, eventAcc);
|
|
169
|
+
}, target);
|
|
170
|
+
toClean.forEach((info) => this.off(info.event, info.listener));
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var eventsHub = () => new EventsHub();
|
|
175
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
176
|
+
0 && (module.exports = {
|
|
177
|
+
EventsHub,
|
|
178
|
+
eventsHub
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/no-explicit-any -- It's a generic function.\ntype GenericFn = (...args: any[]) => any;\ntype GenericParams = Parameters<GenericFn>;\ntype OnceWrapperFn = GenericFn & {\n once: boolean;\n};\n/**\n * When the service receives a \"once subscription\", it needs to track the listener so it\n * gets removed after being called once, and to avoid modifying the original function, it\n * creates a wrapper function that has the \"once\" property set to true. The wrapper and\n * the original function are stored in case `off` is called before the wrapper gets\n * triggered; it will receive the original function, not the wrapper, so the class needs a\n * way to map them together.\n */\ntype OnceWrapper = {\n /**\n * A simple wrapper for the original listener, with the difference that it has a `once`\n * property set to `true`.\n */\n wrapper: OnceWrapperFn;\n /**\n * The original listener that will be called once.\n */\n original: GenericFn;\n};\n/**\n * A minimal implementation of an events handler service.\n */\nexport class EventsHub {\n /**\n * A dictionary of the events and their listeners.\n */\n protected events: Record<string, GenericFn[]> = {};\n /**\n * A dictionary of wrappers that were created for \"one time subscriptions\". This is\n * used by the {@link EventsHub.off}: if it doesn't find the subscriber as it is, it\n * will look for a wrapper and remove it.\n */\n protected onceWrappers: Record<string, OnceWrapper[]> = {};\n /**\n * Gets all the listeners for a specific event.\n * The list is returned by reference, so it can be modified once obtained.\n *\n * @param event The name of the event.\n */\n protected getSubscribers(event: string): (GenericFn | OnceWrapperFn)[] {\n if (!this.events[event]) {\n this.events[event] = [];\n }\n\n return this.events[event]!;\n }\n on<ListenerFn extends GenericFn = GenericFn>(\n event: string,\n listener: ListenerFn,\n ): () => boolean;\n on<ListenerFn extends GenericFn = GenericFn>(\n event: string[],\n listener: ListenerFn,\n ): () => boolean[];\n on<ListenerFn extends GenericFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): () => boolean | boolean[];\n /**\n * Adds a new event listener.\n *\n * @param event An event name or a list of them.\n * @param listener The listener function.\n * @returns An unsubscribe function to remove the listene(s).\n * @template ListenerFn The type of the listener function.\n * @example\n *\n * const events = new EventsHub();\n * type Listener = (arg0: string) => void;\n * const unsubscribe = events.on<Listener>('event', (arg0) => {\n * console.log(`Event received: ${arg0}`);\n * });\n *\n */\n on<ListenerFn extends GenericFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): () => boolean | boolean[] {\n const events = Array.isArray(event) ? event : [event];\n events.forEach((name) => {\n const subscribers = this.getSubscribers(name);\n if (!subscribers.includes(listener)) {\n subscribers.push(listener);\n }\n });\n\n return () => this.off<ListenerFn>(event, listener);\n }\n once<ListenerFn extends GenericFn = GenericFn>(\n event: string,\n listener: ListenerFn,\n ): () => boolean;\n once<ListenerFn extends GenericFn = GenericFn>(\n event: string[],\n listener: ListenerFn,\n ): () => boolean[];\n once<ListenerFn extends GenericFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): () => boolean | boolean[];\n /**\n * Adds an event listener that will only be executed once.\n *\n * @param event An event name or a list of them.\n * @param listener The listener function.\n * @returns An unsubscribe function to remove the listener(s).\n * @template ListenerFn The type of the listener function.\n * @example\n *\n * const events = new EventsHub();\n * type Listener = (arg0: string) => void;\n * const unsubscribe = events.once<Listener>('event', (arg0) => {\n * console.log(`Event received: ${arg0}`);\n * });\n *\n */\n once<ListenerFn extends GenericFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): () => boolean | boolean[] {\n const events = Array.isArray(event) ? event : [event];\n // Try to find an existing wrapper.\n let wrapper = events.reduce<OnceWrapperFn | null>((acc, name) => {\n // A previous iteration found a wrapper, so `continue`.\n if (acc) return acc;\n\n // A list of wrappers exists for the event, so, let's try an find one for this function.\n const onceWrapper = this.onceWrappers[name];\n if (Array.isArray(onceWrapper)) {\n const existing = onceWrapper.find((item) => item.original === listener);\n if (existing) {\n return existing.wrapper;\n }\n\n return null;\n }\n\n // The list didn't even exists, let's at least create it.\n this.onceWrappers[name] = [];\n return null;\n }, null);\n\n if (!wrapper) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- It's a generic function.\n const newWrapper = (...args: any[]) => listener(...args);\n newWrapper.once = true;\n wrapper = newWrapper;\n events.forEach((name) => {\n this.onceWrappers[name]!.push({\n wrapper: wrapper!,\n original: listener,\n });\n });\n }\n\n return this.on(event, wrapper);\n }\n off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(\n event: string[],\n listener: ListenerFn,\n ): boolean[];\n off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(\n event: string,\n listener: ListenerFn,\n ): boolean;\n off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): boolean | boolean[];\n /**\n * Removes an event listener.\n *\n * @param event An event name or a list of them.\n * @param listener The listener function.\n * @returns If `event` was a `string`, it will return whether or not the listener was\n * found and removed; but if `event`\n * was an `Array`, it will return a list of boolean values.\n * @template ListenerFn The type of the listener function.\n * @example\n *\n * const events = new EventsHub();\n * const listener = (arg0) => {\n * console.log(`Event received: ${arg0}`);\n * };\n * events.on('event', listener); // subscribe.\n * events.off('event', listener); // manually unsubscribe.\n *\n */\n off<ListenerFn extends GenericFn | OnceWrapperFn = GenericFn>(\n event: string | string[],\n listener: ListenerFn,\n ): boolean | boolean[] {\n const isArray = Array.isArray(event);\n const events = isArray ? event : [event];\n const result = events.map((name) => {\n const subscribers = this.getSubscribers(name);\n const onceSubscribers = this.onceWrappers[name];\n let found = false;\n let index = subscribers.indexOf(listener);\n if (index > -1) {\n found = true;\n /**\n * If the listener had the `once` flag, then it's a wrapper, so it needs to remove\n * it from the wrappers list too.\n */\n if ('once' in listener && onceSubscribers) {\n const wrapperIndex = onceSubscribers.findIndex(\n (item) => item.wrapper === listener,\n );\n onceSubscribers.splice(wrapperIndex, 1);\n }\n subscribers.splice(index, 1);\n } else if (onceSubscribers) {\n /**\n * If it couldn't found the subscriber, maybe it's because it's the original\n * listener of a wrapper.\n */\n index = onceSubscribers.findIndex((item) => item.original === listener);\n if (index > -1) {\n found = true;\n const originalIndex = subscribers.indexOf(onceSubscribers[index]!.original);\n subscribers.splice(originalIndex, 1);\n onceSubscribers.splice(index, 1);\n }\n }\n\n return found;\n });\n\n return isArray ? result : result[0]!;\n }\n /**\n * Emits an event and call all its listeners.\n *\n * @param event An event name or a list of them.\n * @param args A list of parameters to send to the listeners.\n * @template Args The type of the parameters to send to the listeners.\n * @example\n *\n * const events = new EventsHub();\n * events.on('event', (arg0) => {\n * console.log(`Event received: ${arg0}`);\n * });\n * events.emit('event', 'Hello'); // prints \"Event received: Hello\"\n *\n */\n emit<Args extends GenericParams>(event: string | string[], ...args: Args): void {\n const toClean: { event: string; listener: GenericFn }[] = [];\n const events = Array.isArray(event) ? event : [event];\n events.forEach((name) => {\n this.getSubscribers(name).forEach((subscriber) => {\n subscriber(...args);\n if ('once' in subscriber) {\n toClean.push({\n event: name,\n listener: subscriber,\n });\n }\n });\n });\n\n toClean.forEach((info) => this.off(info.event, info.listener));\n }\n /**\n * Asynchronously reduces a target using an event. It's like emit, but the events\n * listener return a modified (or not) version of the `target`.\n *\n * @param event An event name or a list of them.\n * @param target The variable to reduce with the reducers/listeners.\n * @param args A list of parameters to send to the reducers/listeners.\n * @returns A version of the `target` processed by the listeners.\n * @template Target The type of the target.\n * @template Args The type of the parameters to send to the reducers/listeners.\n * @example\n *\n * const events = new EventsHub();\n * events.on('event', async (target, arg0) => {\n * const data = await fetch(`https://api.example.com/${arg0}`);\n * target.push(data);\n * return target;\n * });\n * const result = await events.reduce('event', [], 'Hello');\n * // result would be a list of data fetched from the API.\n *\n */\n async reduce<Target, Args extends GenericParams>(\n event: string | string[],\n target: Target,\n ...args: Args\n ): Promise<Target> {\n const events = Array.isArray(event) ? event : [event];\n const toClean: { event: string; listener: GenericFn }[] = [];\n const result = await events.reduce<Promise<Target>>(\n (eventAcc, name) =>\n eventAcc.then((eventCurrent) => {\n const subscribers = this.getSubscribers(name);\n return subscribers.reduce(\n (subAcc, subscriber) =>\n subAcc.then((subCurrent) => {\n let useCurrent;\n if (Array.isArray(subCurrent)) {\n useCurrent = subCurrent.slice();\n } else if (typeof subCurrent === 'object') {\n useCurrent = { ...subCurrent };\n } else {\n useCurrent = subCurrent;\n }\n\n const nextStep = subscriber(...[useCurrent, ...args]);\n if ('once' in subscriber) {\n toClean.push({\n event: name,\n listener: subscriber,\n });\n }\n\n return nextStep;\n }),\n Promise.resolve(eventCurrent),\n );\n }),\n Promise.resolve(target),\n );\n\n toClean.forEach((info) => this.off(info.event, info.listener));\n\n return result;\n }\n /**\n * Synchronously reduces a target using an event. It's like emit, but the events\n * listener return a modified (or not) version of the `target`.\n *\n * @param event An event name or a list of them.\n * @param target The variable to reduce with the reducers/listeners.\n * @param args A list of parameters to send to the reducers/listeners.\n * @returns A version of the `target` processed by the listeners.\n * @template Target The type of the target.\n * @template Args The type of the parameters to send to the reducers/listeners.\n * @example\n *\n * const events = new EventsHub();\n * events.on('event', (target, arg0) => {\n * target.push(arg0);\n * return target;\n * });\n * events.reduce('event', [], 'Hello'); // returns ['Hello']\n *\n */\n reduceSync<Target, ReducerArgs extends GenericParams>(\n event: string | string[],\n target: Target,\n ...args: ReducerArgs\n ): Target {\n const events = Array.isArray(event) ? event : [event];\n const toClean: { event: string; listener: GenericFn }[] = [];\n const result = events.reduce((eventAcc, name) => {\n const subscribers = this.getSubscribers(name);\n return subscribers.reduce((subAcc, subscriber) => {\n let useCurrent;\n if (Array.isArray(subAcc)) {\n useCurrent = subAcc.slice();\n } else if (typeof subAcc === 'object') {\n useCurrent = { ...subAcc };\n } else {\n useCurrent = subAcc;\n }\n\n const nextStep = subscriber(...[useCurrent, ...args]);\n if ('once' in subscriber) {\n toClean.push({\n event: name,\n listener: subscriber,\n });\n }\n\n return nextStep;\n }, eventAcc);\n }, target);\n\n toClean.forEach((info) => this.off(info.event, info.listener));\n return result;\n }\n}\n/**\n * Shorthand for `new EventsHub()`.\n *\n * @returns A new instance of {@link EventsHub}.\n */\nexport const eventsHub = () => new EventsHub();\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@homer0/events-hub",
|
|
3
|
+
"description": "A simple implementation of a pubsub service for handling events",
|
|
4
|
+
"version": "1.0.1",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/homer0/packages.git",
|
|
8
|
+
"directory": "packages/public/events-hub"
|
|
9
|
+
},
|
|
10
|
+
"author": "Leonardo Apiwan (@homer0) <me@homer0.com>",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"events",
|
|
14
|
+
"pubsub",
|
|
15
|
+
"utility",
|
|
16
|
+
"wootils",
|
|
17
|
+
"javascript"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist/"
|
|
24
|
+
],
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/jest": "^28.1.4",
|
|
27
|
+
"jest": "^28.1.2",
|
|
28
|
+
"ts-jest": "^28.0.5",
|
|
29
|
+
"tsup": "^6.1.3",
|
|
30
|
+
"typescript": "^4.7.4"
|
|
31
|
+
},
|
|
32
|
+
"engine-strict": true,
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=14"
|
|
35
|
+
},
|
|
36
|
+
"main": "./dist/index.js",
|
|
37
|
+
"module": "./dist/esm/index.js",
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "jest -c ./.jestrc.js",
|
|
41
|
+
"types:check": "tsc --noEmit && tsc --noEmit --project ./tests/tsconfig.json",
|
|
42
|
+
"build": "tsup src/index.ts",
|
|
43
|
+
"prepublishOnly": "npm run build"
|
|
44
|
+
},
|
|
45
|
+
"gitHead": "835d84ced485240f90c9c9173b5fcb53346bf264"
|
|
46
|
+
}
|