@vandeurenglenn/little-pubsub 1.5.1 → 1.5.2
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/README.md +159 -141
- package/index.d.ts +28 -21
- package/index.js +66 -42
- package/package.json +36 -36
package/README.md
CHANGED
|
@@ -1,141 +1,159 @@
|
|
|
1
|
-
# little-pubsub
|
|
2
|
-
|
|
3
|
-
> Small publish & subscribe class
|
|
4
|
-
|
|
5
|
-
## INSTALL
|
|
6
|
-
|
|
7
|
-
#### npm
|
|
8
|
-
|
|
9
|
-
```sh
|
|
10
|
-
npm i --save @vandeurenglenn/little-pubsub
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## USAGE
|
|
14
|
-
|
|
15
|
-
```js
|
|
16
|
-
import PubSub from '@vandeurenglenn/little-pubsub'
|
|
17
|
-
const pubsub = new PubSub()
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Breaking Changes
|
|
21
|
-
|
|
22
|
-
### v1.5.0
|
|
23
|
-
|
|
24
|
-
`subscribe[context]` & `unsubscribe[context]` -> `subscribe[options({keepValue, context})]`
|
|
25
|
-
|
|
26
|
-
```js
|
|
27
|
-
// before
|
|
28
|
-
pusbub.subscribe(topic, handler, context)
|
|
29
|
-
|
|
30
|
-
// now
|
|
31
|
-
pusbub.subscribe(topic, handler, { context })
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Example
|
|
35
|
-
|
|
36
|
-
```js
|
|
37
|
-
import PubSub from '@vandeurenglenn/little-pubsub'
|
|
38
|
-
const pubsub = new PubSub()
|
|
39
|
-
|
|
40
|
-
pubsub.subscribe('event', (value) => {
|
|
41
|
-
console.log(value)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
pubsub.publish('event', 'hello')
|
|
45
|
-
// always runs handler
|
|
46
|
-
// (can use to overide littlePubsub.verbose setting without changing the behavior of the rest)
|
|
47
|
-
pubsub.publishVerbose('event', 'hello')
|
|
48
|
-
|
|
49
|
-
pubsub.unsubscribe('event', (value) => {
|
|
50
|
-
console.log(value)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
pubsub.hasSubscribers('event')
|
|
54
|
-
|
|
55
|
-
await pubsub.once('event')
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## API
|
|
59
|
-
|
|
60
|
-
### pubsub(
|
|
61
|
-
|
|
62
|
-
`verbose`: when false only fires after value change<br>
|
|
63
|
-
|
|
64
|
-
```js
|
|
65
|
-
pubsub = new PubSub(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`
|
|
73
|
-
`
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
`
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
```
|
|
1
|
+
# little-pubsub
|
|
2
|
+
|
|
3
|
+
> Small publish & subscribe class
|
|
4
|
+
|
|
5
|
+
## INSTALL
|
|
6
|
+
|
|
7
|
+
#### npm
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i --save @vandeurenglenn/little-pubsub
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## USAGE
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import PubSub from '@vandeurenglenn/little-pubsub'
|
|
17
|
+
const pubsub = new PubSub()
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Breaking Changes
|
|
21
|
+
|
|
22
|
+
### v1.5.0
|
|
23
|
+
|
|
24
|
+
`subscribe[context]` & `unsubscribe[context]` -> `subscribe[options({keepValue, context})]`
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
// before
|
|
28
|
+
pusbub.subscribe(topic, handler, context)
|
|
29
|
+
|
|
30
|
+
// now
|
|
31
|
+
pusbub.subscribe(topic, handler, { context })
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Example
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import PubSub from '@vandeurenglenn/little-pubsub'
|
|
38
|
+
const pubsub = new PubSub()
|
|
39
|
+
|
|
40
|
+
pubsub.subscribe('event', (value) => {
|
|
41
|
+
console.log(value)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
pubsub.publish('event', 'hello')
|
|
45
|
+
// always runs handler
|
|
46
|
+
// (can use to overide littlePubsub.verbose setting without changing the behavior of the rest)
|
|
47
|
+
pubsub.publishVerbose('event', 'hello')
|
|
48
|
+
|
|
49
|
+
pubsub.unsubscribe('event', (value) => {
|
|
50
|
+
console.log(value)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
pubsub.hasSubscribers('event')
|
|
54
|
+
|
|
55
|
+
await pubsub.once('event')
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## API
|
|
59
|
+
|
|
60
|
+
### pubsub(verbose?)
|
|
61
|
+
|
|
62
|
+
`verbose`: when false only fires after value change (default: false)<br>
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
const pubsub = new PubSub() // verbose defaults to false
|
|
66
|
+
const pubsub = new PubSub(true) // always trigger handlers
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### subscribe
|
|
70
|
+
|
|
71
|
+
`name`: name of the channel to subscribe to<br>
|
|
72
|
+
`handler`: method<br>
|
|
73
|
+
`options`: { context }<br>
|
|
74
|
+
|
|
75
|
+
Subscribing to an event returns an unsubscribe function. If value already exists, handler is called immediately.
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
const unsubscribe = pubsub.subscribe('event-name', (data) => {
|
|
79
|
+
console.log(data)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Later: clean unsubscribe
|
|
83
|
+
unsubscribe()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### unsubscribe
|
|
87
|
+
|
|
88
|
+
`name`: name of the channel to unsubscribe<br>
|
|
89
|
+
`handler`: method<br>
|
|
90
|
+
`options`: { context, keepValue }<br>
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
pubsub.unsubscribe(
|
|
94
|
+
'event-name',
|
|
95
|
+
(data) => {
|
|
96
|
+
console.log(data)
|
|
97
|
+
},
|
|
98
|
+
{ keepValue: false } // default
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### publish
|
|
103
|
+
|
|
104
|
+
`name`: name of the channel to publish to<br>
|
|
105
|
+
`handler`: method<br>
|
|
106
|
+
`verbose`: boolean<br>
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
pubsub.publish('event-name', 'data')
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### publish
|
|
113
|
+
|
|
114
|
+
`name`: name of the channel to publish to<br>
|
|
115
|
+
`handler`: method<br>
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
pubsub.publishVerbose('event-name', 'data')
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### once
|
|
122
|
+
|
|
123
|
+
`name`: name of the channel to get the value from<br>
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
pubsub.getValue('event-name')
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### once
|
|
130
|
+
|
|
131
|
+
`name`: name of the channel to publish to<br>
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
await pubsub.once('event-name')
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### hasSubscribers
|
|
138
|
+
|
|
139
|
+
`name`: name of the channel to check<br>
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
pubsub.hasSubscribers('event-name') // true or false
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### subscriberCount
|
|
146
|
+
|
|
147
|
+
`name`: name of the channel to count<br>
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
pubsub.subscriberCount('event-name') // number of handlers
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### clear
|
|
154
|
+
|
|
155
|
+
Removes all subscribers and values.
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
pubsub.clear()
|
|
159
|
+
```
|
package/index.d.ts
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
|
+
export type Handler<T = any> = (value: T, oldValue?: T) => void;
|
|
2
|
+
export interface HandlerEntry<T = any> {
|
|
3
|
+
original: Handler<T>;
|
|
4
|
+
bound: Handler<T>;
|
|
5
|
+
}
|
|
6
|
+
export interface Subscriber<T = any> {
|
|
7
|
+
value?: T;
|
|
8
|
+
handlers: HandlerEntry<T>[];
|
|
9
|
+
}
|
|
10
|
+
export interface SubscribeOptions {
|
|
11
|
+
context?: object;
|
|
12
|
+
}
|
|
13
|
+
export interface UnsubscribeOptions {
|
|
14
|
+
keepValue?: boolean;
|
|
15
|
+
context?: object;
|
|
16
|
+
}
|
|
17
|
+
export interface OnceOptions extends UnsubscribeOptions {
|
|
18
|
+
timeout?: number;
|
|
19
|
+
}
|
|
1
20
|
export default class LittlePubSub {
|
|
2
|
-
subscribers:
|
|
3
|
-
[index: string]: {
|
|
4
|
-
value?: any;
|
|
5
|
-
handlers?: Function[];
|
|
6
|
-
};
|
|
7
|
-
};
|
|
21
|
+
subscribers: Map<string, Subscriber>;
|
|
8
22
|
verbose: boolean;
|
|
9
23
|
constructor(verbose?: boolean);
|
|
10
|
-
_handleContext(handler: Function, context?: Function): Function;
|
|
11
24
|
hasSubscribers(event: string): boolean;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
unsubscribe(event: string, handler:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
publish(event: string, value: any, verbose?: boolean): void;
|
|
21
|
-
publishVerbose(event: string, value: any): void;
|
|
22
|
-
once(event: string, options?: {
|
|
23
|
-
keepValue?: boolean;
|
|
24
|
-
context?: Function;
|
|
25
|
-
}): Promise<any>;
|
|
25
|
+
subscriberCount(event: string): number;
|
|
26
|
+
clear(): void;
|
|
27
|
+
getValue<T = any>(event: string): T | undefined;
|
|
28
|
+
subscribe<T = any>(event: string, handler: Handler<T>, options?: SubscribeOptions): () => void;
|
|
29
|
+
unsubscribe<T = any>(event: string, handler: Handler<T>, options?: UnsubscribeOptions): void;
|
|
30
|
+
publish<T = any>(event: string, value: T, verbose?: boolean): void;
|
|
31
|
+
publishVerbose<T = any>(event: string, value: T): void;
|
|
32
|
+
once<T = any>(event: string, options?: OnceOptions): Promise<T>;
|
|
26
33
|
}
|
package/index.js
CHANGED
|
@@ -1,56 +1,71 @@
|
|
|
1
1
|
export default class LittlePubSub {
|
|
2
|
-
subscribers =
|
|
2
|
+
subscribers = new Map();
|
|
3
3
|
verbose;
|
|
4
4
|
constructor(verbose) {
|
|
5
|
-
this.verbose = verbose;
|
|
6
|
-
}
|
|
7
|
-
_handleContext(handler, context) {
|
|
8
|
-
if (typeof context === 'undefined') {
|
|
9
|
-
context = handler;
|
|
10
|
-
}
|
|
11
|
-
return context;
|
|
5
|
+
this.verbose = verbose ?? false;
|
|
12
6
|
}
|
|
13
7
|
hasSubscribers(event) {
|
|
14
|
-
return this.subscribers
|
|
8
|
+
return this.subscribers.has(event);
|
|
9
|
+
}
|
|
10
|
+
subscriberCount(event) {
|
|
11
|
+
return this.subscribers.get(event)?.handlers.length ?? 0;
|
|
12
|
+
}
|
|
13
|
+
clear() {
|
|
14
|
+
this.subscribers.clear();
|
|
15
15
|
}
|
|
16
16
|
getValue(event) {
|
|
17
|
-
|
|
18
|
-
return this.subscribers[event].value;
|
|
19
|
-
return undefined;
|
|
17
|
+
return this.subscribers.get(event)?.value;
|
|
20
18
|
}
|
|
21
19
|
subscribe(event, handler, options) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if
|
|
28
|
-
|
|
20
|
+
let subscriber = this.subscribers.get(event);
|
|
21
|
+
if (subscriber === undefined) {
|
|
22
|
+
subscriber = { handlers: [], value: undefined };
|
|
23
|
+
this.subscribers.set(event, subscriber);
|
|
24
|
+
}
|
|
25
|
+
// Only bind if context is provided
|
|
26
|
+
const context = options?.context;
|
|
27
|
+
const boundHandler = context
|
|
28
|
+
? handler.bind(context)
|
|
29
|
+
: handler;
|
|
30
|
+
subscriber.handlers.push({ original: handler, bound: boundHandler });
|
|
31
|
+
// Call handler immediately if value already exists
|
|
32
|
+
if (subscriber.value !== undefined) {
|
|
33
|
+
boundHandler(subscriber.value, undefined);
|
|
34
|
+
}
|
|
35
|
+
// Return unsubscribe function
|
|
36
|
+
return () => this.unsubscribe(event, handler, options);
|
|
29
37
|
}
|
|
30
38
|
unsubscribe(event, handler, options) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (!this.hasSubscribers(event))
|
|
39
|
+
const subscriber = this.subscribers.get(event);
|
|
40
|
+
if (subscriber === undefined)
|
|
34
41
|
return;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
const handlers = subscriber.handlers;
|
|
43
|
+
// Find and remove handler by original reference
|
|
44
|
+
for (let i = 0, len = handlers.length; i < len; i++) {
|
|
45
|
+
if (handlers[i].original === handler) {
|
|
46
|
+
handlers.splice(i, 1);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Delete event if no handlers left (unless keepValue is true)
|
|
51
|
+
if (handlers.length === 0 && !options?.keepValue) {
|
|
52
|
+
this.subscribers.delete(event);
|
|
53
|
+
}
|
|
42
54
|
}
|
|
43
55
|
publish(event, value, verbose) {
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const oldValue =
|
|
56
|
+
let subscriber = this.subscribers.get(event);
|
|
57
|
+
if (subscriber === undefined) {
|
|
58
|
+
subscriber = { handlers: [], value: undefined };
|
|
59
|
+
this.subscribers.set(event, subscriber);
|
|
60
|
+
}
|
|
61
|
+
const oldValue = subscriber.value;
|
|
62
|
+
// Only trigger handlers if verbose or value changed
|
|
50
63
|
if (this.verbose || verbose || oldValue !== value) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
subscriber.value = value;
|
|
65
|
+
const handlers = subscriber.handlers;
|
|
66
|
+
const len = handlers.length;
|
|
67
|
+
for (let i = 0; i < len; i++) {
|
|
68
|
+
handlers[i].bound(value, oldValue);
|
|
54
69
|
}
|
|
55
70
|
}
|
|
56
71
|
}
|
|
@@ -58,12 +73,21 @@ export default class LittlePubSub {
|
|
|
58
73
|
this.publish(event, value, true);
|
|
59
74
|
}
|
|
60
75
|
once(event, options) {
|
|
61
|
-
return new Promise((resolve) => {
|
|
62
|
-
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
let timeoutId;
|
|
78
|
+
const handler = (value) => {
|
|
79
|
+
if (timeoutId !== undefined)
|
|
80
|
+
clearTimeout(timeoutId);
|
|
63
81
|
resolve(value);
|
|
64
|
-
this.unsubscribe(event,
|
|
82
|
+
this.unsubscribe(event, handler, options);
|
|
65
83
|
};
|
|
66
|
-
this.subscribe(event,
|
|
84
|
+
this.subscribe(event, handler, options);
|
|
85
|
+
if (options?.timeout !== undefined) {
|
|
86
|
+
timeoutId = setTimeout(() => {
|
|
87
|
+
this.unsubscribe(event, handler, options);
|
|
88
|
+
reject(new Error(`Timeout waiting for event "${event}"`));
|
|
89
|
+
}, options.timeout);
|
|
90
|
+
}
|
|
67
91
|
});
|
|
68
92
|
}
|
|
69
93
|
}
|
package/package.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@vandeurenglenn/little-pubsub",
|
|
3
|
-
"version": "1.5.
|
|
4
|
-
"description": "Publish & Subscribe",
|
|
5
|
-
"main": "./index.js",
|
|
6
|
-
"types": "./index.d.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"import": "./index.js",
|
|
10
|
-
"types": "./index.d.ts"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"index.js",
|
|
15
|
-
"index.d.ts"
|
|
16
|
-
],
|
|
17
|
-
"repository": "https://github.com/vandeurenglenn/little-pubsub",
|
|
18
|
-
"author": "vandeurenglenn <vandeurenglenn@gmail.com>",
|
|
19
|
-
"license": "MIT",
|
|
20
|
-
"type": "module",
|
|
21
|
-
"private": false,
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "npx tsc",
|
|
24
|
-
"test": "node test.js"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"typescript": "^5.
|
|
35
|
-
}
|
|
36
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@vandeurenglenn/little-pubsub",
|
|
3
|
+
"version": "1.5.2",
|
|
4
|
+
"description": "Publish & Subscribe",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"types": "./index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./index.js",
|
|
10
|
+
"types": "./index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"index.js",
|
|
15
|
+
"index.d.ts"
|
|
16
|
+
],
|
|
17
|
+
"repository": "https://github.com/vandeurenglenn/little-pubsub",
|
|
18
|
+
"author": "vandeurenglenn <vandeurenglenn@gmail.com>",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"private": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "npx tsc",
|
|
24
|
+
"test": "node --test test.js",
|
|
25
|
+
"benchmark": "node benchmark.js"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"publish",
|
|
29
|
+
"subscribe",
|
|
30
|
+
"pubsub",
|
|
31
|
+
"once"
|
|
32
|
+
],
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
}
|
|
36
|
+
}
|