emitochondria 1.0.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 Pablo Díaz A.K.A exudev
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,189 @@
1
+
2
+ # 🔋 Emitochondria
3
+
4
+ > *The powerhouse of your events*
5
+
6
+ [![npm version](https://img.shields.io/npm/v/emitochondria.svg)](https://www.npmjs.com/package/emitochondria)
7
+ [![CI](https://github.com/Exudev/emitochondria/actions/workflows/ci.yml/badge.svg)](https://github.com/Exudev/emitochondria/actions/workflows/ci.yml)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
10
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/emitochondria)](https://bundlephobia.com/package/emitochondria)
11
+
12
+ A tiny, fully-typed event emitter for TypeScript. Zero dependencies, under 1KB.
13
+
14
+ ## Features
15
+
16
+ - ✅ **Full type safety** — Event names and payloads checked at compile time
17
+ - ✅ **Tiny** — Under 1KB minified
18
+ - ✅ **Zero dependencies**
19
+ - ✅ **Universal** — Works in browser and Node.js
20
+ - ✅ **Async support** — `emitAsync` awaits all handlers
21
+ - ✅ **Wildcard listeners** — `onAny` for debugging/logging
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install emitochondria
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { createEmitochondria } from 'emitochondria';
33
+
34
+ // Define your events
35
+ type MyEvents = {
36
+ 'user:login': { userId: string; email: string };
37
+ 'user:logout': { userId: string };
38
+ 'app:ready': void;
39
+ };
40
+
41
+ // Create emitter
42
+ const events = createEmitochondria<MyEvents>();
43
+
44
+ // Subscribe (with full autocomplete!)
45
+ events.on('user:login', (data) => {
46
+ console.log(`${data.email} logged in`);
47
+ });
48
+
49
+ // Emit
50
+ events.emit('user:login', { userId: '123', email: 'pablo@example.com' });
51
+
52
+ // Void events need no payload
53
+ events.emit('app:ready');
54
+ ```
55
+
56
+ ## API
57
+
58
+ ### `createEmitochondria<T>()`
59
+
60
+ Create a new typed emitter.
61
+
62
+ ### `.on(event, handler)`
63
+
64
+ Subscribe to an event. Returns an unsubscribe function.
65
+
66
+ ```typescript
67
+ const unsubscribe = events.on('user:login', (data) => {
68
+ console.log(data);
69
+ });
70
+
71
+ // Later...
72
+ unsubscribe();
73
+ ```
74
+
75
+ ### `.off(event, handler)`
76
+
77
+ Remove a specific handler.
78
+
79
+ ### `.once(event, handler)`
80
+
81
+ Subscribe for a single emission only.
82
+
83
+ ```typescript
84
+ events.once('app:ready', () => {
85
+ console.log('This only runs once');
86
+ });
87
+ ```
88
+
89
+ ### `.emit(event, payload?)`
90
+
91
+ Emit an event synchronously.
92
+
93
+ ### `.emitAsync(event, payload?)`
94
+
95
+ Emit and await all handlers (parallel execution).
96
+
97
+ ```typescript
98
+ events.on('save', async (data) => {
99
+ await saveToDatabase(data);
100
+ });
101
+
102
+ await events.emitAsync('save', { id: '123' });
103
+ console.log('All handlers complete');
104
+ ```
105
+
106
+ ### `.onAny(handler)`
107
+
108
+ Subscribe to all events. Great for logging.
109
+
110
+ ```typescript
111
+ events.onAny((event, payload) => {
112
+ console.log(`[${event}]`, payload);
113
+ });
114
+ ```
115
+
116
+ ### `.offAny(handler)`
117
+
118
+ Remove a wildcard handler.
119
+
120
+ ### `.clear(event?)`
121
+
122
+ Clear handlers for an event, or all handlers if no event specified.
123
+
124
+ ### `.listenerCount(event)`
125
+
126
+ Get the number of listeners for an event.
127
+
128
+ ## ⚡ Biological API (Alternative Naming)
129
+
130
+ Emitochondria offers biologically-themed aliases for all methods. Use whichever style fits your project:
131
+
132
+ | Standard API | Biological Alias | Description |
133
+ |--------------|------------------|-------------|
134
+ | `.on()` | `.bind()` | Bind a receptor to a signal |
135
+ | `.off()` | `.release()` | Release a receptor |
136
+ | `.emit()` | `.pulse()` | Pulse energy through the system |
137
+ | `.emitAsync()` | `.cascade()` | Trigger a signal cascade |
138
+ | `.once()` | `.spike()` | Single spike of energy |
139
+ | `.onAny()` | `.membrane()` | Membrane catches all signals |
140
+ | `.clear()` | `.apoptosis()` | Programmed cell death |
141
+ | `.listenerCount()` | `.receptors()` | Count of receptors |
142
+
143
+ ```typescript
144
+ // Standard API
145
+ events.on('user:login', handler);
146
+ events.emit('user:login', data);
147
+
148
+ // Biological API - same functionality, different style
149
+ events.bind('user:login', handler);
150
+ events.pulse('user:login', data);
151
+
152
+ // Mix and match freely
153
+ events.bind('save', async (data) => await saveToDatabase(data));
154
+ await events.cascade('save', { id: '123' });
155
+ ```
156
+
157
+ ## TypeScript Magic
158
+
159
+ The type system prevents mistakes at compile time:
160
+
161
+ ```typescript
162
+ // ❌ Compile error: event doesn't exist
163
+ events.emit('user:logni', {});
164
+
165
+ // ❌ Compile error: missing required fields
166
+ events.emit('user:login', {});
167
+
168
+ // ❌ Compile error: wrong payload type
169
+ events.emit('user:login', { userId: 123 }); // should be string
170
+
171
+ // ✅ All good
172
+ events.emit('user:login', { userId: '123', email: 'test@example.com' });
173
+ ```
174
+
175
+ ## Examples
176
+
177
+ Check out the [`examples/`](./examples) folder for more comprehensive usage examples including:
178
+ - Basic event subscription and emission
179
+ - Async event handlers
180
+ - Wildcard listeners
181
+ - Biological API usage
182
+
183
+ ## Contributing
184
+
185
+ Contributions are welcome! Please feel free to submit a Pull Request.
186
+
187
+ ## License
188
+
189
+ MIT © Pablo Díaz
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var c=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var m=(a,t)=>{for(var r in t)c(a,r,{get:t[r],enumerable:!0})},u=(a,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of p(t))!v.call(a,e)&&e!==r&&c(a,e,{get:()=>t[e],enumerable:!(n=E(t,e))||n.enumerable});return a};var f=a=>u(c({},"__esModule",{value:!0}),a);var K={};m(K,{createEmitochondria:()=>T,default:()=>y});module.exports=f(K);function T(){let a=new Map,t=new Set;function r(e){let o=a.get(e);return o||(o=new Set,a.set(e,o)),o}let n={on(e,o){return r(e).add(o),()=>n.off(e,o)},off(e,o){r(e).delete(o)},once(e,o){let s=(i=>(n.off(e,s),o(i)));return n.on(e,s)},emit(e,...o){let s=o[0];r(e).forEach(i=>i(s)),t.forEach(i=>i(e,s))},async emitAsync(e,...o){let s=o[0],i=[];r(e).forEach(l=>{let d=l(s);d instanceof Promise&&i.push(d)}),t.forEach(l=>{let d=l(e,s);d instanceof Promise&&i.push(d)}),await Promise.all(i)},onAny(e){return t.add(e),()=>n.offAny(e)},offAny(e){t.delete(e)},clear(e){e?a.delete(e):(a.clear(),t.clear())},listenerCount(e){return r(e).size},bind:null,release:null,pulse:null,cascade:null,spike:null,membrane:null,apoptosis:null,receptors:null};return n.bind=n.on,n.release=n.off,n.pulse=n.emit,n.cascade=n.emitAsync,n.spike=n.once,n.membrane=n.onAny,n.apoptosis=n.clear,n.receptors=n.listenerCount,n}var y=T;0&&(module.exports={createEmitochondria});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Base type for event maps.\n * Keys are event names, values are payload types.\n */\nexport type EventMap = Record<string, unknown>;\n\n/**\n * Extracts event names from an event map as a string union.\n */\nexport type EventKey<T extends EventMap> = keyof T & string;\n\n/**\n * Handler function for a specific event payload.\n * Can be sync or async.\n */\nexport type EventHandler<T> = (payload: T) => void | Promise<void>;\n\n/**\n * Wildcard handler that receives event name and payload.\n * Useful for logging/debugging.\n */\nexport type WildcardHandler<T extends EventMap> = <K extends EventKey<T>>(\n event: K,\n payload: T[K]\n) => void | Promise<void>;\n\n/**\n * The typed emitter interface.\n */\nexport interface Emitochondria<T extends EventMap> {\n /** Subscribe to an event. Returns unsubscribe function. */\n on<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;\n /** Unsubscribe a handler from an event. */\n off<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): void;\n /** Subscribe for a single emission only. */\n once<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;\n /** Emit an event synchronously. */\n emit<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): void;\n /** Emit an event and await all handlers. */\n emitAsync<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): Promise<void>;\n /** Subscribe to all events (wildcard). */\n onAny(handler: WildcardHandler<T>): () => void;\n /** Unsubscribe a wildcard handler. */\n offAny(handler: WildcardHandler<T>): void;\n /** Clear handlers for a specific event or all events. */\n clear<K extends EventKey<T>>(event?: K): void;\n /** Get the number of listeners for an event. */\n listenerCount<K extends EventKey<T>>(event: K): number;\n\n // ⚡ Biological aliases\n /** Alias for `on` — Bind a receptor to a signal. */\n bind: Emitochondria<T>['on'];\n /** Alias for `off` — Release a receptor. */\n release: Emitochondria<T>['off'];\n /** Alias for `emit` — Pulse energy through the system. */\n pulse: Emitochondria<T>['emit'];\n /** Alias for `emitAsync` — Trigger a signal cascade. */\n cascade: Emitochondria<T>['emitAsync'];\n /** Alias for `once` — Single spike of energy. */\n spike: Emitochondria<T>['once'];\n /** Alias for `onAny` — Membrane catches all signals. */\n membrane: Emitochondria<T>['onAny'];\n /** Alias for `clear` — Programmed cell death. */\n apoptosis: Emitochondria<T>['clear'];\n /** Alias for `listenerCount` — Count of receptors. */\n receptors: Emitochondria<T>['listenerCount'];\n}\n\n/**\n * Create a new typed event emitter.\n *\n * @example\n * ```typescript\n * type MyEvents = {\n * 'user:login': { userId: string };\n * 'app:ready': void;\n * };\n *\n * const mito = createEmitochondria<MyEvents>();\n *\n * // Standard API\n * mito.on('user:login', (data) => {\n * console.log(data.userId); // fully typed!\n * });\n * mito.emit('user:login', { userId: '123' });\n * mito.emit('app:ready');\n *\n * // Biological API ⚡\n * mito.bind('user:login', (data) => console.log(data.userId));\n * mito.pulse('user:login', { userId: '123' });\n * ```\n */\nexport function createEmitochondria<T extends EventMap>(): Emitochondria<T> {\n const handlers: Map<string, Set<EventHandler<unknown>>> = new Map();\n const wildcardHandlers: Set<WildcardHandler<T>> = new Set();\n\n function getHandlers(event: string): Set<EventHandler<unknown>> {\n let set = handlers.get(event);\n if (!set) {\n set = new Set();\n handlers.set(event, set);\n }\n return set;\n }\n\n const e: Emitochondria<T> = {\n on(event, handler) {\n getHandlers(event).add(handler as EventHandler<unknown>);\n return () => e.off(event, handler);\n },\n\n off(event, handler) {\n getHandlers(event).delete(handler as EventHandler<unknown>);\n },\n\n once(event, handler) {\n const wrapper = ((payload: T[typeof event]) => {\n e.off(event, wrapper as EventHandler<T[typeof event]>);\n return handler(payload);\n }) as EventHandler<T[typeof event]>;\n\n return e.on(event, wrapper);\n },\n\n emit(event, ...payload) {\n const data = payload[0];\n getHandlers(event).forEach((handler) => handler(data));\n wildcardHandlers.forEach((handler) => handler(event, data as T[typeof event]));\n },\n\n async emitAsync(event, ...payload) {\n const data = payload[0];\n const promises: Promise<void>[] = [];\n\n getHandlers(event).forEach((handler) => {\n const result = handler(data);\n if (result instanceof Promise) {\n promises.push(result);\n }\n });\n\n wildcardHandlers.forEach((handler) => {\n const result = handler(event, data as T[typeof event]);\n if (result instanceof Promise) {\n promises.push(result);\n }\n });\n\n await Promise.all(promises);\n },\n\n onAny(handler) {\n wildcardHandlers.add(handler);\n return () => e.offAny(handler);\n },\n\n offAny(handler) {\n wildcardHandlers.delete(handler);\n },\n\n clear(event?) {\n if (event) {\n handlers.delete(event);\n } else {\n handlers.clear();\n wildcardHandlers.clear();\n }\n },\n\n listenerCount(event) {\n return getHandlers(event).size;\n },\n\n // ⚡ Biological aliases (zero-cost: just references)\n bind: null as unknown as Emitochondria<T>['on'],\n release: null as unknown as Emitochondria<T>['off'],\n pulse: null as unknown as Emitochondria<T>['emit'],\n cascade: null as unknown as Emitochondria<T>['emitAsync'],\n spike: null as unknown as Emitochondria<T>['once'],\n membrane: null as unknown as Emitochondria<T>['onAny'],\n apoptosis: null as unknown as Emitochondria<T>['clear'],\n receptors: null as unknown as Emitochondria<T>['listenerCount'],\n };\n\n // Assign aliases (smaller than repeating in object literal)\n e.bind = e.on;\n e.release = e.off;\n e.pulse = e.emit;\n e.cascade = e.emitAsync;\n e.spike = e.once;\n e.membrane = e.onAny;\n e.apoptosis = e.clear;\n e.receptors = e.listenerCount;\n\n return e;\n}\n\n// Default export for convenience\nexport default createEmitochondria;\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,yBAAAE,EAAA,YAAAC,IAAA,eAAAC,EAAAJ,GA4FO,SAASE,GAA4D,CAC1E,IAAMG,EAAoD,IAAI,IACxDC,EAA4C,IAAI,IAEtD,SAASC,EAAYC,EAA2C,CAC9D,IAAIC,EAAMJ,EAAS,IAAIG,CAAK,EAC5B,OAAKC,IACHA,EAAM,IAAI,IACVJ,EAAS,IAAIG,EAAOC,CAAG,GAElBA,CACT,CAEA,IAAMC,EAAsB,CAC1B,GAAGF,EAAOG,EAAS,CACjB,OAAAJ,EAAYC,CAAK,EAAE,IAAIG,CAAgC,EAChD,IAAMD,EAAE,IAAIF,EAAOG,CAAO,CACnC,EAEA,IAAIH,EAAOG,EAAS,CAClBJ,EAAYC,CAAK,EAAE,OAAOG,CAAgC,CAC5D,EAEA,KAAKH,EAAOG,EAAS,CACnB,IAAMC,GAAYC,IAChBH,EAAE,IAAIF,EAAOI,CAAwC,EAC9CD,EAAQE,CAAO,IAGxB,OAAOH,EAAE,GAAGF,EAAOI,CAAO,CAC5B,EAEA,KAAKJ,KAAUK,EAAS,CACtB,IAAMC,EAAOD,EAAQ,CAAC,EACtBN,EAAYC,CAAK,EAAE,QAASG,GAAYA,EAAQG,CAAI,CAAC,EACrDR,EAAiB,QAASK,GAAYA,EAAQH,EAAOM,CAAuB,CAAC,CAC/E,EAEA,MAAM,UAAUN,KAAUK,EAAS,CACjC,IAAMC,EAAOD,EAAQ,CAAC,EAChBE,EAA4B,CAAC,EAEnCR,EAAYC,CAAK,EAAE,QAASG,GAAY,CACtC,IAAMK,EAASL,EAAQG,CAAI,EACvBE,aAAkB,SACpBD,EAAS,KAAKC,CAAM,CAExB,CAAC,EAEDV,EAAiB,QAASK,GAAY,CACpC,IAAMK,EAASL,EAAQH,EAAOM,CAAuB,EACjDE,aAAkB,SACpBD,EAAS,KAAKC,CAAM,CAExB,CAAC,EAED,MAAM,QAAQ,IAAID,CAAQ,CAC5B,EAEA,MAAMJ,EAAS,CACb,OAAAL,EAAiB,IAAIK,CAAO,EACrB,IAAMD,EAAE,OAAOC,CAAO,CAC/B,EAEA,OAAOA,EAAS,CACdL,EAAiB,OAAOK,CAAO,CACjC,EAEA,MAAMH,EAAQ,CACRA,EACFH,EAAS,OAAOG,CAAK,GAErBH,EAAS,MAAM,EACfC,EAAiB,MAAM,EAE3B,EAEA,cAAcE,EAAO,CACnB,OAAOD,EAAYC,CAAK,EAAE,IAC5B,EAGA,KAAM,KACN,QAAS,KACT,MAAO,KACP,QAAS,KACT,MAAO,KACP,SAAU,KACV,UAAW,KACX,UAAW,IACb,EAGA,OAAAE,EAAE,KAAOA,EAAE,GACXA,EAAE,QAAUA,EAAE,IACdA,EAAE,MAAQA,EAAE,KACZA,EAAE,QAAUA,EAAE,UACdA,EAAE,MAAQA,EAAE,KACZA,EAAE,SAAWA,EAAE,MACfA,EAAE,UAAYA,EAAE,MAChBA,EAAE,UAAYA,EAAE,cAETA,CACT,CAGA,IAAOP,EAAQD","names":["index_exports","__export","createEmitochondria","index_default","__toCommonJS","handlers","wildcardHandlers","getHandlers","event","set","e","handler","wrapper","payload","data","promises","result"]}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Base type for event maps.
3
+ * Keys are event names, values are payload types.
4
+ */
5
+ type EventMap = Record<string, unknown>;
6
+ /**
7
+ * Extracts event names from an event map as a string union.
8
+ */
9
+ type EventKey<T extends EventMap> = keyof T & string;
10
+ /**
11
+ * Handler function for a specific event payload.
12
+ * Can be sync or async.
13
+ */
14
+ type EventHandler<T> = (payload: T) => void | Promise<void>;
15
+ /**
16
+ * Wildcard handler that receives event name and payload.
17
+ * Useful for logging/debugging.
18
+ */
19
+ type WildcardHandler<T extends EventMap> = <K extends EventKey<T>>(event: K, payload: T[K]) => void | Promise<void>;
20
+ /**
21
+ * The typed emitter interface.
22
+ */
23
+ interface Emitochondria<T extends EventMap> {
24
+ /** Subscribe to an event. Returns unsubscribe function. */
25
+ on<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
26
+ /** Unsubscribe a handler from an event. */
27
+ off<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): void;
28
+ /** Subscribe for a single emission only. */
29
+ once<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
30
+ /** Emit an event synchronously. */
31
+ emit<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): void;
32
+ /** Emit an event and await all handlers. */
33
+ emitAsync<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): Promise<void>;
34
+ /** Subscribe to all events (wildcard). */
35
+ onAny(handler: WildcardHandler<T>): () => void;
36
+ /** Unsubscribe a wildcard handler. */
37
+ offAny(handler: WildcardHandler<T>): void;
38
+ /** Clear handlers for a specific event or all events. */
39
+ clear<K extends EventKey<T>>(event?: K): void;
40
+ /** Get the number of listeners for an event. */
41
+ listenerCount<K extends EventKey<T>>(event: K): number;
42
+ /** Alias for `on` — Bind a receptor to a signal. */
43
+ bind: Emitochondria<T>['on'];
44
+ /** Alias for `off` — Release a receptor. */
45
+ release: Emitochondria<T>['off'];
46
+ /** Alias for `emit` — Pulse energy through the system. */
47
+ pulse: Emitochondria<T>['emit'];
48
+ /** Alias for `emitAsync` — Trigger a signal cascade. */
49
+ cascade: Emitochondria<T>['emitAsync'];
50
+ /** Alias for `once` — Single spike of energy. */
51
+ spike: Emitochondria<T>['once'];
52
+ /** Alias for `onAny` — Membrane catches all signals. */
53
+ membrane: Emitochondria<T>['onAny'];
54
+ /** Alias for `clear` — Programmed cell death. */
55
+ apoptosis: Emitochondria<T>['clear'];
56
+ /** Alias for `listenerCount` — Count of receptors. */
57
+ receptors: Emitochondria<T>['listenerCount'];
58
+ }
59
+ /**
60
+ * Create a new typed event emitter.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * type MyEvents = {
65
+ * 'user:login': { userId: string };
66
+ * 'app:ready': void;
67
+ * };
68
+ *
69
+ * const mito = createEmitochondria<MyEvents>();
70
+ *
71
+ * // Standard API
72
+ * mito.on('user:login', (data) => {
73
+ * console.log(data.userId); // fully typed!
74
+ * });
75
+ * mito.emit('user:login', { userId: '123' });
76
+ * mito.emit('app:ready');
77
+ *
78
+ * // Biological API ⚡
79
+ * mito.bind('user:login', (data) => console.log(data.userId));
80
+ * mito.pulse('user:login', { userId: '123' });
81
+ * ```
82
+ */
83
+ declare function createEmitochondria<T extends EventMap>(): Emitochondria<T>;
84
+
85
+ export { type Emitochondria, type EventHandler, type EventKey, type EventMap, type WildcardHandler, createEmitochondria, createEmitochondria as default };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Base type for event maps.
3
+ * Keys are event names, values are payload types.
4
+ */
5
+ type EventMap = Record<string, unknown>;
6
+ /**
7
+ * Extracts event names from an event map as a string union.
8
+ */
9
+ type EventKey<T extends EventMap> = keyof T & string;
10
+ /**
11
+ * Handler function for a specific event payload.
12
+ * Can be sync or async.
13
+ */
14
+ type EventHandler<T> = (payload: T) => void | Promise<void>;
15
+ /**
16
+ * Wildcard handler that receives event name and payload.
17
+ * Useful for logging/debugging.
18
+ */
19
+ type WildcardHandler<T extends EventMap> = <K extends EventKey<T>>(event: K, payload: T[K]) => void | Promise<void>;
20
+ /**
21
+ * The typed emitter interface.
22
+ */
23
+ interface Emitochondria<T extends EventMap> {
24
+ /** Subscribe to an event. Returns unsubscribe function. */
25
+ on<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
26
+ /** Unsubscribe a handler from an event. */
27
+ off<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): void;
28
+ /** Subscribe for a single emission only. */
29
+ once<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
30
+ /** Emit an event synchronously. */
31
+ emit<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): void;
32
+ /** Emit an event and await all handlers. */
33
+ emitAsync<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): Promise<void>;
34
+ /** Subscribe to all events (wildcard). */
35
+ onAny(handler: WildcardHandler<T>): () => void;
36
+ /** Unsubscribe a wildcard handler. */
37
+ offAny(handler: WildcardHandler<T>): void;
38
+ /** Clear handlers for a specific event or all events. */
39
+ clear<K extends EventKey<T>>(event?: K): void;
40
+ /** Get the number of listeners for an event. */
41
+ listenerCount<K extends EventKey<T>>(event: K): number;
42
+ /** Alias for `on` — Bind a receptor to a signal. */
43
+ bind: Emitochondria<T>['on'];
44
+ /** Alias for `off` — Release a receptor. */
45
+ release: Emitochondria<T>['off'];
46
+ /** Alias for `emit` — Pulse energy through the system. */
47
+ pulse: Emitochondria<T>['emit'];
48
+ /** Alias for `emitAsync` — Trigger a signal cascade. */
49
+ cascade: Emitochondria<T>['emitAsync'];
50
+ /** Alias for `once` — Single spike of energy. */
51
+ spike: Emitochondria<T>['once'];
52
+ /** Alias for `onAny` — Membrane catches all signals. */
53
+ membrane: Emitochondria<T>['onAny'];
54
+ /** Alias for `clear` — Programmed cell death. */
55
+ apoptosis: Emitochondria<T>['clear'];
56
+ /** Alias for `listenerCount` — Count of receptors. */
57
+ receptors: Emitochondria<T>['listenerCount'];
58
+ }
59
+ /**
60
+ * Create a new typed event emitter.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * type MyEvents = {
65
+ * 'user:login': { userId: string };
66
+ * 'app:ready': void;
67
+ * };
68
+ *
69
+ * const mito = createEmitochondria<MyEvents>();
70
+ *
71
+ * // Standard API
72
+ * mito.on('user:login', (data) => {
73
+ * console.log(data.userId); // fully typed!
74
+ * });
75
+ * mito.emit('user:login', { userId: '123' });
76
+ * mito.emit('app:ready');
77
+ *
78
+ * // Biological API ⚡
79
+ * mito.bind('user:login', (data) => console.log(data.userId));
80
+ * mito.pulse('user:login', { userId: '123' });
81
+ * ```
82
+ */
83
+ declare function createEmitochondria<T extends EventMap>(): Emitochondria<T>;
84
+
85
+ export { type Emitochondria, type EventHandler, type EventKey, type EventMap, type WildcardHandler, createEmitochondria, createEmitochondria as default };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ function c(){let d=new Map,r=new Set;function i(e){let t=d.get(e);return t||(t=new Set,d.set(e,t)),t}let n={on(e,t){return i(e).add(t),()=>n.off(e,t)},off(e,t){i(e).delete(t)},once(e,t){let a=(o=>(n.off(e,a),t(o)));return n.on(e,a)},emit(e,...t){let a=t[0];i(e).forEach(o=>o(a)),r.forEach(o=>o(e,a))},async emitAsync(e,...t){let a=t[0],o=[];i(e).forEach(l=>{let s=l(a);s instanceof Promise&&o.push(s)}),r.forEach(l=>{let s=l(e,a);s instanceof Promise&&o.push(s)}),await Promise.all(o)},onAny(e){return r.add(e),()=>n.offAny(e)},offAny(e){r.delete(e)},clear(e){e?d.delete(e):(d.clear(),r.clear())},listenerCount(e){return i(e).size},bind:null,release:null,pulse:null,cascade:null,spike:null,membrane:null,apoptosis:null,receptors:null};return n.bind=n.on,n.release=n.off,n.pulse=n.emit,n.cascade=n.emitAsync,n.spike=n.once,n.membrane=n.onAny,n.apoptosis=n.clear,n.receptors=n.listenerCount,n}var T=c;export{c as createEmitochondria,T as default};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Base type for event maps.\n * Keys are event names, values are payload types.\n */\nexport type EventMap = Record<string, unknown>;\n\n/**\n * Extracts event names from an event map as a string union.\n */\nexport type EventKey<T extends EventMap> = keyof T & string;\n\n/**\n * Handler function for a specific event payload.\n * Can be sync or async.\n */\nexport type EventHandler<T> = (payload: T) => void | Promise<void>;\n\n/**\n * Wildcard handler that receives event name and payload.\n * Useful for logging/debugging.\n */\nexport type WildcardHandler<T extends EventMap> = <K extends EventKey<T>>(\n event: K,\n payload: T[K]\n) => void | Promise<void>;\n\n/**\n * The typed emitter interface.\n */\nexport interface Emitochondria<T extends EventMap> {\n /** Subscribe to an event. Returns unsubscribe function. */\n on<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;\n /** Unsubscribe a handler from an event. */\n off<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): void;\n /** Subscribe for a single emission only. */\n once<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;\n /** Emit an event synchronously. */\n emit<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): void;\n /** Emit an event and await all handlers. */\n emitAsync<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): Promise<void>;\n /** Subscribe to all events (wildcard). */\n onAny(handler: WildcardHandler<T>): () => void;\n /** Unsubscribe a wildcard handler. */\n offAny(handler: WildcardHandler<T>): void;\n /** Clear handlers for a specific event or all events. */\n clear<K extends EventKey<T>>(event?: K): void;\n /** Get the number of listeners for an event. */\n listenerCount<K extends EventKey<T>>(event: K): number;\n\n // ⚡ Biological aliases\n /** Alias for `on` — Bind a receptor to a signal. */\n bind: Emitochondria<T>['on'];\n /** Alias for `off` — Release a receptor. */\n release: Emitochondria<T>['off'];\n /** Alias for `emit` — Pulse energy through the system. */\n pulse: Emitochondria<T>['emit'];\n /** Alias for `emitAsync` — Trigger a signal cascade. */\n cascade: Emitochondria<T>['emitAsync'];\n /** Alias for `once` — Single spike of energy. */\n spike: Emitochondria<T>['once'];\n /** Alias for `onAny` — Membrane catches all signals. */\n membrane: Emitochondria<T>['onAny'];\n /** Alias for `clear` — Programmed cell death. */\n apoptosis: Emitochondria<T>['clear'];\n /** Alias for `listenerCount` — Count of receptors. */\n receptors: Emitochondria<T>['listenerCount'];\n}\n\n/**\n * Create a new typed event emitter.\n *\n * @example\n * ```typescript\n * type MyEvents = {\n * 'user:login': { userId: string };\n * 'app:ready': void;\n * };\n *\n * const mito = createEmitochondria<MyEvents>();\n *\n * // Standard API\n * mito.on('user:login', (data) => {\n * console.log(data.userId); // fully typed!\n * });\n * mito.emit('user:login', { userId: '123' });\n * mito.emit('app:ready');\n *\n * // Biological API ⚡\n * mito.bind('user:login', (data) => console.log(data.userId));\n * mito.pulse('user:login', { userId: '123' });\n * ```\n */\nexport function createEmitochondria<T extends EventMap>(): Emitochondria<T> {\n const handlers: Map<string, Set<EventHandler<unknown>>> = new Map();\n const wildcardHandlers: Set<WildcardHandler<T>> = new Set();\n\n function getHandlers(event: string): Set<EventHandler<unknown>> {\n let set = handlers.get(event);\n if (!set) {\n set = new Set();\n handlers.set(event, set);\n }\n return set;\n }\n\n const e: Emitochondria<T> = {\n on(event, handler) {\n getHandlers(event).add(handler as EventHandler<unknown>);\n return () => e.off(event, handler);\n },\n\n off(event, handler) {\n getHandlers(event).delete(handler as EventHandler<unknown>);\n },\n\n once(event, handler) {\n const wrapper = ((payload: T[typeof event]) => {\n e.off(event, wrapper as EventHandler<T[typeof event]>);\n return handler(payload);\n }) as EventHandler<T[typeof event]>;\n\n return e.on(event, wrapper);\n },\n\n emit(event, ...payload) {\n const data = payload[0];\n getHandlers(event).forEach((handler) => handler(data));\n wildcardHandlers.forEach((handler) => handler(event, data as T[typeof event]));\n },\n\n async emitAsync(event, ...payload) {\n const data = payload[0];\n const promises: Promise<void>[] = [];\n\n getHandlers(event).forEach((handler) => {\n const result = handler(data);\n if (result instanceof Promise) {\n promises.push(result);\n }\n });\n\n wildcardHandlers.forEach((handler) => {\n const result = handler(event, data as T[typeof event]);\n if (result instanceof Promise) {\n promises.push(result);\n }\n });\n\n await Promise.all(promises);\n },\n\n onAny(handler) {\n wildcardHandlers.add(handler);\n return () => e.offAny(handler);\n },\n\n offAny(handler) {\n wildcardHandlers.delete(handler);\n },\n\n clear(event?) {\n if (event) {\n handlers.delete(event);\n } else {\n handlers.clear();\n wildcardHandlers.clear();\n }\n },\n\n listenerCount(event) {\n return getHandlers(event).size;\n },\n\n // ⚡ Biological aliases (zero-cost: just references)\n bind: null as unknown as Emitochondria<T>['on'],\n release: null as unknown as Emitochondria<T>['off'],\n pulse: null as unknown as Emitochondria<T>['emit'],\n cascade: null as unknown as Emitochondria<T>['emitAsync'],\n spike: null as unknown as Emitochondria<T>['once'],\n membrane: null as unknown as Emitochondria<T>['onAny'],\n apoptosis: null as unknown as Emitochondria<T>['clear'],\n receptors: null as unknown as Emitochondria<T>['listenerCount'],\n };\n\n // Assign aliases (smaller than repeating in object literal)\n e.bind = e.on;\n e.release = e.off;\n e.pulse = e.emit;\n e.cascade = e.emitAsync;\n e.spike = e.once;\n e.membrane = e.onAny;\n e.apoptosis = e.clear;\n e.receptors = e.listenerCount;\n\n return e;\n}\n\n// Default export for convenience\nexport default createEmitochondria;\n"],"mappings":"AA4FO,SAASA,GAA4D,CAC1E,IAAMC,EAAoD,IAAI,IACxDC,EAA4C,IAAI,IAEtD,SAASC,EAAYC,EAA2C,CAC9D,IAAIC,EAAMJ,EAAS,IAAIG,CAAK,EAC5B,OAAKC,IACHA,EAAM,IAAI,IACVJ,EAAS,IAAIG,EAAOC,CAAG,GAElBA,CACT,CAEA,IAAMC,EAAsB,CAC1B,GAAGF,EAAOG,EAAS,CACjB,OAAAJ,EAAYC,CAAK,EAAE,IAAIG,CAAgC,EAChD,IAAMD,EAAE,IAAIF,EAAOG,CAAO,CACnC,EAEA,IAAIH,EAAOG,EAAS,CAClBJ,EAAYC,CAAK,EAAE,OAAOG,CAAgC,CAC5D,EAEA,KAAKH,EAAOG,EAAS,CACnB,IAAMC,GAAYC,IAChBH,EAAE,IAAIF,EAAOI,CAAwC,EAC9CD,EAAQE,CAAO,IAGxB,OAAOH,EAAE,GAAGF,EAAOI,CAAO,CAC5B,EAEA,KAAKJ,KAAUK,EAAS,CACtB,IAAMC,EAAOD,EAAQ,CAAC,EACtBN,EAAYC,CAAK,EAAE,QAASG,GAAYA,EAAQG,CAAI,CAAC,EACrDR,EAAiB,QAASK,GAAYA,EAAQH,EAAOM,CAAuB,CAAC,CAC/E,EAEA,MAAM,UAAUN,KAAUK,EAAS,CACjC,IAAMC,EAAOD,EAAQ,CAAC,EAChBE,EAA4B,CAAC,EAEnCR,EAAYC,CAAK,EAAE,QAASG,GAAY,CACtC,IAAMK,EAASL,EAAQG,CAAI,EACvBE,aAAkB,SACpBD,EAAS,KAAKC,CAAM,CAExB,CAAC,EAEDV,EAAiB,QAASK,GAAY,CACpC,IAAMK,EAASL,EAAQH,EAAOM,CAAuB,EACjDE,aAAkB,SACpBD,EAAS,KAAKC,CAAM,CAExB,CAAC,EAED,MAAM,QAAQ,IAAID,CAAQ,CAC5B,EAEA,MAAMJ,EAAS,CACb,OAAAL,EAAiB,IAAIK,CAAO,EACrB,IAAMD,EAAE,OAAOC,CAAO,CAC/B,EAEA,OAAOA,EAAS,CACdL,EAAiB,OAAOK,CAAO,CACjC,EAEA,MAAMH,EAAQ,CACRA,EACFH,EAAS,OAAOG,CAAK,GAErBH,EAAS,MAAM,EACfC,EAAiB,MAAM,EAE3B,EAEA,cAAcE,EAAO,CACnB,OAAOD,EAAYC,CAAK,EAAE,IAC5B,EAGA,KAAM,KACN,QAAS,KACT,MAAO,KACP,QAAS,KACT,MAAO,KACP,SAAU,KACV,UAAW,KACX,UAAW,IACb,EAGA,OAAAE,EAAE,KAAOA,EAAE,GACXA,EAAE,QAAUA,EAAE,IACdA,EAAE,MAAQA,EAAE,KACZA,EAAE,QAAUA,EAAE,UACdA,EAAE,MAAQA,EAAE,KACZA,EAAE,SAAWA,EAAE,MACfA,EAAE,UAAYA,EAAE,MAChBA,EAAE,UAAYA,EAAE,cAETA,CACT,CAGA,IAAOO,EAAQb","names":["createEmitochondria","handlers","wildcardHandlers","getHandlers","event","set","e","handler","wrapper","payload","data","promises","result","index_default"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "emitochondria",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "The powerhouse of your events — A tiny, fully-typed event emitter for TypeScript",
6
+ "author": "Pablo Díaz A.K.A exudev",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "events",
10
+ "emitter",
11
+ "event-emitter",
12
+ "typescript",
13
+ "typed",
14
+ "pubsub",
15
+ "pub-sub",
16
+ "subscribe",
17
+ "publish"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/Exudev/emitochondria.git"
22
+ },
23
+ "main": "./dist/index.js",
24
+ "module": "./dist/index.mjs",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "import": {
29
+ "types": "./dist/index.d.mts",
30
+ "default": "./dist/index.mjs"
31
+ },
32
+ "require": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.js"
35
+ }
36
+ }
37
+ },
38
+ "files": [
39
+ "dist"
40
+ ],
41
+ "sideEffects": false,
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "test": "vitest",
45
+ "test:run": "vitest run",
46
+ "test:coverage": "vitest run --coverage",
47
+ "prepublishOnly": "npm run test:run && npm run build"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^20.0.0",
51
+ "tsup": "^8.0.0",
52
+ "typescript": "^5.0.0",
53
+ "vitest": "^1.0.0"
54
+ }
55
+ }