emitochondria 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -4
- package/dist/index.d.cts +126 -67
- package/dist/index.d.ts +126 -67
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +15 -9
- package/schema.json +75 -0
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -9,16 +9,19 @@
|
|
|
9
9
|
[](https://www.typescriptlang.org/)
|
|
10
10
|
[](https://bundlephobia.com/package/emitochondria)
|
|
11
11
|
|
|
12
|
-
A tiny, fully-typed event emitter for TypeScript. Zero dependencies, under
|
|
12
|
+
A tiny, fully-typed event emitter for TypeScript with built-in error handling. Zero dependencies, under 2KB.
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
16
16
|
- ✅ **Full type safety** — Event names and payloads checked at compile time
|
|
17
|
-
- ✅ **Tiny** — Under
|
|
17
|
+
- ✅ **Tiny** — Under 2KB minified
|
|
18
18
|
- ✅ **Zero dependencies**
|
|
19
19
|
- ✅ **Universal** — Works in browser and Node.js
|
|
20
20
|
- ✅ **Async support** — `emitAsync` awaits all handlers
|
|
21
21
|
- ✅ **Wildcard listeners** — `onAny` for debugging/logging
|
|
22
|
+
- ✅ **Error handling** — Configurable error handling for resilient apps
|
|
23
|
+
- ✅ **Memory leak detection** — Warns when too many listeners are added
|
|
24
|
+
- ✅ **Inspection tools** — Debug your emitter with introspection methods
|
|
22
25
|
|
|
23
26
|
## Installation
|
|
24
27
|
|
|
@@ -55,9 +58,28 @@ events.emit('app:ready');
|
|
|
55
58
|
|
|
56
59
|
## API
|
|
57
60
|
|
|
58
|
-
### `createEmitochondria<T>()`
|
|
61
|
+
### `createEmitochondria<T>(options?)`
|
|
59
62
|
|
|
60
|
-
Create a new typed emitter.
|
|
63
|
+
Create a new typed emitter with optional configuration.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const events = createEmitochondria<MyEvents>({
|
|
67
|
+
// Custom error handler (default: logs in dev, silent in production)
|
|
68
|
+
onError: (error, event, handler) => {
|
|
69
|
+
console.error(`Error in ${event}:`, error);
|
|
70
|
+
},
|
|
71
|
+
// Or preserve throwing behavior
|
|
72
|
+
// onError: 'throw',
|
|
73
|
+
|
|
74
|
+
// Max listeners before warning (default: 10, 0 to disable)
|
|
75
|
+
maxListeners: 20,
|
|
76
|
+
|
|
77
|
+
// Custom warning handler
|
|
78
|
+
onMaxListenersExceeded: (event, count, max) => {
|
|
79
|
+
console.warn(`Too many listeners on ${event}: ${count}/${max}`);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
```
|
|
61
83
|
|
|
62
84
|
### `.on(event, handler)`
|
|
63
85
|
|
|
@@ -125,6 +147,115 @@ Clear handlers for an event, or all handlers if no event specified.
|
|
|
125
147
|
|
|
126
148
|
Get the number of listeners for an event.
|
|
127
149
|
|
|
150
|
+
### `.setErrorHandler(handler)`
|
|
151
|
+
|
|
152
|
+
Change the error handler at runtime.
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
events.setErrorHandler((error, event) => {
|
|
156
|
+
logger.error(`Event ${event} failed:`, error);
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `.setMaxListeners(n)` / `.getMaxListeners()`
|
|
161
|
+
|
|
162
|
+
Adjust or check the max listener warning threshold.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
events.setMaxListeners(50); // Increase limit
|
|
166
|
+
console.log(events.getMaxListeners()); // 50
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### `.eventNames()`
|
|
170
|
+
|
|
171
|
+
Get all event names that currently have registered listeners.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
events.on('user:login', handler1);
|
|
175
|
+
events.on('user:logout', handler2);
|
|
176
|
+
console.log(events.eventNames()); // ['user:login', 'user:logout']
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `.listeners(event)`
|
|
180
|
+
|
|
181
|
+
Get all handlers registered for a specific event.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
const handlers = events.listeners('user:login');
|
|
185
|
+
console.log(handlers.length); // Number of handlers
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `.wildcardListeners()`
|
|
189
|
+
|
|
190
|
+
Get all wildcard handlers.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const wildcards = events.wildcardListeners();
|
|
194
|
+
console.log(wildcards.length);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### `.hasListener(event, handler)`
|
|
198
|
+
|
|
199
|
+
Check if a specific handler is registered for an event.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
if (events.hasListener('user:login', myHandler)) {
|
|
203
|
+
console.log('Handler is registered');
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Error Handling
|
|
208
|
+
|
|
209
|
+
By default, errors thrown by event handlers are caught and logged in development (silent in production). This prevents one failing handler from breaking others:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
events.on('save', () => { throw new Error('DB error'); });
|
|
213
|
+
events.on('save', () => console.log('This still runs!')); // ✅ Executes
|
|
214
|
+
|
|
215
|
+
events.emit('save');
|
|
216
|
+
// Console (dev): [Emitochondria] Error in handler for event "save": Error: DB error
|
|
217
|
+
// Output: "This still runs!"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Custom error handling:**
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const events = createEmitochondria<MyEvents>({
|
|
224
|
+
onError: (error, event, handler) => {
|
|
225
|
+
// Send to your error tracking service
|
|
226
|
+
Sentry.captureException(error, { tags: { event } });
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Preserve throwing behavior (v1.0 compatibility):**
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const events = createEmitochondria<MyEvents>({
|
|
235
|
+
onError: 'throw' // Errors will throw like before
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Memory Leak Detection
|
|
240
|
+
|
|
241
|
+
Emitochondria warns you when adding too many listeners to a single event (default: 10), which often indicates a bug:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// This might indicate a leak (handler not cleaned up in a loop)
|
|
245
|
+
for (let i = 0; i < 15; i++) {
|
|
246
|
+
events.on('tick', handler); // Warning after 10th iteration
|
|
247
|
+
}
|
|
248
|
+
// Console: [Emitochondria] Possible memory leak detected: 11 listeners...
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Disable or customize:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
const events = createEmitochondria<MyEvents>({
|
|
255
|
+
maxListeners: 0 // Disable warnings
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
128
259
|
## ⚡ Biological API (Alternative Naming)
|
|
129
260
|
|
|
130
261
|
Emitochondria offers biologically-themed aliases for all methods. Use whichever style fits your project:
|
package/dist/index.d.cts
CHANGED
|
@@ -1,85 +1,144 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base type for event maps.
|
|
3
|
-
* Keys are event names, values are payload types.
|
|
4
|
-
*/
|
|
5
1
|
type EventMap = Record<string, unknown>;
|
|
6
|
-
/**
|
|
7
|
-
* Extracts event names from an event map as a string union.
|
|
8
|
-
*/
|
|
9
2
|
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
3
|
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
4
|
type WildcardHandler<T extends EventMap> = <K extends EventKey<T>>(event: K, payload: T[K]) => void | Promise<void>;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
type ErrorHandler<T extends EventMap> = (error: unknown, event: EventKey<T>, handler: EventHandler<unknown>) => void;
|
|
6
|
+
type MaxListenersHandler<T extends EventMap> = (event: EventKey<T>, count: number, max: number) => void;
|
|
7
|
+
type TimestampFormat = 'iso' | 'unix' | 'human';
|
|
8
|
+
type Timezone = 'utc' | 'local';
|
|
9
|
+
type LogFormat = 'text' | 'json';
|
|
10
|
+
interface LoggingOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Enable timestamps in logs.
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
timestamps?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Timestamp format.
|
|
18
|
+
* - 'iso': 2025-01-13T14:30:00.000Z
|
|
19
|
+
* - 'unix': 1736776200000
|
|
20
|
+
* - 'human': Jan 13, 2:30:00 PM
|
|
21
|
+
* @default 'iso'
|
|
22
|
+
*/
|
|
23
|
+
timestampFormat?: TimestampFormat;
|
|
24
|
+
/**
|
|
25
|
+
* Timezone for timestamps.
|
|
26
|
+
* - 'utc': UTC time
|
|
27
|
+
* - 'local': System local time
|
|
28
|
+
* @default 'utc'
|
|
29
|
+
*/
|
|
30
|
+
timezone?: Timezone;
|
|
31
|
+
/**
|
|
32
|
+
* Enable file logging.
|
|
33
|
+
* ⚠️ Only works in Node.js. Browser will show a warning.
|
|
34
|
+
* ⚠️ Payloads may contain sensitive data — they will be written to disk.
|
|
35
|
+
* @default false
|
|
36
|
+
*/
|
|
37
|
+
persist?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Path to log file.
|
|
40
|
+
* Directory will be created if it doesn't exist.
|
|
41
|
+
* @default './logs/emitochondria.log'
|
|
42
|
+
*/
|
|
43
|
+
path?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Log file format.
|
|
46
|
+
* - 'text': Human-readable plain text
|
|
47
|
+
* - 'json': JSON lines (one JSON object per line)
|
|
48
|
+
* @default 'text'
|
|
49
|
+
*/
|
|
50
|
+
format?: LogFormat;
|
|
51
|
+
/**
|
|
52
|
+
* Maximum log file size before rotation.
|
|
53
|
+
* Supports: '10MB', '1GB', '500KB'
|
|
54
|
+
* When exceeded, current file is renamed to .1, .2, etc.
|
|
55
|
+
* @default '10MB'
|
|
56
|
+
*/
|
|
57
|
+
maxSize?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Log every emitted event.
|
|
60
|
+
* ⚠️ Can be very verbose in production!
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
logEvents?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Log handler errors.
|
|
66
|
+
* @default true
|
|
67
|
+
*/
|
|
68
|
+
logErrors?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Log memory leak warnings.
|
|
71
|
+
* @default true
|
|
72
|
+
*/
|
|
73
|
+
logWarnings?: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface EmitochondriaConfig {
|
|
76
|
+
/**
|
|
77
|
+
* Maximum listeners per event before warning.
|
|
78
|
+
* Set to 0 to disable warnings.
|
|
79
|
+
* @default 10
|
|
80
|
+
*/
|
|
81
|
+
maxListeners?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Custom error handler for when handlers throw.
|
|
84
|
+
* - 'throw': Re-throw errors
|
|
85
|
+
* - undefined: Default (logs in dev, silent in prod)
|
|
86
|
+
* Note: Function handlers only work in options, not config file.
|
|
87
|
+
*/
|
|
88
|
+
onError?: 'throw';
|
|
89
|
+
/**
|
|
90
|
+
* Logging configuration.
|
|
91
|
+
*/
|
|
92
|
+
logging?: LoggingOptions;
|
|
93
|
+
}
|
|
94
|
+
interface EmitochondriaOptions<T extends EventMap> {
|
|
95
|
+
/**
|
|
96
|
+
* Maximum listeners per event before warning.
|
|
97
|
+
* Set to 0 to disable warnings.
|
|
98
|
+
* @default 10
|
|
99
|
+
*/
|
|
100
|
+
maxListeners?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Custom error handler function or 'throw'.
|
|
103
|
+
* - ErrorHandler function: Custom error handling logic
|
|
104
|
+
* - 'throw': Re-throw errors
|
|
105
|
+
* - undefined: Default (logs in dev, silent in prod)
|
|
106
|
+
*/
|
|
107
|
+
onError?: ErrorHandler<T> | 'throw';
|
|
108
|
+
/**
|
|
109
|
+
* Custom max listeners exceeded handler.
|
|
110
|
+
* If not provided, logs a warning to console.
|
|
111
|
+
*/
|
|
112
|
+
onMaxListenersExceeded?: MaxListenersHandler<T>;
|
|
113
|
+
/**
|
|
114
|
+
* Logging configuration.
|
|
115
|
+
*/
|
|
116
|
+
logging?: LoggingOptions;
|
|
117
|
+
}
|
|
23
118
|
interface Emitochondria<T extends EventMap> {
|
|
24
|
-
/** Subscribe to an event. Returns unsubscribe function. */
|
|
25
119
|
on<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
|
|
26
|
-
/** Unsubscribe a handler from an event. */
|
|
27
120
|
off<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): void;
|
|
28
|
-
/** Subscribe for a single emission only. */
|
|
29
121
|
once<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
|
|
30
|
-
/** Emit an event synchronously. */
|
|
31
122
|
emit<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): void;
|
|
32
|
-
/** Emit an event and await all handlers. */
|
|
33
123
|
emitAsync<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): Promise<void>;
|
|
34
|
-
/** Subscribe to all events (wildcard). */
|
|
35
124
|
onAny(handler: WildcardHandler<T>): () => void;
|
|
36
|
-
/** Unsubscribe a wildcard handler. */
|
|
37
125
|
offAny(handler: WildcardHandler<T>): void;
|
|
38
|
-
/** Clear handlers for a specific event or all events. */
|
|
39
126
|
clear<K extends EventKey<T>>(event?: K): void;
|
|
40
|
-
/** Get the number of listeners for an event. */
|
|
41
127
|
listenerCount<K extends EventKey<T>>(event: K): number;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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'];
|
|
128
|
+
setErrorHandler(handler: ErrorHandler<T> | 'throw'): void;
|
|
129
|
+
setMaxListeners(n: number): void;
|
|
130
|
+
getMaxListeners(): number;
|
|
131
|
+
eventNames(): EventKey<T>[];
|
|
132
|
+
listeners<K extends EventKey<T>>(event: K): EventHandler<T[K]>[];
|
|
133
|
+
wildcardListeners(): WildcardHandler<T>[];
|
|
134
|
+
hasListener<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): boolean;
|
|
58
135
|
}
|
|
136
|
+
|
|
137
|
+
declare function createEmitochondria<T extends EventMap>(options?: EmitochondriaOptions<T>): Emitochondria<T>;
|
|
138
|
+
|
|
59
139
|
/**
|
|
60
|
-
*
|
|
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
|
-
* ```
|
|
140
|
+
* Clear cached config (useful for testing).
|
|
82
141
|
*/
|
|
83
|
-
declare function
|
|
142
|
+
declare function clearConfigCache(): void;
|
|
84
143
|
|
|
85
|
-
export { type Emitochondria, type EventHandler, type EventKey, type EventMap, type WildcardHandler, createEmitochondria, createEmitochondria as default };
|
|
144
|
+
export { type Emitochondria, type EmitochondriaConfig, type EmitochondriaOptions, type ErrorHandler, type EventHandler, type EventKey, type EventMap, type LogFormat, type LoggingOptions, type MaxListenersHandler, type TimestampFormat, type Timezone, type WildcardHandler, clearConfigCache, createEmitochondria, createEmitochondria as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,85 +1,144 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base type for event maps.
|
|
3
|
-
* Keys are event names, values are payload types.
|
|
4
|
-
*/
|
|
5
1
|
type EventMap = Record<string, unknown>;
|
|
6
|
-
/**
|
|
7
|
-
* Extracts event names from an event map as a string union.
|
|
8
|
-
*/
|
|
9
2
|
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
3
|
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
4
|
type WildcardHandler<T extends EventMap> = <K extends EventKey<T>>(event: K, payload: T[K]) => void | Promise<void>;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
type ErrorHandler<T extends EventMap> = (error: unknown, event: EventKey<T>, handler: EventHandler<unknown>) => void;
|
|
6
|
+
type MaxListenersHandler<T extends EventMap> = (event: EventKey<T>, count: number, max: number) => void;
|
|
7
|
+
type TimestampFormat = 'iso' | 'unix' | 'human';
|
|
8
|
+
type Timezone = 'utc' | 'local';
|
|
9
|
+
type LogFormat = 'text' | 'json';
|
|
10
|
+
interface LoggingOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Enable timestamps in logs.
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
timestamps?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Timestamp format.
|
|
18
|
+
* - 'iso': 2025-01-13T14:30:00.000Z
|
|
19
|
+
* - 'unix': 1736776200000
|
|
20
|
+
* - 'human': Jan 13, 2:30:00 PM
|
|
21
|
+
* @default 'iso'
|
|
22
|
+
*/
|
|
23
|
+
timestampFormat?: TimestampFormat;
|
|
24
|
+
/**
|
|
25
|
+
* Timezone for timestamps.
|
|
26
|
+
* - 'utc': UTC time
|
|
27
|
+
* - 'local': System local time
|
|
28
|
+
* @default 'utc'
|
|
29
|
+
*/
|
|
30
|
+
timezone?: Timezone;
|
|
31
|
+
/**
|
|
32
|
+
* Enable file logging.
|
|
33
|
+
* ⚠️ Only works in Node.js. Browser will show a warning.
|
|
34
|
+
* ⚠️ Payloads may contain sensitive data — they will be written to disk.
|
|
35
|
+
* @default false
|
|
36
|
+
*/
|
|
37
|
+
persist?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Path to log file.
|
|
40
|
+
* Directory will be created if it doesn't exist.
|
|
41
|
+
* @default './logs/emitochondria.log'
|
|
42
|
+
*/
|
|
43
|
+
path?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Log file format.
|
|
46
|
+
* - 'text': Human-readable plain text
|
|
47
|
+
* - 'json': JSON lines (one JSON object per line)
|
|
48
|
+
* @default 'text'
|
|
49
|
+
*/
|
|
50
|
+
format?: LogFormat;
|
|
51
|
+
/**
|
|
52
|
+
* Maximum log file size before rotation.
|
|
53
|
+
* Supports: '10MB', '1GB', '500KB'
|
|
54
|
+
* When exceeded, current file is renamed to .1, .2, etc.
|
|
55
|
+
* @default '10MB'
|
|
56
|
+
*/
|
|
57
|
+
maxSize?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Log every emitted event.
|
|
60
|
+
* ⚠️ Can be very verbose in production!
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
logEvents?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Log handler errors.
|
|
66
|
+
* @default true
|
|
67
|
+
*/
|
|
68
|
+
logErrors?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Log memory leak warnings.
|
|
71
|
+
* @default true
|
|
72
|
+
*/
|
|
73
|
+
logWarnings?: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface EmitochondriaConfig {
|
|
76
|
+
/**
|
|
77
|
+
* Maximum listeners per event before warning.
|
|
78
|
+
* Set to 0 to disable warnings.
|
|
79
|
+
* @default 10
|
|
80
|
+
*/
|
|
81
|
+
maxListeners?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Custom error handler for when handlers throw.
|
|
84
|
+
* - 'throw': Re-throw errors
|
|
85
|
+
* - undefined: Default (logs in dev, silent in prod)
|
|
86
|
+
* Note: Function handlers only work in options, not config file.
|
|
87
|
+
*/
|
|
88
|
+
onError?: 'throw';
|
|
89
|
+
/**
|
|
90
|
+
* Logging configuration.
|
|
91
|
+
*/
|
|
92
|
+
logging?: LoggingOptions;
|
|
93
|
+
}
|
|
94
|
+
interface EmitochondriaOptions<T extends EventMap> {
|
|
95
|
+
/**
|
|
96
|
+
* Maximum listeners per event before warning.
|
|
97
|
+
* Set to 0 to disable warnings.
|
|
98
|
+
* @default 10
|
|
99
|
+
*/
|
|
100
|
+
maxListeners?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Custom error handler function or 'throw'.
|
|
103
|
+
* - ErrorHandler function: Custom error handling logic
|
|
104
|
+
* - 'throw': Re-throw errors
|
|
105
|
+
* - undefined: Default (logs in dev, silent in prod)
|
|
106
|
+
*/
|
|
107
|
+
onError?: ErrorHandler<T> | 'throw';
|
|
108
|
+
/**
|
|
109
|
+
* Custom max listeners exceeded handler.
|
|
110
|
+
* If not provided, logs a warning to console.
|
|
111
|
+
*/
|
|
112
|
+
onMaxListenersExceeded?: MaxListenersHandler<T>;
|
|
113
|
+
/**
|
|
114
|
+
* Logging configuration.
|
|
115
|
+
*/
|
|
116
|
+
logging?: LoggingOptions;
|
|
117
|
+
}
|
|
23
118
|
interface Emitochondria<T extends EventMap> {
|
|
24
|
-
/** Subscribe to an event. Returns unsubscribe function. */
|
|
25
119
|
on<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
|
|
26
|
-
/** Unsubscribe a handler from an event. */
|
|
27
120
|
off<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): void;
|
|
28
|
-
/** Subscribe for a single emission only. */
|
|
29
121
|
once<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
|
|
30
|
-
/** Emit an event synchronously. */
|
|
31
122
|
emit<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): void;
|
|
32
|
-
/** Emit an event and await all handlers. */
|
|
33
123
|
emitAsync<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): Promise<void>;
|
|
34
|
-
/** Subscribe to all events (wildcard). */
|
|
35
124
|
onAny(handler: WildcardHandler<T>): () => void;
|
|
36
|
-
/** Unsubscribe a wildcard handler. */
|
|
37
125
|
offAny(handler: WildcardHandler<T>): void;
|
|
38
|
-
/** Clear handlers for a specific event or all events. */
|
|
39
126
|
clear<K extends EventKey<T>>(event?: K): void;
|
|
40
|
-
/** Get the number of listeners for an event. */
|
|
41
127
|
listenerCount<K extends EventKey<T>>(event: K): number;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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'];
|
|
128
|
+
setErrorHandler(handler: ErrorHandler<T> | 'throw'): void;
|
|
129
|
+
setMaxListeners(n: number): void;
|
|
130
|
+
getMaxListeners(): number;
|
|
131
|
+
eventNames(): EventKey<T>[];
|
|
132
|
+
listeners<K extends EventKey<T>>(event: K): EventHandler<T[K]>[];
|
|
133
|
+
wildcardListeners(): WildcardHandler<T>[];
|
|
134
|
+
hasListener<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): boolean;
|
|
58
135
|
}
|
|
136
|
+
|
|
137
|
+
declare function createEmitochondria<T extends EventMap>(options?: EmitochondriaOptions<T>): Emitochondria<T>;
|
|
138
|
+
|
|
59
139
|
/**
|
|
60
|
-
*
|
|
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
|
-
* ```
|
|
140
|
+
* Clear cached config (useful for testing).
|
|
82
141
|
*/
|
|
83
|
-
declare function
|
|
142
|
+
declare function clearConfigCache(): void;
|
|
84
143
|
|
|
85
|
-
export { type Emitochondria, type EventHandler, type EventKey, type EventMap, type WildcardHandler, createEmitochondria, createEmitochondria as default };
|
|
144
|
+
export { type Emitochondria, type EmitochondriaConfig, type EmitochondriaOptions, type ErrorHandler, type EventHandler, type EventKey, type EventMap, type LogFormat, type LoggingOptions, type MaxListenersHandler, type TimestampFormat, type Timezone, type WildcardHandler, clearConfigCache, createEmitochondria, createEmitochondria as default };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";var w=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var O=(a,e)=>{for(var n in e)w(a,n,{get:e[n],enumerable:!0})},W=(a,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of C(e))!M.call(a,i)&&i!==n&&w(a,i,{get:()=>e[i],enumerable:!(r=z(e,i))||r.enumerable});return a};var K=a=>W(w({},"__esModule",{value:!0}),a);var N={};O(N,{clearConfigCache:()=>H,createEmitochondria:()=>v,default:()=>v});module.exports=K(N);var x=typeof globalThis.process<"u"&&globalThis.process?.versions?.node!=null,_=typeof globalThis.window<"u";function $(a,e){let n=new Date;switch(a){case"unix":return String(n.getTime());case"human":return n.toLocaleString("en-US",{timeZone:e==="utc"?"UTC":void 0,month:"short",day:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0});default:return e==="utc"?n.toISOString():new Date(n.getTime()-n.getTimezoneOffset()*6e4).toISOString().slice(0,-1)}}function B(a){let e=a.match(/^(\d+(?:\.\d+)?)\s*(KB|MB|GB)$/i);if(!e)return 10*1024*1024;let n=parseFloat(e[1]),r=e[2].toUpperCase(),i={KB:1024,MB:1024*1024,GB:1024*1024*1024};return n*(i[r]??10*1024*1024)}var y=class{options;maxSizeBytes;browserWarned=!1;fs=null;path=null;constructor(e={}){this.options={timestamps:e.timestamps??!1,timestampFormat:e.timestampFormat??"iso",timezone:e.timezone??"utc",persist:e.persist??!1,path:e.path??"./logs/emitochondria.log",format:e.format??"text",maxSize:e.maxSize??"10MB",logEvents:e.logEvents??!1,logErrors:e.logErrors??!0,logWarnings:e.logWarnings??!0},this.maxSizeBytes=B(this.options.maxSize),this.options.persist&&this.initFileLogging()}initFileLogging(){if(_){this.browserWarned||(console.warn("[Emitochondria] File logging is not available in browser environments. Logs will only be written to console."),this.browserWarned=!0);return}if(x)try{let e=globalThis.require;if(e){this.fs=e("fs"),this.path=e("path");let n=this.path.dirname(this.options.path);this.fs.existsSync(n)||this.fs.mkdirSync(n,{recursive:!0})}}catch(e){console.error("[Emitochondria] Failed to initialize file logging:",e)}}getTimestamp(){if(this.options.timestamps)return $(this.options.timestampFormat,this.options.timezone)}formatEntry(e){let n=this.getTimestamp();if(this.options.format==="json"){let i={level:e.level,type:e.type,message:e.message};return n&&(i.timestamp=n),e.event&&(i.event=e.event),e.payload!==void 0&&(i.payload=e.payload),e.error&&(i.error=String(e.error)),JSON.stringify(i)}let r=[];return n&&r.push(`[${n}]`),r.push(`[${e.level.toUpperCase()}]`),e.event&&r.push(`[${e.event}]`),r.push(e.message),e.payload!==void 0&&r.push(`| Payload: ${JSON.stringify(e.payload)}`),e.error&&r.push(`| Error: ${e.error}`),r.join(" ")}writeToFile(e){if(!(!this.fs||!this.path||!x))try{let n=e+`
|
|
2
|
+
`;this.fs.existsSync(this.options.path)&&this.fs.statSync(this.options.path).size>=this.maxSizeBytes&&this.rotateLog(),this.fs.appendFileSync(this.options.path,n,"utf8")}catch(n){console.error("[Emitochondria] Failed to write to log file:",n)}}rotateLog(){if(!(!this.fs||!this.path))try{let e=1;for(;this.fs.existsSync(`${this.options.path}.${e}`);)e++;this.fs.renameSync(this.options.path,`${this.options.path}.${e}`)}catch(e){console.error("[Emitochondria] Failed to rotate log file:",e)}}logEvent(e,n){if(!this.options.logEvents)return;let r={level:"info",type:"event",event:e,message:"Event emitted",payload:n},i=this.formatEntry(r);console.log(i),this.options.persist&&this.writeToFile(i)}logError(e,n,r){if(!this.options.logErrors)return;let i={level:"error",type:"error",event:e,message:"Handler threw an error",error:n},c=this.formatEntry(i);console.error(c),this.options.persist&&this.writeToFile(c)}logWarning(e,n,r){if(!this.options.logWarnings)return;let i={level:"warn",type:"warning",event:e,message:n,payload:r},c=this.formatEntry(i);console.warn(c),this.options.persist&&this.writeToFile(c)}};var A="emitochondria.config.json",d=null;function P(){if(d!==null)return d;let a=globalThis.process;if(typeof a>"u"||!a?.versions?.node)return d={},d;try{let e=globalThis.require;if(!e)return d={},d;let n=e("fs"),i=e("path").join(a.cwd(),A),c=n.readFileSync(i,"utf-8");return d=JSON.parse(c),d}catch{return d={},d}}function L(a,e){let n={...a};for(let r in e)e[r]!==void 0&&(typeof e[r]=="object"&&e[r]!==null&&!Array.isArray(e[r])&&typeof n[r]=="object"&&n[r]!==null?n[r]=L(n[r],e[r]):n[r]=e[r]);return n}function S(a={}){let e=P();return L(e,a)}function H(){d=null}var j=(a,e,n)=>{};function v(a={}){let e=S(a),n=new y(e.logging),r=new Map,i=new Set,c=e.maxListeners??10,k=a.onMaxListenersExceeded??j,E=e.onError??"default",p=new Set,m=t=>r.get(t)??r.set(t,new Set).get(t),u=(t,s,o)=>{if(n.logError(s,t,o),E==="throw")throw t;typeof E=="function"&&E(t,s,o)},T=(t,s,o)=>{try{let l=t(o);if(l instanceof Promise)return l.catch(f=>u(f,s,t))}catch(l){u(l,s,t)}},b=(t,s)=>{if(c>0&&s>c&&!p.has(t)){p.add(t);let o=`Possible memory leak: ${s} listeners (max: ${c})`;n.logWarning(t,o,{count:s,max:c}),k(t,s,c)}},h={on(t,s){let o=m(t);return o.add(s),b(t,o.size),()=>h.off(t,s)},off(t,s){let o=m(t);if(o.delete(s)){o.size<=c&&p.delete(t);return}for(let l of o)if(l.__original===s){o.delete(l),o.size<=c&&p.delete(t);break}},once(t,s){let o=(l=>(h.off(t,o),s(l)));return o.__original=s,h.on(t,o)},emit(t,...s){let o=s[0];n.logEvent(t,o),m(t).forEach(l=>T(l,t,o)),i.forEach(l=>{try{l(t,o)}catch(f){u(f,t,l)}})},async emitAsync(t,...s){let o=s[0],l=[];n.logEvent(t,o),m(t).forEach(f=>{let g=T(f,t,o);g instanceof Promise&&l.push(g)}),i.forEach(f=>{try{let g=f(t,o);g instanceof Promise&&l.push(g.catch(F=>u(F,t,f)))}catch(g){u(g,t,f)}}),await Promise.all(l)},onAny(t){return i.add(t),()=>h.offAny(t)},offAny(t){i.delete(t)},clear(t){t?(r.delete(t),p.delete(t)):(r.clear(),i.clear(),p.clear())},listenerCount(t){return m(t).size},setErrorHandler(t){E=t},setMaxListeners(t){c=t,t===0&&p.clear()},getMaxListeners(){return c},eventNames(){return Array.from(r.keys()).filter(t=>r.get(t).size>0)},listeners(t){let s=m(t);return Array.from(s).map(o=>o.__original||o)},wildcardListeners(){return Array.from(i)},hasListener(t,s){let o=m(t);if(o.has(s))return!0;for(let l of o)if(l.__original===s)return!0;return!1}};return h}0&&(module.exports={clearConfigCache,createEmitochondria});
|
|
2
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/logger.ts","../src/config.ts","../src/emitter.ts"],"sourcesContent":["// Core\nexport { createEmitochondria } from './emitter.js';\n\n// Types\nexport type {\n EventMap,\n EventKey,\n EventHandler,\n WildcardHandler,\n ErrorHandler,\n MaxListenersHandler,\n EmitochondriaConfig,\n EmitochondriaOptions,\n Emitochondria,\n LoggingOptions,\n TimestampFormat,\n Timezone,\n LogFormat,\n} from './types.js';\n\n// Utilities (for testing/advanced use)\nexport { clearConfigCache } from './config.js';\n\n// Default export\nexport { createEmitochondria as default } from './emitter.js';\n","import { LoggingOptions, TimestampFormat, Timezone } from './types.js';\n\n// ============================================================\n// ENVIRONMENT DETECTION\n// ============================================================\n\nconst isNode = typeof (globalThis as any).process !== 'undefined'\n && (globalThis as any).process?.versions?.node != null;\n\nconst isBrowser = typeof (globalThis as any).window !== 'undefined';\n\n// ============================================================\n// TIMESTAMP FORMATTING\n// ============================================================\n\nfunction formatTimestamp(format: TimestampFormat, timezone: Timezone): string {\n const date = new Date();\n\n switch (format) {\n case 'unix':\n return String(date.getTime());\n\n case 'human':\n return date.toLocaleString('en-US', {\n timeZone: timezone === 'utc' ? 'UTC' : undefined,\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n second: '2-digit',\n hour12: true,\n });\n\n case 'iso':\n default:\n return timezone === 'utc'\n ? date.toISOString()\n : new Date(date.getTime() - date.getTimezoneOffset() * 60000)\n .toISOString()\n .slice(0, -1); // Remove 'Z' for local\n }\n}\n\n// ============================================================\n// SIZE PARSING\n// ============================================================\n\nfunction parseSize(size: string): number {\n const match = size.match(/^(\\d+(?:\\.\\d+)?)\\s*(KB|MB|GB)$/i);\n if (!match) return 10 * 1024 * 1024; // Default 10MB\n\n const value = parseFloat(match[1]!);\n const unit = match[2]!.toUpperCase();\n\n const multipliers: Record<string, number> = {\n 'KB': 1024,\n 'MB': 1024 * 1024,\n 'GB': 1024 * 1024 * 1024,\n };\n\n return value * (multipliers[unit] ?? 10 * 1024 * 1024);\n}\n\n// ============================================================\n// LOG ENTRY TYPES\n// ============================================================\n\nexport type LogLevel = 'info' | 'warn' | 'error';\n\nexport interface LogEntry {\n level: LogLevel;\n type: 'event' | 'error' | 'warning';\n event?: string;\n message: string;\n payload?: unknown;\n error?: unknown;\n}\n\n// ============================================================\n// LOGGER CLASS\n// ============================================================\n\nexport class Logger {\n private options: Required<LoggingOptions>;\n private maxSizeBytes: number;\n private browserWarned = false;\n private fs: any = null;\n private path: any = null;\n\n constructor(options: LoggingOptions = {}) {\n this.options = {\n timestamps: options.timestamps ?? false,\n timestampFormat: options.timestampFormat ?? 'iso',\n timezone: options.timezone ?? 'utc',\n persist: options.persist ?? false,\n path: options.path ?? './logs/emitochondria.log',\n format: options.format ?? 'text',\n maxSize: options.maxSize ?? '10MB',\n logEvents: options.logEvents ?? false,\n logErrors: options.logErrors ?? true,\n logWarnings: options.logWarnings ?? true,\n };\n\n this.maxSizeBytes = parseSize(this.options.maxSize);\n\n if (this.options.persist) {\n this.initFileLogging();\n }\n }\n\n private initFileLogging(): void {\n if (isBrowser) {\n if (!this.browserWarned) {\n console.warn(\n '[Emitochondria] File logging is not available in browser environments. ' +\n 'Logs will only be written to console.'\n );\n this.browserWarned = true;\n }\n return;\n }\n\n if (!isNode) return;\n\n try {\n // Require fs and path synchronously\n const req = (globalThis as any).require;\n if (req) {\n this.fs = req('fs');\n this.path = req('path');\n\n // Create directory if needed\n const dir = this.path.dirname(this.options.path);\n if (!this.fs.existsSync(dir)) {\n this.fs.mkdirSync(dir, { recursive: true });\n }\n }\n } catch (err) {\n console.error('[Emitochondria] Failed to initialize file logging:', err);\n }\n }\n\n private getTimestamp(): string | undefined {\n if (!this.options.timestamps) return undefined;\n return formatTimestamp(this.options.timestampFormat, this.options.timezone);\n }\n\n private formatEntry(entry: LogEntry): string {\n const timestamp = this.getTimestamp();\n\n if (this.options.format === 'json') {\n const obj: any = {\n level: entry.level,\n type: entry.type,\n message: entry.message,\n };\n if (timestamp) obj.timestamp = timestamp;\n if (entry.event) obj.event = entry.event;\n if (entry.payload !== undefined) obj.payload = entry.payload;\n if (entry.error) obj.error = String(entry.error);\n return JSON.stringify(obj);\n }\n\n // Text format\n const parts: string[] = [];\n if (timestamp) parts.push(`[${timestamp}]`);\n parts.push(`[${entry.level.toUpperCase()}]`);\n if (entry.event) parts.push(`[${entry.event}]`);\n parts.push(entry.message);\n if (entry.payload !== undefined) {\n parts.push(`| Payload: ${JSON.stringify(entry.payload)}`);\n }\n if (entry.error) {\n parts.push(`| Error: ${entry.error}`);\n }\n\n return parts.join(' ');\n }\n\n private writeToFile(content: string): void {\n if (!this.fs || !this.path || !isNode) return;\n\n try {\n const line = content + '\\n';\n\n // Check if rotation needed\n if (this.fs.existsSync(this.options.path)) {\n const stats = this.fs.statSync(this.options.path);\n if (stats.size >= this.maxSizeBytes) {\n this.rotateLog();\n }\n }\n\n // Sync write — simple, ordered, reliable\n this.fs.appendFileSync(this.options.path, line, 'utf8');\n } catch (err) {\n console.error('[Emitochondria] Failed to write to log file:', err);\n }\n }\n\n private rotateLog(): void {\n if (!this.fs || !this.path) return;\n\n try {\n // Find next rotation number\n let rotationNum = 1;\n while (this.fs.existsSync(`${this.options.path}.${rotationNum}`)) {\n rotationNum++;\n }\n\n // Rename current file\n this.fs.renameSync(this.options.path, `${this.options.path}.${rotationNum}`);\n } catch (err) {\n console.error('[Emitochondria] Failed to rotate log file:', err);\n }\n }\n\n // ============================================================\n // PUBLIC LOGGING METHODS\n // ============================================================\n\n logEvent(event: string, payload: unknown): void {\n if (!this.options.logEvents) return;\n\n const entry: LogEntry = {\n level: 'info',\n type: 'event',\n event,\n message: 'Event emitted',\n payload,\n };\n\n const formatted = this.formatEntry(entry);\n console.log(formatted);\n\n if (this.options.persist) {\n this.writeToFile(formatted);\n }\n }\n\n logError(event: string, error: unknown, _handler?: unknown): void {\n if (!this.options.logErrors) return;\n\n const entry: LogEntry = {\n level: 'error',\n type: 'error',\n event,\n message: 'Handler threw an error',\n error,\n };\n\n const formatted = this.formatEntry(entry);\n console.error(formatted);\n\n if (this.options.persist) {\n this.writeToFile(formatted);\n }\n }\n\n logWarning(event: string, message: string, details?: Record<string, unknown>): void {\n if (!this.options.logWarnings) return;\n\n const entry: LogEntry = {\n level: 'warn',\n type: 'warning',\n event,\n message,\n payload: details,\n };\n\n const formatted = this.formatEntry(entry);\n console.warn(formatted);\n\n if (this.options.persist) {\n this.writeToFile(formatted);\n }\n }\n}\n","import { EmitochondriaConfig, EmitochondriaOptions, EventMap } from './types.js';\n\n// ============================================================\n// CONFIG FILE LOADING\n// ============================================================\n\nconst CONFIG_FILENAME = 'emitochondria.config.json';\n\nlet cachedConfig: EmitochondriaConfig | null = null;\n\n/**\n * Synchronous version for initial load.\n * Falls back to empty config if loading fails.\n */\nfunction loadConfigFileSync(): EmitochondriaConfig {\n if (cachedConfig !== null) return cachedConfig;\n\n const proc = (globalThis as any).process;\n if (typeof proc === 'undefined' || !proc?.versions?.node) {\n cachedConfig = {};\n return cachedConfig;\n }\n\n try {\n const req = (globalThis as any).require;\n if (!req) {\n cachedConfig = {};\n return cachedConfig;\n }\n\n const fs = req('fs');\n const path = req('path');\n\n const configPath = path.join(proc.cwd(), CONFIG_FILENAME);\n const content = fs.readFileSync(configPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n return cachedConfig;\n } catch {\n cachedConfig = {};\n return cachedConfig;\n }\n}\n\n// ============================================================\n// CONFIG MERGING\n// ============================================================\n\n/**\n * Deep merge two objects, with second taking precedence.\n */\nfunction deepMerge<T extends Record<string, any>>(base: T, override: Partial<T>): T {\n const result = { ...base };\n\n for (const key in override) {\n if (override[key] !== undefined) {\n if (\n typeof override[key] === 'object' &&\n override[key] !== null &&\n !Array.isArray(override[key]) &&\n typeof result[key] === 'object' &&\n result[key] !== null\n ) {\n result[key] = deepMerge(result[key], override[key]);\n } else {\n result[key] = override[key] as T[typeof key];\n }\n }\n }\n\n return result;\n}\n\n/**\n * Merge file config with options.\n * Options take precedence over file config.\n */\nexport function mergeConfig<T extends EventMap>(\n options: EmitochondriaOptions<T> = {}\n): EmitochondriaOptions<T> {\n const fileConfig = loadConfigFileSync();\n // Type assertion is safe here because we control both inputs\n const merged = deepMerge(fileConfig as any, options as any);\n return merged as EmitochondriaOptions<T>;\n}\n\n/**\n * Clear cached config (useful for testing).\n */\nexport function clearConfigCache(): void {\n cachedConfig = null;\n}\n","import {\n EventMap,\n EventKey,\n EventHandler,\n WildcardHandler,\n ErrorHandler,\n EmitochondriaOptions,\n Emitochondria,\n} from './types.js';\nimport { Logger } from './logger.js';\nimport { mergeConfig } from './config.js';\n\n// ============================================================\n// DEFAULT HANDLERS\n// ============================================================\n\nconst defaultMaxListenersHandler = <T extends EventMap>(\n _event: EventKey<T>,\n _count: number,\n _max: number\n): void => {\n // Handled by logger\n};\n\n// ============================================================\n// FACTORY FUNCTION\n// ============================================================\n\nexport function createEmitochondria<T extends EventMap>(\n options: EmitochondriaOptions<T> = {}\n): Emitochondria<T> {\n // Merge file config with options\n const config = mergeConfig(options);\n\n // Initialize logger\n const logger = new Logger(config.logging);\n\n // Handler storage\n const handlers: Map<string, Set<EventHandler<unknown>>> = new Map();\n const wildcards: Set<WildcardHandler<T>> = new Set();\n\n // Configuration state\n let maxListeners = config.maxListeners ?? 10;\n let onMaxListenersExceeded = options.onMaxListenersExceeded ?? defaultMaxListenersHandler;\n let errorHandler: ErrorHandler<T> | 'throw' | 'default' = config.onError ?? 'default';\n\n // Memory leak tracking\n const warned = new Set<string>();\n\n // ============================================================\n // HELPER FUNCTIONS\n // ============================================================\n\n // Track original handlers for once() wrappers\n type WrappedHandler<T> = EventHandler<T> & { __original?: EventHandler<T> };\n\n const get = (e: string) => handlers.get(e) ?? handlers.set(e, new Set()).get(e)!;\n\n const handleError = <K extends EventKey<T>>(\n error: unknown,\n event: K,\n handler: EventHandler<unknown>\n ): void => {\n logger.logError(event, error, handler);\n\n if (errorHandler === 'throw') {\n throw error;\n } else if (typeof errorHandler === 'function') {\n errorHandler(error, event, handler);\n }\n // 'default' = just log (already done above)\n };\n\n const safeCall = <K extends EventKey<T>>(\n handler: EventHandler<unknown>,\n event: K,\n payload: unknown\n ): void | Promise<void> => {\n try {\n const result = handler(payload);\n if (result instanceof Promise) {\n return result.catch((err) => handleError(err, event, handler));\n }\n } catch (err) {\n handleError(err, event, handler);\n }\n };\n\n const checkMaxListeners = <K extends EventKey<T>>(event: K, count: number): void => {\n if (maxListeners > 0 && count > maxListeners && !warned.has(event)) {\n warned.add(event);\n\n const message = `Possible memory leak: ${count} listeners (max: ${maxListeners})`;\n logger.logWarning(event, message, { count, max: maxListeners });\n\n onMaxListenersExceeded(event, count, maxListeners);\n }\n };\n\n // ============================================================\n // EMITTER IMPLEMENTATION\n // ============================================================\n\n const emitter: Emitochondria<T> = {\n on(event, handler) {\n const set = get(event);\n set.add(handler as EventHandler<unknown>);\n checkMaxListeners(event, set.size);\n return () => emitter.off(event, handler);\n },\n\n off(event, handler) {\n const set = get(event);\n\n // Try direct removal first\n if (set.delete(handler as EventHandler<unknown>)) {\n if (set.size <= maxListeners) {\n warned.delete(event);\n }\n return;\n }\n\n // Search for wrapped handler (for once handlers)\n for (const wrapped of set) {\n const w = wrapped as WrappedHandler<unknown>;\n if (w.__original === handler) {\n set.delete(wrapped);\n if (set.size <= maxListeners) {\n warned.delete(event);\n }\n break;\n }\n }\n },\n\n once(event, handler) {\n const wrapper = ((payload: T[typeof event]) => {\n emitter.off(event, wrapper as EventHandler<T[typeof event]>);\n return handler(payload);\n }) as WrappedHandler<T[typeof event]>;\n\n // Store original reference for manual removal\n wrapper.__original = handler;\n\n return emitter.on(event, wrapper);\n },\n\n emit(event, ...payload) {\n const data = payload[0];\n\n logger.logEvent(event, data);\n\n get(event).forEach((h) => safeCall(h, event, data));\n wildcards.forEach((h) => {\n try {\n h(event, data as T[typeof event]);\n } catch (err) {\n handleError(err, event, h as unknown as EventHandler<unknown>);\n }\n });\n },\n\n async emitAsync(event, ...payload) {\n const data = payload[0];\n const promises: Promise<void>[] = [];\n\n logger.logEvent(event, data);\n\n get(event).forEach((h) => {\n const result = safeCall(h, event, data);\n if (result instanceof Promise) promises.push(result);\n });\n\n wildcards.forEach((h) => {\n try {\n const result = h(event, data as T[typeof event]);\n if (result instanceof Promise) {\n promises.push(result.catch((err) =>\n handleError(err, event, h as unknown as EventHandler<unknown>)\n ));\n }\n } catch (err) {\n handleError(err, event, h as unknown as EventHandler<unknown>);\n }\n });\n\n await Promise.all(promises);\n },\n\n onAny(handler) {\n wildcards.add(handler);\n return () => emitter.offAny(handler);\n },\n\n offAny(handler) {\n wildcards.delete(handler);\n },\n\n clear(event?) {\n if (event) {\n handlers.delete(event);\n warned.delete(event);\n } else {\n handlers.clear();\n wildcards.clear();\n warned.clear();\n }\n },\n\n listenerCount(event) {\n return get(event).size;\n },\n\n setErrorHandler(handler) {\n errorHandler = handler;\n },\n\n setMaxListeners(n) {\n maxListeners = n;\n if (n === 0) warned.clear();\n },\n\n getMaxListeners() {\n return maxListeners;\n },\n\n eventNames() {\n return Array.from(handlers.keys()).filter(\n (k) => handlers.get(k)!.size > 0\n ) as EventKey<T>[];\n },\n\n listeners(event) {\n const set = get(event);\n // Return unwrapped handlers for better debugging\n return Array.from(set).map(handler => {\n const wrapped = handler as WrappedHandler<unknown>;\n return (wrapped.__original || handler) as EventHandler<T[typeof event]>;\n });\n },\n\n wildcardListeners() {\n return Array.from(wildcards);\n },\n\n hasListener(event, handler) {\n const set = get(event);\n\n // Check direct match\n if (set.has(handler as EventHandler<unknown>)) {\n return true;\n }\n\n // Check wrapped handlers (once)\n for (const wrapped of set) {\n const w = wrapped as WrappedHandler<unknown>;\n if (w.__original === handler) {\n return true;\n }\n }\n\n return false;\n },\n };\n\n return emitter;\n}\n"],"mappings":"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","names":["index_exports","__export","clearConfigCache","createEmitochondria","__toCommonJS","isNode","isBrowser","formatTimestamp","format","timezone","date","parseSize","size","match","value","unit","multipliers","Logger","options","req","dir","err","entry","timestamp","obj","parts","content","line","rotationNum","event","payload","formatted","error","_handler","message","details","CONFIG_FILENAME","cachedConfig","loadConfigFileSync","proc","req","fs","configPath","content","deepMerge","base","override","result","key","mergeConfig","options","fileConfig","clearConfigCache","defaultMaxListenersHandler","_event","_count","_max","createEmitochondria","options","config","mergeConfig","logger","Logger","handlers","wildcards","maxListeners","onMaxListenersExceeded","errorHandler","warned","get","e","handleError","error","event","handler","safeCall","payload","result","err","checkMaxListeners","count","message","emitter","set","wrapped","wrapper","data","h","promises","n","k"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var v=typeof globalThis.process<"u"&&globalThis.process?.versions?.node!=null,b=typeof globalThis.window<"u";function F(c,e){let n=new Date;switch(c){case"unix":return String(n.getTime());case"human":return n.toLocaleString("en-US",{timeZone:e==="utc"?"UTC":void 0,month:"short",day:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0});default:return e==="utc"?n.toISOString():new Date(n.getTime()-n.getTimezoneOffset()*6e4).toISOString().slice(0,-1)}}function z(c){let e=c.match(/^(\d+(?:\.\d+)?)\s*(KB|MB|GB)$/i);if(!e)return 10*1024*1024;let n=parseFloat(e[1]),r=e[2].toUpperCase(),s={KB:1024,MB:1024*1024,GB:1024*1024*1024};return n*(s[r]??10*1024*1024)}var y=class{options;maxSizeBytes;browserWarned=!1;fs=null;path=null;constructor(e={}){this.options={timestamps:e.timestamps??!1,timestampFormat:e.timestampFormat??"iso",timezone:e.timezone??"utc",persist:e.persist??!1,path:e.path??"./logs/emitochondria.log",format:e.format??"text",maxSize:e.maxSize??"10MB",logEvents:e.logEvents??!1,logErrors:e.logErrors??!0,logWarnings:e.logWarnings??!0},this.maxSizeBytes=z(this.options.maxSize),this.options.persist&&this.initFileLogging()}initFileLogging(){if(b){this.browserWarned||(console.warn("[Emitochondria] File logging is not available in browser environments. Logs will only be written to console."),this.browserWarned=!0);return}if(v)try{let e=globalThis.require;if(e){this.fs=e("fs"),this.path=e("path");let n=this.path.dirname(this.options.path);this.fs.existsSync(n)||this.fs.mkdirSync(n,{recursive:!0})}}catch(e){console.error("[Emitochondria] Failed to initialize file logging:",e)}}getTimestamp(){if(this.options.timestamps)return F(this.options.timestampFormat,this.options.timezone)}formatEntry(e){let n=this.getTimestamp();if(this.options.format==="json"){let s={level:e.level,type:e.type,message:e.message};return n&&(s.timestamp=n),e.event&&(s.event=e.event),e.payload!==void 0&&(s.payload=e.payload),e.error&&(s.error=String(e.error)),JSON.stringify(s)}let r=[];return n&&r.push(`[${n}]`),r.push(`[${e.level.toUpperCase()}]`),e.event&&r.push(`[${e.event}]`),r.push(e.message),e.payload!==void 0&&r.push(`| Payload: ${JSON.stringify(e.payload)}`),e.error&&r.push(`| Error: ${e.error}`),r.join(" ")}writeToFile(e){if(!(!this.fs||!this.path||!v))try{let n=e+`
|
|
2
|
+
`;this.fs.existsSync(this.options.path)&&this.fs.statSync(this.options.path).size>=this.maxSizeBytes&&this.rotateLog(),this.fs.appendFileSync(this.options.path,n,"utf8")}catch(n){console.error("[Emitochondria] Failed to write to log file:",n)}}rotateLog(){if(!(!this.fs||!this.path))try{let e=1;for(;this.fs.existsSync(`${this.options.path}.${e}`);)e++;this.fs.renameSync(this.options.path,`${this.options.path}.${e}`)}catch(e){console.error("[Emitochondria] Failed to rotate log file:",e)}}logEvent(e,n){if(!this.options.logEvents)return;let r={level:"info",type:"event",event:e,message:"Event emitted",payload:n},s=this.formatEntry(r);console.log(s),this.options.persist&&this.writeToFile(s)}logError(e,n,r){if(!this.options.logErrors)return;let s={level:"error",type:"error",event:e,message:"Handler threw an error",error:n},l=this.formatEntry(s);console.error(l),this.options.persist&&this.writeToFile(l)}logWarning(e,n,r){if(!this.options.logWarnings)return;let s={level:"warn",type:"warning",event:e,message:n,payload:r},l=this.formatEntry(s);console.warn(l),this.options.persist&&this.writeToFile(l)}};var C="emitochondria.config.json",d=null;function M(){if(d!==null)return d;let c=globalThis.process;if(typeof c>"u"||!c?.versions?.node)return d={},d;try{let e=globalThis.require;if(!e)return d={},d;let n=e("fs"),s=e("path").join(c.cwd(),C),l=n.readFileSync(s,"utf-8");return d=JSON.parse(l),d}catch{return d={},d}}function T(c,e){let n={...c};for(let r in e)e[r]!==void 0&&(typeof e[r]=="object"&&e[r]!==null&&!Array.isArray(e[r])&&typeof n[r]=="object"&&n[r]!==null?n[r]=T(n[r],e[r]):n[r]=e[r]);return n}function x(c={}){let e=M();return T(e,c)}function O(){d=null}var W=(c,e,n)=>{};function L(c={}){let e=x(c),n=new y(e.logging),r=new Map,s=new Set,l=e.maxListeners??10,S=c.onMaxListenersExceeded??W,E=e.onError??"default",p=new Set,m=t=>r.get(t)??r.set(t,new Set).get(t),u=(t,i,o)=>{if(n.logError(i,t,o),E==="throw")throw t;typeof E=="function"&&E(t,i,o)},w=(t,i,o)=>{try{let a=t(o);if(a instanceof Promise)return a.catch(f=>u(f,i,t))}catch(a){u(a,i,t)}},H=(t,i)=>{if(l>0&&i>l&&!p.has(t)){p.add(t);let o=`Possible memory leak: ${i} listeners (max: ${l})`;n.logWarning(t,o,{count:i,max:l}),S(t,i,l)}},h={on(t,i){let o=m(t);return o.add(i),H(t,o.size),()=>h.off(t,i)},off(t,i){let o=m(t);if(o.delete(i)){o.size<=l&&p.delete(t);return}for(let a of o)if(a.__original===i){o.delete(a),o.size<=l&&p.delete(t);break}},once(t,i){let o=(a=>(h.off(t,o),i(a)));return o.__original=i,h.on(t,o)},emit(t,...i){let o=i[0];n.logEvent(t,o),m(t).forEach(a=>w(a,t,o)),s.forEach(a=>{try{a(t,o)}catch(f){u(f,t,a)}})},async emitAsync(t,...i){let o=i[0],a=[];n.logEvent(t,o),m(t).forEach(f=>{let g=w(f,t,o);g instanceof Promise&&a.push(g)}),s.forEach(f=>{try{let g=f(t,o);g instanceof Promise&&a.push(g.catch(k=>u(k,t,f)))}catch(g){u(g,t,f)}}),await Promise.all(a)},onAny(t){return s.add(t),()=>h.offAny(t)},offAny(t){s.delete(t)},clear(t){t?(r.delete(t),p.delete(t)):(r.clear(),s.clear(),p.clear())},listenerCount(t){return m(t).size},setErrorHandler(t){E=t},setMaxListeners(t){l=t,t===0&&p.clear()},getMaxListeners(){return l},eventNames(){return Array.from(r.keys()).filter(t=>r.get(t).size>0)},listeners(t){let i=m(t);return Array.from(i).map(o=>o.__original||o)},wildcardListeners(){return Array.from(s)},hasListener(t,i){let o=m(t);if(o.has(i))return!0;for(let a of o)if(a.__original===i)return!0;return!1}};return h}export{O as clearConfigCache,L as createEmitochondria,L as default};
|
|
3
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/logger.ts","../src/config.ts","../src/emitter.ts"],"sourcesContent":["import { LoggingOptions, TimestampFormat, Timezone } from './types.js';\n\n// ============================================================\n// ENVIRONMENT DETECTION\n// ============================================================\n\nconst isNode = typeof (globalThis as any).process !== 'undefined'\n && (globalThis as any).process?.versions?.node != null;\n\nconst isBrowser = typeof (globalThis as any).window !== 'undefined';\n\n// ============================================================\n// TIMESTAMP FORMATTING\n// ============================================================\n\nfunction formatTimestamp(format: TimestampFormat, timezone: Timezone): string {\n const date = new Date();\n\n switch (format) {\n case 'unix':\n return String(date.getTime());\n\n case 'human':\n return date.toLocaleString('en-US', {\n timeZone: timezone === 'utc' ? 'UTC' : undefined,\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n second: '2-digit',\n hour12: true,\n });\n\n case 'iso':\n default:\n return timezone === 'utc'\n ? date.toISOString()\n : new Date(date.getTime() - date.getTimezoneOffset() * 60000)\n .toISOString()\n .slice(0, -1); // Remove 'Z' for local\n }\n}\n\n// ============================================================\n// SIZE PARSING\n// ============================================================\n\nfunction parseSize(size: string): number {\n const match = size.match(/^(\\d+(?:\\.\\d+)?)\\s*(KB|MB|GB)$/i);\n if (!match) return 10 * 1024 * 1024; // Default 10MB\n\n const value = parseFloat(match[1]!);\n const unit = match[2]!.toUpperCase();\n\n const multipliers: Record<string, number> = {\n 'KB': 1024,\n 'MB': 1024 * 1024,\n 'GB': 1024 * 1024 * 1024,\n };\n\n return value * (multipliers[unit] ?? 10 * 1024 * 1024);\n}\n\n// ============================================================\n// LOG ENTRY TYPES\n// ============================================================\n\nexport type LogLevel = 'info' | 'warn' | 'error';\n\nexport interface LogEntry {\n level: LogLevel;\n type: 'event' | 'error' | 'warning';\n event?: string;\n message: string;\n payload?: unknown;\n error?: unknown;\n}\n\n// ============================================================\n// LOGGER CLASS\n// ============================================================\n\nexport class Logger {\n private options: Required<LoggingOptions>;\n private maxSizeBytes: number;\n private browserWarned = false;\n private fs: any = null;\n private path: any = null;\n\n constructor(options: LoggingOptions = {}) {\n this.options = {\n timestamps: options.timestamps ?? false,\n timestampFormat: options.timestampFormat ?? 'iso',\n timezone: options.timezone ?? 'utc',\n persist: options.persist ?? false,\n path: options.path ?? './logs/emitochondria.log',\n format: options.format ?? 'text',\n maxSize: options.maxSize ?? '10MB',\n logEvents: options.logEvents ?? false,\n logErrors: options.logErrors ?? true,\n logWarnings: options.logWarnings ?? true,\n };\n\n this.maxSizeBytes = parseSize(this.options.maxSize);\n\n if (this.options.persist) {\n this.initFileLogging();\n }\n }\n\n private initFileLogging(): void {\n if (isBrowser) {\n if (!this.browserWarned) {\n console.warn(\n '[Emitochondria] File logging is not available in browser environments. ' +\n 'Logs will only be written to console.'\n );\n this.browserWarned = true;\n }\n return;\n }\n\n if (!isNode) return;\n\n try {\n // Require fs and path synchronously\n const req = (globalThis as any).require;\n if (req) {\n this.fs = req('fs');\n this.path = req('path');\n\n // Create directory if needed\n const dir = this.path.dirname(this.options.path);\n if (!this.fs.existsSync(dir)) {\n this.fs.mkdirSync(dir, { recursive: true });\n }\n }\n } catch (err) {\n console.error('[Emitochondria] Failed to initialize file logging:', err);\n }\n }\n\n private getTimestamp(): string | undefined {\n if (!this.options.timestamps) return undefined;\n return formatTimestamp(this.options.timestampFormat, this.options.timezone);\n }\n\n private formatEntry(entry: LogEntry): string {\n const timestamp = this.getTimestamp();\n\n if (this.options.format === 'json') {\n const obj: any = {\n level: entry.level,\n type: entry.type,\n message: entry.message,\n };\n if (timestamp) obj.timestamp = timestamp;\n if (entry.event) obj.event = entry.event;\n if (entry.payload !== undefined) obj.payload = entry.payload;\n if (entry.error) obj.error = String(entry.error);\n return JSON.stringify(obj);\n }\n\n // Text format\n const parts: string[] = [];\n if (timestamp) parts.push(`[${timestamp}]`);\n parts.push(`[${entry.level.toUpperCase()}]`);\n if (entry.event) parts.push(`[${entry.event}]`);\n parts.push(entry.message);\n if (entry.payload !== undefined) {\n parts.push(`| Payload: ${JSON.stringify(entry.payload)}`);\n }\n if (entry.error) {\n parts.push(`| Error: ${entry.error}`);\n }\n\n return parts.join(' ');\n }\n\n private writeToFile(content: string): void {\n if (!this.fs || !this.path || !isNode) return;\n\n try {\n const line = content + '\\n';\n\n // Check if rotation needed\n if (this.fs.existsSync(this.options.path)) {\n const stats = this.fs.statSync(this.options.path);\n if (stats.size >= this.maxSizeBytes) {\n this.rotateLog();\n }\n }\n\n // Sync write — simple, ordered, reliable\n this.fs.appendFileSync(this.options.path, line, 'utf8');\n } catch (err) {\n console.error('[Emitochondria] Failed to write to log file:', err);\n }\n }\n\n private rotateLog(): void {\n if (!this.fs || !this.path) return;\n\n try {\n // Find next rotation number\n let rotationNum = 1;\n while (this.fs.existsSync(`${this.options.path}.${rotationNum}`)) {\n rotationNum++;\n }\n\n // Rename current file\n this.fs.renameSync(this.options.path, `${this.options.path}.${rotationNum}`);\n } catch (err) {\n console.error('[Emitochondria] Failed to rotate log file:', err);\n }\n }\n\n // ============================================================\n // PUBLIC LOGGING METHODS\n // ============================================================\n\n logEvent(event: string, payload: unknown): void {\n if (!this.options.logEvents) return;\n\n const entry: LogEntry = {\n level: 'info',\n type: 'event',\n event,\n message: 'Event emitted',\n payload,\n };\n\n const formatted = this.formatEntry(entry);\n console.log(formatted);\n\n if (this.options.persist) {\n this.writeToFile(formatted);\n }\n }\n\n logError(event: string, error: unknown, _handler?: unknown): void {\n if (!this.options.logErrors) return;\n\n const entry: LogEntry = {\n level: 'error',\n type: 'error',\n event,\n message: 'Handler threw an error',\n error,\n };\n\n const formatted = this.formatEntry(entry);\n console.error(formatted);\n\n if (this.options.persist) {\n this.writeToFile(formatted);\n }\n }\n\n logWarning(event: string, message: string, details?: Record<string, unknown>): void {\n if (!this.options.logWarnings) return;\n\n const entry: LogEntry = {\n level: 'warn',\n type: 'warning',\n event,\n message,\n payload: details,\n };\n\n const formatted = this.formatEntry(entry);\n console.warn(formatted);\n\n if (this.options.persist) {\n this.writeToFile(formatted);\n }\n }\n}\n","import { EmitochondriaConfig, EmitochondriaOptions, EventMap } from './types.js';\n\n// ============================================================\n// CONFIG FILE LOADING\n// ============================================================\n\nconst CONFIG_FILENAME = 'emitochondria.config.json';\n\nlet cachedConfig: EmitochondriaConfig | null = null;\n\n/**\n * Synchronous version for initial load.\n * Falls back to empty config if loading fails.\n */\nfunction loadConfigFileSync(): EmitochondriaConfig {\n if (cachedConfig !== null) return cachedConfig;\n\n const proc = (globalThis as any).process;\n if (typeof proc === 'undefined' || !proc?.versions?.node) {\n cachedConfig = {};\n return cachedConfig;\n }\n\n try {\n const req = (globalThis as any).require;\n if (!req) {\n cachedConfig = {};\n return cachedConfig;\n }\n\n const fs = req('fs');\n const path = req('path');\n\n const configPath = path.join(proc.cwd(), CONFIG_FILENAME);\n const content = fs.readFileSync(configPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n return cachedConfig;\n } catch {\n cachedConfig = {};\n return cachedConfig;\n }\n}\n\n// ============================================================\n// CONFIG MERGING\n// ============================================================\n\n/**\n * Deep merge two objects, with second taking precedence.\n */\nfunction deepMerge<T extends Record<string, any>>(base: T, override: Partial<T>): T {\n const result = { ...base };\n\n for (const key in override) {\n if (override[key] !== undefined) {\n if (\n typeof override[key] === 'object' &&\n override[key] !== null &&\n !Array.isArray(override[key]) &&\n typeof result[key] === 'object' &&\n result[key] !== null\n ) {\n result[key] = deepMerge(result[key], override[key]);\n } else {\n result[key] = override[key] as T[typeof key];\n }\n }\n }\n\n return result;\n}\n\n/**\n * Merge file config with options.\n * Options take precedence over file config.\n */\nexport function mergeConfig<T extends EventMap>(\n options: EmitochondriaOptions<T> = {}\n): EmitochondriaOptions<T> {\n const fileConfig = loadConfigFileSync();\n // Type assertion is safe here because we control both inputs\n const merged = deepMerge(fileConfig as any, options as any);\n return merged as EmitochondriaOptions<T>;\n}\n\n/**\n * Clear cached config (useful for testing).\n */\nexport function clearConfigCache(): void {\n cachedConfig = null;\n}\n","import {\n EventMap,\n EventKey,\n EventHandler,\n WildcardHandler,\n ErrorHandler,\n EmitochondriaOptions,\n Emitochondria,\n} from './types.js';\nimport { Logger } from './logger.js';\nimport { mergeConfig } from './config.js';\n\n// ============================================================\n// DEFAULT HANDLERS\n// ============================================================\n\nconst defaultMaxListenersHandler = <T extends EventMap>(\n _event: EventKey<T>,\n _count: number,\n _max: number\n): void => {\n // Handled by logger\n};\n\n// ============================================================\n// FACTORY FUNCTION\n// ============================================================\n\nexport function createEmitochondria<T extends EventMap>(\n options: EmitochondriaOptions<T> = {}\n): Emitochondria<T> {\n // Merge file config with options\n const config = mergeConfig(options);\n\n // Initialize logger\n const logger = new Logger(config.logging);\n\n // Handler storage\n const handlers: Map<string, Set<EventHandler<unknown>>> = new Map();\n const wildcards: Set<WildcardHandler<T>> = new Set();\n\n // Configuration state\n let maxListeners = config.maxListeners ?? 10;\n let onMaxListenersExceeded = options.onMaxListenersExceeded ?? defaultMaxListenersHandler;\n let errorHandler: ErrorHandler<T> | 'throw' | 'default' = config.onError ?? 'default';\n\n // Memory leak tracking\n const warned = new Set<string>();\n\n // ============================================================\n // HELPER FUNCTIONS\n // ============================================================\n\n // Track original handlers for once() wrappers\n type WrappedHandler<T> = EventHandler<T> & { __original?: EventHandler<T> };\n\n const get = (e: string) => handlers.get(e) ?? handlers.set(e, new Set()).get(e)!;\n\n const handleError = <K extends EventKey<T>>(\n error: unknown,\n event: K,\n handler: EventHandler<unknown>\n ): void => {\n logger.logError(event, error, handler);\n\n if (errorHandler === 'throw') {\n throw error;\n } else if (typeof errorHandler === 'function') {\n errorHandler(error, event, handler);\n }\n // 'default' = just log (already done above)\n };\n\n const safeCall = <K extends EventKey<T>>(\n handler: EventHandler<unknown>,\n event: K,\n payload: unknown\n ): void | Promise<void> => {\n try {\n const result = handler(payload);\n if (result instanceof Promise) {\n return result.catch((err) => handleError(err, event, handler));\n }\n } catch (err) {\n handleError(err, event, handler);\n }\n };\n\n const checkMaxListeners = <K extends EventKey<T>>(event: K, count: number): void => {\n if (maxListeners > 0 && count > maxListeners && !warned.has(event)) {\n warned.add(event);\n\n const message = `Possible memory leak: ${count} listeners (max: ${maxListeners})`;\n logger.logWarning(event, message, { count, max: maxListeners });\n\n onMaxListenersExceeded(event, count, maxListeners);\n }\n };\n\n // ============================================================\n // EMITTER IMPLEMENTATION\n // ============================================================\n\n const emitter: Emitochondria<T> = {\n on(event, handler) {\n const set = get(event);\n set.add(handler as EventHandler<unknown>);\n checkMaxListeners(event, set.size);\n return () => emitter.off(event, handler);\n },\n\n off(event, handler) {\n const set = get(event);\n\n // Try direct removal first\n if (set.delete(handler as EventHandler<unknown>)) {\n if (set.size <= maxListeners) {\n warned.delete(event);\n }\n return;\n }\n\n // Search for wrapped handler (for once handlers)\n for (const wrapped of set) {\n const w = wrapped as WrappedHandler<unknown>;\n if (w.__original === handler) {\n set.delete(wrapped);\n if (set.size <= maxListeners) {\n warned.delete(event);\n }\n break;\n }\n }\n },\n\n once(event, handler) {\n const wrapper = ((payload: T[typeof event]) => {\n emitter.off(event, wrapper as EventHandler<T[typeof event]>);\n return handler(payload);\n }) as WrappedHandler<T[typeof event]>;\n\n // Store original reference for manual removal\n wrapper.__original = handler;\n\n return emitter.on(event, wrapper);\n },\n\n emit(event, ...payload) {\n const data = payload[0];\n\n logger.logEvent(event, data);\n\n get(event).forEach((h) => safeCall(h, event, data));\n wildcards.forEach((h) => {\n try {\n h(event, data as T[typeof event]);\n } catch (err) {\n handleError(err, event, h as unknown as EventHandler<unknown>);\n }\n });\n },\n\n async emitAsync(event, ...payload) {\n const data = payload[0];\n const promises: Promise<void>[] = [];\n\n logger.logEvent(event, data);\n\n get(event).forEach((h) => {\n const result = safeCall(h, event, data);\n if (result instanceof Promise) promises.push(result);\n });\n\n wildcards.forEach((h) => {\n try {\n const result = h(event, data as T[typeof event]);\n if (result instanceof Promise) {\n promises.push(result.catch((err) =>\n handleError(err, event, h as unknown as EventHandler<unknown>)\n ));\n }\n } catch (err) {\n handleError(err, event, h as unknown as EventHandler<unknown>);\n }\n });\n\n await Promise.all(promises);\n },\n\n onAny(handler) {\n wildcards.add(handler);\n return () => emitter.offAny(handler);\n },\n\n offAny(handler) {\n wildcards.delete(handler);\n },\n\n clear(event?) {\n if (event) {\n handlers.delete(event);\n warned.delete(event);\n } else {\n handlers.clear();\n wildcards.clear();\n warned.clear();\n }\n },\n\n listenerCount(event) {\n return get(event).size;\n },\n\n setErrorHandler(handler) {\n errorHandler = handler;\n },\n\n setMaxListeners(n) {\n maxListeners = n;\n if (n === 0) warned.clear();\n },\n\n getMaxListeners() {\n return maxListeners;\n },\n\n eventNames() {\n return Array.from(handlers.keys()).filter(\n (k) => handlers.get(k)!.size > 0\n ) as EventKey<T>[];\n },\n\n listeners(event) {\n const set = get(event);\n // Return unwrapped handlers for better debugging\n return Array.from(set).map(handler => {\n const wrapped = handler as WrappedHandler<unknown>;\n return (wrapped.__original || handler) as EventHandler<T[typeof event]>;\n });\n },\n\n wildcardListeners() {\n return Array.from(wildcards);\n },\n\n hasListener(event, handler) {\n const set = get(event);\n\n // Check direct match\n if (set.has(handler as EventHandler<unknown>)) {\n return true;\n }\n\n // Check wrapped handlers (once)\n for (const wrapped of set) {\n const w = wrapped as WrappedHandler<unknown>;\n if (w.__original === handler) {\n return true;\n }\n }\n\n return false;\n },\n };\n\n return emitter;\n}\n"],"mappings":"AAMA,IAAMA,EAAS,OAAQ,WAAmB,QAAY,KAChD,WAAmB,SAAS,UAAU,MAAQ,KAE9CC,EAAY,OAAQ,WAAmB,OAAW,IAMxD,SAASC,EAAgBC,EAAyBC,EAA4B,CAC5E,IAAMC,EAAO,IAAI,KAEjB,OAAQF,EAAQ,CACd,IAAK,OACH,OAAO,OAAOE,EAAK,QAAQ,CAAC,EAE9B,IAAK,QACH,OAAOA,EAAK,eAAe,QAAS,CAClC,SAAUD,IAAa,MAAQ,MAAQ,OACvC,MAAO,QACP,IAAK,UACL,KAAM,UACN,OAAQ,UACR,OAAQ,UACR,OAAQ,EACV,CAAC,EAGH,QACE,OAAOA,IAAa,MAChBC,EAAK,YAAY,EACjB,IAAI,KAAKA,EAAK,QAAQ,EAAIA,EAAK,kBAAkB,EAAI,GAAK,EACvD,YAAY,EACZ,MAAM,EAAG,EAAE,CACtB,CACF,CAMA,SAASC,EAAUC,EAAsB,CACvC,IAAMC,EAAQD,EAAK,MAAM,iCAAiC,EAC1D,GAAI,CAACC,EAAO,MAAO,IAAK,KAAO,KAE/B,IAAMC,EAAQ,WAAWD,EAAM,CAAC,CAAE,EAC5BE,EAAOF,EAAM,CAAC,EAAG,YAAY,EAE7BG,EAAsC,CAC1C,GAAM,KACN,GAAM,KAAO,KACb,GAAM,KAAO,KAAO,IACtB,EAEA,OAAOF,GAASE,EAAYD,CAAI,GAAK,GAAK,KAAO,KACnD,CAqBO,IAAME,EAAN,KAAa,CACV,QACA,aACA,cAAgB,GAChB,GAAU,KACV,KAAY,KAEpB,YAAYC,EAA0B,CAAC,EAAG,CACxC,KAAK,QAAU,CACb,WAAYA,EAAQ,YAAc,GAClC,gBAAiBA,EAAQ,iBAAmB,MAC5C,SAAUA,EAAQ,UAAY,MAC9B,QAASA,EAAQ,SAAW,GAC5B,KAAMA,EAAQ,MAAQ,2BACtB,OAAQA,EAAQ,QAAU,OAC1B,QAASA,EAAQ,SAAW,OAC5B,UAAWA,EAAQ,WAAa,GAChC,UAAWA,EAAQ,WAAa,GAChC,YAAaA,EAAQ,aAAe,EACtC,EAEA,KAAK,aAAeP,EAAU,KAAK,QAAQ,OAAO,EAE9C,KAAK,QAAQ,SACf,KAAK,gBAAgB,CAEzB,CAEQ,iBAAwB,CAC9B,GAAIL,EAAW,CACR,KAAK,gBACR,QAAQ,KACN,8GAEF,EACA,KAAK,cAAgB,IAEvB,MACF,CAEA,GAAKD,EAEL,GAAI,CAEF,IAAMc,EAAO,WAAmB,QAChC,GAAIA,EAAK,CACP,KAAK,GAAKA,EAAI,IAAI,EAClB,KAAK,KAAOA,EAAI,MAAM,EAGtB,IAAMC,EAAM,KAAK,KAAK,QAAQ,KAAK,QAAQ,IAAI,EAC1C,KAAK,GAAG,WAAWA,CAAG,GACzB,KAAK,GAAG,UAAUA,EAAK,CAAE,UAAW,EAAK,CAAC,CAE9C,CACF,OAASC,EAAK,CACZ,QAAQ,MAAM,qDAAsDA,CAAG,CACzE,CACF,CAEQ,cAAmC,CACzC,GAAK,KAAK,QAAQ,WAClB,OAAOd,EAAgB,KAAK,QAAQ,gBAAiB,KAAK,QAAQ,QAAQ,CAC5E,CAEQ,YAAYe,EAAyB,CAC3C,IAAMC,EAAY,KAAK,aAAa,EAEpC,GAAI,KAAK,QAAQ,SAAW,OAAQ,CAClC,IAAMC,EAAW,CACf,MAAOF,EAAM,MACb,KAAMA,EAAM,KACZ,QAASA,EAAM,OACjB,EACA,OAAIC,IAAWC,EAAI,UAAYD,GAC3BD,EAAM,QAAOE,EAAI,MAAQF,EAAM,OAC/BA,EAAM,UAAY,SAAWE,EAAI,QAAUF,EAAM,SACjDA,EAAM,QAAOE,EAAI,MAAQ,OAAOF,EAAM,KAAK,GACxC,KAAK,UAAUE,CAAG,CAC3B,CAGA,IAAMC,EAAkB,CAAC,EACzB,OAAIF,GAAWE,EAAM,KAAK,IAAIF,CAAS,GAAG,EAC1CE,EAAM,KAAK,IAAIH,EAAM,MAAM,YAAY,CAAC,GAAG,EACvCA,EAAM,OAAOG,EAAM,KAAK,IAAIH,EAAM,KAAK,GAAG,EAC9CG,EAAM,KAAKH,EAAM,OAAO,EACpBA,EAAM,UAAY,QACpBG,EAAM,KAAK,cAAc,KAAK,UAAUH,EAAM,OAAO,CAAC,EAAE,EAEtDA,EAAM,OACRG,EAAM,KAAK,YAAYH,EAAM,KAAK,EAAE,EAG/BG,EAAM,KAAK,GAAG,CACvB,CAEQ,YAAYC,EAAuB,CACzC,GAAI,GAAC,KAAK,IAAM,CAAC,KAAK,MAAQ,CAACrB,GAE/B,GAAI,CACF,IAAMsB,EAAOD,EAAU;AAAA,EAGnB,KAAK,GAAG,WAAW,KAAK,QAAQ,IAAI,GACxB,KAAK,GAAG,SAAS,KAAK,QAAQ,IAAI,EACtC,MAAQ,KAAK,cACrB,KAAK,UAAU,EAKnB,KAAK,GAAG,eAAe,KAAK,QAAQ,KAAMC,EAAM,MAAM,CACxD,OAASN,EAAK,CACZ,QAAQ,MAAM,+CAAgDA,CAAG,CACnE,CACF,CAEQ,WAAkB,CACxB,GAAI,GAAC,KAAK,IAAM,CAAC,KAAK,MAEtB,GAAI,CAEF,IAAIO,EAAc,EAClB,KAAO,KAAK,GAAG,WAAW,GAAG,KAAK,QAAQ,IAAI,IAAIA,CAAW,EAAE,GAC7DA,IAIF,KAAK,GAAG,WAAW,KAAK,QAAQ,KAAM,GAAG,KAAK,QAAQ,IAAI,IAAIA,CAAW,EAAE,CAC7E,OAASP,EAAK,CACZ,QAAQ,MAAM,6CAA8CA,CAAG,CACjE,CACF,CAMA,SAASQ,EAAeC,EAAwB,CAC9C,GAAI,CAAC,KAAK,QAAQ,UAAW,OAE7B,IAAMR,EAAkB,CACtB,MAAO,OACP,KAAM,QACN,MAAAO,EACA,QAAS,gBACT,QAAAC,CACF,EAEMC,EAAY,KAAK,YAAYT,CAAK,EACxC,QAAQ,IAAIS,CAAS,EAEjB,KAAK,QAAQ,SACf,KAAK,YAAYA,CAAS,CAE9B,CAEA,SAASF,EAAeG,EAAgBC,EAA0B,CAChE,GAAI,CAAC,KAAK,QAAQ,UAAW,OAE7B,IAAMX,EAAkB,CACtB,MAAO,QACP,KAAM,QACN,MAAAO,EACA,QAAS,yBACT,MAAAG,CACF,EAEMD,EAAY,KAAK,YAAYT,CAAK,EACxC,QAAQ,MAAMS,CAAS,EAEnB,KAAK,QAAQ,SACf,KAAK,YAAYA,CAAS,CAE9B,CAEA,WAAWF,EAAeK,EAAiBC,EAAyC,CAClF,GAAI,CAAC,KAAK,QAAQ,YAAa,OAE/B,IAAMb,EAAkB,CACtB,MAAO,OACP,KAAM,UACN,MAAAO,EACA,QAAAK,EACA,QAASC,CACX,EAEMJ,EAAY,KAAK,YAAYT,CAAK,EACxC,QAAQ,KAAKS,CAAS,EAElB,KAAK,QAAQ,SACf,KAAK,YAAYA,CAAS,CAE9B,CACF,EC/QA,IAAMK,EAAkB,4BAEpBC,EAA2C,KAM/C,SAASC,GAA0C,CACjD,GAAID,IAAiB,KAAM,OAAOA,EAElC,IAAME,EAAQ,WAAmB,QACjC,GAAI,OAAOA,EAAS,KAAe,CAACA,GAAM,UAAU,KAClD,OAAAF,EAAe,CAAC,EACTA,EAGT,GAAI,CACF,IAAMG,EAAO,WAAmB,QAChC,GAAI,CAACA,EACH,OAAAH,EAAe,CAAC,EACTA,EAGT,IAAMI,EAAKD,EAAI,IAAI,EAGbE,EAFOF,EAAI,MAAM,EAEC,KAAKD,EAAK,IAAI,EAAGH,CAAe,EAClDO,EAAUF,EAAG,aAAaC,EAAY,OAAO,EACnD,OAAAL,EAAe,KAAK,MAAMM,CAAO,EAE1BN,CACT,MAAQ,CACN,OAAAA,EAAe,CAAC,EACTA,CACT,CACF,CASA,SAASO,EAAyCC,EAASC,EAAyB,CAClF,IAAMC,EAAS,CAAE,GAAGF,CAAK,EAEzB,QAAWG,KAAOF,EACZA,EAASE,CAAG,IAAM,SAElB,OAAOF,EAASE,CAAG,GAAM,UACzBF,EAASE,CAAG,IAAM,MAClB,CAAC,MAAM,QAAQF,EAASE,CAAG,CAAC,GAC5B,OAAOD,EAAOC,CAAG,GAAM,UACvBD,EAAOC,CAAG,IAAM,KAEhBD,EAAOC,CAAG,EAAIJ,EAAUG,EAAOC,CAAG,EAAGF,EAASE,CAAG,CAAC,EAElDD,EAAOC,CAAG,EAAIF,EAASE,CAAG,GAKhC,OAAOD,CACT,CAMO,SAASE,EACdC,EAAmC,CAAC,EACX,CACzB,IAAMC,EAAab,EAAmB,EAGtC,OADeM,EAAUO,EAAmBD,CAAc,CAE5D,CAKO,SAASE,GAAyB,CACvCf,EAAe,IACjB,CC3EA,IAAMgB,EAA6B,CACjCC,EACAC,EACAC,IACS,CAEX,EAMO,SAASC,EACdC,EAAmC,CAAC,EAClB,CAElB,IAAMC,EAASC,EAAYF,CAAO,EAG5BG,EAAS,IAAIC,EAAOH,EAAO,OAAO,EAGlCI,EAAoD,IAAI,IACxDC,EAAqC,IAAI,IAG3CC,EAAeN,EAAO,cAAgB,GACtCO,EAAyBR,EAAQ,wBAA0BL,EAC3Dc,EAAsDR,EAAO,SAAW,UAGtES,EAAS,IAAI,IASbC,EAAOC,GAAcP,EAAS,IAAIO,CAAC,GAAKP,EAAS,IAAIO,EAAG,IAAI,GAAK,EAAE,IAAIA,CAAC,EAExEC,EAAc,CAClBC,EACAC,EACAC,IACS,CAGT,GAFAb,EAAO,SAASY,EAAOD,EAAOE,CAAO,EAEjCP,IAAiB,QACnB,MAAMK,EACG,OAAOL,GAAiB,YACjCA,EAAaK,EAAOC,EAAOC,CAAO,CAGtC,EAEMC,EAAW,CACfD,EACAD,EACAG,IACyB,CACzB,GAAI,CACF,IAAMC,EAASH,EAAQE,CAAO,EAC9B,GAAIC,aAAkB,QACpB,OAAOA,EAAO,MAAOC,GAAQP,EAAYO,EAAKL,EAAOC,CAAO,CAAC,CAEjE,OAASI,EAAK,CACZP,EAAYO,EAAKL,EAAOC,CAAO,CACjC,CACF,EAEMK,EAAoB,CAAwBN,EAAUO,IAAwB,CAClF,GAAIf,EAAe,GAAKe,EAAQf,GAAgB,CAACG,EAAO,IAAIK,CAAK,EAAG,CAClEL,EAAO,IAAIK,CAAK,EAEhB,IAAMQ,EAAU,yBAAyBD,CAAK,oBAAoBf,CAAY,IAC9EJ,EAAO,WAAWY,EAAOQ,EAAS,CAAE,MAAAD,EAAO,IAAKf,CAAa,CAAC,EAE9DC,EAAuBO,EAAOO,EAAOf,CAAY,CACnD,CACF,EAMMiB,EAA4B,CAChC,GAAGT,EAAOC,EAAS,CACjB,IAAMS,EAAMd,EAAII,CAAK,EACrB,OAAAU,EAAI,IAAIT,CAAgC,EACxCK,EAAkBN,EAAOU,EAAI,IAAI,EAC1B,IAAMD,EAAQ,IAAIT,EAAOC,CAAO,CACzC,EAEA,IAAID,EAAOC,EAAS,CAClB,IAAMS,EAAMd,EAAII,CAAK,EAGrB,GAAIU,EAAI,OAAOT,CAAgC,EAAG,CAC5CS,EAAI,MAAQlB,GACdG,EAAO,OAAOK,CAAK,EAErB,MACF,CAGA,QAAWW,KAAWD,EAEpB,GADUC,EACJ,aAAeV,EAAS,CAC5BS,EAAI,OAAOC,CAAO,EACdD,EAAI,MAAQlB,GACdG,EAAO,OAAOK,CAAK,EAErB,KACF,CAEJ,EAEA,KAAKA,EAAOC,EAAS,CACnB,IAAMW,GAAYT,IAChBM,EAAQ,IAAIT,EAAOY,CAAwC,EACpDX,EAAQE,CAAO,IAIxB,OAAAS,EAAQ,WAAaX,EAEdQ,EAAQ,GAAGT,EAAOY,CAAO,CAClC,EAEA,KAAKZ,KAAUG,EAAS,CACtB,IAAMU,EAAOV,EAAQ,CAAC,EAEtBf,EAAO,SAASY,EAAOa,CAAI,EAE3BjB,EAAII,CAAK,EAAE,QAASc,GAAMZ,EAASY,EAAGd,EAAOa,CAAI,CAAC,EAClDtB,EAAU,QAASuB,GAAM,CACvB,GAAI,CACFA,EAAEd,EAAOa,CAAuB,CAClC,OAASR,EAAK,CACZP,EAAYO,EAAKL,EAAOc,CAAqC,CAC/D,CACF,CAAC,CACH,EAEA,MAAM,UAAUd,KAAUG,EAAS,CACjC,IAAMU,EAAOV,EAAQ,CAAC,EAChBY,EAA4B,CAAC,EAEnC3B,EAAO,SAASY,EAAOa,CAAI,EAE3BjB,EAAII,CAAK,EAAE,QAASc,GAAM,CACxB,IAAMV,EAASF,EAASY,EAAGd,EAAOa,CAAI,EAClCT,aAAkB,SAASW,EAAS,KAAKX,CAAM,CACrD,CAAC,EAEDb,EAAU,QAASuB,GAAM,CACvB,GAAI,CACF,IAAMV,EAASU,EAAEd,EAAOa,CAAuB,EAC3CT,aAAkB,SACpBW,EAAS,KAAKX,EAAO,MAAOC,GAC1BP,EAAYO,EAAKL,EAAOc,CAAqC,CAC/D,CAAC,CAEL,OAAST,EAAK,CACZP,EAAYO,EAAKL,EAAOc,CAAqC,CAC/D,CACF,CAAC,EAED,MAAM,QAAQ,IAAIC,CAAQ,CAC5B,EAEA,MAAMd,EAAS,CACb,OAAAV,EAAU,IAAIU,CAAO,EACd,IAAMQ,EAAQ,OAAOR,CAAO,CACrC,EAEA,OAAOA,EAAS,CACdV,EAAU,OAAOU,CAAO,CAC1B,EAEA,MAAMD,EAAQ,CACRA,GACFV,EAAS,OAAOU,CAAK,EACrBL,EAAO,OAAOK,CAAK,IAEnBV,EAAS,MAAM,EACfC,EAAU,MAAM,EAChBI,EAAO,MAAM,EAEjB,EAEA,cAAcK,EAAO,CACnB,OAAOJ,EAAII,CAAK,EAAE,IACpB,EAEA,gBAAgBC,EAAS,CACvBP,EAAeO,CACjB,EAEA,gBAAgBe,EAAG,CACjBxB,EAAewB,EACXA,IAAM,GAAGrB,EAAO,MAAM,CAC5B,EAEA,iBAAkB,CAChB,OAAOH,CACT,EAEA,YAAa,CACX,OAAO,MAAM,KAAKF,EAAS,KAAK,CAAC,EAAE,OAChC2B,GAAM3B,EAAS,IAAI2B,CAAC,EAAG,KAAO,CACjC,CACF,EAEA,UAAUjB,EAAO,CACf,IAAMU,EAAMd,EAAII,CAAK,EAErB,OAAO,MAAM,KAAKU,CAAG,EAAE,IAAIT,GACTA,EACA,YAAcA,CAC/B,CACH,EAEA,mBAAoB,CAClB,OAAO,MAAM,KAAKV,CAAS,CAC7B,EAEA,YAAYS,EAAOC,EAAS,CAC1B,IAAMS,EAAMd,EAAII,CAAK,EAGrB,GAAIU,EAAI,IAAIT,CAAgC,EAC1C,MAAO,GAIT,QAAWU,KAAWD,EAEpB,GADUC,EACJ,aAAeV,EACnB,MAAO,GAIX,MAAO,EACT,CACF,EAEA,OAAOQ,CACT","names":["isNode","isBrowser","formatTimestamp","format","timezone","date","parseSize","size","match","value","unit","multipliers","Logger","options","req","dir","err","entry","timestamp","obj","parts","content","line","rotationNum","event","payload","formatted","error","_handler","message","details","CONFIG_FILENAME","cachedConfig","loadConfigFileSync","proc","req","fs","configPath","content","deepMerge","base","override","result","key","mergeConfig","options","fileConfig","clearConfigCache","defaultMaxListenersHandler","_event","_count","_max","createEmitochondria","options","config","mergeConfig","logger","Logger","handlers","wildcards","maxListeners","onMaxListenersExceeded","errorHandler","warned","get","e","handleError","error","event","handler","safeCall","payload","result","err","checkMaxListeners","count","message","emitter","set","wrapped","wrapper","data","h","promises","n","k"]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emitochondria",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "The powerhouse of your events — A tiny, fully-typed event emitter for TypeScript",
|
|
5
|
+
"description": "The powerhouse of your events — A tiny, fully-typed event emitter for TypeScript with built-in error handling and memory leak detection",
|
|
6
6
|
"author": "Pablo Díaz A.K.A exudev",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"keywords": [
|
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
"pubsub",
|
|
15
15
|
"pub-sub",
|
|
16
16
|
"subscribe",
|
|
17
|
-
"publish"
|
|
17
|
+
"publish",
|
|
18
|
+
"error-handling",
|
|
19
|
+
"memory-leak-detection",
|
|
20
|
+
"async",
|
|
21
|
+
"type-safe"
|
|
18
22
|
],
|
|
19
23
|
"repository": {
|
|
20
24
|
"type": "git",
|
|
@@ -33,10 +37,12 @@
|
|
|
33
37
|
"types": "./dist/index.d.ts",
|
|
34
38
|
"default": "./dist/index.js"
|
|
35
39
|
}
|
|
36
|
-
}
|
|
40
|
+
},
|
|
41
|
+
"./schema.json": "./schema.json"
|
|
37
42
|
},
|
|
38
43
|
"files": [
|
|
39
|
-
"dist"
|
|
44
|
+
"dist",
|
|
45
|
+
"schema.json"
|
|
40
46
|
],
|
|
41
47
|
"sideEffects": false,
|
|
42
48
|
"scripts": {
|
|
@@ -47,9 +53,9 @@
|
|
|
47
53
|
"prepublishOnly": "npm run test:run && npm run build"
|
|
48
54
|
},
|
|
49
55
|
"devDependencies": {
|
|
50
|
-
"@types/node": "^
|
|
51
|
-
"tsup": "^8.
|
|
52
|
-
"typescript": "^5.
|
|
53
|
-
"vitest": "^
|
|
56
|
+
"@types/node": "^25.0.3",
|
|
57
|
+
"tsup": "^8.5.1",
|
|
58
|
+
"typescript": "^5.9.3",
|
|
59
|
+
"vitest": "^4.0.16"
|
|
54
60
|
}
|
|
55
61
|
}
|
package/schema.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Emitochondria Configuration",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"maxListeners": {
|
|
7
|
+
"type": "number",
|
|
8
|
+
"default": 10,
|
|
9
|
+
"description": "Maximum listeners per event before warning. Set to 0 to disable."
|
|
10
|
+
},
|
|
11
|
+
"onError": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"enum": ["throw"],
|
|
14
|
+
"description": "Set to 'throw' to re-throw errors instead of catching them."
|
|
15
|
+
},
|
|
16
|
+
"logging": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"timestamps": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"default": false,
|
|
22
|
+
"description": "Enable timestamps in logs."
|
|
23
|
+
},
|
|
24
|
+
"timestampFormat": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["iso", "unix", "human"],
|
|
27
|
+
"default": "iso",
|
|
28
|
+
"description": "Format for timestamps."
|
|
29
|
+
},
|
|
30
|
+
"timezone": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": ["utc", "local"],
|
|
33
|
+
"default": "utc",
|
|
34
|
+
"description": "Timezone for timestamps."
|
|
35
|
+
},
|
|
36
|
+
"persist": {
|
|
37
|
+
"type": "boolean",
|
|
38
|
+
"default": false,
|
|
39
|
+
"description": "Enable file logging. Only works in Node.js."
|
|
40
|
+
},
|
|
41
|
+
"path": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"default": "./logs/emitochondria.log",
|
|
44
|
+
"description": "Path to log file."
|
|
45
|
+
},
|
|
46
|
+
"format": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"enum": ["text", "json"],
|
|
49
|
+
"default": "text",
|
|
50
|
+
"description": "Log file format."
|
|
51
|
+
},
|
|
52
|
+
"maxSize": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"default": "10MB",
|
|
55
|
+
"description": "Maximum log file size before rotation. E.g., '10MB', '1GB'."
|
|
56
|
+
},
|
|
57
|
+
"logEvents": {
|
|
58
|
+
"type": "boolean",
|
|
59
|
+
"default": false,
|
|
60
|
+
"description": "Log every emitted event. Can be verbose!"
|
|
61
|
+
},
|
|
62
|
+
"logErrors": {
|
|
63
|
+
"type": "boolean",
|
|
64
|
+
"default": true,
|
|
65
|
+
"description": "Log handler errors."
|
|
66
|
+
},
|
|
67
|
+
"logWarnings": {
|
|
68
|
+
"type": "boolean",
|
|
69
|
+
"default": true,
|
|
70
|
+
"description": "Log memory leak warnings."
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
package/dist/index.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
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
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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"]}
|