hemmiter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dirold2
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.
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # hemmiter
2
+
3
+ A small, type-safe, low-overhead event emitter for TypeScript.
4
+
5
+ > English | [Русский](https://github.com/dirold2/hemmiter/tree/main/lang/ru)
6
+
7
+ [![npm version](https://img.shields.io/npm/v/hemmiter)](https://www.npmjs.com/package/hemmiter)
8
+ [![npm downloads](https://img.shields.io/npm/dm/hemmiter)](https://www.npmjs.com/package/hemmiter)
9
+ [![license](https://img.shields.io/npm/l/hemmiter)](./LICENSE)
10
+ [![typescript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install hemmiter
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```ts
21
+ import { MiniEmitter } from "hemmiter";
22
+
23
+ type Events = {
24
+ data: [value: Uint8Array];
25
+ close: [];
26
+ };
27
+
28
+ class StreamEvents extends MiniEmitter<Events> {
29
+ push(value: Uint8Array): void {
30
+ this.emit("data", value);
31
+ }
32
+
33
+ close(): void {
34
+ this.emit("close");
35
+ }
36
+ }
37
+
38
+ const events = new StreamEvents();
39
+
40
+ events.on("data", (value) => {
41
+ console.log(value.length);
42
+ });
43
+
44
+ events.once("close", () => {
45
+ console.log("closed");
46
+ });
47
+
48
+ events.push(new Uint8Array([1, 2, 3]));
49
+ events.close();
50
+ ```
51
+
52
+ Event names and listener arguments are inferred and checked from the `Events` type.
53
+
54
+ ## API
55
+
56
+ - `on(event, listener)` / `addListener(event, listener)` — register a listener.
57
+ - `off(event, listener)` / `removeListener(event, listener)` — remove one listener registration.
58
+ - `once(event, listener)` — register a listener that runs once. It can be removed using the original listener.
59
+ - `listeners(event)` — get a snapshot of the listeners registered for an event.
60
+ - `listenerCount(event)` — get the number of listeners registered for an event.
61
+ - `removeAllListeners(event?)` — remove listeners for one event or all events.
62
+ - `emit(event, ...args)` — dispatch an event. `emit` is `protected`, allowing subclasses to control event emission.
63
+
64
+ ## Type-safe events
65
+
66
+ Events are defined as a map from event names to argument tuples:
67
+
68
+ ```ts
69
+ type Events = {
70
+ data: [value: Uint8Array];
71
+ error: [error: Error];
72
+ close: [];
73
+ };
74
+
75
+ class Example extends MiniEmitter<Events> {
76
+ emitData(value: Uint8Array): void {
77
+ this.emit("data", value);
78
+ }
79
+ }
80
+ ```
81
+
82
+ TypeScript validates both the event name and its arguments:
83
+
84
+ ```ts
85
+ emitter.on("data", (value) => {
86
+ // value: Uint8Array
87
+ });
88
+
89
+ emitter.on("missing", () => {});
90
+ // ^ TypeScript error
91
+
92
+ emitter.emit("data", "invalid");
93
+ // ^ TypeScript error
94
+ ```
95
+
96
+ ## Design
97
+
98
+ `hemmiter` is intentionally small and focused on low-overhead event dispatch.
99
+
100
+ It provides:
101
+
102
+ - strongly typed event names and arguments;
103
+ - `on`, `off`, `once`, and listener aliases;
104
+ - listener snapshots during dispatch;
105
+ - lazy internal allocation;
106
+ - a fast path for single-listener events;
107
+ - no runtime dependencies.
108
+
109
+ ## License
110
+
111
+ MIT
package/hemmiter.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @ru Тип обработчика события с типизированными аргументами.
3
+ * @en Event listener type with typed arguments.
4
+ * @template Args Event argument tuple.
5
+ */
6
+ export type EventListener<Args extends readonly unknown[]> = (...args: Args) => void;
7
+ /**
8
+ * @ru Минимальный типобезопасный emitter событий.
9
+ * @en Minimal type-safe event emitter.
10
+ * @template Events Event map where each value is an argument tuple.
11
+ */
12
+ export declare class MiniEmitter<Events extends {
13
+ [K in keyof Events]: readonly unknown[];
14
+ }> {
15
+ private listenerMap?;
16
+ /**
17
+ * @ru Зарегистрировать обработчик события.
18
+ * @en Register an event listener.
19
+ * @param event Event name.
20
+ * @param listener Event listener.
21
+ * @returns This emitter instance.
22
+ */
23
+ on<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this;
24
+ /**
25
+ * @ru Зарегистрировать обработчик события.
26
+ * @en Register an event listener.
27
+ * @param event Event name.
28
+ * @param listener Event listener.
29
+ * @returns This emitter instance.
30
+ */
31
+ addListener<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this;
32
+ /**
33
+ * @ru Снять одну регистрацию обработчика события.
34
+ * @en Remove one registration of an event listener.
35
+ * @param event Event name.
36
+ * @param listener Event listener to remove.
37
+ * @returns This emitter instance.
38
+ */
39
+ off<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this;
40
+ /**
41
+ * @ru Снять одну регистрацию обработчика события.
42
+ * @en Remove one registration of an event listener.
43
+ * @param event Event name.
44
+ * @param listener Event listener to remove.
45
+ * @returns This emitter instance.
46
+ */
47
+ removeListener<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this;
48
+ /**
49
+ * @ru Зарегистрировать обработчик, который сработает один раз.
50
+ * @en Register a listener that runs once.
51
+ * @param event Event name.
52
+ * @param listener Event listener.
53
+ * @returns This emitter instance.
54
+ */
55
+ once<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this;
56
+ /**
57
+ * @ru Удалить обработчики одного события или всех событий.
58
+ * @en Remove listeners for one event or for all events.
59
+ * @param event Optional event name.
60
+ * @returns This emitter instance.
61
+ */
62
+ removeAllListeners<K extends keyof Events>(event?: K): this;
63
+ /**
64
+ * @ru Получить копию обработчиков события.
65
+ * @en Get a snapshot of the listeners for an event.
66
+ * @param event Event name.
67
+ * @returns A new array containing the registered listeners.
68
+ */
69
+ listeners<K extends keyof Events>(event: K): EventListener<Events[K]>[];
70
+ listenerCount<K extends keyof Events>(event: K): number;
71
+ /**
72
+ * @ru Вызвать обработчики события.
73
+ * @en Dispatch an event to its listeners.
74
+ * @param event Event name.
75
+ * @param args Event arguments.
76
+ */
77
+ protected emit<K extends keyof Events>(event: K, ...args: Events[K]): void;
78
+ }
79
+ //# sourceMappingURL=hemmiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hemmiter.d.ts","sourceRoot":"","sources":["../src/hemmiter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,IAAI,SAAS,SAAS,OAAO,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;AAMrF;;;;GAIG;AACH,qBAAa,WAAW,CAAC,MAAM,SAAS;KAAG,CAAC,IAAI,MAAM,MAAM,GAAG,SAAS,OAAO,EAAE;CAAE;IACjF,OAAO,CAAC,WAAW,CAAC,CAAwC;IAE5D;;;;;;OAMG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAO7E;IAED;;;;;;OAMG;IACH,WAAW,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAEtF;IAED;;;;;;OAMG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAuB9E;IAED;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAEzF;IAED;;;;;;OAMG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAQ/E;IAED;;;;;OAKG;IACH,kBAAkB,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAQ1D;IAED;;;;;OAKG;IACH,SAAS,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAUtE;IAED,aAAa,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAEtD;IAED;;;;;OAKG;IACH,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAgBzE;CACF"}
package/hemmiter.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";const o=Symbol("hemmiter.onceOriginal");export class MiniEmitter{listenerMap;on(t,s){const i=s,e=this.listenerMap??=new Map,r=e.get(t);return r?r.push(i):e.set(t,[i]),this}addListener(t,s){return this.on(t,s)}off(t,s){const i=this.listenerMap?.get(t);if(!i)return this;const e=s;let r=-1;for(let n=0;n<i.length;n++){const l=i[n];if(l&&(l===e||l[o]===e)){r=n;break}}return r!==-1&&(i.splice(r,1),i.length===0&&this.listenerMap?.delete(t)),this}removeListener(t,s){return this.off(t,s)}once(t,s){const i=s,e=((...r)=>{this.off(t,e),s(...r)});return e[o]=i,this.on(t,e)}removeAllListeners(t){return t===void 0?this.listenerMap?.clear():this.listenerMap?.delete(t),this}listeners(t){const s=this.listenerMap?.get(t);if(!s)return[];const i=s.slice();for(let e=0;e<s.length;e++){const r=s[e];i[e]=r[o]??r}return i}listenerCount(t){return this.listenerMap?.get(t)?.length??0}emit(t,...s){const i=this.listenerMap;if(!i)return;const e=i.get(t);if(!e)return;if(e.length===1){e[0](...s);return}const r=e.slice();for(const n of r)n(...s)}}
2
+ //# sourceMappingURL=hemmiter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/hemmiter.ts"],
4
+ "sourcesContent": ["/**\n * @ru \u0422\u0438\u043F \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0441 \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u043C\u0438 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u0430\u043C\u0438.\n * @en Event listener type with typed arguments.\n * @template Args Event argument tuple.\n */\nexport type EventListener<Args extends readonly unknown[]> = (...args: Args) => void;\n\ntype AnyEventListener = (...args: readonly unknown[]) => void;\nconst onceOriginal = Symbol(\"hemmiter.onceOriginal\");\ntype OnceListener = AnyEventListener & { [onceOriginal]: AnyEventListener };\n\n/**\n * @ru \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0442\u0438\u043F\u043E\u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0439 emitter \u0441\u043E\u0431\u044B\u0442\u0438\u0439.\n * @en Minimal type-safe event emitter.\n * @template Events Event map where each value is an argument tuple.\n */\nexport class MiniEmitter<Events extends { [K in keyof Events]: readonly unknown[] }> {\n private listenerMap?: Map<keyof Events, AnyEventListener[]>;\n\n /**\n * @ru \u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Register an event listener.\n * @param event Event name.\n * @param listener Event listener.\n * @returns This emitter instance.\n */\n on<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n const target = listener as AnyEventListener;\n const listeners = (this.listenerMap ??= new Map());\n const list = listeners.get(event);\n if (list) list.push(target);\n else listeners.set(event, [target]);\n return this;\n }\n\n /**\n * @ru \u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Register an event listener.\n * @param event Event name.\n * @param listener Event listener.\n * @returns This emitter instance.\n */\n addListener<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n return this.on(event, listener);\n }\n\n /**\n * @ru \u0421\u043D\u044F\u0442\u044C \u043E\u0434\u043D\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044E \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Remove one registration of an event listener.\n * @param event Event name.\n * @param listener Event listener to remove.\n * @returns This emitter instance.\n */\n off<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n const list = this.listenerMap?.get(event);\n if (!list) return this;\n\n const target = listener as AnyEventListener;\n let index = -1;\n for (let i = 0; i < list.length; i++) {\n const registered = list[i];\n if (\n registered &&\n (registered === target || (registered as OnceListener)[onceOriginal] === target)\n ) {\n index = i;\n break;\n }\n }\n\n if (index !== -1) {\n list.splice(index, 1);\n if (list.length === 0) this.listenerMap?.delete(event);\n }\n\n return this;\n }\n\n /**\n * @ru \u0421\u043D\u044F\u0442\u044C \u043E\u0434\u043D\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044E \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Remove one registration of an event listener.\n * @param event Event name.\n * @param listener Event listener to remove.\n * @returns This emitter instance.\n */\n removeListener<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n return this.off(event, listener);\n }\n\n /**\n * @ru \u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043E\u0434\u0438\u043D \u0440\u0430\u0437.\n * @en Register a listener that runs once.\n * @param event Event name.\n * @param listener Event listener.\n * @returns This emitter instance.\n */\n once<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n const target = listener as AnyEventListener;\n const wrapper = ((...args: Events[K]) => {\n this.off(event, wrapper);\n listener(...args);\n }) as OnceListener;\n wrapper[onceOriginal] = target;\n return this.on(event, wrapper);\n }\n\n /**\n * @ru \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0438\u043B\u0438 \u0432\u0441\u0435\u0445 \u0441\u043E\u0431\u044B\u0442\u0438\u0439.\n * @en Remove listeners for one event or for all events.\n * @param event Optional event name.\n * @returns This emitter instance.\n */\n removeAllListeners<K extends keyof Events>(event?: K): this {\n if (event === undefined) {\n this.listenerMap?.clear();\n } else {\n this.listenerMap?.delete(event);\n }\n\n return this;\n }\n\n /**\n * @ru \u041F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043A\u043E\u043F\u0438\u044E \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u043E\u0432 \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Get a snapshot of the listeners for an event.\n * @param event Event name.\n * @returns A new array containing the registered listeners.\n */\n listeners<K extends keyof Events>(event: K): EventListener<Events[K]>[] {\n const list = this.listenerMap?.get(event);\n if (!list) return [];\n\n const result = list.slice();\n for (let i = 0; i < list.length; i++) {\n const listener = list[i];\n result[i] = (listener as OnceListener)[onceOriginal] ?? listener;\n }\n return result as EventListener<Events[K]>[];\n }\n\n listenerCount<K extends keyof Events>(event: K): number {\n return this.listenerMap?.get(event)?.length ?? 0;\n }\n\n /**\n * @ru \u0412\u044B\u0437\u0432\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0438 \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Dispatch an event to its listeners.\n * @param event Event name.\n * @param args Event arguments.\n */\n protected emit<K extends keyof Events>(event: K, ...args: Events[K]): void {\n const listeners = this.listenerMap;\n if (!listeners) return;\n\n const list = listeners.get(event);\n if (!list) return;\n\n if (list.length === 1) {\n list[0]!(...args);\n return;\n }\n\n const targets = list.slice();\n for (const listener of targets) {\n listener(...args);\n }\n }\n}\n"],
5
+ "mappings": "aAQA,MAAMA,EAAe,OAAO,uBAAuB,EAQ7C,aAAO,WAAW,CACd,YASR,GAA2BC,EAAUC,EAAkC,CACrE,MAAMC,EAASD,EACTE,EAAa,KAAK,cAAgB,IAAI,IACtCC,EAAOD,EAAU,IAAIH,CAAK,EAChC,OAAII,EAAMA,EAAK,KAAKF,CAAM,EACrBC,EAAU,IAAIH,EAAO,CAACE,CAAM,CAAC,EAC3B,IACT,CASA,YAAoCF,EAAUC,EAAkC,CAC9E,OAAO,KAAK,GAAGD,EAAOC,CAAQ,CAChC,CASA,IAA4BD,EAAUC,EAAkC,CACtE,MAAMG,EAAO,KAAK,aAAa,IAAIJ,CAAK,EACxC,GAAI,CAACI,EAAM,OAAO,KAElB,MAAMF,EAASD,EACf,IAAII,EAAQ,GACZ,QAASC,EAAI,EAAGA,EAAIF,EAAK,OAAQE,IAAK,CACpC,MAAMC,EAAaH,EAAKE,CAAC,EACzB,GACEC,IACCA,IAAeL,GAAWK,EAA4BR,CAAY,IAAMG,GACzE,CACAG,EAAQC,EACR,KACF,CACF,CAEA,OAAID,IAAU,KACZD,EAAK,OAAOC,EAAO,CAAC,EAChBD,EAAK,SAAW,GAAG,KAAK,aAAa,OAAOJ,CAAK,GAGhD,IACT,CASA,eAAuCA,EAAUC,EAAkC,CACjF,OAAO,KAAK,IAAID,EAAOC,CAAQ,CACjC,CASA,KAA6BD,EAAUC,EAAkC,CACvE,MAAMC,EAASD,EACTO,GAAW,IAAIC,IAAmB,CACtC,KAAK,IAAIT,EAAOQ,CAAO,EACvBP,EAAS,GAAGQ,CAAI,CAClB,GACA,OAAAD,EAAQT,CAAY,EAAIG,EACjB,KAAK,GAAGF,EAAOQ,CAAO,CAC/B,CAQA,mBAA2CR,EAAS,CAClD,OAAIA,IAAU,OACZ,KAAK,aAAa,MAAK,EAEvB,KAAK,aAAa,OAAOA,CAAK,EAGzB,IACT,CAQA,UAAkCA,EAAQ,CACxC,MAAMI,EAAO,KAAK,aAAa,IAAIJ,CAAK,EACxC,GAAI,CAACI,EAAM,MAAO,CAAA,EAElB,MAAMM,EAASN,EAAK,MAAK,EACzB,QAASE,EAAI,EAAGA,EAAIF,EAAK,OAAQE,IAAK,CACpC,MAAML,EAAWG,EAAKE,CAAC,EACvBI,EAAOJ,CAAC,EAAKL,EAA0BF,CAAY,GAAKE,CAC1D,CACA,OAAOS,CACT,CAEA,cAAsCV,EAAQ,CAC5C,OAAO,KAAK,aAAa,IAAIA,CAAK,GAAG,QAAU,CACjD,CAQU,KAA6BA,KAAaS,EAAe,CACjE,MAAMN,EAAY,KAAK,YACvB,GAAI,CAACA,EAAW,OAEhB,MAAMC,EAAOD,EAAU,IAAIH,CAAK,EAChC,GAAI,CAACI,EAAM,OAEX,GAAIA,EAAK,SAAW,EAAG,CACrBA,EAAK,CAAC,EAAG,GAAGK,CAAI,EAChB,MACF,CAEA,MAAME,EAAUP,EAAK,MAAK,EAC1B,UAAWH,KAAYU,EACrBV,EAAS,GAAGQ,CAAI,CAEpB",
6
+ "names": ["onceOriginal", "event", "listener", "target", "listeners", "list", "index", "i", "registered", "wrapper", "args", "result", "targets"]
7
+ }
package/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var c=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var g=(i,e)=>{for(var r in e)c(i,r,{get:e[r],enumerable:!0})},M=(i,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of a(e))!p.call(i,t)&&t!==r&&c(i,t,{get:()=>e[t],enumerable:!(n=u(e,t))||n.enumerable});return i};var m=i=>M(c({},"__esModule",{value:!0}),i);var d={};g(d,{MiniEmitter:()=>l});module.exports=m(d);var f=Symbol("hemmiter.onceOriginal"),l=class{listenerMap;on(e,r){let n=r,t=this.listenerMap??=new Map,s=t.get(e);return s?s.push(n):t.set(e,[n]),this}addListener(e,r){return this.on(e,r)}off(e,r){let n=this.listenerMap?.get(e);if(!n)return this;let t=r,s=-1;for(let o=0;o<n.length;o++){let h=n[o];if(h&&(h===t||h[f]===t)){s=o;break}}return s!==-1&&(n.splice(s,1),n.length===0&&this.listenerMap?.delete(e)),this}removeListener(e,r){return this.off(e,r)}once(e,r){let n=r,t=((...s)=>{this.off(e,t),r(...s)});return t[f]=n,this.on(e,t)}removeAllListeners(e){return e===void 0?this.listenerMap?.clear():this.listenerMap?.delete(e),this}listeners(e){let r=this.listenerMap?.get(e);if(!r)return[];let n=r.slice();for(let t=0;t<r.length;t++){let s=r[t];n[t]=s[f]??s}return n}listenerCount(e){return this.listenerMap?.get(e)?.length??0}emit(e,...r){let n=this.listenerMap;if(!n)return;let t=n.get(e);if(!t)return;if(t.length===1){t[0](...r);return}let s=t.slice();for(let o of s)o(...r)}};
2
+ //# sourceMappingURL=index.cjs.map
package/index.cjs.map ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/hemmiter.ts"],
4
+ "sourcesContent": ["/**\n * @ru \u0422\u0438\u043F \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0441 \u0442\u0438\u043F\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u043C\u0438 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u0430\u043C\u0438.\n * @en Event listener type with typed arguments.\n * @template Args Event argument tuple.\n */\nexport type EventListener<Args extends readonly unknown[]> = (...args: Args) => void;\n\ntype AnyEventListener = (...args: readonly unknown[]) => void;\nconst onceOriginal = Symbol(\"hemmiter.onceOriginal\");\ntype OnceListener = AnyEventListener & { [onceOriginal]: AnyEventListener };\n\n/**\n * @ru \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0442\u0438\u043F\u043E\u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0439 emitter \u0441\u043E\u0431\u044B\u0442\u0438\u0439.\n * @en Minimal type-safe event emitter.\n * @template Events Event map where each value is an argument tuple.\n */\nexport class MiniEmitter<Events extends { [K in keyof Events]: readonly unknown[] }> {\n private listenerMap?: Map<keyof Events, AnyEventListener[]>;\n\n /**\n * @ru \u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Register an event listener.\n * @param event Event name.\n * @param listener Event listener.\n * @returns This emitter instance.\n */\n on<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n const target = listener as AnyEventListener;\n const listeners = (this.listenerMap ??= new Map());\n const list = listeners.get(event);\n if (list) list.push(target);\n else listeners.set(event, [target]);\n return this;\n }\n\n /**\n * @ru \u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Register an event listener.\n * @param event Event name.\n * @param listener Event listener.\n * @returns This emitter instance.\n */\n addListener<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n return this.on(event, listener);\n }\n\n /**\n * @ru \u0421\u043D\u044F\u0442\u044C \u043E\u0434\u043D\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044E \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Remove one registration of an event listener.\n * @param event Event name.\n * @param listener Event listener to remove.\n * @returns This emitter instance.\n */\n off<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n const list = this.listenerMap?.get(event);\n if (!list) return this;\n\n const target = listener as AnyEventListener;\n let index = -1;\n for (let i = 0; i < list.length; i++) {\n const registered = list[i];\n if (\n registered &&\n (registered === target || (registered as OnceListener)[onceOriginal] === target)\n ) {\n index = i;\n break;\n }\n }\n\n if (index !== -1) {\n list.splice(index, 1);\n if (list.length === 0) this.listenerMap?.delete(event);\n }\n\n return this;\n }\n\n /**\n * @ru \u0421\u043D\u044F\u0442\u044C \u043E\u0434\u043D\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044E \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Remove one registration of an event listener.\n * @param event Event name.\n * @param listener Event listener to remove.\n * @returns This emitter instance.\n */\n removeListener<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n return this.off(event, listener);\n }\n\n /**\n * @ru \u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043E\u0434\u0438\u043D \u0440\u0430\u0437.\n * @en Register a listener that runs once.\n * @param event Event name.\n * @param listener Event listener.\n * @returns This emitter instance.\n */\n once<K extends keyof Events>(event: K, listener: EventListener<Events[K]>): this {\n const target = listener as AnyEventListener;\n const wrapper = ((...args: Events[K]) => {\n this.off(event, wrapper);\n listener(...args);\n }) as OnceListener;\n wrapper[onceOriginal] = target;\n return this.on(event, wrapper);\n }\n\n /**\n * @ru \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0438\u043B\u0438 \u0432\u0441\u0435\u0445 \u0441\u043E\u0431\u044B\u0442\u0438\u0439.\n * @en Remove listeners for one event or for all events.\n * @param event Optional event name.\n * @returns This emitter instance.\n */\n removeAllListeners<K extends keyof Events>(event?: K): this {\n if (event === undefined) {\n this.listenerMap?.clear();\n } else {\n this.listenerMap?.delete(event);\n }\n\n return this;\n }\n\n /**\n * @ru \u041F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043A\u043E\u043F\u0438\u044E \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u043E\u0432 \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Get a snapshot of the listeners for an event.\n * @param event Event name.\n * @returns A new array containing the registered listeners.\n */\n listeners<K extends keyof Events>(event: K): EventListener<Events[K]>[] {\n const list = this.listenerMap?.get(event);\n if (!list) return [];\n\n const result = list.slice();\n for (let i = 0; i < list.length; i++) {\n const listener = list[i];\n result[i] = (listener as OnceListener)[onceOriginal] ?? listener;\n }\n return result as EventListener<Events[K]>[];\n }\n\n listenerCount<K extends keyof Events>(event: K): number {\n return this.listenerMap?.get(event)?.length ?? 0;\n }\n\n /**\n * @ru \u0412\u044B\u0437\u0432\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0438 \u0441\u043E\u0431\u044B\u0442\u0438\u044F.\n * @en Dispatch an event to its listeners.\n * @param event Event name.\n * @param args Event arguments.\n */\n protected emit<K extends keyof Events>(event: K, ...args: Events[K]): void {\n const listeners = this.listenerMap;\n if (!listeners) return;\n\n const list = listeners.get(event);\n if (!list) return;\n\n if (list.length === 1) {\n list[0]!(...args);\n return;\n }\n\n const targets = list.slice();\n for (const listener of targets) {\n listener(...args);\n }\n }\n}\n"],
5
+ "mappings": "+dAQA,IAAMA,EAAe,OAAO,uBAAuB,EAQtCC,EAAP,KAAkB,CACd,YASR,GAA2BC,EAAUC,EAAkC,CACrE,IAAMC,EAASD,EACTE,EAAa,KAAK,cAAgB,IAAI,IACtCC,EAAOD,EAAU,IAAIH,CAAK,EAChC,OAAII,EAAMA,EAAK,KAAKF,CAAM,EACrBC,EAAU,IAAIH,EAAO,CAACE,CAAM,CAAC,EAC3B,IACT,CASA,YAAoCF,EAAUC,EAAkC,CAC9E,OAAO,KAAK,GAAGD,EAAOC,CAAQ,CAChC,CASA,IAA4BD,EAAUC,EAAkC,CACtE,IAAMG,EAAO,KAAK,aAAa,IAAIJ,CAAK,EACxC,GAAI,CAACI,EAAM,OAAO,KAElB,IAAMF,EAASD,EACXI,EAAQ,GACZ,QAASC,EAAI,EAAGA,EAAIF,EAAK,OAAQE,IAAK,CACpC,IAAMC,EAAaH,EAAKE,CAAC,EACzB,GACEC,IACCA,IAAeL,GAAWK,EAA4BT,CAAY,IAAMI,GACzE,CACAG,EAAQC,EACR,KACF,CACF,CAEA,OAAID,IAAU,KACZD,EAAK,OAAOC,EAAO,CAAC,EAChBD,EAAK,SAAW,GAAG,KAAK,aAAa,OAAOJ,CAAK,GAGhD,IACT,CASA,eAAuCA,EAAUC,EAAkC,CACjF,OAAO,KAAK,IAAID,EAAOC,CAAQ,CACjC,CASA,KAA6BD,EAAUC,EAAkC,CACvE,IAAMC,EAASD,EACTO,GAAW,IAAIC,IAAmB,CACtC,KAAK,IAAIT,EAAOQ,CAAO,EACvBP,EAAS,GAAGQ,CAAI,CAClB,GACA,OAAAD,EAAQV,CAAY,EAAII,EACjB,KAAK,GAAGF,EAAOQ,CAAO,CAC/B,CAQA,mBAA2CR,EAAS,CAClD,OAAIA,IAAU,OACZ,KAAK,aAAa,MAAK,EAEvB,KAAK,aAAa,OAAOA,CAAK,EAGzB,IACT,CAQA,UAAkCA,EAAQ,CACxC,IAAMI,EAAO,KAAK,aAAa,IAAIJ,CAAK,EACxC,GAAI,CAACI,EAAM,MAAO,CAAA,EAElB,IAAMM,EAASN,EAAK,MAAK,EACzB,QAASE,EAAI,EAAGA,EAAIF,EAAK,OAAQE,IAAK,CACpC,IAAML,EAAWG,EAAKE,CAAC,EACvBI,EAAOJ,CAAC,EAAKL,EAA0BH,CAAY,GAAKG,CAC1D,CACA,OAAOS,CACT,CAEA,cAAsCV,EAAQ,CAC5C,OAAO,KAAK,aAAa,IAAIA,CAAK,GAAG,QAAU,CACjD,CAQU,KAA6BA,KAAaS,EAAe,CACjE,IAAMN,EAAY,KAAK,YACvB,GAAI,CAACA,EAAW,OAEhB,IAAMC,EAAOD,EAAU,IAAIH,CAAK,EAChC,GAAI,CAACI,EAAM,OAEX,GAAIA,EAAK,SAAW,EAAG,CACrBA,EAAK,CAAC,EAAG,GAAGK,CAAI,EAChB,MACF,CAEA,IAAME,EAAUP,EAAK,MAAK,EAC1B,QAAWH,KAAYU,EACrBV,EAAS,GAAGQ,CAAI,CAEpB,CAAA",
6
+ "names": ["onceOriginal", "MiniEmitter", "event", "listener", "target", "listeners", "list", "index", "i", "registered", "wrapper", "args", "result", "targets"]
7
+ }
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { MiniEmitter } from "./hemmiter.js";
2
+ export type { EventListener } from "./hemmiter.js";
3
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";export{MiniEmitter}from"./hemmiter.js";
2
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["export { MiniEmitter } from \"./hemmiter.js\";\nexport type { EventListener } from \"./hemmiter.js\";\n"],
5
+ "mappings": "aAAA,OAAS,gBAAmB",
6
+ "names": []
7
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "hemmiter",
3
+ "version": "0.1.0",
4
+ "description": "A tiny type-safe event emitter for TypeScript.",
5
+ "keywords": [
6
+ "events",
7
+ "in-memory",
8
+ "typescript"
9
+ ],
10
+ "homepage": "https://github.com/dirold2/hemmiter#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/dirold2/hemmiter/issues"
13
+ },
14
+ "license": "MIT",
15
+ "author": "dirold2",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/dirold2/hemmiter.git"
19
+ },
20
+ "type": "module",
21
+ "sideEffects": false,
22
+ "main": "./index.сjs",
23
+ "module": "./index.js",
24
+ "types": "./index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./index.d.ts",
28
+ "import": "./index.js",
29
+ "require": "./index.cjs"
30
+ }
31
+ },
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "private": false
36
+ }