utilful 1.1.1 → 1.2.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/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.events.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
+ events: 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>>(events?: 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
+ events: 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>>(events?: 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,61 @@
1
- function createEmitter() {
1
+ function createEmitter(events) {
2
+ events ||= /* @__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
+ events,
8
+ /**
9
+ * Register an event handler for the given type.
10
+ *
11
+ * @memberOf createEmitter
12
+ */
13
+ on(type, handler) {
14
+ const handlers = events.get(type);
15
+ if (handlers) {
16
+ handlers.push(handler);
17
+ } else {
18
+ events.set(type, [handler]);
6
19
  }
7
20
  },
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
- };
21
+ /**
22
+ * Remove an event handler for the given type.
23
+ *
24
+ * @remarks
25
+ * If `handler` is omitted, all handlers of the given type are removed.
26
+ *
27
+ * @memberOf createEmitter
28
+ */
29
+ off(type, handler) {
30
+ const handlers = events.get(type);
31
+ if (handlers) {
32
+ if (handler) {
33
+ handlers.splice(handlers.indexOf(handler) >>> 0, 1);
34
+ } else {
35
+ events.set(type, []);
36
+ }
37
+ }
38
+ },
39
+ /**
40
+ * Invoke all handlers for the given type.
41
+ *
42
+ * @remarks
43
+ * If present, `'*'` handlers are invoked after type-matched handlers.
44
+ * Manually firing '*' handlers is not supported.
45
+ *
46
+ * @memberOf createEmitter
47
+ */
48
+ emit(type, evt) {
49
+ let handlers = events.get(type);
50
+ if (handlers) {
51
+ for (const handler of [...handlers])
52
+ handler(evt);
53
+ }
54
+ handlers = events.get("*");
55
+ if (handlers) {
56
+ for (const handler of [...handlers])
57
+ handler(type, evt);
58
+ }
14
59
  }
15
60
  };
16
61
  }
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.1",
5
5
  "packageManager": "pnpm@10.5.2",
6
6
  "description": "A collection of TypeScript utilities",
7
7
  "author": "Johann Schopplich <hello@johannschopplich.com>",