emitochondria 1.2.2 → 1.2.4

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.
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "emitochondria",
34
- version: "1.2.2",
34
+ version: "1.2.4",
35
35
  type: "module",
36
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
37
  author: "Pablo D\xEDaz A.K.A exudev",
@@ -58,9 +58,9 @@ var require_package = __commonJS({
58
58
  type: "git",
59
59
  url: "https://github.com/Exudev/emitochondria.git"
60
60
  },
61
- main: "./dist/index.js",
61
+ main: "./dist/index.cjs",
62
62
  module: "./dist/index.mjs",
63
- types: "./dist/index.d.ts",
63
+ types: "./dist/index.d.cts",
64
64
  exports: {
65
65
  ".": {
66
66
  import: {
@@ -68,8 +68,8 @@ var require_package = __commonJS({
68
68
  default: "./dist/index.mjs"
69
69
  },
70
70
  require: {
71
- types: "./dist/index.d.ts",
72
- default: "./dist/index.js"
71
+ types: "./dist/index.d.cts",
72
+ default: "./dist/index.cjs"
73
73
  }
74
74
  },
75
75
  "./schema.json": "./schema.json"
package/dist/index.cjs ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";var q=Object.create;var b=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,U=Object.prototype.hasOwnProperty;var G=(o,t)=>{for(var e in t)b(o,e,{get:t[e],enumerable:!0})},C=(o,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of I(t))!U.call(o,i)&&i!==e&&b(o,i,{get:()=>t[i],enumerable:!(r=D(t,i))||r.enumerable});return o};var O=(o,t,e)=>(e=o!=null?q(J(o)):{},C(t||!o||!o.__esModule?b(e,"default",{value:o,enumerable:!0}):e,o)),V=o=>C(b({},"__esModule",{value:!0}),o);var nt={};G(nt,{clearConfigCache:()=>$,createEmitochondria:()=>H,createEmitochondriaAsync:()=>K,default:()=>H});module.exports=V(nt);var M=typeof globalThis.process<"u"&&globalThis.process?.versions?.node!=null,u=null,m=null,F=null,w=!1;function L(){return M}function Z(){if(!M||w)return u!==null;try{if(typeof globalThis.require=="function")return u=globalThis.require("fs"),m=globalThis.require("path"),w=!0,!0;let o=Function('return typeof require !== "undefined" ? require : null')();if(o)return u=o("fs"),m=o("path"),w=!0,!0}catch{}return!1}async function P(){return M?w&&u&&m?{fs:u,path:m}:F||(F=(async()=>{try{let[o,t]=await Promise.all([import("fs"),import("path")]);u=o,m=t}catch(o){console.warn("[Emitochondria] Failed to load Node.js modules:",o),u=null,m=null}finally{w=!0}return{fs:u,path:m}})(),F):(w=!0,{fs:null,path:null})}function S(){return{fs:u,path:m,loaded:w}}Z();var W="emitochondria.config.json",d=null,g=null;function j(){if(g!==null)return g;let o=globalThis.process;if(!L())return g="","";try{let{fs:t,path:e,loaded:r}=S();if(!r||!t||!e){let v=o.cwd();return g=v,v}let i=o.cwd();for(;i!==e.dirname(i);){if(t.existsSync(e.join(i,"package.json")))return g=i,i;i=e.dirname(i)}let l=o.cwd();return g=l,l}catch{let t=o.cwd();return g=t,t}}function z(){return j()}function Q(){if(d!==null)return d;let o=globalThis.process;if(!L())return d={},d;try{let{fs:t,path:e,loaded:r}=S();if(!r||!t||!e)return d={},d;let i=j(),l=e.join(i,W);if(!t.existsSync(l)){let h=e.join(o.cwd(),W);if(t.existsSync(h)){let f=t.readFileSync(h,"utf-8");return d=JSON.parse(f),o.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Config loaded from: ${h}`),d}return d={},d}let v=t.readFileSync(l,"utf-8");return d=JSON.parse(v),o.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Config loaded from: ${l}`),d}catch(t){return t?.code!=="ENOENT"&&console.warn(`[Emitochondria] Failed to load config: ${t?.message}`),d={},d}}function _(o,t){let e={...o};for(let r in t)t[r]!==void 0&&(typeof t[r]=="object"&&t[r]!==null&&!Array.isArray(t[r])&&typeof e[r]=="object"&&e[r]!==null?e[r]=_(e[r],t[r]):e[r]=t[r]);return e}function A(o={}){let t=Q();return _(t,o)}function $(){d=null,g=null}var X=typeof globalThis.window<"u";function Y(o,t){let e=new Date;switch(o){case"unix":return String(e.getTime());case"human":return e.toLocaleString("en-US",{timeZone:t==="utc"?"UTC":void 0,month:"short",day:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0});default:return t==="utc"?e.toISOString():new Date(e.getTime()-e.getTimezoneOffset()*6e4).toISOString().slice(0,-1)}}function tt(o){let t=o.match(/^(\d+(?:\.\d+)?)\s*(KB|MB|GB)$/i);if(!t)return 10*1024*1024;let e=parseFloat(t[1]),r=t[2].toUpperCase(),i={KB:1024,MB:1024*1024,GB:1024*1024*1024};return e*(i[r]??10*1024*1024)}var k=class{options;maxSizeBytes;browserWarned=!1;fs=null;path=null;initPromise=null;pendingWrites=[];constructor(t={}){this.options={timestamps:t.timestamps??!1,timestampFormat:t.timestampFormat??"iso",timezone:t.timezone??"utc",persist:t.persist??!1,path:t.path??"./logs/emitochondria.log",format:t.format??"text",maxSize:t.maxSize??"10MB",logEvents:t.logEvents??!1,logErrors:t.logErrors??!0,logWarnings:t.logWarnings??!0},this.maxSizeBytes=tt(this.options.maxSize),this.options.persist&&this.initFileLogging()}initFileLogging(){if(X){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(!L())return;let{fs:t,path:e,loaded:r}=S();if(r&&t&&e){this.fs=t,this.path=e,this.setupLogPath();return}this.initPromise=this.initFileLoggingAsync()}async initFileLoggingAsync(){try{let{fs:t,path:e}=await P();if(!t||!e){console.warn("[Emitochondria] File logging unavailable: Could not load Node.js fs/path modules. Logs will only be written to console.");return}this.fs=t,this.path=e,this.setupLogPath(),this.flushPendingWrites()}catch(t){console.error("[Emitochondria] Failed to initialize file logging:",t)}}setupLogPath(){if(!(!this.fs||!this.path))try{let t=z(),e=this.path.isAbsolute(this.options.path)?this.options.path:this.path.join(t,this.options.path);this.options.path=e;let r=this.path.dirname(e);this.fs.existsSync(r)||this.fs.mkdirSync(r,{recursive:!0}),globalThis.process?.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Logs will be written to: ${e}`)}catch(t){console.error("[Emitochondria] Failed to setup log path:",t)}}flushPendingWrites(){if(this.pendingWrites.length!==0){for(let t of this.pendingWrites)this.writeToFileSync(t);this.pendingWrites=[]}}getTimestamp(){if(this.options.timestamps)return Y(this.options.timestampFormat,this.options.timezone)}formatEntry(t){let e=this.getTimestamp();if(this.options.format==="json"){let i={level:t.level,type:t.type,message:t.message};return e&&(i.timestamp=e),t.event&&(i.event=t.event),t.payload!==void 0&&(i.payload=t.payload),t.error&&(i.error=String(t.error)),JSON.stringify(i)}let r=[];return e&&r.push(`[${e}]`),r.push(`[${t.level.toUpperCase()}]`),t.event&&r.push(`[${t.event}]`),r.push(t.message),t.payload!==void 0&&r.push(`| Payload: ${JSON.stringify(t.payload)}`),t.error&&r.push(`| Error: ${t.error}`),r.join(" ")}writeToFile(t){if(this.fs&&this.path){this.writeToFileSync(t);return}if(this.initPromise){this.pendingWrites.push(t);return}}writeToFileSync(t){if(!(!this.fs||!this.path))try{let e=t+`
2
+ `;this.fs.existsSync(this.options.path)&&this.fs.statSync(this.options.path).size>=this.maxSizeBytes&&this.rotateLog(),this.fs.appendFileSync(this.options.path,e,"utf8")}catch(e){console.error("[Emitochondria] Failed to write to log file:",e)}}rotateLog(){if(!(!this.fs||!this.path))try{let t=1;for(;this.fs.existsSync(`${this.options.path}.${t}`);)t++;this.fs.renameSync(this.options.path,`${this.options.path}.${t}`)}catch(t){console.error("[Emitochondria] Failed to rotate log file:",t)}}logEvent(t,e){if(!this.options.logEvents)return;let r={level:"info",type:"event",event:t,message:"Event emitted",payload:e},i=this.formatEntry(r);console.log(i),this.options.persist&&this.writeToFile(i)}logError(t,e,r){if(!this.options.logErrors)return;let i={level:"error",type:"error",event:t,message:"Handler threw an error",error:e},l=this.formatEntry(i);console.error(l),this.options.persist&&this.writeToFile(l)}logWarning(t,e,r){if(!this.options.logWarnings)return;let i={level:"warn",type:"warning",event:t,message:e,payload:r},l=this.formatEntry(i);console.warn(l),this.options.persist&&this.writeToFile(l)}};var et=(o,t,e)=>{};function H(o={}){let t=A(o),e=new k(t.logging),r=new Map,i=new Set,l=t.maxListeners??10,v=o.onMaxListenersExceeded??et,h=t.onError??"default",f=new Set,y=n=>r.get(n)??r.set(n,new Set).get(n),T=(n,a,s)=>{if(e.logError(a,n,s),h==="throw")throw n;typeof h=="function"&&h(n,a,s)},N=(n,a,s)=>{try{let c=n(s);if(c instanceof Promise)return c.catch(p=>T(p,a,n))}catch(c){T(c,a,n)}},B=(n,a)=>{if(l>0&&a>l&&!f.has(n)){f.add(n);let s=`Possible memory leak: ${a} listeners (max: ${l})`;e.logWarning(n,s,{count:a,max:l}),v(n,a,l)}},x={on(n,a){let s=y(n);return s.add(a),B(n,s.size),()=>x.off(n,a)},off(n,a){let s=y(n);if(s.delete(a)){s.size<=l&&f.delete(n);return}for(let c of s)if(c.__original===a){s.delete(c),s.size<=l&&f.delete(n);break}},once(n,a){let s=(c=>(x.off(n,s),a(c)));return s.__original=a,x.on(n,s)},emit(n,...a){let s=a[0];e.logEvent(n,s),y(n).forEach(c=>N(c,n,s)),i.forEach(c=>{try{c(n,s)}catch(p){T(p,n,c)}})},async emitAsync(n,...a){let s=a[0],c=[];e.logEvent(n,s),y(n).forEach(p=>{let E=N(p,n,s);E instanceof Promise&&c.push(E)}),i.forEach(p=>{try{let E=p(n,s);E instanceof Promise&&c.push(E.catch(R=>T(R,n,p)))}catch(E){T(E,n,p)}}),await Promise.all(c)},onAny(n){return i.add(n),()=>x.offAny(n)},offAny(n){i.delete(n)},clear(n){n?(r.delete(n),f.delete(n)):(r.clear(),i.clear(),f.clear())},listenerCount(n){return y(n).size},setErrorHandler(n){h=n},setMaxListeners(n){l=n,n===0&&f.clear()},getMaxListeners(){return l},eventNames(){return Array.from(r.keys()).filter(n=>r.get(n).size>0)},listeners(n){let a=y(n);return Array.from(a).map(s=>s.__original||s)},wildcardListeners(){return Array.from(i)},hasListener(n,a){let s=y(n);if(s.has(a))return!0;for(let c of s)if(c.__original===a)return!0;return!1}};return x}async function K(o={}){return o.logging?.persist&&await P(),H(o)}0&&(module.exports={clearConfigCache,createEmitochondria,createEmitochondriaAsync});
3
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/node-loader.ts","../src/config.ts","../src/logger.ts","../src/emitter.ts"],"sourcesContent":["// Core\nexport { createEmitochondria, createEmitochondriaAsync } 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","// ============================================================\n// NODE MODULE LOADER\n// Provides ESM and CJS compatible loading of fs and path modules\n// ============================================================\n\nconst isNode = typeof (globalThis as any).process !== 'undefined'\n && (globalThis as any).process?.versions?.node != null;\n\n// Cached modules\nlet fsModule: typeof import('fs') | null = null;\nlet pathModule: typeof import('path') | null = null;\nlet loadPromise: Promise<{ fs: typeof import('fs') | null; path: typeof import('path') | null }> | null = null;\nlet loadAttempted = false;\n\n/**\n * Check if we're in a Node.js environment.\n */\nexport function isNodeEnvironment(): boolean {\n return isNode;\n}\n\n/**\n * Try to load modules synchronously using require (CJS).\n * This is attempted first for immediate availability in CJS environments.\n * Returns true if sync loading succeeded.\n */\nexport function tryLoadSync(): boolean {\n if (!isNode || loadAttempted) return fsModule !== null;\n\n try {\n // Try globalThis.require (available in bundled CJS)\n if (typeof (globalThis as any).require === 'function') {\n fsModule = (globalThis as any).require('fs');\n pathModule = (globalThis as any).require('path');\n loadAttempted = true;\n return true;\n }\n\n // Try Function constructor for CJS context\n const req = Function('return typeof require !== \"undefined\" ? require : null')();\n if (req) {\n fsModule = req('fs');\n pathModule = req('path');\n loadAttempted = true;\n return true;\n }\n } catch {\n // Sync loading failed, will need async\n }\n\n return false;\n}\n\n/**\n * Load fs and path modules asynchronously.\n * Works in both ESM and CJS environments.\n * Returns cached modules if already loaded.\n */\nexport async function loadNodeModules(): Promise<{ fs: typeof import('fs') | null; path: typeof import('path') | null }> {\n if (!isNode) {\n loadAttempted = true;\n return { fs: null, path: null };\n }\n\n // Return cached if already loaded\n if (loadAttempted && fsModule && pathModule) {\n return { fs: fsModule, path: pathModule };\n }\n\n // Return existing promise if load in progress\n if (loadPromise) {\n return loadPromise;\n }\n\n // Start async load\n loadPromise = (async () => {\n try {\n // Dynamic import works in both ESM and CJS\n const [fs, path] = await Promise.all([\n import('fs'),\n import('path')\n ]);\n fsModule = fs;\n pathModule = path;\n } catch (err) {\n console.warn('[Emitochondria] Failed to load Node.js modules:', err);\n fsModule = null;\n pathModule = null;\n } finally {\n loadAttempted = true;\n }\n return { fs: fsModule, path: pathModule };\n })();\n\n return loadPromise;\n}\n\n/**\n * Get cached modules if already loaded (synchronous access).\n * Returns loaded state to indicate if loading has completed.\n */\nexport function getNodeModules(): { fs: typeof import('fs') | null; path: typeof import('path') | null; loaded: boolean } {\n return {\n fs: fsModule,\n path: pathModule,\n loaded: loadAttempted\n };\n}\n\n/**\n * Check if async loading is in progress.\n */\nexport function isLoadingInProgress(): boolean {\n return loadPromise !== null && !loadAttempted;\n}\n\n// Attempt sync load immediately at module load time\n// This ensures CJS environments have modules available right away\ntryLoadSync();\n","import { EmitochondriaConfig, EmitochondriaOptions, EventMap } from './types.js';\nimport { getNodeModules, isNodeEnvironment } from './node-loader.js';\n\n// ============================================================\n// CONFIG FILE LOADING\n// ============================================================\n\nconst CONFIG_FILENAME = 'emitochondria.config.json';\n\nlet cachedConfig: EmitochondriaConfig | null = null;\nlet cachedProjectRoot: string | null = null;\n\n/**\n * Find project root by walking up directories looking for package.json.\n */\nfunction findProjectRoot(): string {\n if (cachedProjectRoot !== null) return cachedProjectRoot;\n\n const proc = (globalThis as any).process;\n if (!isNodeEnvironment()) {\n cachedProjectRoot = '';\n return '';\n }\n\n try {\n const { fs, path, loaded } = getNodeModules();\n\n if (!loaded || !fs || !path) {\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n }\n\n let dir: string = proc.cwd();\n\n // Walk up looking for package.json\n while (dir !== path.dirname(dir)) {\n if (fs.existsSync(path.join(dir, 'package.json'))) {\n cachedProjectRoot = dir;\n return dir;\n }\n dir = path.dirname(dir);\n }\n\n // Fallback to cwd if no package.json found\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n } catch {\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n }\n}\n\n/**\n * Get the cached project root (for use by logger).\n */\nexport function getProjectRoot(): string {\n return findProjectRoot();\n}\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 (!isNodeEnvironment()) {\n cachedConfig = {};\n return cachedConfig;\n }\n\n try {\n const { fs, path, loaded } = getNodeModules();\n\n if (!loaded || !fs || !path) {\n cachedConfig = {};\n return cachedConfig;\n }\n\n // Look for config in project root first\n const projectRoot = findProjectRoot();\n const configPath = path.join(projectRoot, CONFIG_FILENAME);\n\n if (!fs.existsSync(configPath)) {\n // Also try cwd as fallback\n const cwdPath = path.join(proc.cwd(), CONFIG_FILENAME);\n if (fs.existsSync(cwdPath)) {\n const content = fs.readFileSync(cwdPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n if (proc.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Config loaded from: ${cwdPath}`);\n }\n\n return cachedConfig;\n }\n\n cachedConfig = {};\n return cachedConfig;\n }\n\n const content = fs.readFileSync(configPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n if (proc.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Config loaded from: ${configPath}`);\n }\n\n return cachedConfig;\n } catch (err: any) {\n if (err?.code !== 'ENOENT') {\n console.warn(`[Emitochondria] Failed to load config: ${err?.message}`);\n }\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 cachedProjectRoot = null;\n}\n","import { LoggingOptions, TimestampFormat, Timezone } from './types.js';\nimport { getProjectRoot } from './config.js';\nimport { getNodeModules, loadNodeModules, isNodeEnvironment } from './node-loader.js';\n\n// ============================================================\n// ENVIRONMENT DETECTION\n// ============================================================\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: typeof import('fs') | null = null;\n private path: typeof import('path') | null = null;\n private initPromise: Promise<void> | null = null;\n private pendingWrites: string[] = [];\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 (!isNodeEnvironment()) return;\n\n // Try synchronous loading first (works in CJS)\n const { fs, path, loaded } = getNodeModules();\n\n if (loaded && fs && path) {\n this.fs = fs;\n this.path = path;\n this.setupLogPath();\n return;\n }\n\n // Need async loading (ESM environment)\n this.initPromise = this.initFileLoggingAsync();\n }\n\n private async initFileLoggingAsync(): Promise<void> {\n try {\n const { fs, path } = await loadNodeModules();\n\n if (!fs || !path) {\n console.warn(\n '[Emitochondria] File logging unavailable: Could not load Node.js fs/path modules. ' +\n 'Logs will only be written to console.'\n );\n return;\n }\n\n this.fs = fs;\n this.path = path;\n this.setupLogPath();\n\n // Flush any pending writes\n this.flushPendingWrites();\n } catch (err) {\n console.error('[Emitochondria] Failed to initialize file logging:', err);\n }\n }\n\n private setupLogPath(): void {\n if (!this.fs || !this.path) return;\n\n try {\n // Resolve path relative to project root (where package.json is)\n const projectRoot = getProjectRoot();\n const resolvedPath = this.path.isAbsolute(this.options.path)\n ? this.options.path\n : this.path.join(projectRoot, this.options.path);\n\n // Update options with resolved path\n this.options.path = resolvedPath;\n\n // Create directory if needed\n const dir = this.path.dirname(resolvedPath);\n if (!this.fs.existsSync(dir)) {\n this.fs.mkdirSync(dir, { recursive: true });\n }\n\n // Log where logs are being written (helpful for debugging)\n const proc = (globalThis as any).process;\n if (proc?.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Logs will be written to: ${resolvedPath}`);\n }\n } catch (err) {\n console.error('[Emitochondria] Failed to setup log path:', err);\n }\n }\n\n private flushPendingWrites(): void {\n if (this.pendingWrites.length === 0) return;\n\n for (const content of this.pendingWrites) {\n this.writeToFileSync(content);\n }\n this.pendingWrites = [];\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 modules are loaded, write immediately\n if (this.fs && this.path) {\n this.writeToFileSync(content);\n return;\n }\n\n // If still initializing, queue the write\n if (this.initPromise) {\n this.pendingWrites.push(content);\n return;\n }\n\n // No init happening and modules not loaded - file logging not available\n // This case is already warned about in initFileLoggingAsync\n }\n\n private writeToFileSync(content: string): void {\n if (!this.fs || !this.path) 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 {\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';\nimport { loadNodeModules } from './node-loader.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\n/**\n * Create an emitter with guaranteed async initialization.\n * Use this in ESM environments when you need file logging to be ready immediately.\n *\n * @example\n * const emitter = await createEmitochondriaAsync({ logging: { persist: true } });\n * emitter.emit('ready', {}); // File logging is guaranteed to be available\n */\nexport async function createEmitochondriaAsync<T extends EventMap>(\n options: EmitochondriaOptions<T> = {}\n): Promise<Emitochondria<T>> {\n // Pre-load Node modules before creating the emitter\n if (options.logging?.persist) {\n await loadNodeModules();\n }\n\n return createEmitochondria(options);\n}\n"],"mappings":"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","names":["index_exports","__export","clearConfigCache","createEmitochondria","createEmitochondriaAsync","__toCommonJS","isNode","fsModule","pathModule","loadPromise","loadAttempted","isNodeEnvironment","tryLoadSync","req","loadNodeModules","fs","path","err","getNodeModules","tryLoadSync","CONFIG_FILENAME","cachedConfig","cachedProjectRoot","findProjectRoot","proc","isNodeEnvironment","fs","path","loaded","getNodeModules","cwd","dir","getProjectRoot","loadConfigFileSync","projectRoot","configPath","cwdPath","content","err","deepMerge","base","override","result","key","mergeConfig","options","fileConfig","clearConfigCache","isBrowser","formatTimestamp","format","timezone","date","parseSize","size","match","value","unit","multipliers","Logger","options","isNodeEnvironment","fs","path","loaded","getNodeModules","loadNodeModules","err","projectRoot","getProjectRoot","resolvedPath","dir","content","entry","timestamp","obj","parts","line","rotationNum","event","payload","formatted","error","_handler","message","details","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","k","createEmitochondriaAsync","loadNodeModules"]}
package/dist/index.d.cts CHANGED
@@ -135,10 +135,19 @@ interface Emitochondria<T extends EventMap> {
135
135
  }
136
136
 
137
137
  declare function createEmitochondria<T extends EventMap>(options?: EmitochondriaOptions<T>): Emitochondria<T>;
138
+ /**
139
+ * Create an emitter with guaranteed async initialization.
140
+ * Use this in ESM environments when you need file logging to be ready immediately.
141
+ *
142
+ * @example
143
+ * const emitter = await createEmitochondriaAsync({ logging: { persist: true } });
144
+ * emitter.emit('ready', {}); // File logging is guaranteed to be available
145
+ */
146
+ declare function createEmitochondriaAsync<T extends EventMap>(options?: EmitochondriaOptions<T>): Promise<Emitochondria<T>>;
138
147
 
139
148
  /**
140
149
  * Clear cached config (useful for testing).
141
150
  */
142
151
  declare function clearConfigCache(): void;
143
152
 
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 };
153
+ 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, createEmitochondriaAsync, createEmitochondria as default };
package/dist/index.d.ts CHANGED
@@ -135,10 +135,19 @@ interface Emitochondria<T extends EventMap> {
135
135
  }
136
136
 
137
137
  declare function createEmitochondria<T extends EventMap>(options?: EmitochondriaOptions<T>): Emitochondria<T>;
138
+ /**
139
+ * Create an emitter with guaranteed async initialization.
140
+ * Use this in ESM environments when you need file logging to be ready immediately.
141
+ *
142
+ * @example
143
+ * const emitter = await createEmitochondriaAsync({ logging: { persist: true } });
144
+ * emitter.emit('ready', {}); // File logging is guaranteed to be available
145
+ */
146
+ declare function createEmitochondriaAsync<T extends EventMap>(options?: EmitochondriaOptions<T>): Promise<Emitochondria<T>>;
138
147
 
139
148
  /**
140
149
  * Clear cached config (useful for testing).
141
150
  */
142
151
  declare function clearConfigCache(): void;
143
152
 
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 };
153
+ 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, createEmitochondriaAsync, createEmitochondria as default };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- var x="emitochondria.config.json",d=null,m=null;function S(){if(m!==null)return m;let a=globalThis.process;if(typeof a>"u"||!a?.versions?.node)return m="","";try{let t=globalThis.require;if(!t){let E=a.cwd();return m=E,E}let n=t("path"),o=t("fs"),r=a.cwd();for(;r!==n.dirname(r);){if(o.existsSync(n.join(r,"package.json")))return m=r,r;r=n.dirname(r)}let c=a.cwd();return m=c,c}catch{let t=a.cwd();return m=t,t}}function L(){return S()}function z(){if(d!==null)return d;let a=globalThis.process;if(typeof a>"u"||!a?.versions?.node)return d={},d;try{let t=globalThis.require;if(!t)return d={},d;let n=t("fs"),o=t("path"),r=S(),c=o.join(r,x);if(!n.existsSync(c)){let g=o.join(a.cwd(),x);if(n.existsSync(g)){let p=n.readFileSync(g,"utf-8");return d=JSON.parse(p),a.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Config loaded from: ${g}`),d}return d={},d}let E=n.readFileSync(c,"utf-8");return d=JSON.parse(E),a.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Config loaded from: ${c}`),d}catch(t){return t?.code!=="ENOENT"&&console.warn(`[Emitochondria] Failed to load config: ${t?.message}`),d={},d}}function k(a,t){let n={...a};for(let o in t)t[o]!==void 0&&(typeof t[o]=="object"&&t[o]!==null&&!Array.isArray(t[o])&&typeof n[o]=="object"&&n[o]!==null?n[o]=k(n[o],t[o]):n[o]=t[o]);return n}function H(a={}){let t=z();return k(t,a)}function M(){d=null,m=null}var b=typeof globalThis.process<"u"&&globalThis.process?.versions?.node!=null,j=typeof globalThis.window<"u";function _(a,t){let n=new Date;switch(a){case"unix":return String(n.getTime());case"human":return n.toLocaleString("en-US",{timeZone:t==="utc"?"UTC":void 0,month:"short",day:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0});default:return t==="utc"?n.toISOString():new Date(n.getTime()-n.getTimezoneOffset()*6e4).toISOString().slice(0,-1)}}function N(a){let t=a.match(/^(\d+(?:\.\d+)?)\s*(KB|MB|GB)$/i);if(!t)return 10*1024*1024;let n=parseFloat(t[1]),o=t[2].toUpperCase(),r={KB:1024,MB:1024*1024,GB:1024*1024*1024};return n*(r[o]??10*1024*1024)}var v=class{options;maxSizeBytes;browserWarned=!1;fs=null;path=null;constructor(t={}){this.options={timestamps:t.timestamps??!1,timestampFormat:t.timestampFormat??"iso",timezone:t.timezone??"utc",persist:t.persist??!1,path:t.path??"./logs/emitochondria.log",format:t.format??"text",maxSize:t.maxSize??"10MB",logEvents:t.logEvents??!1,logErrors:t.logErrors??!0,logWarnings:t.logWarnings??!0},this.maxSizeBytes=N(this.options.maxSize),this.options.persist&&this.initFileLogging()}initFileLogging(){if(j){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(b)try{let t=globalThis.require;if(t){this.fs=t("fs"),this.path=t("path");let n=L(),o=this.path.isAbsolute(this.options.path)?this.options.path:this.path.join(n,this.options.path);this.options.path=o;let r=this.path.dirname(o);this.fs.existsSync(r)||this.fs.mkdirSync(r,{recursive:!0}),globalThis.process?.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Logs will be written to: ${o}`)}}catch(t){console.error("[Emitochondria] Failed to initialize file logging:",t)}}getTimestamp(){if(this.options.timestamps)return _(this.options.timestampFormat,this.options.timezone)}formatEntry(t){let n=this.getTimestamp();if(this.options.format==="json"){let r={level:t.level,type:t.type,message:t.message};return n&&(r.timestamp=n),t.event&&(r.event=t.event),t.payload!==void 0&&(r.payload=t.payload),t.error&&(r.error=String(t.error)),JSON.stringify(r)}let o=[];return n&&o.push(`[${n}]`),o.push(`[${t.level.toUpperCase()}]`),t.event&&o.push(`[${t.event}]`),o.push(t.message),t.payload!==void 0&&o.push(`| Payload: ${JSON.stringify(t.payload)}`),t.error&&o.push(`| Error: ${t.error}`),o.join(" ")}writeToFile(t){if(!(!this.fs||!this.path||!b))try{let n=t+`
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 t=1;for(;this.fs.existsSync(`${this.options.path}.${t}`);)t++;this.fs.renameSync(this.options.path,`${this.options.path}.${t}`)}catch(t){console.error("[Emitochondria] Failed to rotate log file:",t)}}logEvent(t,n){if(!this.options.logEvents)return;let o={level:"info",type:"event",event:t,message:"Event emitted",payload:n},r=this.formatEntry(o);console.log(r),this.options.persist&&this.writeToFile(r)}logError(t,n,o){if(!this.options.logErrors)return;let r={level:"error",type:"error",event:t,message:"Handler threw an error",error:n},c=this.formatEntry(r);console.error(c),this.options.persist&&this.writeToFile(c)}logWarning(t,n,o){if(!this.options.logWarnings)return;let r={level:"warn",type:"warning",event:t,message:n,payload:o},c=this.formatEntry(r);console.warn(c),this.options.persist&&this.writeToFile(c)}};var P=(a,t,n)=>{};function F(a={}){let t=H(a),n=new v(t.logging),o=new Map,r=new Set,c=t.maxListeners??10,E=a.onMaxListenersExceeded??P,g=t.onError??"default",p=new Set,u=e=>o.get(e)??o.set(e,new Set).get(e),y=(e,s,i)=>{if(n.logError(s,e,i),g==="throw")throw e;typeof g=="function"&&g(e,s,i)},T=(e,s,i)=>{try{let l=e(i);if(l instanceof Promise)return l.catch(f=>y(f,s,e))}catch(l){y(l,s,e)}},C=(e,s)=>{if(c>0&&s>c&&!p.has(e)){p.add(e);let i=`Possible memory leak: ${s} listeners (max: ${c})`;n.logWarning(e,i,{count:s,max:c}),E(e,s,c)}},w={on(e,s){let i=u(e);return i.add(s),C(e,i.size),()=>w.off(e,s)},off(e,s){let i=u(e);if(i.delete(s)){i.size<=c&&p.delete(e);return}for(let l of i)if(l.__original===s){i.delete(l),i.size<=c&&p.delete(e);break}},once(e,s){let i=(l=>(w.off(e,i),s(l)));return i.__original=s,w.on(e,i)},emit(e,...s){let i=s[0];n.logEvent(e,i),u(e).forEach(l=>T(l,e,i)),r.forEach(l=>{try{l(e,i)}catch(f){y(f,e,l)}})},async emitAsync(e,...s){let i=s[0],l=[];n.logEvent(e,i),u(e).forEach(f=>{let h=T(f,e,i);h instanceof Promise&&l.push(h)}),r.forEach(f=>{try{let h=f(e,i);h instanceof Promise&&l.push(h.catch(O=>y(O,e,f)))}catch(h){y(h,e,f)}}),await Promise.all(l)},onAny(e){return r.add(e),()=>w.offAny(e)},offAny(e){r.delete(e)},clear(e){e?(o.delete(e),p.delete(e)):(o.clear(),r.clear(),p.clear())},listenerCount(e){return u(e).size},setErrorHandler(e){g=e},setMaxListeners(e){c=e,e===0&&p.clear()},getMaxListeners(){return c},eventNames(){return Array.from(o.keys()).filter(e=>o.get(e).size>0)},listeners(e){let s=u(e);return Array.from(s).map(i=>i.__original||i)},wildcardListeners(){return Array.from(r)},hasListener(e,s){let i=u(e);if(i.has(s))return!0;for(let l of i)if(l.__original===s)return!0;return!1}};return w}export{M as clearConfigCache,F as createEmitochondria,F as default};
1
+ var k=typeof globalThis.process<"u"&&globalThis.process?.versions?.node!=null,u=null,m=null,b=null,w=!1;function L(){return k}function A(){if(!k||w)return u!==null;try{if(typeof globalThis.require=="function")return u=globalThis.require("fs"),m=globalThis.require("path"),w=!0,!0;let i=Function('return typeof require !== "undefined" ? require : null')();if(i)return u=i("fs"),m=i("path"),w=!0,!0}catch{}return!1}async function F(){return k?w&&u&&m?{fs:u,path:m}:b||(b=(async()=>{try{let[i,t]=await Promise.all([import("fs"),import("path")]);u=i,m=t}catch(i){console.warn("[Emitochondria] Failed to load Node.js modules:",i),u=null,m=null}finally{w=!0}return{fs:u,path:m}})(),b):(w=!0,{fs:null,path:null})}function S(){return{fs:u,path:m,loaded:w}}A();var N="emitochondria.config.json",d=null,g=null;function C(){if(g!==null)return g;let i=globalThis.process;if(!L())return g="","";try{let{fs:t,path:n,loaded:o}=S();if(!o||!t||!n){let v=i.cwd();return g=v,v}let s=i.cwd();for(;s!==n.dirname(s);){if(t.existsSync(n.join(s,"package.json")))return g=s,s;s=n.dirname(s)}let l=i.cwd();return g=l,l}catch{let t=i.cwd();return g=t,t}}function O(){return C()}function $(){if(d!==null)return d;let i=globalThis.process;if(!L())return d={},d;try{let{fs:t,path:n,loaded:o}=S();if(!o||!t||!n)return d={},d;let s=C(),l=n.join(s,N);if(!t.existsSync(l)){let h=n.join(i.cwd(),N);if(t.existsSync(h)){let f=t.readFileSync(h,"utf-8");return d=JSON.parse(f),i.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Config loaded from: ${h}`),d}return d={},d}let v=t.readFileSync(l,"utf-8");return d=JSON.parse(v),i.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Config loaded from: ${l}`),d}catch(t){return t?.code!=="ENOENT"&&console.warn(`[Emitochondria] Failed to load config: ${t?.message}`),d={},d}}function W(i,t){let n={...i};for(let o in t)t[o]!==void 0&&(typeof t[o]=="object"&&t[o]!==null&&!Array.isArray(t[o])&&typeof n[o]=="object"&&n[o]!==null?n[o]=W(n[o],t[o]):n[o]=t[o]);return n}function j(i={}){let t=$();return W(t,i)}function K(){d=null,g=null}var B=typeof globalThis.window<"u";function R(i,t){let n=new Date;switch(i){case"unix":return String(n.getTime());case"human":return n.toLocaleString("en-US",{timeZone:t==="utc"?"UTC":void 0,month:"short",day:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0});default:return t==="utc"?n.toISOString():new Date(n.getTime()-n.getTimezoneOffset()*6e4).toISOString().slice(0,-1)}}function q(i){let t=i.match(/^(\d+(?:\.\d+)?)\s*(KB|MB|GB)$/i);if(!t)return 10*1024*1024;let n=parseFloat(t[1]),o=t[2].toUpperCase(),s={KB:1024,MB:1024*1024,GB:1024*1024*1024};return n*(s[o]??10*1024*1024)}var P=class{options;maxSizeBytes;browserWarned=!1;fs=null;path=null;initPromise=null;pendingWrites=[];constructor(t={}){this.options={timestamps:t.timestamps??!1,timestampFormat:t.timestampFormat??"iso",timezone:t.timezone??"utc",persist:t.persist??!1,path:t.path??"./logs/emitochondria.log",format:t.format??"text",maxSize:t.maxSize??"10MB",logEvents:t.logEvents??!1,logErrors:t.logErrors??!0,logWarnings:t.logWarnings??!0},this.maxSizeBytes=q(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(!L())return;let{fs:t,path:n,loaded:o}=S();if(o&&t&&n){this.fs=t,this.path=n,this.setupLogPath();return}this.initPromise=this.initFileLoggingAsync()}async initFileLoggingAsync(){try{let{fs:t,path:n}=await F();if(!t||!n){console.warn("[Emitochondria] File logging unavailable: Could not load Node.js fs/path modules. Logs will only be written to console.");return}this.fs=t,this.path=n,this.setupLogPath(),this.flushPendingWrites()}catch(t){console.error("[Emitochondria] Failed to initialize file logging:",t)}}setupLogPath(){if(!(!this.fs||!this.path))try{let t=O(),n=this.path.isAbsolute(this.options.path)?this.options.path:this.path.join(t,this.options.path);this.options.path=n;let o=this.path.dirname(n);this.fs.existsSync(o)||this.fs.mkdirSync(o,{recursive:!0}),globalThis.process?.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Logs will be written to: ${n}`)}catch(t){console.error("[Emitochondria] Failed to setup log path:",t)}}flushPendingWrites(){if(this.pendingWrites.length!==0){for(let t of this.pendingWrites)this.writeToFileSync(t);this.pendingWrites=[]}}getTimestamp(){if(this.options.timestamps)return R(this.options.timestampFormat,this.options.timezone)}formatEntry(t){let n=this.getTimestamp();if(this.options.format==="json"){let s={level:t.level,type:t.type,message:t.message};return n&&(s.timestamp=n),t.event&&(s.event=t.event),t.payload!==void 0&&(s.payload=t.payload),t.error&&(s.error=String(t.error)),JSON.stringify(s)}let o=[];return n&&o.push(`[${n}]`),o.push(`[${t.level.toUpperCase()}]`),t.event&&o.push(`[${t.event}]`),o.push(t.message),t.payload!==void 0&&o.push(`| Payload: ${JSON.stringify(t.payload)}`),t.error&&o.push(`| Error: ${t.error}`),o.join(" ")}writeToFile(t){if(this.fs&&this.path){this.writeToFileSync(t);return}if(this.initPromise){this.pendingWrites.push(t);return}}writeToFileSync(t){if(!(!this.fs||!this.path))try{let n=t+`
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 t=1;for(;this.fs.existsSync(`${this.options.path}.${t}`);)t++;this.fs.renameSync(this.options.path,`${this.options.path}.${t}`)}catch(t){console.error("[Emitochondria] Failed to rotate log file:",t)}}logEvent(t,n){if(!this.options.logEvents)return;let o={level:"info",type:"event",event:t,message:"Event emitted",payload:n},s=this.formatEntry(o);console.log(s),this.options.persist&&this.writeToFile(s)}logError(t,n,o){if(!this.options.logErrors)return;let s={level:"error",type:"error",event:t,message:"Handler threw an error",error:n},l=this.formatEntry(s);console.error(l),this.options.persist&&this.writeToFile(l)}logWarning(t,n,o){if(!this.options.logWarnings)return;let s={level:"warn",type:"warning",event:t,message:n,payload:o},l=this.formatEntry(s);console.warn(l),this.options.persist&&this.writeToFile(l)}};var D=(i,t,n)=>{};function H(i={}){let t=j(i),n=new P(t.logging),o=new Map,s=new Set,l=t.maxListeners??10,v=i.onMaxListenersExceeded??D,h=t.onError??"default",f=new Set,y=e=>o.get(e)??o.set(e,new Set).get(e),T=(e,a,r)=>{if(n.logError(a,e,r),h==="throw")throw e;typeof h=="function"&&h(e,a,r)},M=(e,a,r)=>{try{let c=e(r);if(c instanceof Promise)return c.catch(p=>T(p,a,e))}catch(c){T(c,a,e)}},z=(e,a)=>{if(l>0&&a>l&&!f.has(e)){f.add(e);let r=`Possible memory leak: ${a} listeners (max: ${l})`;n.logWarning(e,r,{count:a,max:l}),v(e,a,l)}},x={on(e,a){let r=y(e);return r.add(a),z(e,r.size),()=>x.off(e,a)},off(e,a){let r=y(e);if(r.delete(a)){r.size<=l&&f.delete(e);return}for(let c of r)if(c.__original===a){r.delete(c),r.size<=l&&f.delete(e);break}},once(e,a){let r=(c=>(x.off(e,r),a(c)));return r.__original=a,x.on(e,r)},emit(e,...a){let r=a[0];n.logEvent(e,r),y(e).forEach(c=>M(c,e,r)),s.forEach(c=>{try{c(e,r)}catch(p){T(p,e,c)}})},async emitAsync(e,...a){let r=a[0],c=[];n.logEvent(e,r),y(e).forEach(p=>{let E=M(p,e,r);E instanceof Promise&&c.push(E)}),s.forEach(p=>{try{let E=p(e,r);E instanceof Promise&&c.push(E.catch(_=>T(_,e,p)))}catch(E){T(E,e,p)}}),await Promise.all(c)},onAny(e){return s.add(e),()=>x.offAny(e)},offAny(e){s.delete(e)},clear(e){e?(o.delete(e),f.delete(e)):(o.clear(),s.clear(),f.clear())},listenerCount(e){return y(e).size},setErrorHandler(e){h=e},setMaxListeners(e){l=e,e===0&&f.clear()},getMaxListeners(){return l},eventNames(){return Array.from(o.keys()).filter(e=>o.get(e).size>0)},listeners(e){let a=y(e);return Array.from(a).map(r=>r.__original||r)},wildcardListeners(){return Array.from(s)},hasListener(e,a){let r=y(e);if(r.has(a))return!0;for(let c of r)if(c.__original===a)return!0;return!1}};return x}async function I(i={}){return i.logging?.persist&&await F(),H(i)}export{K as clearConfigCache,H as createEmitochondria,I as createEmitochondriaAsync,H as default};
3
3
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/logger.ts","../src/emitter.ts"],"sourcesContent":["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;\nlet cachedProjectRoot: string | null = null;\n\n/**\n * Find project root by walking up directories looking for package.json.\n */\nfunction findProjectRoot(): string {\n if (cachedProjectRoot !== null) return cachedProjectRoot;\n\n const proc = (globalThis as any).process;\n if (typeof proc === 'undefined' || !proc?.versions?.node) {\n cachedProjectRoot = '';\n return '';\n }\n\n try {\n const req = (globalThis as any).require;\n if (!req) {\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n }\n\n const path = req('path');\n const fs = req('fs');\n\n let dir: string = proc.cwd();\n\n // Walk up looking for package.json\n while (dir !== path.dirname(dir)) {\n if (fs.existsSync(path.join(dir, 'package.json'))) {\n cachedProjectRoot = dir;\n return dir;\n }\n dir = path.dirname(dir);\n }\n\n // Fallback to cwd if no package.json found\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n } catch {\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n }\n}\n\n/**\n * Get the cached project root (for use by logger).\n */\nexport function getProjectRoot(): string {\n return findProjectRoot();\n}\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 // Look for config in project root first\n const projectRoot = findProjectRoot();\n const configPath = path.join(projectRoot, CONFIG_FILENAME);\n\n if (!fs.existsSync(configPath)) {\n // Also try cwd as fallback\n const cwdPath = path.join(proc.cwd(), CONFIG_FILENAME);\n if (fs.existsSync(cwdPath)) {\n const content = fs.readFileSync(cwdPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n if (proc.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Config loaded from: ${cwdPath}`);\n }\n\n return cachedConfig;\n }\n\n cachedConfig = {};\n return cachedConfig;\n }\n\n const content = fs.readFileSync(configPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n if (proc.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Config loaded from: ${configPath}`);\n }\n\n return cachedConfig;\n } catch (err: any) {\n if (err?.code !== 'ENOENT') {\n console.warn(`[Emitochondria] Failed to load config: ${err?.message}`);\n }\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 cachedProjectRoot = null;\n}\n","import { LoggingOptions, TimestampFormat, Timezone } from './types.js';\nimport { getProjectRoot } from './config.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 // Resolve path relative to project root (where package.json is)\n const projectRoot = getProjectRoot();\n const resolvedPath = this.path.isAbsolute(this.options.path)\n ? this.options.path\n : this.path.join(projectRoot, this.options.path);\n\n // Update options with resolved path\n this.options.path = resolvedPath;\n\n // Create directory if needed\n const dir = this.path.dirname(resolvedPath);\n if (!this.fs.existsSync(dir)) {\n this.fs.mkdirSync(dir, { recursive: true });\n }\n\n // Log where logs are being written (helpful for debugging)\n const proc = (globalThis as any).process;\n if (proc?.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Logs will be written to: ${resolvedPath}`);\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 {\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":["CONFIG_FILENAME","cachedConfig","cachedProjectRoot","findProjectRoot","proc","req","cwd","path","fs","dir","getProjectRoot","loadConfigFileSync","projectRoot","configPath","cwdPath","content","err","deepMerge","base","override","result","key","mergeConfig","options","fileConfig","clearConfigCache","isNode","isBrowser","formatTimestamp","format","timezone","date","parseSize","size","match","value","unit","multipliers","Logger","options","req","projectRoot","getProjectRoot","resolvedPath","dir","err","entry","timestamp","obj","parts","content","line","rotationNum","event","payload","formatted","error","_handler","message","details","defaultMaxListenersHandler","_event","_count","_max","createEmitochondria","options","config","mergeConfig","logger","Logger","handlers","wildcards","maxListeners","onMaxListenersExceeded","errorHandler","warned","get","handleError","error","event","handler","safeCall","payload","result","err","checkMaxListeners","count","message","emitter","set","wrapped","wrapper","data","h","promises","n","k"]}
1
+ {"version":3,"sources":["../src/node-loader.ts","../src/config.ts","../src/logger.ts","../src/emitter.ts"],"sourcesContent":["// ============================================================\n// NODE MODULE LOADER\n// Provides ESM and CJS compatible loading of fs and path modules\n// ============================================================\n\nconst isNode = typeof (globalThis as any).process !== 'undefined'\n && (globalThis as any).process?.versions?.node != null;\n\n// Cached modules\nlet fsModule: typeof import('fs') | null = null;\nlet pathModule: typeof import('path') | null = null;\nlet loadPromise: Promise<{ fs: typeof import('fs') | null; path: typeof import('path') | null }> | null = null;\nlet loadAttempted = false;\n\n/**\n * Check if we're in a Node.js environment.\n */\nexport function isNodeEnvironment(): boolean {\n return isNode;\n}\n\n/**\n * Try to load modules synchronously using require (CJS).\n * This is attempted first for immediate availability in CJS environments.\n * Returns true if sync loading succeeded.\n */\nexport function tryLoadSync(): boolean {\n if (!isNode || loadAttempted) return fsModule !== null;\n\n try {\n // Try globalThis.require (available in bundled CJS)\n if (typeof (globalThis as any).require === 'function') {\n fsModule = (globalThis as any).require('fs');\n pathModule = (globalThis as any).require('path');\n loadAttempted = true;\n return true;\n }\n\n // Try Function constructor for CJS context\n const req = Function('return typeof require !== \"undefined\" ? require : null')();\n if (req) {\n fsModule = req('fs');\n pathModule = req('path');\n loadAttempted = true;\n return true;\n }\n } catch {\n // Sync loading failed, will need async\n }\n\n return false;\n}\n\n/**\n * Load fs and path modules asynchronously.\n * Works in both ESM and CJS environments.\n * Returns cached modules if already loaded.\n */\nexport async function loadNodeModules(): Promise<{ fs: typeof import('fs') | null; path: typeof import('path') | null }> {\n if (!isNode) {\n loadAttempted = true;\n return { fs: null, path: null };\n }\n\n // Return cached if already loaded\n if (loadAttempted && fsModule && pathModule) {\n return { fs: fsModule, path: pathModule };\n }\n\n // Return existing promise if load in progress\n if (loadPromise) {\n return loadPromise;\n }\n\n // Start async load\n loadPromise = (async () => {\n try {\n // Dynamic import works in both ESM and CJS\n const [fs, path] = await Promise.all([\n import('fs'),\n import('path')\n ]);\n fsModule = fs;\n pathModule = path;\n } catch (err) {\n console.warn('[Emitochondria] Failed to load Node.js modules:', err);\n fsModule = null;\n pathModule = null;\n } finally {\n loadAttempted = true;\n }\n return { fs: fsModule, path: pathModule };\n })();\n\n return loadPromise;\n}\n\n/**\n * Get cached modules if already loaded (synchronous access).\n * Returns loaded state to indicate if loading has completed.\n */\nexport function getNodeModules(): { fs: typeof import('fs') | null; path: typeof import('path') | null; loaded: boolean } {\n return {\n fs: fsModule,\n path: pathModule,\n loaded: loadAttempted\n };\n}\n\n/**\n * Check if async loading is in progress.\n */\nexport function isLoadingInProgress(): boolean {\n return loadPromise !== null && !loadAttempted;\n}\n\n// Attempt sync load immediately at module load time\n// This ensures CJS environments have modules available right away\ntryLoadSync();\n","import { EmitochondriaConfig, EmitochondriaOptions, EventMap } from './types.js';\nimport { getNodeModules, isNodeEnvironment } from './node-loader.js';\n\n// ============================================================\n// CONFIG FILE LOADING\n// ============================================================\n\nconst CONFIG_FILENAME = 'emitochondria.config.json';\n\nlet cachedConfig: EmitochondriaConfig | null = null;\nlet cachedProjectRoot: string | null = null;\n\n/**\n * Find project root by walking up directories looking for package.json.\n */\nfunction findProjectRoot(): string {\n if (cachedProjectRoot !== null) return cachedProjectRoot;\n\n const proc = (globalThis as any).process;\n if (!isNodeEnvironment()) {\n cachedProjectRoot = '';\n return '';\n }\n\n try {\n const { fs, path, loaded } = getNodeModules();\n\n if (!loaded || !fs || !path) {\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n }\n\n let dir: string = proc.cwd();\n\n // Walk up looking for package.json\n while (dir !== path.dirname(dir)) {\n if (fs.existsSync(path.join(dir, 'package.json'))) {\n cachedProjectRoot = dir;\n return dir;\n }\n dir = path.dirname(dir);\n }\n\n // Fallback to cwd if no package.json found\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n } catch {\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n }\n}\n\n/**\n * Get the cached project root (for use by logger).\n */\nexport function getProjectRoot(): string {\n return findProjectRoot();\n}\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 (!isNodeEnvironment()) {\n cachedConfig = {};\n return cachedConfig;\n }\n\n try {\n const { fs, path, loaded } = getNodeModules();\n\n if (!loaded || !fs || !path) {\n cachedConfig = {};\n return cachedConfig;\n }\n\n // Look for config in project root first\n const projectRoot = findProjectRoot();\n const configPath = path.join(projectRoot, CONFIG_FILENAME);\n\n if (!fs.existsSync(configPath)) {\n // Also try cwd as fallback\n const cwdPath = path.join(proc.cwd(), CONFIG_FILENAME);\n if (fs.existsSync(cwdPath)) {\n const content = fs.readFileSync(cwdPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n if (proc.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Config loaded from: ${cwdPath}`);\n }\n\n return cachedConfig;\n }\n\n cachedConfig = {};\n return cachedConfig;\n }\n\n const content = fs.readFileSync(configPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n if (proc.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Config loaded from: ${configPath}`);\n }\n\n return cachedConfig;\n } catch (err: any) {\n if (err?.code !== 'ENOENT') {\n console.warn(`[Emitochondria] Failed to load config: ${err?.message}`);\n }\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 cachedProjectRoot = null;\n}\n","import { LoggingOptions, TimestampFormat, Timezone } from './types.js';\nimport { getProjectRoot } from './config.js';\nimport { getNodeModules, loadNodeModules, isNodeEnvironment } from './node-loader.js';\n\n// ============================================================\n// ENVIRONMENT DETECTION\n// ============================================================\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: typeof import('fs') | null = null;\n private path: typeof import('path') | null = null;\n private initPromise: Promise<void> | null = null;\n private pendingWrites: string[] = [];\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 (!isNodeEnvironment()) return;\n\n // Try synchronous loading first (works in CJS)\n const { fs, path, loaded } = getNodeModules();\n\n if (loaded && fs && path) {\n this.fs = fs;\n this.path = path;\n this.setupLogPath();\n return;\n }\n\n // Need async loading (ESM environment)\n this.initPromise = this.initFileLoggingAsync();\n }\n\n private async initFileLoggingAsync(): Promise<void> {\n try {\n const { fs, path } = await loadNodeModules();\n\n if (!fs || !path) {\n console.warn(\n '[Emitochondria] File logging unavailable: Could not load Node.js fs/path modules. ' +\n 'Logs will only be written to console.'\n );\n return;\n }\n\n this.fs = fs;\n this.path = path;\n this.setupLogPath();\n\n // Flush any pending writes\n this.flushPendingWrites();\n } catch (err) {\n console.error('[Emitochondria] Failed to initialize file logging:', err);\n }\n }\n\n private setupLogPath(): void {\n if (!this.fs || !this.path) return;\n\n try {\n // Resolve path relative to project root (where package.json is)\n const projectRoot = getProjectRoot();\n const resolvedPath = this.path.isAbsolute(this.options.path)\n ? this.options.path\n : this.path.join(projectRoot, this.options.path);\n\n // Update options with resolved path\n this.options.path = resolvedPath;\n\n // Create directory if needed\n const dir = this.path.dirname(resolvedPath);\n if (!this.fs.existsSync(dir)) {\n this.fs.mkdirSync(dir, { recursive: true });\n }\n\n // Log where logs are being written (helpful for debugging)\n const proc = (globalThis as any).process;\n if (proc?.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Logs will be written to: ${resolvedPath}`);\n }\n } catch (err) {\n console.error('[Emitochondria] Failed to setup log path:', err);\n }\n }\n\n private flushPendingWrites(): void {\n if (this.pendingWrites.length === 0) return;\n\n for (const content of this.pendingWrites) {\n this.writeToFileSync(content);\n }\n this.pendingWrites = [];\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 modules are loaded, write immediately\n if (this.fs && this.path) {\n this.writeToFileSync(content);\n return;\n }\n\n // If still initializing, queue the write\n if (this.initPromise) {\n this.pendingWrites.push(content);\n return;\n }\n\n // No init happening and modules not loaded - file logging not available\n // This case is already warned about in initFileLoggingAsync\n }\n\n private writeToFileSync(content: string): void {\n if (!this.fs || !this.path) 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 {\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';\nimport { loadNodeModules } from './node-loader.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\n/**\n * Create an emitter with guaranteed async initialization.\n * Use this in ESM environments when you need file logging to be ready immediately.\n *\n * @example\n * const emitter = await createEmitochondriaAsync({ logging: { persist: true } });\n * emitter.emit('ready', {}); // File logging is guaranteed to be available\n */\nexport async function createEmitochondriaAsync<T extends EventMap>(\n options: EmitochondriaOptions<T> = {}\n): Promise<Emitochondria<T>> {\n // Pre-load Node modules before creating the emitter\n if (options.logging?.persist) {\n await loadNodeModules();\n }\n\n return createEmitochondria(options);\n}\n"],"mappings":"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","names":["isNode","fsModule","pathModule","loadPromise","loadAttempted","isNodeEnvironment","tryLoadSync","req","loadNodeModules","fs","path","err","getNodeModules","tryLoadSync","CONFIG_FILENAME","cachedConfig","cachedProjectRoot","findProjectRoot","proc","isNodeEnvironment","fs","path","loaded","getNodeModules","cwd","dir","getProjectRoot","loadConfigFileSync","projectRoot","configPath","cwdPath","content","err","deepMerge","base","override","result","key","mergeConfig","options","fileConfig","clearConfigCache","isBrowser","formatTimestamp","format","timezone","date","parseSize","size","match","value","unit","multipliers","Logger","options","isNodeEnvironment","fs","path","loaded","getNodeModules","loadNodeModules","err","projectRoot","getProjectRoot","resolvedPath","dir","content","entry","timestamp","obj","parts","line","rotationNum","event","payload","formatted","error","_handler","message","details","defaultMaxListenersHandler","_event","_count","_max","createEmitochondria","options","config","mergeConfig","logger","Logger","handlers","wildcards","maxListeners","onMaxListenersExceeded","errorHandler","warned","get","handleError","error","event","handler","safeCall","payload","result","err","checkMaxListeners","count","message","emitter","set","wrapped","wrapper","data","h","promises","n","k","createEmitochondriaAsync","loadNodeModules"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emitochondria",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
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",
@@ -27,9 +27,9 @@
27
27
  "type": "git",
28
28
  "url": "https://github.com/Exudev/emitochondria.git"
29
29
  },
30
- "main": "./dist/index.js",
30
+ "main": "./dist/index.cjs",
31
31
  "module": "./dist/index.mjs",
32
- "types": "./dist/index.d.ts",
32
+ "types": "./dist/index.d.cts",
33
33
  "exports": {
34
34
  ".": {
35
35
  "import": {
@@ -37,8 +37,8 @@
37
37
  "default": "./dist/index.mjs"
38
38
  },
39
39
  "require": {
40
- "types": "./dist/index.d.ts",
41
- "default": "./dist/index.js"
40
+ "types": "./dist/index.d.cts",
41
+ "default": "./dist/index.cjs"
42
42
  }
43
43
  },
44
44
  "./schema.json": "./schema.json"
package/dist/index.js DELETED
@@ -1,3 +0,0 @@
1
- "use strict";var T=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var P=(i,t)=>{for(var n in t)T(i,n,{get:t[n],enumerable:!0})},W=(i,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of _(t))!N.call(i,r)&&r!==n&&T(i,r,{get:()=>t[r],enumerable:!(o=j(t,r))||o.enumerable});return i};var $=i=>W(T({},"__esModule",{value:!0}),i);var D={};P(D,{clearConfigCache:()=>C,createEmitochondria:()=>x,default:()=>x});module.exports=$(D);var L="emitochondria.config.json",d=null,m=null;function k(){if(m!==null)return m;let i=globalThis.process;if(typeof i>"u"||!i?.versions?.node)return m="","";try{let t=globalThis.require;if(!t){let E=i.cwd();return m=E,E}let n=t("path"),o=t("fs"),r=i.cwd();for(;r!==n.dirname(r);){if(o.existsSync(n.join(r,"package.json")))return m=r,r;r=n.dirname(r)}let c=i.cwd();return m=c,c}catch{let t=i.cwd();return m=t,t}}function H(){return k()}function K(){if(d!==null)return d;let i=globalThis.process;if(typeof i>"u"||!i?.versions?.node)return d={},d;try{let t=globalThis.require;if(!t)return d={},d;let n=t("fs"),o=t("path"),r=k(),c=o.join(r,L);if(!n.existsSync(c)){let g=o.join(i.cwd(),L);if(n.existsSync(g)){let p=n.readFileSync(g,"utf-8");return d=JSON.parse(p),i.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Config loaded from: ${g}`),d}return d={},d}let E=n.readFileSync(c,"utf-8");return d=JSON.parse(E),i.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Config loaded from: ${c}`),d}catch(t){return t?.code!=="ENOENT"&&console.warn(`[Emitochondria] Failed to load config: ${t?.message}`),d={},d}}function b(i,t){let n={...i};for(let o in t)t[o]!==void 0&&(typeof t[o]=="object"&&t[o]!==null&&!Array.isArray(t[o])&&typeof n[o]=="object"&&n[o]!==null?n[o]=b(n[o],t[o]):n[o]=t[o]);return n}function F(i={}){let t=K();return b(t,i)}function C(){d=null,m=null}var O=typeof globalThis.process<"u"&&globalThis.process?.versions?.node!=null,A=typeof globalThis.window<"u";function B(i,t){let n=new Date;switch(i){case"unix":return String(n.getTime());case"human":return n.toLocaleString("en-US",{timeZone:t==="utc"?"UTC":void 0,month:"short",day:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0});default:return t==="utc"?n.toISOString():new Date(n.getTime()-n.getTimezoneOffset()*6e4).toISOString().slice(0,-1)}}function R(i){let t=i.match(/^(\d+(?:\.\d+)?)\s*(KB|MB|GB)$/i);if(!t)return 10*1024*1024;let n=parseFloat(t[1]),o=t[2].toUpperCase(),r={KB:1024,MB:1024*1024,GB:1024*1024*1024};return n*(r[o]??10*1024*1024)}var v=class{options;maxSizeBytes;browserWarned=!1;fs=null;path=null;constructor(t={}){this.options={timestamps:t.timestamps??!1,timestampFormat:t.timestampFormat??"iso",timezone:t.timezone??"utc",persist:t.persist??!1,path:t.path??"./logs/emitochondria.log",format:t.format??"text",maxSize:t.maxSize??"10MB",logEvents:t.logEvents??!1,logErrors:t.logErrors??!0,logWarnings:t.logWarnings??!0},this.maxSizeBytes=R(this.options.maxSize),this.options.persist&&this.initFileLogging()}initFileLogging(){if(A){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(O)try{let t=globalThis.require;if(t){this.fs=t("fs"),this.path=t("path");let n=H(),o=this.path.isAbsolute(this.options.path)?this.options.path:this.path.join(n,this.options.path);this.options.path=o;let r=this.path.dirname(o);this.fs.existsSync(r)||this.fs.mkdirSync(r,{recursive:!0}),globalThis.process?.env?.NODE_ENV!=="production"&&console.log(`[Emitochondria] Logs will be written to: ${o}`)}}catch(t){console.error("[Emitochondria] Failed to initialize file logging:",t)}}getTimestamp(){if(this.options.timestamps)return B(this.options.timestampFormat,this.options.timezone)}formatEntry(t){let n=this.getTimestamp();if(this.options.format==="json"){let r={level:t.level,type:t.type,message:t.message};return n&&(r.timestamp=n),t.event&&(r.event=t.event),t.payload!==void 0&&(r.payload=t.payload),t.error&&(r.error=String(t.error)),JSON.stringify(r)}let o=[];return n&&o.push(`[${n}]`),o.push(`[${t.level.toUpperCase()}]`),t.event&&o.push(`[${t.event}]`),o.push(t.message),t.payload!==void 0&&o.push(`| Payload: ${JSON.stringify(t.payload)}`),t.error&&o.push(`| Error: ${t.error}`),o.join(" ")}writeToFile(t){if(!(!this.fs||!this.path||!O))try{let n=t+`
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 t=1;for(;this.fs.existsSync(`${this.options.path}.${t}`);)t++;this.fs.renameSync(this.options.path,`${this.options.path}.${t}`)}catch(t){console.error("[Emitochondria] Failed to rotate log file:",t)}}logEvent(t,n){if(!this.options.logEvents)return;let o={level:"info",type:"event",event:t,message:"Event emitted",payload:n},r=this.formatEntry(o);console.log(r),this.options.persist&&this.writeToFile(r)}logError(t,n,o){if(!this.options.logErrors)return;let r={level:"error",type:"error",event:t,message:"Handler threw an error",error:n},c=this.formatEntry(r);console.error(c),this.options.persist&&this.writeToFile(c)}logWarning(t,n,o){if(!this.options.logWarnings)return;let r={level:"warn",type:"warning",event:t,message:n,payload:o},c=this.formatEntry(r);console.warn(c),this.options.persist&&this.writeToFile(c)}};var q=(i,t,n)=>{};function x(i={}){let t=F(i),n=new v(t.logging),o=new Map,r=new Set,c=t.maxListeners??10,E=i.onMaxListenersExceeded??q,g=t.onError??"default",p=new Set,u=e=>o.get(e)??o.set(e,new Set).get(e),y=(e,a,s)=>{if(n.logError(a,e,s),g==="throw")throw e;typeof g=="function"&&g(e,a,s)},S=(e,a,s)=>{try{let l=e(s);if(l instanceof Promise)return l.catch(f=>y(f,a,e))}catch(l){y(l,a,e)}},z=(e,a)=>{if(c>0&&a>c&&!p.has(e)){p.add(e);let s=`Possible memory leak: ${a} listeners (max: ${c})`;n.logWarning(e,s,{count:a,max:c}),E(e,a,c)}},w={on(e,a){let s=u(e);return s.add(a),z(e,s.size),()=>w.off(e,a)},off(e,a){let s=u(e);if(s.delete(a)){s.size<=c&&p.delete(e);return}for(let l of s)if(l.__original===a){s.delete(l),s.size<=c&&p.delete(e);break}},once(e,a){let s=(l=>(w.off(e,s),a(l)));return s.__original=a,w.on(e,s)},emit(e,...a){let s=a[0];n.logEvent(e,s),u(e).forEach(l=>S(l,e,s)),r.forEach(l=>{try{l(e,s)}catch(f){y(f,e,l)}})},async emitAsync(e,...a){let s=a[0],l=[];n.logEvent(e,s),u(e).forEach(f=>{let h=S(f,e,s);h instanceof Promise&&l.push(h)}),r.forEach(f=>{try{let h=f(e,s);h instanceof Promise&&l.push(h.catch(M=>y(M,e,f)))}catch(h){y(h,e,f)}}),await Promise.all(l)},onAny(e){return r.add(e),()=>w.offAny(e)},offAny(e){r.delete(e)},clear(e){e?(o.delete(e),p.delete(e)):(o.clear(),r.clear(),p.clear())},listenerCount(e){return u(e).size},setErrorHandler(e){g=e},setMaxListeners(e){c=e,e===0&&p.clear()},getMaxListeners(){return c},eventNames(){return Array.from(o.keys()).filter(e=>o.get(e).size>0)},listeners(e){let a=u(e);return Array.from(a).map(s=>s.__original||s)},wildcardListeners(){return Array.from(r)},hasListener(e,a){let s=u(e);if(s.has(a))return!0;for(let l of s)if(l.__original===a)return!0;return!1}};return w}0&&(module.exports={clearConfigCache,createEmitochondria});
3
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/logger.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 { 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;\nlet cachedProjectRoot: string | null = null;\n\n/**\n * Find project root by walking up directories looking for package.json.\n */\nfunction findProjectRoot(): string {\n if (cachedProjectRoot !== null) return cachedProjectRoot;\n\n const proc = (globalThis as any).process;\n if (typeof proc === 'undefined' || !proc?.versions?.node) {\n cachedProjectRoot = '';\n return '';\n }\n\n try {\n const req = (globalThis as any).require;\n if (!req) {\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n }\n\n const path = req('path');\n const fs = req('fs');\n\n let dir: string = proc.cwd();\n\n // Walk up looking for package.json\n while (dir !== path.dirname(dir)) {\n if (fs.existsSync(path.join(dir, 'package.json'))) {\n cachedProjectRoot = dir;\n return dir;\n }\n dir = path.dirname(dir);\n }\n\n // Fallback to cwd if no package.json found\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n } catch {\n const cwd: string = proc.cwd();\n cachedProjectRoot = cwd;\n return cwd;\n }\n}\n\n/**\n * Get the cached project root (for use by logger).\n */\nexport function getProjectRoot(): string {\n return findProjectRoot();\n}\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 // Look for config in project root first\n const projectRoot = findProjectRoot();\n const configPath = path.join(projectRoot, CONFIG_FILENAME);\n\n if (!fs.existsSync(configPath)) {\n // Also try cwd as fallback\n const cwdPath = path.join(proc.cwd(), CONFIG_FILENAME);\n if (fs.existsSync(cwdPath)) {\n const content = fs.readFileSync(cwdPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n if (proc.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Config loaded from: ${cwdPath}`);\n }\n\n return cachedConfig;\n }\n\n cachedConfig = {};\n return cachedConfig;\n }\n\n const content = fs.readFileSync(configPath, 'utf-8');\n cachedConfig = JSON.parse(content) as EmitochondriaConfig;\n\n if (proc.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Config loaded from: ${configPath}`);\n }\n\n return cachedConfig;\n } catch (err: any) {\n if (err?.code !== 'ENOENT') {\n console.warn(`[Emitochondria] Failed to load config: ${err?.message}`);\n }\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 cachedProjectRoot = null;\n}\n","import { LoggingOptions, TimestampFormat, Timezone } from './types.js';\nimport { getProjectRoot } from './config.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 // Resolve path relative to project root (where package.json is)\n const projectRoot = getProjectRoot();\n const resolvedPath = this.path.isAbsolute(this.options.path)\n ? this.options.path\n : this.path.join(projectRoot, this.options.path);\n\n // Update options with resolved path\n this.options.path = resolvedPath;\n\n // Create directory if needed\n const dir = this.path.dirname(resolvedPath);\n if (!this.fs.existsSync(dir)) {\n this.fs.mkdirSync(dir, { recursive: true });\n }\n\n // Log where logs are being written (helpful for debugging)\n const proc = (globalThis as any).process;\n if (proc?.env?.NODE_ENV !== 'production') {\n console.log(`[Emitochondria] Logs will be written to: ${resolvedPath}`);\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 {\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","CONFIG_FILENAME","cachedConfig","cachedProjectRoot","findProjectRoot","proc","req","cwd","path","fs","dir","getProjectRoot","loadConfigFileSync","projectRoot","configPath","cwdPath","content","err","deepMerge","base","override","result","key","mergeConfig","options","fileConfig","clearConfigCache","isNode","isBrowser","formatTimestamp","format","timezone","date","parseSize","size","match","value","unit","multipliers","Logger","options","req","projectRoot","getProjectRoot","resolvedPath","dir","err","entry","timestamp","obj","parts","content","line","rotationNum","event","payload","formatted","error","_handler","message","details","defaultMaxListenersHandler","_event","_count","_max","createEmitochondria","options","config","mergeConfig","logger","Logger","handlers","wildcards","maxListeners","onMaxListenersExceeded","errorHandler","warned","get","handleError","error","event","handler","safeCall","payload","result","err","checkMaxListeners","count","message","emitter","set","wrapped","wrapper","data","h","promises","n","k"]}