emitochondria 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -30
- package/bin/emitochondria.cjs +4 -0
- package/dist/cli/index.cjs +258 -0
- package/dist/index.d.cts +110 -89
- package/dist/index.d.ts +110 -89
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -3
- package/schema.json +75 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://www.typescriptlang.org/)
|
|
10
10
|
[](https://bundlephobia.com/package/emitochondria)
|
|
11
11
|
|
|
12
|
-
A tiny, fully-typed event emitter for TypeScript with built-in error handling
|
|
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
|
|
|
@@ -256,35 +256,6 @@ const events = createEmitochondria<MyEvents>({
|
|
|
256
256
|
});
|
|
257
257
|
```
|
|
258
258
|
|
|
259
|
-
## ⚡ Biological API (Alternative Naming)
|
|
260
|
-
|
|
261
|
-
Emitochondria offers biologically-themed aliases for all methods. Use whichever style fits your project:
|
|
262
|
-
|
|
263
|
-
| Standard API | Biological Alias | Description |
|
|
264
|
-
|--------------|------------------|-------------|
|
|
265
|
-
| `.on()` | `.bind()` | Bind a receptor to a signal |
|
|
266
|
-
| `.off()` | `.release()` | Release a receptor |
|
|
267
|
-
| `.emit()` | `.pulse()` | Pulse energy through the system |
|
|
268
|
-
| `.emitAsync()` | `.cascade()` | Trigger a signal cascade |
|
|
269
|
-
| `.once()` | `.spike()` | Single spike of energy |
|
|
270
|
-
| `.onAny()` | `.membrane()` | Membrane catches all signals |
|
|
271
|
-
| `.clear()` | `.apoptosis()` | Programmed cell death |
|
|
272
|
-
| `.listenerCount()` | `.receptors()` | Count of receptors |
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
// Standard API
|
|
276
|
-
events.on('user:login', handler);
|
|
277
|
-
events.emit('user:login', data);
|
|
278
|
-
|
|
279
|
-
// Biological API - same functionality, different style
|
|
280
|
-
events.bind('user:login', handler);
|
|
281
|
-
events.pulse('user:login', data);
|
|
282
|
-
|
|
283
|
-
// Mix and match freely
|
|
284
|
-
events.bind('save', async (data) => await saveToDatabase(data));
|
|
285
|
-
await events.cascade('save', { id: '123' });
|
|
286
|
-
```
|
|
287
|
-
|
|
288
259
|
## TypeScript Magic
|
|
289
260
|
|
|
290
261
|
The type system prevents mistakes at compile time:
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
|
|
29
|
+
// package.json
|
|
30
|
+
var require_package = __commonJS({
|
|
31
|
+
"package.json"(exports2, module2) {
|
|
32
|
+
module2.exports = {
|
|
33
|
+
name: "emitochondria",
|
|
34
|
+
version: "1.2.1",
|
|
35
|
+
type: "module",
|
|
36
|
+
description: "The powerhouse of your events \u2014 A tiny, fully-typed event emitter for TypeScript with built-in error handling and memory leak detection",
|
|
37
|
+
author: "Pablo D\xEDaz A.K.A exudev",
|
|
38
|
+
license: "MIT",
|
|
39
|
+
bin: {
|
|
40
|
+
emitochondria: "./bin/emitochondria.cjs"
|
|
41
|
+
},
|
|
42
|
+
keywords: [
|
|
43
|
+
"events",
|
|
44
|
+
"emitter",
|
|
45
|
+
"event-emitter",
|
|
46
|
+
"typescript",
|
|
47
|
+
"typed",
|
|
48
|
+
"pubsub",
|
|
49
|
+
"pub-sub",
|
|
50
|
+
"subscribe",
|
|
51
|
+
"publish",
|
|
52
|
+
"error-handling",
|
|
53
|
+
"memory-leak-detection",
|
|
54
|
+
"async",
|
|
55
|
+
"type-safe"
|
|
56
|
+
],
|
|
57
|
+
repository: {
|
|
58
|
+
type: "git",
|
|
59
|
+
url: "https://github.com/Exudev/emitochondria.git"
|
|
60
|
+
},
|
|
61
|
+
main: "./dist/index.js",
|
|
62
|
+
module: "./dist/index.mjs",
|
|
63
|
+
types: "./dist/index.d.ts",
|
|
64
|
+
exports: {
|
|
65
|
+
".": {
|
|
66
|
+
import: {
|
|
67
|
+
types: "./dist/index.d.mts",
|
|
68
|
+
default: "./dist/index.mjs"
|
|
69
|
+
},
|
|
70
|
+
require: {
|
|
71
|
+
types: "./dist/index.d.ts",
|
|
72
|
+
default: "./dist/index.js"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"./schema.json": "./schema.json"
|
|
76
|
+
},
|
|
77
|
+
files: [
|
|
78
|
+
"dist",
|
|
79
|
+
"bin",
|
|
80
|
+
"schema.json"
|
|
81
|
+
],
|
|
82
|
+
sideEffects: false,
|
|
83
|
+
scripts: {
|
|
84
|
+
build: "tsup",
|
|
85
|
+
test: "vitest",
|
|
86
|
+
"test:run": "vitest run",
|
|
87
|
+
"test:coverage": "vitest run --coverage",
|
|
88
|
+
prepublishOnly: "npm run test:run && npm run build"
|
|
89
|
+
},
|
|
90
|
+
devDependencies: {
|
|
91
|
+
"@types/node": "^25.0.3",
|
|
92
|
+
tsup: "^8.5.1",
|
|
93
|
+
typescript: "^5.9.3",
|
|
94
|
+
vitest: "^4.0.16"
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// src/cli/index.ts
|
|
101
|
+
var fs = __toESM(require("fs"), 1);
|
|
102
|
+
var path = __toESM(require("path"), 1);
|
|
103
|
+
var readline = __toESM(require("readline"), 1);
|
|
104
|
+
var CONFIG_FILENAME = "emitochondria.config.json";
|
|
105
|
+
var SCHEMA_URL = "https://unpkg.com/emitochondria/schema.json";
|
|
106
|
+
var DEFAULT_CONFIG = {
|
|
107
|
+
$schema: SCHEMA_URL,
|
|
108
|
+
maxListeners: 10,
|
|
109
|
+
logging: {
|
|
110
|
+
timestamps: false,
|
|
111
|
+
timestampFormat: "iso",
|
|
112
|
+
timezone: "utc",
|
|
113
|
+
persist: false,
|
|
114
|
+
path: "./logs/emitochondria.log",
|
|
115
|
+
format: "text",
|
|
116
|
+
maxSize: "10MB",
|
|
117
|
+
logEvents: false,
|
|
118
|
+
logErrors: true,
|
|
119
|
+
logWarnings: true
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var c = {
|
|
123
|
+
reset: "\x1B[0m",
|
|
124
|
+
bold: "\x1B[1m",
|
|
125
|
+
green: "\x1B[32m",
|
|
126
|
+
yellow: "\x1B[33m",
|
|
127
|
+
blue: "\x1B[34m",
|
|
128
|
+
cyan: "\x1B[36m",
|
|
129
|
+
red: "\x1B[31m",
|
|
130
|
+
dim: "\x1B[2m"
|
|
131
|
+
};
|
|
132
|
+
function log(icon, color, msg) {
|
|
133
|
+
console.log(`${color}${icon}${c.reset} ${msg}`);
|
|
134
|
+
}
|
|
135
|
+
var info = (msg) => log("\u2139", c.blue, msg);
|
|
136
|
+
var success = (msg) => log("\u2713", c.green, msg);
|
|
137
|
+
var warn = (msg) => log("\u26A0 ", c.yellow, msg);
|
|
138
|
+
var error = (msg) => log("\u2717", c.red, msg);
|
|
139
|
+
function prompt(question) {
|
|
140
|
+
const rl = readline.createInterface({
|
|
141
|
+
input: process.stdin,
|
|
142
|
+
output: process.stdout
|
|
143
|
+
});
|
|
144
|
+
return new Promise((resolve) => {
|
|
145
|
+
rl.question(question, (answer) => {
|
|
146
|
+
rl.close();
|
|
147
|
+
resolve(answer.trim().toLowerCase());
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function printHelp() {
|
|
152
|
+
console.log(`
|
|
153
|
+
${c.bold}\u{1F9EC} Emitochondria CLI${c.reset}
|
|
154
|
+
${c.dim}The powerhouse of your events${c.reset}
|
|
155
|
+
|
|
156
|
+
${c.bold}Usage:${c.reset}
|
|
157
|
+
npx emitochondria <command>
|
|
158
|
+
|
|
159
|
+
${c.bold}Commands:${c.reset}
|
|
160
|
+
init Create emitochondria.config.json
|
|
161
|
+
init --force Overwrite existing config file
|
|
162
|
+
help Show this help message
|
|
163
|
+
|
|
164
|
+
${c.bold}Examples:${c.reset}
|
|
165
|
+
${c.dim}# Create config file${c.reset}
|
|
166
|
+
npx emitochondria init
|
|
167
|
+
|
|
168
|
+
${c.dim}# Overwrite existing config${c.reset}
|
|
169
|
+
npx emitochondria init --force
|
|
170
|
+
|
|
171
|
+
${c.bold}Documentation:${c.reset}
|
|
172
|
+
https://github.com/Exudev/emitochondria
|
|
173
|
+
`);
|
|
174
|
+
}
|
|
175
|
+
function printVersion() {
|
|
176
|
+
try {
|
|
177
|
+
const pkg = require_package();
|
|
178
|
+
console.log(`v${pkg.version}`);
|
|
179
|
+
} catch {
|
|
180
|
+
console.log("unknown");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function initCommand(force) {
|
|
184
|
+
const configPath = path.join(process.cwd(), CONFIG_FILENAME);
|
|
185
|
+
console.log(`
|
|
186
|
+
${c.bold}\u{1F9EC} Emitochondria${c.reset}
|
|
187
|
+
`);
|
|
188
|
+
if (fs.existsSync(configPath)) {
|
|
189
|
+
if (!force) {
|
|
190
|
+
warn(`${CONFIG_FILENAME} already exists.`);
|
|
191
|
+
const answer = await prompt(` Overwrite? ${c.dim}(y/N)${c.reset} `);
|
|
192
|
+
if (answer !== "y" && answer !== "yes") {
|
|
193
|
+
info("Aborted. No changes made.");
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
warn("Overwriting existing config file.");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const content = JSON.stringify(DEFAULT_CONFIG, null, 2);
|
|
202
|
+
fs.writeFileSync(configPath, content + "\n", "utf8");
|
|
203
|
+
success(`Created ${c.bold}${CONFIG_FILENAME}${c.reset}`);
|
|
204
|
+
console.log(`
|
|
205
|
+
${c.dim} Location: ${configPath}${c.reset}`);
|
|
206
|
+
console.log(`
|
|
207
|
+
${c.bold}Next steps:${c.reset}
|
|
208
|
+
|
|
209
|
+
1. Edit the config file to match your needs
|
|
210
|
+
2. Import and use emitochondria:
|
|
211
|
+
|
|
212
|
+
${c.cyan}import { createEmitochondria } from 'emitochondria';${c.reset}
|
|
213
|
+
|
|
214
|
+
${c.cyan}type MyEvents = {${c.reset}
|
|
215
|
+
${c.cyan}'user:login': { userId: string };${c.reset}
|
|
216
|
+
${c.cyan}};${c.reset}
|
|
217
|
+
|
|
218
|
+
${c.cyan}const events = createEmitochondria<MyEvents>();${c.reset}
|
|
219
|
+
|
|
220
|
+
${c.dim}\u{1F4D6} Docs: https://github.com/Exudev/emitochondria${c.reset}
|
|
221
|
+
`);
|
|
222
|
+
} catch (err) {
|
|
223
|
+
error(`Failed to create config file: ${err}`);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function main() {
|
|
228
|
+
const args = process.argv.slice(2);
|
|
229
|
+
const command = args[0];
|
|
230
|
+
const flags = args.slice(1);
|
|
231
|
+
const hasForce = flags.includes("--force") || flags.includes("-f");
|
|
232
|
+
switch (command) {
|
|
233
|
+
case "init":
|
|
234
|
+
await initCommand(hasForce);
|
|
235
|
+
break;
|
|
236
|
+
case "help":
|
|
237
|
+
case "--help":
|
|
238
|
+
case "-h":
|
|
239
|
+
case void 0:
|
|
240
|
+
printHelp();
|
|
241
|
+
break;
|
|
242
|
+
case "version":
|
|
243
|
+
case "--version":
|
|
244
|
+
case "-v":
|
|
245
|
+
printVersion();
|
|
246
|
+
break;
|
|
247
|
+
default:
|
|
248
|
+
error(`Unknown command: ${command}`);
|
|
249
|
+
console.log(`
|
|
250
|
+
Run ${c.cyan}npx emitochondria help${c.reset} for usage.
|
|
251
|
+
`);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
main().catch((err) => {
|
|
256
|
+
error(`Unexpected error: ${err}`);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,34 +1,97 @@
|
|
|
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
|
-
|
|
23
|
-
|
|
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;
|
|
24
58
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* @default
|
|
28
|
-
* @example 'throw' - Preserve original throwing behavior
|
|
29
|
-
* @example (error, event) => logger.error(error)
|
|
59
|
+
* Log every emitted event.
|
|
60
|
+
* ⚠️ Can be very verbose in production!
|
|
61
|
+
* @default false
|
|
30
62
|
*/
|
|
31
|
-
|
|
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> {
|
|
32
95
|
/**
|
|
33
96
|
* Maximum listeners per event before warning.
|
|
34
97
|
* Set to 0 to disable warnings.
|
|
@@ -36,88 +99,46 @@ interface EmitochondriaOptions {
|
|
|
36
99
|
*/
|
|
37
100
|
maxListeners?: number;
|
|
38
101
|
/**
|
|
39
|
-
* Custom handler
|
|
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.
|
|
40
110
|
* If not provided, logs a warning to console.
|
|
41
111
|
*/
|
|
42
|
-
onMaxListenersExceeded?:
|
|
112
|
+
onMaxListenersExceeded?: MaxListenersHandler<T>;
|
|
113
|
+
/**
|
|
114
|
+
* Logging configuration.
|
|
115
|
+
*/
|
|
116
|
+
logging?: LoggingOptions;
|
|
43
117
|
}
|
|
44
|
-
/**
|
|
45
|
-
* The typed emitter interface.
|
|
46
|
-
*/
|
|
47
118
|
interface Emitochondria<T extends EventMap> {
|
|
48
|
-
/** Subscribe to an event. Returns unsubscribe function. */
|
|
49
119
|
on<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
|
|
50
|
-
/** Unsubscribe a handler from an event. */
|
|
51
120
|
off<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): void;
|
|
52
|
-
/** Subscribe for a single emission only. */
|
|
53
121
|
once<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
|
|
54
|
-
/** Emit an event synchronously. */
|
|
55
122
|
emit<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): void;
|
|
56
|
-
/** Emit an event and await all handlers. */
|
|
57
123
|
emitAsync<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): Promise<void>;
|
|
58
|
-
/** Subscribe to all events (wildcard). */
|
|
59
124
|
onAny(handler: WildcardHandler<T>): () => void;
|
|
60
|
-
/** Unsubscribe a wildcard handler. */
|
|
61
125
|
offAny(handler: WildcardHandler<T>): void;
|
|
62
|
-
/** Clear handlers for a specific event or all events. */
|
|
63
126
|
clear<K extends EventKey<T>>(event?: K): void;
|
|
64
|
-
/** Get the number of listeners for an event. */
|
|
65
127
|
listenerCount<K extends EventKey<T>>(event: K): number;
|
|
66
|
-
|
|
67
|
-
setErrorHandler(handler: EmitochondriaOptions['onError']): void;
|
|
68
|
-
/** Set the maximum number of listeners per event before warning. */
|
|
128
|
+
setErrorHandler(handler: ErrorHandler<T> | 'throw'): void;
|
|
69
129
|
setMaxListeners(n: number): void;
|
|
70
|
-
/** Get the current max listener limit. */
|
|
71
130
|
getMaxListeners(): number;
|
|
72
|
-
/** Get all event names that currently have registered listeners. */
|
|
73
131
|
eventNames(): EventKey<T>[];
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/** Get all wildcard handlers. */
|
|
77
|
-
wildcardListeners(): ReadonlyArray<WildcardHandler<T>>;
|
|
78
|
-
/** Check if a specific handler is registered for an event. */
|
|
132
|
+
listeners<K extends EventKey<T>>(event: K): EventHandler<T[K]>[];
|
|
133
|
+
wildcardListeners(): WildcardHandler<T>[];
|
|
79
134
|
hasListener<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): boolean;
|
|
80
|
-
/** Alias for `on` — Bind a receptor to a signal. */
|
|
81
|
-
bind: Emitochondria<T>['on'];
|
|
82
|
-
/** Alias for `off` — Release a receptor. */
|
|
83
|
-
release: Emitochondria<T>['off'];
|
|
84
|
-
/** Alias for `emit` — Pulse energy through the system. */
|
|
85
|
-
pulse: Emitochondria<T>['emit'];
|
|
86
|
-
/** Alias for `emitAsync` — Trigger a signal cascade. */
|
|
87
|
-
cascade: Emitochondria<T>['emitAsync'];
|
|
88
|
-
/** Alias for `once` — Single spike of energy. */
|
|
89
|
-
spike: Emitochondria<T>['once'];
|
|
90
|
-
/** Alias for `onAny` — Membrane catches all signals. */
|
|
91
|
-
membrane: Emitochondria<T>['onAny'];
|
|
92
|
-
/** Alias for `clear` — Programmed cell death. */
|
|
93
|
-
apoptosis: Emitochondria<T>['clear'];
|
|
94
|
-
/** Alias for `listenerCount` — Count of receptors. */
|
|
95
|
-
receptors: Emitochondria<T>['listenerCount'];
|
|
96
135
|
}
|
|
136
|
+
|
|
137
|
+
declare function createEmitochondria<T extends EventMap>(options?: EmitochondriaOptions<T>): Emitochondria<T>;
|
|
138
|
+
|
|
97
139
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* ```typescript
|
|
102
|
-
* type MyEvents = {
|
|
103
|
-
* 'user:login': { userId: string };
|
|
104
|
-
* 'app:ready': void;
|
|
105
|
-
* };
|
|
106
|
-
*
|
|
107
|
-
* const mito = createEmitochondria<MyEvents>();
|
|
108
|
-
*
|
|
109
|
-
* // Standard API
|
|
110
|
-
* mito.on('user:login', (data) => {
|
|
111
|
-
* console.log(data.userId); // fully typed!
|
|
112
|
-
* });
|
|
113
|
-
* mito.emit('user:login', { userId: '123' });
|
|
114
|
-
* mito.emit('app:ready');
|
|
115
|
-
*
|
|
116
|
-
* // Biological API
|
|
117
|
-
* mito.bind('user:login', (data) => console.log(data.userId));
|
|
118
|
-
* mito.pulse('user:login', { userId: '123' });
|
|
119
|
-
* ```
|
|
140
|
+
* Clear cached config (useful for testing).
|
|
120
141
|
*/
|
|
121
|
-
declare function
|
|
142
|
+
declare function clearConfigCache(): void;
|
|
122
143
|
|
|
123
|
-
export { type Emitochondria, type EmitochondriaOptions, 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,34 +1,97 @@
|
|
|
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
|
-
|
|
23
|
-
|
|
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;
|
|
24
58
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* @default
|
|
28
|
-
* @example 'throw' - Preserve original throwing behavior
|
|
29
|
-
* @example (error, event) => logger.error(error)
|
|
59
|
+
* Log every emitted event.
|
|
60
|
+
* ⚠️ Can be very verbose in production!
|
|
61
|
+
* @default false
|
|
30
62
|
*/
|
|
31
|
-
|
|
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> {
|
|
32
95
|
/**
|
|
33
96
|
* Maximum listeners per event before warning.
|
|
34
97
|
* Set to 0 to disable warnings.
|
|
@@ -36,88 +99,46 @@ interface EmitochondriaOptions {
|
|
|
36
99
|
*/
|
|
37
100
|
maxListeners?: number;
|
|
38
101
|
/**
|
|
39
|
-
* Custom handler
|
|
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.
|
|
40
110
|
* If not provided, logs a warning to console.
|
|
41
111
|
*/
|
|
42
|
-
onMaxListenersExceeded?:
|
|
112
|
+
onMaxListenersExceeded?: MaxListenersHandler<T>;
|
|
113
|
+
/**
|
|
114
|
+
* Logging configuration.
|
|
115
|
+
*/
|
|
116
|
+
logging?: LoggingOptions;
|
|
43
117
|
}
|
|
44
|
-
/**
|
|
45
|
-
* The typed emitter interface.
|
|
46
|
-
*/
|
|
47
118
|
interface Emitochondria<T extends EventMap> {
|
|
48
|
-
/** Subscribe to an event. Returns unsubscribe function. */
|
|
49
119
|
on<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
|
|
50
|
-
/** Unsubscribe a handler from an event. */
|
|
51
120
|
off<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): void;
|
|
52
|
-
/** Subscribe for a single emission only. */
|
|
53
121
|
once<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): () => void;
|
|
54
|
-
/** Emit an event synchronously. */
|
|
55
122
|
emit<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): void;
|
|
56
|
-
/** Emit an event and await all handlers. */
|
|
57
123
|
emitAsync<K extends EventKey<T>>(event: K, ...payload: T[K] extends void ? [] : [T[K]]): Promise<void>;
|
|
58
|
-
/** Subscribe to all events (wildcard). */
|
|
59
124
|
onAny(handler: WildcardHandler<T>): () => void;
|
|
60
|
-
/** Unsubscribe a wildcard handler. */
|
|
61
125
|
offAny(handler: WildcardHandler<T>): void;
|
|
62
|
-
/** Clear handlers for a specific event or all events. */
|
|
63
126
|
clear<K extends EventKey<T>>(event?: K): void;
|
|
64
|
-
/** Get the number of listeners for an event. */
|
|
65
127
|
listenerCount<K extends EventKey<T>>(event: K): number;
|
|
66
|
-
|
|
67
|
-
setErrorHandler(handler: EmitochondriaOptions['onError']): void;
|
|
68
|
-
/** Set the maximum number of listeners per event before warning. */
|
|
128
|
+
setErrorHandler(handler: ErrorHandler<T> | 'throw'): void;
|
|
69
129
|
setMaxListeners(n: number): void;
|
|
70
|
-
/** Get the current max listener limit. */
|
|
71
130
|
getMaxListeners(): number;
|
|
72
|
-
/** Get all event names that currently have registered listeners. */
|
|
73
131
|
eventNames(): EventKey<T>[];
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/** Get all wildcard handlers. */
|
|
77
|
-
wildcardListeners(): ReadonlyArray<WildcardHandler<T>>;
|
|
78
|
-
/** Check if a specific handler is registered for an event. */
|
|
132
|
+
listeners<K extends EventKey<T>>(event: K): EventHandler<T[K]>[];
|
|
133
|
+
wildcardListeners(): WildcardHandler<T>[];
|
|
79
134
|
hasListener<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): boolean;
|
|
80
|
-
/** Alias for `on` — Bind a receptor to a signal. */
|
|
81
|
-
bind: Emitochondria<T>['on'];
|
|
82
|
-
/** Alias for `off` — Release a receptor. */
|
|
83
|
-
release: Emitochondria<T>['off'];
|
|
84
|
-
/** Alias for `emit` — Pulse energy through the system. */
|
|
85
|
-
pulse: Emitochondria<T>['emit'];
|
|
86
|
-
/** Alias for `emitAsync` — Trigger a signal cascade. */
|
|
87
|
-
cascade: Emitochondria<T>['emitAsync'];
|
|
88
|
-
/** Alias for `once` — Single spike of energy. */
|
|
89
|
-
spike: Emitochondria<T>['once'];
|
|
90
|
-
/** Alias for `onAny` — Membrane catches all signals. */
|
|
91
|
-
membrane: Emitochondria<T>['onAny'];
|
|
92
|
-
/** Alias for `clear` — Programmed cell death. */
|
|
93
|
-
apoptosis: Emitochondria<T>['clear'];
|
|
94
|
-
/** Alias for `listenerCount` — Count of receptors. */
|
|
95
|
-
receptors: Emitochondria<T>['listenerCount'];
|
|
96
135
|
}
|
|
136
|
+
|
|
137
|
+
declare function createEmitochondria<T extends EventMap>(options?: EmitochondriaOptions<T>): Emitochondria<T>;
|
|
138
|
+
|
|
97
139
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* ```typescript
|
|
102
|
-
* type MyEvents = {
|
|
103
|
-
* 'user:login': { userId: string };
|
|
104
|
-
* 'app:ready': void;
|
|
105
|
-
* };
|
|
106
|
-
*
|
|
107
|
-
* const mito = createEmitochondria<MyEvents>();
|
|
108
|
-
*
|
|
109
|
-
* // Standard API
|
|
110
|
-
* mito.on('user:login', (data) => {
|
|
111
|
-
* console.log(data.userId); // fully typed!
|
|
112
|
-
* });
|
|
113
|
-
* mito.emit('user:login', { userId: '123' });
|
|
114
|
-
* mito.emit('app:ready');
|
|
115
|
-
*
|
|
116
|
-
* // Biological API
|
|
117
|
-
* mito.bind('user:login', (data) => console.log(data.userId));
|
|
118
|
-
* mito.pulse('user:login', { userId: '123' });
|
|
119
|
-
* ```
|
|
140
|
+
* Clear cached config (useful for testing).
|
|
120
141
|
*/
|
|
121
|
-
declare function
|
|
142
|
+
declare function clearConfigCache(): void;
|
|
122
143
|
|
|
123
|
-
export { type Emitochondria, type EmitochondriaOptions, 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
|
-
"use strict";var
|
|
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 * Internal type for wrapped handlers (used by once()).\n * Stores reference to original handler for manual removal.\n * @internal\n */\ntype WrappedHandler<T> = EventHandler<T> & {\n __original?: EventHandler<T>;\n};\n\n/**\n * Options for creating an Emitochondria instance.\n */\nexport interface EmitochondriaOptions {\n /**\n * Custom error handler for errors thrown by event handlers.\n *\n * @default - Logs to console in development, silent in production\n * @example 'throw' - Preserve original throwing behavior\n * @example (error, event) => logger.error(error)\n */\n onError?:\n | ((error: Error, event: string, handler: EventHandler<unknown>) => void)\n | 'throw';\n\n /**\n * Maximum listeners per event before warning.\n * Set to 0 to disable warnings.\n * @default 10\n */\n maxListeners?: number;\n\n /**\n * Custom handler when max listeners is exceeded.\n * If not provided, logs a warning to console.\n */\n onMaxListenersExceeded?: (event: string, count: number, max: number) => void;\n}\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 /** Set a custom error handler at runtime. */\n setErrorHandler(handler: EmitochondriaOptions['onError']): void;\n /** Set the maximum number of listeners per event before warning. */\n setMaxListeners(n: number): void;\n /** Get the current max listener limit. */\n getMaxListeners(): number;\n /** Get all event names that currently have registered listeners. */\n eventNames(): EventKey<T>[];\n /** Get all handlers registered for a specific event. */\n listeners<K extends EventKey<T>>(event: K): ReadonlyArray<EventHandler<T[K]>>;\n /** Get all wildcard handlers. */\n wildcardListeners(): ReadonlyArray<WildcardHandler<T>>;\n /** Check if a specific handler is registered for an event. */\n hasListener<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): boolean;\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>(\n options: EmitochondriaOptions = {}\n): Emitochondria<T> {\n const handlers: Map<string, Set<WrappedHandler<unknown>>> = new Map();\n const wildcardHandlers: Set<WildcardHandler<T>> = new Set();\n\n // Default error handler\n let errorHandler: EmitochondriaOptions['onError'] = options.onError ?? ((error: Error, event: string) => {\n // Log in development, silent in production\n // Check for NODE_ENV in a way that works in both Node and browser\n const nodeEnv = (globalThis as any).process?.env?.NODE_ENV;\n if (nodeEnv !== 'production') {\n console.error(`[Emitochondria] Error in handler for event \"${event}\":`, error);\n }\n });\n\n // Max listeners configuration\n let maxListeners = options.maxListeners ?? 10;\n\n const onMaxListenersExceeded = options.onMaxListenersExceeded ?? ((event, count, max) => {\n console.warn(\n `[Emitochondria] Possible memory leak detected: ` +\n `${count} listeners added for event \"${event}\". ` +\n `Maximum is ${max}. Use setMaxListeners() to increase limit.`\n );\n });\n\n /**\n * Check if max listeners exceeded and warn if needed.\n */\n function checkMaxListeners(event: string, count: number): void {\n if (maxListeners > 0 && count > maxListeners) {\n onMaxListenersExceeded(event, count, maxListeners);\n }\n }\n\n /**\n * Safely execute a handler, catching errors according to configuration.\n */\n function 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\n // Handle async errors\n if (result instanceof Promise) {\n return result.catch((error) => {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error, event, handler);\n }\n });\n }\n\n return result;\n } catch (error) {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error as Error, event, handler);\n }\n }\n }\n\n function getHandlers(event: string): Set<WrappedHandler<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 const set = getHandlers(event);\n set.add(handler as EventHandler<unknown>);\n\n // Check for potential memory leak\n checkMaxListeners(event, set.size);\n\n return () => e.off(event, handler);\n },\n\n off(event, handler) {\n const set = handlers.get(event);\n if (!set) return;\n\n // Try direct removal first (for regular handlers)\n if (set.delete(handler as EventHandler<unknown>)) {\n // Clean up empty sets to prevent memory leak\n if (set.size === 0) {\n handlers.delete(event);\n }\n return;\n }\n\n // Search for wrapped handler (for once handlers)\n for (const wrapped of set) {\n if (wrapped.__original === handler) {\n set.delete(wrapped);\n if (set.size === 0) {\n handlers.delete(event);\n }\n break;\n }\n }\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 WrappedHandler<T[typeof event]>;\n\n // Store original reference for manual removal\n wrapper.__original = handler;\n\n return e.on(event, wrapper);\n },\n\n emit(event, ...payload) {\n const data = payload[0];\n getHandlers(event).forEach((handler) => {\n safeCall(handler, event, data);\n });\n wildcardHandlers.forEach((handler) => {\n // Wildcard handlers have different signature (event, payload)\n try {\n const result = handler(event, data as T[typeof event]);\n if (result instanceof Promise) {\n result.catch((error) => {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error, event, handler as EventHandler<unknown>);\n }\n });\n }\n } catch (error) {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error as Error, event, handler as EventHandler<unknown>);\n }\n }\n });\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 = safeCall(handler, event, data);\n if (result instanceof Promise) {\n promises.push(result);\n }\n });\n\n wildcardHandlers.forEach((handler) => {\n try {\n const result = handler(event, data as T[typeof event]);\n if (result instanceof Promise) {\n promises.push(\n result.catch((error) => {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error, event, handler as EventHandler<unknown>);\n }\n })\n );\n }\n } catch (error) {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error as Error, event, handler as EventHandler<unknown>);\n }\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 setErrorHandler(handler) {\n errorHandler = handler;\n },\n\n setMaxListeners(n) {\n maxListeners = n;\n },\n\n getMaxListeners() {\n return maxListeners;\n },\n\n eventNames() {\n return Array.from(handlers.keys()).filter(\n key => {\n const set = handlers.get(key);\n return set && set.size > 0;\n }\n ) as EventKey<T>[];\n },\n\n listeners(event) {\n const set = handlers.get(event);\n if (!set) return [];\n\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(wildcardHandlers);\n },\n\n hasListener(event, handler) {\n const set = handlers.get(event);\n if (!set) return false;\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 if (wrapped.__original === handler) {\n return true;\n }\n }\n\n return false;\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":"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","names":["index_exports","__export","createEmitochondria","index_default","__toCommonJS","options","handlers","wildcardHandlers","errorHandler","error","event","maxListeners","onMaxListenersExceeded","count","max","checkMaxListeners","safeCall","handler","payload","result","getHandlers","set","e","wrapped","wrapper","data","promises","n","key"]}
|
|
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
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
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};
|
|
2
3
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.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 * Internal type for wrapped handlers (used by once()).\n * Stores reference to original handler for manual removal.\n * @internal\n */\ntype WrappedHandler<T> = EventHandler<T> & {\n __original?: EventHandler<T>;\n};\n\n/**\n * Options for creating an Emitochondria instance.\n */\nexport interface EmitochondriaOptions {\n /**\n * Custom error handler for errors thrown by event handlers.\n *\n * @default - Logs to console in development, silent in production\n * @example 'throw' - Preserve original throwing behavior\n * @example (error, event) => logger.error(error)\n */\n onError?:\n | ((error: Error, event: string, handler: EventHandler<unknown>) => void)\n | 'throw';\n\n /**\n * Maximum listeners per event before warning.\n * Set to 0 to disable warnings.\n * @default 10\n */\n maxListeners?: number;\n\n /**\n * Custom handler when max listeners is exceeded.\n * If not provided, logs a warning to console.\n */\n onMaxListenersExceeded?: (event: string, count: number, max: number) => void;\n}\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 /** Set a custom error handler at runtime. */\n setErrorHandler(handler: EmitochondriaOptions['onError']): void;\n /** Set the maximum number of listeners per event before warning. */\n setMaxListeners(n: number): void;\n /** Get the current max listener limit. */\n getMaxListeners(): number;\n /** Get all event names that currently have registered listeners. */\n eventNames(): EventKey<T>[];\n /** Get all handlers registered for a specific event. */\n listeners<K extends EventKey<T>>(event: K): ReadonlyArray<EventHandler<T[K]>>;\n /** Get all wildcard handlers. */\n wildcardListeners(): ReadonlyArray<WildcardHandler<T>>;\n /** Check if a specific handler is registered for an event. */\n hasListener<K extends EventKey<T>>(event: K, handler: EventHandler<T[K]>): boolean;\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>(\n options: EmitochondriaOptions = {}\n): Emitochondria<T> {\n const handlers: Map<string, Set<WrappedHandler<unknown>>> = new Map();\n const wildcardHandlers: Set<WildcardHandler<T>> = new Set();\n\n // Default error handler\n let errorHandler: EmitochondriaOptions['onError'] = options.onError ?? ((error: Error, event: string) => {\n // Log in development, silent in production\n // Check for NODE_ENV in a way that works in both Node and browser\n const nodeEnv = (globalThis as any).process?.env?.NODE_ENV;\n if (nodeEnv !== 'production') {\n console.error(`[Emitochondria] Error in handler for event \"${event}\":`, error);\n }\n });\n\n // Max listeners configuration\n let maxListeners = options.maxListeners ?? 10;\n\n const onMaxListenersExceeded = options.onMaxListenersExceeded ?? ((event, count, max) => {\n console.warn(\n `[Emitochondria] Possible memory leak detected: ` +\n `${count} listeners added for event \"${event}\". ` +\n `Maximum is ${max}. Use setMaxListeners() to increase limit.`\n );\n });\n\n /**\n * Check if max listeners exceeded and warn if needed.\n */\n function checkMaxListeners(event: string, count: number): void {\n if (maxListeners > 0 && count > maxListeners) {\n onMaxListenersExceeded(event, count, maxListeners);\n }\n }\n\n /**\n * Safely execute a handler, catching errors according to configuration.\n */\n function 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\n // Handle async errors\n if (result instanceof Promise) {\n return result.catch((error) => {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error, event, handler);\n }\n });\n }\n\n return result;\n } catch (error) {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error as Error, event, handler);\n }\n }\n }\n\n function getHandlers(event: string): Set<WrappedHandler<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 const set = getHandlers(event);\n set.add(handler as EventHandler<unknown>);\n\n // Check for potential memory leak\n checkMaxListeners(event, set.size);\n\n return () => e.off(event, handler);\n },\n\n off(event, handler) {\n const set = handlers.get(event);\n if (!set) return;\n\n // Try direct removal first (for regular handlers)\n if (set.delete(handler as EventHandler<unknown>)) {\n // Clean up empty sets to prevent memory leak\n if (set.size === 0) {\n handlers.delete(event);\n }\n return;\n }\n\n // Search for wrapped handler (for once handlers)\n for (const wrapped of set) {\n if (wrapped.__original === handler) {\n set.delete(wrapped);\n if (set.size === 0) {\n handlers.delete(event);\n }\n break;\n }\n }\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 WrappedHandler<T[typeof event]>;\n\n // Store original reference for manual removal\n wrapper.__original = handler;\n\n return e.on(event, wrapper);\n },\n\n emit(event, ...payload) {\n const data = payload[0];\n getHandlers(event).forEach((handler) => {\n safeCall(handler, event, data);\n });\n wildcardHandlers.forEach((handler) => {\n // Wildcard handlers have different signature (event, payload)\n try {\n const result = handler(event, data as T[typeof event]);\n if (result instanceof Promise) {\n result.catch((error) => {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error, event, handler as EventHandler<unknown>);\n }\n });\n }\n } catch (error) {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error as Error, event, handler as EventHandler<unknown>);\n }\n }\n });\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 = safeCall(handler, event, data);\n if (result instanceof Promise) {\n promises.push(result);\n }\n });\n\n wildcardHandlers.forEach((handler) => {\n try {\n const result = handler(event, data as T[typeof event]);\n if (result instanceof Promise) {\n promises.push(\n result.catch((error) => {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error, event, handler as EventHandler<unknown>);\n }\n })\n );\n }\n } catch (error) {\n if (errorHandler === 'throw') throw error;\n if (typeof errorHandler === 'function') {\n errorHandler(error as Error, event, handler as EventHandler<unknown>);\n }\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 setErrorHandler(handler) {\n errorHandler = handler;\n },\n\n setMaxListeners(n) {\n maxListeners = n;\n },\n\n getMaxListeners() {\n return maxListeners;\n },\n\n eventNames() {\n return Array.from(handlers.keys()).filter(\n key => {\n const set = handlers.get(key);\n return set && set.size > 0;\n }\n ) as EventKey<T>[];\n },\n\n listeners(event) {\n const set = handlers.get(event);\n if (!set) return [];\n\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(wildcardHandlers);\n },\n\n hasListener(event, handler) {\n const set = handlers.get(event);\n if (!set) return false;\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 if (wrapped.__original === handler) {\n return true;\n }\n }\n\n return false;\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":"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","names":["createEmitochondria","options","handlers","wildcardHandlers","errorHandler","error","event","maxListeners","onMaxListenersExceeded","count","max","checkMaxListeners","safeCall","handler","payload","result","getHandlers","set","e","wrapped","wrapper","data","promises","n","key","index_default"]}
|
|
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":"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","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,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emitochondria",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"type": "module",
|
|
5
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
|
+
"bin": {
|
|
9
|
+
"emitochondria": "./bin/emitochondria.cjs"
|
|
10
|
+
},
|
|
8
11
|
"keywords": [
|
|
9
12
|
"events",
|
|
10
13
|
"emitter",
|
|
@@ -37,10 +40,13 @@
|
|
|
37
40
|
"types": "./dist/index.d.ts",
|
|
38
41
|
"default": "./dist/index.js"
|
|
39
42
|
}
|
|
40
|
-
}
|
|
43
|
+
},
|
|
44
|
+
"./schema.json": "./schema.json"
|
|
41
45
|
},
|
|
42
46
|
"files": [
|
|
43
|
-
"dist"
|
|
47
|
+
"dist",
|
|
48
|
+
"bin",
|
|
49
|
+
"schema.json"
|
|
44
50
|
],
|
|
45
51
|
"sideEffects": false,
|
|
46
52
|
"scripts": {
|
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
|
+
}
|