utilful 1.1.1 → 1.2.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/README.md CHANGED
@@ -113,49 +113,36 @@ const data = parseCSV<'name' | 'age'>(csv) // [{ name: 'John', age: '30' }, { na
113
113
 
114
114
  ### Emitter
115
115
 
116
- Simple and tiny event emitter library for JavaScript.
116
+ Tiny functional event emitter / pubsub, based on [mitt](https://github.com/developit/mitt).
117
117
 
118
- `createEmitter` accepts interface with event name to listener argument types mapping:
118
+ **Example:**
119
119
 
120
120
  ```ts
121
121
  import { createEmitter } from 'utilful'
122
122
 
123
- interface Events {
124
- set: (name: string, count: number) => void
125
- tick: () => void
123
+ // eslint-disable-next-line ts/consistent-type-definitions
124
+ type Events = {
125
+ foo: { a: string }
126
126
  }
127
127
 
128
128
  const emitter = createEmitter<Events>()
129
129
 
130
- // Correct calls:
131
- emitter.emit('set', 'prop', 1)
132
- emitter.emit('tick')
133
-
134
- // Compilation errors:
135
- emitter.emit('set', 'prop', '1')
136
- emitter.emit('tick', 2)
137
- ```
130
+ // Listen to an event
131
+ emitter.on('foo', e => console.log('foo', e))
138
132
 
139
- The `on` method returns an `unbind` function. Call it and this listener will be removed from event:
133
+ // Listen to all events
134
+ emitter.on('*', (type, e) => console.log(type, e))
140
135
 
141
- ```ts
142
- const unbind = emitter.on('tick', (number) => {
143
- console.log(`on ${number}`)
144
- })
136
+ // Fire an event
137
+ emitter.emit('foo', { a: 'b' })
145
138
 
146
- emitter.emit('tick', 1)
147
- // Prints "on 1"
139
+ // Clearing all events
140
+ emitter.all.clear()
148
141
 
149
- unbind()
150
- emitter.emit('tick', 2)
151
- // Prints nothing
152
- ```
153
-
154
- You can get the used events list by accessing the `events` property:
155
-
156
- ```ts
157
- const unbind = emitter.on('tick', () => { })
158
- emitter.events // => { tick: [ [Function] ] }
142
+ // Working with handler references:
143
+ function onFoo() {}
144
+ emitter.on('foo', onFoo) // Listen
145
+ emitter.off('foo', onFoo) // Unlisten
159
146
  ```
160
147
 
161
148
  ### JSON
@@ -1,72 +1,24 @@
1
- interface EventsMap {
2
- [event: string]: any;
3
- }
4
- interface DefaultEvents extends EventsMap {
5
- [event: string]: (...args: any) => void;
6
- }
7
- interface Unsubscribe {
8
- (): void;
9
- }
10
- interface Emitter<Events extends EventsMap = DefaultEvents> {
11
- /**
12
- * Calls each of the listeners registered for a given event.
13
- *
14
- * @example
15
- * emitter.emit('tick', tickType, tickDuration)
16
- *
17
- * @param event The event name.
18
- * @param args The arguments for listeners.
19
- */
20
- emit: <K extends keyof Events>(this: this, event: K, ...args: Parameters<Events[K]>) => void;
21
- /**
22
- * Event names in keys and arrays with listeners in values.
23
- *
24
- * @example
25
- * emitter1.events = emitter2.events
26
- * emitter2.events = { }
27
- */
28
- events: Partial<{
29
- [E in keyof Events]: Events[E][];
30
- }>;
31
- /**
32
- * Add a listener for a given event.
33
- *
34
- * @example
35
- * const unbind = emitter.on('tick', (tickType, tickDuration) => {
36
- * count += 1
37
- * })
38
- *
39
- * disable () {
40
- * unbind()
41
- * }
42
- *
43
- * @param event The event name.
44
- * @param cb The listener function.
45
- * @returns Unbind listener from event.
46
- */
47
- on: <K extends keyof Events>(this: this, event: K, cb: Events[K]) => Unsubscribe;
1
+ type EventType = string | symbol;
2
+ type Handler<T = unknown> = (event: T) => void;
3
+ type WildcardHandler<T = Record<string, unknown>> = (type: keyof T, event: T[keyof T]) => void;
4
+ type EventHandlerList<T = unknown> = Handler<T>[];
5
+ type WildCardEventHandlerList<T = Record<string, unknown>> = WildcardHandler<T>[];
6
+ type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<keyof Events | '*', EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>>;
7
+ interface Emitter<Events extends Record<EventType, unknown>> {
8
+ all: EventHandlerMap<Events>;
9
+ on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
10
+ on(type: '*', handler: WildcardHandler<Events>): void;
11
+ off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
12
+ off(type: '*', handler: WildcardHandler<Events>): void;
13
+ emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
14
+ emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
48
15
  }
49
16
  /**
50
- * Create event emitter.
51
- *
52
- * @example
53
- * import { createEmitter } from 'nanoevents'
54
- *
55
- * class Ticker {
56
- * constructor() {
57
- * this.emitter = createEmitter()
58
- * }
59
- * on(...args) {
60
- * return this.emitter.on(...args)
61
- * }
62
- * tick() {
63
- * this.emitter.emit('tick')
64
- * }
65
- * }
17
+ * Simple functional event emitter / pubsub.
66
18
  *
67
- * @remarks Ported from `nanoevents`.
68
- * @see https://github.com/ai/nanoevents
19
+ * @remarks Ported from `mitt`.
20
+ * @see https://github.com/developit/mitt
69
21
  */
70
- declare function createEmitter<Events extends EventsMap = DefaultEvents>(): Emitter<Events>;
22
+ declare function createEmitter<Events extends Record<EventType, unknown>>(all?: EventHandlerMap<Events>): Emitter<Events>;
71
23
 
72
- export { type Emitter, createEmitter };
24
+ export { type Emitter, type EventHandlerList, type EventHandlerMap, type EventType, type Handler, type WildCardEventHandlerList, type WildcardHandler, createEmitter };
package/dist/emitter.d.ts CHANGED
@@ -1,72 +1,24 @@
1
- interface EventsMap {
2
- [event: string]: any;
3
- }
4
- interface DefaultEvents extends EventsMap {
5
- [event: string]: (...args: any) => void;
6
- }
7
- interface Unsubscribe {
8
- (): void;
9
- }
10
- interface Emitter<Events extends EventsMap = DefaultEvents> {
11
- /**
12
- * Calls each of the listeners registered for a given event.
13
- *
14
- * @example
15
- * emitter.emit('tick', tickType, tickDuration)
16
- *
17
- * @param event The event name.
18
- * @param args The arguments for listeners.
19
- */
20
- emit: <K extends keyof Events>(this: this, event: K, ...args: Parameters<Events[K]>) => void;
21
- /**
22
- * Event names in keys and arrays with listeners in values.
23
- *
24
- * @example
25
- * emitter1.events = emitter2.events
26
- * emitter2.events = { }
27
- */
28
- events: Partial<{
29
- [E in keyof Events]: Events[E][];
30
- }>;
31
- /**
32
- * Add a listener for a given event.
33
- *
34
- * @example
35
- * const unbind = emitter.on('tick', (tickType, tickDuration) => {
36
- * count += 1
37
- * })
38
- *
39
- * disable () {
40
- * unbind()
41
- * }
42
- *
43
- * @param event The event name.
44
- * @param cb The listener function.
45
- * @returns Unbind listener from event.
46
- */
47
- on: <K extends keyof Events>(this: this, event: K, cb: Events[K]) => Unsubscribe;
1
+ type EventType = string | symbol;
2
+ type Handler<T = unknown> = (event: T) => void;
3
+ type WildcardHandler<T = Record<string, unknown>> = (type: keyof T, event: T[keyof T]) => void;
4
+ type EventHandlerList<T = unknown> = Handler<T>[];
5
+ type WildCardEventHandlerList<T = Record<string, unknown>> = WildcardHandler<T>[];
6
+ type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<keyof Events | '*', EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>>;
7
+ interface Emitter<Events extends Record<EventType, unknown>> {
8
+ all: EventHandlerMap<Events>;
9
+ on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
10
+ on(type: '*', handler: WildcardHandler<Events>): void;
11
+ off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
12
+ off(type: '*', handler: WildcardHandler<Events>): void;
13
+ emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
14
+ emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
48
15
  }
49
16
  /**
50
- * Create event emitter.
51
- *
52
- * @example
53
- * import { createEmitter } from 'nanoevents'
54
- *
55
- * class Ticker {
56
- * constructor() {
57
- * this.emitter = createEmitter()
58
- * }
59
- * on(...args) {
60
- * return this.emitter.on(...args)
61
- * }
62
- * tick() {
63
- * this.emitter.emit('tick')
64
- * }
65
- * }
17
+ * Simple functional event emitter / pubsub.
66
18
  *
67
- * @remarks Ported from `nanoevents`.
68
- * @see https://github.com/ai/nanoevents
19
+ * @remarks Ported from `mitt`.
20
+ * @see https://github.com/developit/mitt
69
21
  */
70
- declare function createEmitter<Events extends EventsMap = DefaultEvents>(): Emitter<Events>;
22
+ declare function createEmitter<Events extends Record<EventType, unknown>>(all?: EventHandlerMap<Events>): Emitter<Events>;
71
23
 
72
- export { type Emitter, createEmitter };
24
+ export { type Emitter, type EventHandlerList, type EventHandlerMap, type EventType, type Handler, type WildCardEventHandlerList, type WildcardHandler, createEmitter };
package/dist/emitter.mjs CHANGED
@@ -1,16 +1,62 @@
1
- function createEmitter() {
1
+ function createEmitter(all) {
2
+ all ||= /* @__PURE__ */ new Map();
2
3
  return {
3
- emit(event, ...args) {
4
- for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
5
- callbacks[i](...args);
4
+ /**
5
+ * A Map of event names to registered handler functions.
6
+ */
7
+ all,
8
+ /**
9
+ * Register an event handler for the given type.
10
+ * @param {string|symbol} type Type of event to listen for, or `'*'` for all events
11
+ * @param {Function} handler Function to call in response to given event
12
+ * @memberOf createEmitter
13
+ */
14
+ on(type, handler) {
15
+ const handlers = all.get(type);
16
+ if (handlers) {
17
+ handlers.push(handler);
18
+ } else {
19
+ all.set(type, [handler]);
6
20
  }
7
21
  },
8
- events: {},
9
- on(event, cb) {
10
- (this.events[event] ||= []).push(cb);
11
- return () => {
12
- this.events[event] = this.events[event]?.filter((i) => cb !== i);
13
- };
22
+ /**
23
+ * Remove an event handler for the given type.
24
+ * If `handler` is omitted, all handlers of the given type are removed.
25
+ *
26
+ * @param {string|symbol} type Type of event to unregister `handler` from (`'*'` to remove a wildcard handler)
27
+ * @param {Function} [handler] Handler function to remove
28
+ * @memberOf createEmitter
29
+ */
30
+ off(type, handler) {
31
+ const handlers = all.get(type);
32
+ if (handlers) {
33
+ if (handler) {
34
+ handlers.splice(handlers.indexOf(handler) >>> 0, 1);
35
+ } else {
36
+ all.set(type, []);
37
+ }
38
+ }
39
+ },
40
+ /**
41
+ * Invoke all handlers for the given type.
42
+ * If present, `'*'` handlers are invoked after type-matched handlers.
43
+ *
44
+ * @remarks
45
+ * Manually firing '*' handlers is not supported.
46
+ *
47
+ * @param {string|symbol} type The event type to invoke
48
+ * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
49
+ * @memberOf createEmitter
50
+ */
51
+ emit(type, evt) {
52
+ let handlers = all.get(type);
53
+ handlers?.slice().forEach((handler) => {
54
+ handler(evt);
55
+ });
56
+ handlers = all.get("*");
57
+ handlers?.slice().forEach((handler) => {
58
+ handler(type, evt);
59
+ });
14
60
  }
15
61
  };
16
62
  }
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { MaybeArray, toArray } from './array.mjs';
2
2
  export { CSVRow, createCSV, escapeCSVValue, parseCSV } from './csv.mjs';
3
- export { Emitter, createEmitter } from './emitter.mjs';
3
+ export { Emitter, EventHandlerList, EventHandlerMap, EventType, Handler, WildCardEventHandlerList, WildcardHandler, createEmitter } from './emitter.mjs';
4
4
  export { cloneJSON, tryParseJSON } from './json.mjs';
5
5
  export { lazy } from './lazy.mjs';
6
6
  export { interopDefault } from './module.mjs';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { MaybeArray, toArray } from './array.js';
2
2
  export { CSVRow, createCSV, escapeCSVValue, parseCSV } from './csv.js';
3
- export { Emitter, createEmitter } from './emitter.js';
3
+ export { Emitter, EventHandlerList, EventHandlerMap, EventType, Handler, WildCardEventHandlerList, WildcardHandler, createEmitter } from './emitter.js';
4
4
  export { cloneJSON, tryParseJSON } from './json.js';
5
5
  export { lazy } from './lazy.js';
6
6
  export { interopDefault } from './module.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "utilful",
3
3
  "type": "module",
4
- "version": "1.1.1",
4
+ "version": "1.2.0",
5
5
  "packageManager": "pnpm@10.5.2",
6
6
  "description": "A collection of TypeScript utilities",
7
7
  "author": "Johann Schopplich <hello@johannschopplich.com>",