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 +21 -0
- package/README.md +189 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +85 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
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
|
+
[](https://www.npmjs.com/package/emitochondria)
|
|
7
|
+
[](https://github.com/Exudev/emitochondria/actions/workflows/ci.yml)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://www.typescriptlang.org/)
|
|
10
|
+
[](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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|