@asaidimu/utils-events 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.mts CHANGED
@@ -72,16 +72,18 @@ interface EventBus<TEventMap extends Record<string, any>> {
72
72
  * Subscribes to a specific event by name.
73
73
  * @param eventName - The name of the event to subscribe to.
74
74
  * @param callback - The function to call when the event is emitted.
75
+ * @param options - Extra options to determine the behaviour of the
76
+ * subscription
75
77
  * @returns A function to unsubscribe from the event.
76
78
  */
77
- subscribe: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void) => () => void;
79
+ subscribe: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void, options?: SubscribeOptions) => () => void;
78
80
  /**
79
81
  * Subscribes to an event and automatically unsubscribes after it fires once.
80
82
  * @param eventName - The name of the event to subscribe to.
81
83
  * @param callback - The function to call when the event is emitted.
82
84
  * @returns A function to cancel the one-shot subscription before it fires.
83
85
  */
84
- once: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void) => () => void;
86
+ once: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void, options?: SubscribeOptions) => () => void;
85
87
  /**
86
88
  * Emits an event with a payload to all subscribed listeners.
87
89
  * @param event - An object containing the event name and payload.
@@ -97,10 +99,18 @@ interface EventBus<TEventMap extends Record<string, any>> {
97
99
  metrics: () => EventMetrics;
98
100
  /**
99
101
  * Clears all subscriptions and resets metrics.
100
- * After calling clear(), the bus is fully reset and can be reused —
102
+ *
103
+ * After calling `clear()`, the bus is fully reset and can be reused
101
104
  * cross-tab communication is re-established if it was previously enabled.
105
+ *
106
+ * @param options - Optional configuration object.
107
+ * @param options.permanent - If `true`, the bus becomes permanently unusable after clearing.
108
+ * Defaults to `false`.
109
+ * @returns {void}
102
110
  */
103
- clear: () => void;
111
+ clear: (options?: {
112
+ permanent?: boolean;
113
+ }) => void;
104
114
  }
105
115
  /**
106
116
  * Interface defining the metrics tracked by the EventBus.
@@ -125,6 +135,14 @@ interface EventError extends Error {
125
135
  /** Optional payload that caused the error. */
126
136
  payload?: unknown;
127
137
  }
138
+ interface SubscribeOptions {
139
+ /**
140
+ * Debounce delay in milliseconds. When multiple events arrive in quick
141
+ * succession, the callback runs only after the quiet period ends, using the
142
+ * latest payload. Default = no debouncing.
143
+ */
144
+ debounce?: number;
145
+ }
128
146
 
129
147
  /**
130
148
  * Creates a typed event bus.
@@ -172,4 +190,4 @@ declare class Events<TEventMap extends Record<string, any>> implements EventBus<
172
190
  clear(): void;
173
191
  }
174
192
 
175
- export { type DebouncerResult, type EventBus, type EventBusOptions, type EventError, type EventMetrics, Events, createEventBus };
193
+ export { type DebouncerResult, type EventBus, type EventBusOptions, type EventError, type EventMetrics, Events, type SubscribeOptions, createEventBus };
package/index.d.ts CHANGED
@@ -72,16 +72,18 @@ interface EventBus<TEventMap extends Record<string, any>> {
72
72
  * Subscribes to a specific event by name.
73
73
  * @param eventName - The name of the event to subscribe to.
74
74
  * @param callback - The function to call when the event is emitted.
75
+ * @param options - Extra options to determine the behaviour of the
76
+ * subscription
75
77
  * @returns A function to unsubscribe from the event.
76
78
  */
77
- subscribe: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void) => () => void;
79
+ subscribe: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void, options?: SubscribeOptions) => () => void;
78
80
  /**
79
81
  * Subscribes to an event and automatically unsubscribes after it fires once.
80
82
  * @param eventName - The name of the event to subscribe to.
81
83
  * @param callback - The function to call when the event is emitted.
82
84
  * @returns A function to cancel the one-shot subscription before it fires.
83
85
  */
84
- once: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void) => () => void;
86
+ once: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void, options?: SubscribeOptions) => () => void;
85
87
  /**
86
88
  * Emits an event with a payload to all subscribed listeners.
87
89
  * @param event - An object containing the event name and payload.
@@ -97,10 +99,18 @@ interface EventBus<TEventMap extends Record<string, any>> {
97
99
  metrics: () => EventMetrics;
98
100
  /**
99
101
  * Clears all subscriptions and resets metrics.
100
- * After calling clear(), the bus is fully reset and can be reused —
102
+ *
103
+ * After calling `clear()`, the bus is fully reset and can be reused
101
104
  * cross-tab communication is re-established if it was previously enabled.
105
+ *
106
+ * @param options - Optional configuration object.
107
+ * @param options.permanent - If `true`, the bus becomes permanently unusable after clearing.
108
+ * Defaults to `false`.
109
+ * @returns {void}
102
110
  */
103
- clear: () => void;
111
+ clear: (options?: {
112
+ permanent?: boolean;
113
+ }) => void;
104
114
  }
105
115
  /**
106
116
  * Interface defining the metrics tracked by the EventBus.
@@ -125,6 +135,14 @@ interface EventError extends Error {
125
135
  /** Optional payload that caused the error. */
126
136
  payload?: unknown;
127
137
  }
138
+ interface SubscribeOptions {
139
+ /**
140
+ * Debounce delay in milliseconds. When multiple events arrive in quick
141
+ * succession, the callback runs only after the quiet period ends, using the
142
+ * latest payload. Default = no debouncing.
143
+ */
144
+ debounce?: number;
145
+ }
128
146
 
129
147
  /**
130
148
  * Creates a typed event bus.
@@ -172,4 +190,4 @@ declare class Events<TEventMap extends Record<string, any>> implements EventBus<
172
190
  clear(): void;
173
191
  }
174
192
 
175
- export { type DebouncerResult, type EventBus, type EventBusOptions, type EventError, type EventMetrics, Events, createEventBus };
193
+ export { type DebouncerResult, type EventBus, type EventBusOptions, type EventError, type EventMetrics, Events, type SubscribeOptions, createEventBus };
package/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=class{_delay;_leading;_timer;_pendingFn;_pendingResolvers=[];_leadingFired=!1;constructor(e){this._delay=e?.delay??300,this._leading=e?.leading??!1}do(e){return new Promise((t=>{this._enqueue(e,t)}))}fire(e){this._enqueue(e,void 0)}_enqueue(e,t){if(this._pendingFn=e,t&&this._pendingResolvers.push(t),this._leading&&!this._leadingFired)return this._leadingFired=!0,this._fire(),clearTimeout(this._timer),void(this._timer=setTimeout((()=>{this._leadingFired=!1,void 0!==this._pendingFn&&this._fire()}),this._delay));clearTimeout(this._timer),this._timer=setTimeout((()=>{this._leadingFired=!1,this._fire()}),this._delay)}cancel(){clearTimeout(this._timer),this._timer=void 0,this._pendingFn=void 0,this._leadingFired=!1;const e=this._pendingResolvers.splice(0);for(const t of e)t({status:"cancelled"})}async flush(){return this._pendingFn?(clearTimeout(this._timer),this._timer=void 0,this._leadingFired=!1,this._fire()):null}pending(){return void 0!==this._pendingFn}async _fire(){const e=this._pendingFn,t=this._pendingResolvers.splice(0);let n;this._pendingFn=void 0;try{n={status:"ok",value:await e()}}catch(e){n={status:"error",error:e}}for(const e of t)e(n);return n}};function t(t){const n=t??{},i=n.errorHandler??(e=>console.error("EventBus Error:",e)),s=null!=n.batch?.size,r=n.batch?.size,a=n.batch?.delay??16;if(s&&(r<=0||!Number.isFinite(r)))throw new Error(`EventBus: batch.size must be a positive finite number, got ${r}.`);if(s&&(a<0||!Number.isFinite(a)))throw new Error(`EventBus: batch.delay must be a non-negative finite number, got ${a}.`);const o=n.broadcast?.channel,c=null!=o&&o.length>0,l=new Map;let u=[];const d=s?new e({delay:a}):null;let h=0,m=0;const _=new Map,p=()=>{if(!c)return null;if("undefined"==typeof BroadcastChannel)return console.warn("EventBus: BroadcastChannel is not supported in this environment. Cross-tab notifications are disabled."),null;const e=new BroadcastChannel(o);return e.onmessage=e=>{const{name:t,payload:n}=e.data;v(t,n)},e};let g=p();const f=(e,t)=>{h++,m+=t,_.set(e,(_.get(e)??0)+1)},v=(e,t)=>{const n=l.get(e);if(n&&0!==n.size)for(const s of Array.from(n))try{s(t)}catch(n){const s=n instanceof Error?n:Object.assign(new Error(String(n)),{cause:n}),r=Object.assign(s,{eventName:e,payload:t});i(r)}},b=(e,t)=>{l.has(e)||l.set(e,new Set);const n=l.get(e);return n.add(t),()=>{n.delete(t),0===n.size&&l.delete(e)}},y=()=>{const e=u;u=[];for(const{name:t,payload:n}of e){const e=performance.now();v(t,n),f(t,performance.now()-e)}};return{subscribe:(e,t)=>b(e,t),once:(e,t)=>{let n;return n=b(e,(e=>{n(),t(e)})),n},emit:({name:e,payload:t})=>{if(s)return u.push({name:e,payload:t}),u.length>=r?(d.cancel(),void y()):(d.fire((()=>y())),void g?.postMessage({name:e,payload:t}));const n=performance.now();v(e,t),f(e,performance.now()-n),g?.postMessage({name:e,payload:t})},metrics:()=>({totalEvents:h,activeSubscriptions:Array.from(l.values()).reduce(((e,t)=>e+t.size),0),eventCounts:new Map(_),averageEmitDuration:h>0?m/h:0}),clear:()=>{d?.cancel(),u=[],l.clear(),h=0,m=0,_.clear(),g?.close(),g=p()}}}exports.Events=class{bus;constructor(e){this.bus=t(e)}subscribe(e,t){return this.bus.subscribe(e,t)}once(e,t){return this.bus.once(e,t)}emit(e){return this.bus.emit(e)}metrics(){return this.bus.metrics()}clear(){return this.bus.clear()}},exports.createEventBus=t;
1
+ "use strict";var e=class{_delay;_leading;_timer;_pendingFn;_pendingResolvers=[];_leadingFired=!1;constructor(e){this._delay=e?.delay??300,this._leading=e?.leading??!1}do(e){return new Promise((t=>{this._enqueue(e,t)}))}fire(e){this._enqueue(e,void 0)}_enqueue(e,t){if(this._pendingFn=e,t&&this._pendingResolvers.push(t),this._leading&&!this._leadingFired)return this._leadingFired=!0,this._fire(),clearTimeout(this._timer),void(this._timer=setTimeout((()=>{this._leadingFired=!1,void 0!==this._pendingFn&&this._fire()}),this._delay));clearTimeout(this._timer),this._timer=setTimeout((()=>{this._leadingFired=!1,this._fire()}),this._delay)}cancel(){clearTimeout(this._timer),this._timer=void 0,this._pendingFn=void 0,this._leadingFired=!1;const e=this._pendingResolvers.splice(0);for(const t of e)t({status:"cancelled"})}async flush(){return this._pendingFn?(clearTimeout(this._timer),this._timer=void 0,this._leadingFired=!1,this._fire()):null}pending(){return void 0!==this._pendingFn}async _fire(){const e=this._pendingFn,t=this._pendingResolvers.splice(0);let n;this._pendingFn=void 0;try{n={status:"ok",value:await e()}}catch(e){n={status:"error",error:e}}for(const e of t)e(n);return n}};function t(t){const n=t??{},s=n.errorHandler??(e=>console.error("EventBus Error:",e)),i=null!=n.batch?.size,r=n.batch?.size,a=n.batch?.delay??16;if(i&&(r<=0||!Number.isFinite(r)))throw new Error(`EventBus: batch.size must be a positive finite number, got ${r}.`);if(i&&(a<0||!Number.isFinite(a)))throw new Error(`EventBus: batch.delay must be a non-negative finite number, got ${a}.`);const o=n.broadcast?.channel,c=null!=o&&o.length>0,l=new Map;let u=[];const d=i?new e({delay:a}):null;let h=0,m=0;const _=new Map,p=()=>{if(!c)return null;if("undefined"==typeof BroadcastChannel)return console.warn("EventBus: BroadcastChannel is not supported in this environment. Cross-tab notifications are disabled."),null;const e=new BroadcastChannel(o);return e.onmessage=e=>{const{name:t,payload:n}=e.data;b(t,n)},e};let f=p();const g=(e,t)=>{h++,m+=t,_.set(e,(_.get(e)??0)+1)},b=(e,t)=>{const n=l.get(e);if(n&&0!==n.size)for(const{callback:i}of Array.from(n.values()))try{i(t)}catch(n){const i=n instanceof Error?n:Object.assign(new Error(String(n)),{cause:n}),r=Object.assign(i,{eventName:e,payload:t});s(r)}},v=(t,n,s)=>{l.has(t)||l.set(t,new Map);const i=l.get(t);let r,a;return void 0!==s&&s>0?(a=new e({delay:s,leading:!1}),r=e=>{a.fire((()=>{n(e)}))}):r=n,i.set(n,{callback:r,debouncer:a}),()=>{const e=i.get(n);e&&(e.debouncer?.cancel(),i.delete(n),0===i.size&&l.delete(t))}},y=()=>{const e=u;u=[];for(const{name:t,payload:n}of e){const e=performance.now();b(t,n),g(t,performance.now()-e)}};return{subscribe:(e,t,n)=>v(e,t,n?.debounce),once:(e,t,n)=>{let s;return s=v(e,(e=>{s(),t(e)}),n?.debounce),s},emit:({name:e,payload:t})=>{if(i)return u.push({name:e,payload:t}),u.length>=r?(d.cancel(),void y()):(d.fire((()=>y())),void f?.postMessage({name:e,payload:t}));const n=performance.now();b(e,t),g(e,performance.now()-n),f?.postMessage({name:e,payload:t})},metrics:()=>({totalEvents:h,activeSubscriptions:Array.from(l.values()).reduce(((e,t)=>e+t.size),0),eventCounts:new Map(_),averageEmitDuration:h>0?m/h:0}),clear:e=>{d?.cancel(),u=[];for(const e of l.values())for(const{debouncer:t}of e.values())t?.cancel();l.clear(),h=0,m=0,_.clear(),f?.close(),f=e?.permanent?null:p()}}}exports.Events=class{bus;constructor(e){this.bus=t(e)}subscribe(e,t){return this.bus.subscribe(e,t)}once(e,t){return this.bus.once(e,t)}emit(e){return this.bus.emit(e)}metrics(){return this.bus.metrics()}clear(){return this.bus.clear()}},exports.createEventBus=t;
package/index.mjs CHANGED
@@ -1 +1 @@
1
- var e=class{_delay;_leading;_timer;_pendingFn;_pendingResolvers=[];_leadingFired=!1;constructor(e){this._delay=e?.delay??300,this._leading=e?.leading??!1}do(e){return new Promise((t=>{this._enqueue(e,t)}))}fire(e){this._enqueue(e,void 0)}_enqueue(e,t){if(this._pendingFn=e,t&&this._pendingResolvers.push(t),this._leading&&!this._leadingFired)return this._leadingFired=!0,this._fire(),clearTimeout(this._timer),void(this._timer=setTimeout((()=>{this._leadingFired=!1,void 0!==this._pendingFn&&this._fire()}),this._delay));clearTimeout(this._timer),this._timer=setTimeout((()=>{this._leadingFired=!1,this._fire()}),this._delay)}cancel(){clearTimeout(this._timer),this._timer=void 0,this._pendingFn=void 0,this._leadingFired=!1;const e=this._pendingResolvers.splice(0);for(const t of e)t({status:"cancelled"})}async flush(){return this._pendingFn?(clearTimeout(this._timer),this._timer=void 0,this._leadingFired=!1,this._fire()):null}pending(){return void 0!==this._pendingFn}async _fire(){const e=this._pendingFn,t=this._pendingResolvers.splice(0);let n;this._pendingFn=void 0;try{n={status:"ok",value:await e()}}catch(e){n={status:"error",error:e}}for(const e of t)e(n);return n}};function t(t){const n=t??{},i=n.errorHandler??(e=>console.error("EventBus Error:",e)),s=null!=n.batch?.size,r=n.batch?.size,a=n.batch?.delay??16;if(s&&(r<=0||!Number.isFinite(r)))throw new Error(`EventBus: batch.size must be a positive finite number, got ${r}.`);if(s&&(a<0||!Number.isFinite(a)))throw new Error(`EventBus: batch.delay must be a non-negative finite number, got ${a}.`);const o=n.broadcast?.channel,c=null!=o&&o.length>0,l=new Map;let d=[];const u=s?new e({delay:a}):null;let h=0,m=0;const _=new Map,g=()=>{if(!c)return null;if("undefined"==typeof BroadcastChannel)return console.warn("EventBus: BroadcastChannel is not supported in this environment. Cross-tab notifications are disabled."),null;const e=new BroadcastChannel(o);return e.onmessage=e=>{const{name:t,payload:n}=e.data;b(t,n)},e};let p=g();const f=(e,t)=>{h++,m+=t,_.set(e,(_.get(e)??0)+1)},b=(e,t)=>{const n=l.get(e);if(n&&0!==n.size)for(const s of Array.from(n))try{s(t)}catch(n){const s=n instanceof Error?n:Object.assign(new Error(String(n)),{cause:n}),r=Object.assign(s,{eventName:e,payload:t});i(r)}},v=(e,t)=>{l.has(e)||l.set(e,new Set);const n=l.get(e);return n.add(t),()=>{n.delete(t),0===n.size&&l.delete(e)}},y=()=>{const e=d;d=[];for(const{name:t,payload:n}of e){const e=performance.now();b(t,n),f(t,performance.now()-e)}};return{subscribe:(e,t)=>v(e,t),once:(e,t)=>{let n;return n=v(e,(e=>{n(),t(e)})),n},emit:({name:e,payload:t})=>{if(s)return d.push({name:e,payload:t}),d.length>=r?(u.cancel(),void y()):(u.fire((()=>y())),void p?.postMessage({name:e,payload:t}));const n=performance.now();b(e,t),f(e,performance.now()-n),p?.postMessage({name:e,payload:t})},metrics:()=>({totalEvents:h,activeSubscriptions:Array.from(l.values()).reduce(((e,t)=>e+t.size),0),eventCounts:new Map(_),averageEmitDuration:h>0?m/h:0}),clear:()=>{u?.cancel(),d=[],l.clear(),h=0,m=0,_.clear(),p?.close(),p=g()}}}var n=class{bus;constructor(e){this.bus=t(e)}subscribe(e,t){return this.bus.subscribe(e,t)}once(e,t){return this.bus.once(e,t)}emit(e){return this.bus.emit(e)}metrics(){return this.bus.metrics()}clear(){return this.bus.clear()}};export{n as Events,t as createEventBus};
1
+ var e=class{_delay;_leading;_timer;_pendingFn;_pendingResolvers=[];_leadingFired=!1;constructor(e){this._delay=e?.delay??300,this._leading=e?.leading??!1}do(e){return new Promise((n=>{this._enqueue(e,n)}))}fire(e){this._enqueue(e,void 0)}_enqueue(e,n){if(this._pendingFn=e,n&&this._pendingResolvers.push(n),this._leading&&!this._leadingFired)return this._leadingFired=!0,this._fire(),clearTimeout(this._timer),void(this._timer=setTimeout((()=>{this._leadingFired=!1,void 0!==this._pendingFn&&this._fire()}),this._delay));clearTimeout(this._timer),this._timer=setTimeout((()=>{this._leadingFired=!1,this._fire()}),this._delay)}cancel(){clearTimeout(this._timer),this._timer=void 0,this._pendingFn=void 0,this._leadingFired=!1;const e=this._pendingResolvers.splice(0);for(const n of e)n({status:"cancelled"})}async flush(){return this._pendingFn?(clearTimeout(this._timer),this._timer=void 0,this._leadingFired=!1,this._fire()):null}pending(){return void 0!==this._pendingFn}async _fire(){const e=this._pendingFn,n=this._pendingResolvers.splice(0);let t;this._pendingFn=void 0;try{t={status:"ok",value:await e()}}catch(e){t={status:"error",error:e}}for(const e of n)e(t);return t}};function n(n){const t=n??{},i=t.errorHandler??(e=>console.error("EventBus Error:",e)),s=null!=t.batch?.size,r=t.batch?.size,a=t.batch?.delay??16;if(s&&(r<=0||!Number.isFinite(r)))throw new Error(`EventBus: batch.size must be a positive finite number, got ${r}.`);if(s&&(a<0||!Number.isFinite(a)))throw new Error(`EventBus: batch.delay must be a non-negative finite number, got ${a}.`);const o=t.broadcast?.channel,c=null!=o&&o.length>0,l=new Map;let u=[];const d=s?new e({delay:a}):null;let h=0,m=0;const _=new Map,f=()=>{if(!c)return null;if("undefined"==typeof BroadcastChannel)return console.warn("EventBus: BroadcastChannel is not supported in this environment. Cross-tab notifications are disabled."),null;const e=new BroadcastChannel(o);return e.onmessage=e=>{const{name:n,payload:t}=e.data;b(n,t)},e};let g=f();const p=(e,n)=>{h++,m+=n,_.set(e,(_.get(e)??0)+1)},b=(e,n)=>{const t=l.get(e);if(t&&0!==t.size)for(const{callback:s}of Array.from(t.values()))try{s(n)}catch(t){const s=t instanceof Error?t:Object.assign(new Error(String(t)),{cause:t}),r=Object.assign(s,{eventName:e,payload:n});i(r)}},v=(n,t,i)=>{l.has(n)||l.set(n,new Map);const s=l.get(n);let r,a;return void 0!==i&&i>0?(a=new e({delay:i,leading:!1}),r=e=>{a.fire((()=>{t(e)}))}):r=t,s.set(t,{callback:r,debouncer:a}),()=>{const e=s.get(t);e&&(e.debouncer?.cancel(),s.delete(t),0===s.size&&l.delete(n))}},y=()=>{const e=u;u=[];for(const{name:n,payload:t}of e){const e=performance.now();b(n,t),p(n,performance.now()-e)}};return{subscribe:(e,n,t)=>v(e,n,t?.debounce),once:(e,n,t)=>{let i;return i=v(e,(e=>{i(),n(e)}),t?.debounce),i},emit:({name:e,payload:n})=>{if(s)return u.push({name:e,payload:n}),u.length>=r?(d.cancel(),void y()):(d.fire((()=>y())),void g?.postMessage({name:e,payload:n}));const t=performance.now();b(e,n),p(e,performance.now()-t),g?.postMessage({name:e,payload:n})},metrics:()=>({totalEvents:h,activeSubscriptions:Array.from(l.values()).reduce(((e,n)=>e+n.size),0),eventCounts:new Map(_),averageEmitDuration:h>0?m/h:0}),clear:e=>{d?.cancel(),u=[];for(const e of l.values())for(const{debouncer:n}of e.values())n?.cancel();l.clear(),h=0,m=0,_.clear(),g?.close(),g=e?.permanent?null:f()}}}var t=class{bus;constructor(e){this.bus=n(e)}subscribe(e,n){return this.bus.subscribe(e,n)}once(e,n){return this.bus.once(e,n)}emit(e){return this.bus.emit(e)}metrics(){return this.bus.metrics()}clear(){return this.bus.clear()}};export{t as Events,n as createEventBus};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asaidimu/utils-events",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A lightweight, type-safe event bus implementation for TypeScript applications.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",