You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

hookified

Package Overview
Dependencies
Maintainers
1
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hookified - npm Package Compare versions

Comparing version
1.12.1
to
1.12.2
+570
-1
dist/browser/index.global.js

@@ -1,2 +0,571 @@

"use strict";(()=>{var g=Object.defineProperty;var p=(n,t,e)=>t in n?g(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var i=(n,t,e)=>p(n,typeof t!="symbol"?t+"":t,e);var a=class{constructor(t){i(this,"_eventListeners");i(this,"_maxListeners");i(this,"_logger");i(this,"_throwOnEmitError",!1);this._eventListeners=new Map,this._maxListeners=100,this._logger=t?.logger,t?.throwOnEmitError!==void 0&&(this._throwOnEmitError=t.throwOnEmitError)}get logger(){return this._logger}set logger(t){this._logger=t}get throwOnEmitError(){return this._throwOnEmitError}set throwOnEmitError(t){this._throwOnEmitError=t}once(t,e){let r=(...s)=>{this.off(t,r),e(...s)};return this.on(t,r),this}listenerCount(t){if(t===void 0)return this.getAllListeners().length;let e=this._eventListeners.get(t);return e?e.length:0}eventNames(){return[...this._eventListeners.keys()]}rawListeners(t){return t===void 0?this.getAllListeners():this._eventListeners.get(t)??[]}prependListener(t,e){let r=this._eventListeners.get(t)??[];return r.unshift(e),this._eventListeners.set(t,r),this}prependOnceListener(t,e){let r=(...s)=>{this.off(t,r),e(...s)};return this.prependListener(t,r),this}maxListeners(){return this._maxListeners}addListener(t,e){return this.on(t,e),this}on(t,e){this._eventListeners.has(t)||this._eventListeners.set(t,[]);let r=this._eventListeners.get(t);return r&&(r.length>=this._maxListeners&&console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${r.length+1} ${t} listeners added. Use setMaxListeners() to increase limit.`),r.push(e)),this}removeListener(t,e){return this.off(t,e),this}off(t,e){let r=this._eventListeners.get(t)??[],s=r.indexOf(e);return s!==-1&&r.splice(s,1),r.length===0&&this._eventListeners.delete(t),this}emit(t,...e){let r=!1,s=this._eventListeners.get(t);if(s&&s.length>0)for(let o of s)o(...e),r=!0;if(t==="error"){let o=e[0]instanceof Error?e[0]:new Error(`${e[0]}`);if(this._throwOnEmitError&&!r)throw o}return r}listeners(t){return this._eventListeners.get(t)??[]}removeAllListeners(t){return t!==void 0?this._eventListeners.delete(t):this._eventListeners.clear(),this}setMaxListeners(t){this._maxListeners=t;for(let e of this._eventListeners.values())e.length>t&&e.splice(t)}getAllListeners(){let t=[];for(let e of this._eventListeners.values())t=[...t,...e];return t}};var h=class extends a{constructor(e){super({logger:e?.logger});i(this,"_hooks");i(this,"_throwHookErrors",!1);i(this,"_enforceBeforeAfter",!1);i(this,"_deprecatedHooks");i(this,"_allowDeprecated",!0);this._hooks=new Map,this._deprecatedHooks=e?.deprecatedHooks?new Map(e.deprecatedHooks):new Map,e?.throwHookErrors!==void 0&&(this._throwHookErrors=e.throwHookErrors),e?.enforceBeforeAfter!==void 0&&(this._enforceBeforeAfter=e.enforceBeforeAfter),e?.allowDeprecated!==void 0&&(this._allowDeprecated=e.allowDeprecated)}get hooks(){return this._hooks}get throwHookErrors(){return this._throwHookErrors}set throwHookErrors(e){this._throwHookErrors=e}get enforceBeforeAfter(){return this._enforceBeforeAfter}set enforceBeforeAfter(e){this._enforceBeforeAfter=e}get deprecatedHooks(){return this._deprecatedHooks}set deprecatedHooks(e){this._deprecatedHooks=e}get allowDeprecated(){return this._allowDeprecated}set allowDeprecated(e){this._allowDeprecated=e}validateHookName(e){if(this._enforceBeforeAfter){let r=e.trim().toLocaleLowerCase();if(!r.startsWith("before")&&!r.startsWith("after"))throw new Error(`Hook event "${e}" must start with "before" or "after" when enforceBeforeAfter is enabled`)}}checkDeprecatedHook(e){if(this._deprecatedHooks.has(e)){let r=this._deprecatedHooks.get(e),s=`Hook "${e}" is deprecated${r?`: ${r}`:""}`;return this.emit("warn",{hook:e,message:s}),this.logger?.warn&&this.logger.warn(s),this._allowDeprecated}return!0}onHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=this._hooks.get(e);s?s.push(r):this._hooks.set(e,[r])}onHookEntry(e){this.onHook(e.event,e.handler)}addHook(e,r){this.onHook(e,r)}onHooks(e){for(let r of e)this.onHook(r.event,r.handler)}prependHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=this._hooks.get(e);s?s.unshift(r):this._hooks.set(e,[r])}prependOnceHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=async(...o)=>(this.removeHook(e,s),r(...o));this.prependHook(e,s)}onceHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=async(...o)=>(this.removeHook(e,s),r(...o));this.onHook(e,s)}removeHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=this._hooks.get(e);if(s){let o=s.indexOf(r);o!==-1&&s.splice(o,1)}}removeHooks(e){for(let r of e)this.removeHook(r.event,r.handler)}async hook(e,...r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=this._hooks.get(e);if(s)for(let o of s)try{await o(...r)}catch(c){let l=`${e}: ${c.message}`;if(this.emit("error",new Error(l)),this.logger&&this.logger.error(l),this._throwHookErrors)throw new Error(l)}}async beforeHook(e,...r){await this.hook(`before:${e}`,...r)}async afterHook(e,...r){await this.hook(`after:${e}`,...r)}async callHook(e,...r){await this.hook(e,...r)}getHooks(e){if(this.validateHookName(e),!!this.checkDeprecatedHook(e))return this._hooks.get(e)}clearHooks(){this._hooks.clear()}};})();
"use strict";
(() => {
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/eventified.ts
var Eventified = class {
constructor(options) {
__publicField(this, "_eventListeners");
__publicField(this, "_maxListeners");
__publicField(this, "_logger");
__publicField(this, "_throwOnEmitError", false);
this._eventListeners = /* @__PURE__ */ new Map();
this._maxListeners = 100;
this._logger = options?.logger;
if (options?.throwOnEmitError !== void 0) {
this._throwOnEmitError = options.throwOnEmitError;
}
}
/**
* Gets the logger
* @returns {Logger}
*/
get logger() {
return this._logger;
}
/**
* Sets the logger
* @param {Logger} logger
*/
set logger(logger) {
this._logger = logger;
}
/**
* Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
* @returns {boolean}
*/
get throwOnEmitError() {
return this._throwOnEmitError;
}
/**
* Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
* @param {boolean} value
*/
set throwOnEmitError(value) {
this._throwOnEmitError = value;
}
/**
* Adds a handler function for a specific event that will run only once
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
once(eventName, listener) {
const onceListener = (...arguments_) => {
this.off(eventName, onceListener);
listener(...arguments_);
};
this.on(eventName, onceListener);
return this;
}
/**
* Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners
* @param {string} eventName The event name. Not required
* @returns {number} The number of listeners
*/
listenerCount(eventName) {
if (eventName === void 0) {
return this.getAllListeners().length;
}
const listeners = this._eventListeners.get(eventName);
return listeners ? listeners.length : 0;
}
/**
* Gets an array of event names
* @returns {Array<string | symbol>} An array of event names
*/
eventNames() {
return [...this._eventListeners.keys()];
}
/**
* Gets an array of listeners for a specific event. If no event is provided, it returns all listeners
* @param {string} [event] (Optional) The event name
* @returns {EventListener[]} An array of listeners
*/
rawListeners(event) {
if (event === void 0) {
return this.getAllListeners();
}
return this._eventListeners.get(event) ?? [];
}
/**
* Prepends a listener to the beginning of the listeners array for the specified event
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
prependListener(eventName, listener) {
const listeners = this._eventListeners.get(eventName) ?? [];
listeners.unshift(listener);
this._eventListeners.set(eventName, listeners);
return this;
}
/**
* Prepends a one-time listener to the beginning of the listeners array for the specified event
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
prependOnceListener(eventName, listener) {
const onceListener = (...arguments_) => {
this.off(eventName, onceListener);
listener(...arguments_);
};
this.prependListener(eventName, onceListener);
return this;
}
/**
* Gets the maximum number of listeners that can be added for a single event
* @returns {number} The maximum number of listeners
*/
maxListeners() {
return this._maxListeners;
}
/**
* Adds a listener for a specific event. It is an alias for the on() method
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
addListener(event, listener) {
this.on(event, listener);
return this;
}
/**
* Adds a listener for a specific event
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
on(event, listener) {
if (!this._eventListeners.has(event)) {
this._eventListeners.set(event, []);
}
const listeners = this._eventListeners.get(event);
if (listeners) {
if (listeners.length >= this._maxListeners) {
console.warn(
`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
);
}
listeners.push(listener);
}
return this;
}
/**
* Removes a listener for a specific event. It is an alias for the off() method
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
removeListener(event, listener) {
this.off(event, listener);
return this;
}
/**
* Removes a listener for a specific event
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
off(event, listener) {
const listeners = this._eventListeners.get(event) ?? [];
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length === 0) {
this._eventListeners.delete(event);
}
return this;
}
/**
* Calls all listeners for a specific event
* @param {string | symbol} event
* @param arguments_ The arguments to pass to the listeners
* @returns {boolean} Returns true if the event had listeners, false otherwise
*/
emit(event, ...arguments_) {
let result = false;
const listeners = this._eventListeners.get(event);
if (listeners && listeners.length > 0) {
for (const listener of listeners) {
listener(...arguments_);
result = true;
}
}
if (event === "error") {
const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
if (this._throwOnEmitError && !result) {
throw error;
}
}
return result;
}
/**
* Gets all listeners for a specific event. If no event is provided, it returns all listeners
* @param {string} [event] (Optional) The event name
* @returns {EventListener[]} An array of listeners
*/
listeners(event) {
return this._eventListeners.get(event) ?? [];
}
/**
* Removes all listeners for a specific event. If no event is provided, it removes all listeners
* @param {string} [event] (Optional) The event name
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
removeAllListeners(event) {
if (event !== void 0) {
this._eventListeners.delete(event);
} else {
this._eventListeners.clear();
}
return this;
}
/**
* Sets the maximum number of listeners that can be added for a single event
* @param {number} n The maximum number of listeners
* @returns {void}
*/
setMaxListeners(n) {
this._maxListeners = n;
for (const listeners of this._eventListeners.values()) {
if (listeners.length > n) {
listeners.splice(n);
}
}
}
/**
* Gets all listeners
* @returns {EventListener[]} An array of listeners
*/
getAllListeners() {
let result = [];
for (const listeners of this._eventListeners.values()) {
result = [...result, ...listeners];
}
return result;
}
};
// src/index.ts
var Hookified = class extends Eventified {
constructor(options) {
super({ logger: options?.logger });
__publicField(this, "_hooks");
__publicField(this, "_throwHookErrors", false);
__publicField(this, "_enforceBeforeAfter", false);
__publicField(this, "_deprecatedHooks");
__publicField(this, "_allowDeprecated", true);
this._hooks = /* @__PURE__ */ new Map();
this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
if (options?.throwHookErrors !== void 0) {
this._throwHookErrors = options.throwHookErrors;
}
if (options?.enforceBeforeAfter !== void 0) {
this._enforceBeforeAfter = options.enforceBeforeAfter;
}
if (options?.allowDeprecated !== void 0) {
this._allowDeprecated = options.allowDeprecated;
}
}
/**
* Gets all hooks
* @returns {Map<string, Hook[]>}
*/
get hooks() {
return this._hooks;
}
/**
* Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
* @returns {boolean}
*/
get throwHookErrors() {
return this._throwHookErrors;
}
/**
* Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
* @param {boolean} value
*/
set throwHookErrors(value) {
this._throwHookErrors = value;
}
/**
* Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
* @returns {boolean}
* @default false
*/
get enforceBeforeAfter() {
return this._enforceBeforeAfter;
}
/**
* Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
* @param {boolean} value
*/
set enforceBeforeAfter(value) {
this._enforceBeforeAfter = value;
}
/**
* Gets the map of deprecated hook names to deprecation messages.
* @returns {Map<string, string>}
*/
get deprecatedHooks() {
return this._deprecatedHooks;
}
/**
* Sets the map of deprecated hook names to deprecation messages.
* @param {Map<string, string>} value
*/
set deprecatedHooks(value) {
this._deprecatedHooks = value;
}
/**
* Gets whether deprecated hooks are allowed to be registered and executed. Default is true.
* @returns {boolean}
*/
get allowDeprecated() {
return this._allowDeprecated;
}
/**
* Sets whether deprecated hooks are allowed to be registered and executed. Default is true.
* @param {boolean} value
*/
set allowDeprecated(value) {
this._allowDeprecated = value;
}
/**
* Validates hook event name if enforceBeforeAfter is enabled
* @param {string} event - The event name to validate
* @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
*/
validateHookName(event) {
if (this._enforceBeforeAfter) {
const eventValue = event.trim().toLocaleLowerCase();
if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
throw new Error(
`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
);
}
}
}
/**
* Checks if a hook is deprecated and emits a warning if it is
* @param {string} event - The event name to check
* @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
*/
checkDeprecatedHook(event) {
if (this._deprecatedHooks.has(event)) {
const message = this._deprecatedHooks.get(event);
const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
this.emit("warn", { hook: event, message: warningMessage });
if (this.logger?.warn) {
this.logger.warn(warningMessage);
}
return this._allowDeprecated;
}
return true;
}
/**
* Adds a handler function for a specific event
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
onHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
eventHandlers.push(handler);
} else {
this._hooks.set(event, [handler]);
}
}
/**
* Adds a handler function for a specific event that runs before all other handlers
* @param {HookEntry} hookEntry
* @returns {void}
*/
onHookEntry(hookEntry) {
this.onHook(hookEntry.event, hookEntry.handler);
}
/**
* Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
addHook(event, handler) {
this.onHook(event, handler);
}
/**
* Adds a handler function for a specific event
* @param {Array<HookEntry>} hooks
* @returns {void}
*/
onHooks(hooks) {
for (const hook of hooks) {
this.onHook(hook.event, hook.handler);
}
}
/**
* Adds a handler function for a specific event that runs before all other handlers
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
prependHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
eventHandlers.unshift(handler);
} else {
this._hooks.set(event, [handler]);
}
}
/**
* Adds a handler that only executes once for a specific event before all other handlers
* @param event
* @param handler
*/
prependOnceHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const hook = async (...arguments_) => {
this.removeHook(event, hook);
return handler(...arguments_);
};
this.prependHook(event, hook);
}
/**
* Adds a handler that only executes once for a specific event
* @param event
* @param handler
*/
onceHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const hook = async (...arguments_) => {
this.removeHook(event, hook);
return handler(...arguments_);
};
this.onHook(event, hook);
}
/**
* Removes a handler function for a specific event
* @param {string} event
* @param {Hook} handler
* @returns {void}
*/
removeHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
const index = eventHandlers.indexOf(handler);
if (index !== -1) {
eventHandlers.splice(index, 1);
}
}
}
/**
* Removes all handlers for a specific event
* @param {Array<HookEntry>} hooks
* @returns {void}
*/
removeHooks(hooks) {
for (const hook of hooks) {
this.removeHook(hook.event, hook.handler);
}
}
/**
* Calls all handlers for a specific event
* @param {string} event
* @param {T[]} arguments_
* @returns {Promise<void>}
*/
async hook(event, ...arguments_) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
for (const handler of eventHandlers) {
try {
await handler(...arguments_);
} catch (error) {
const message = `${event}: ${error.message}`;
this.emit("error", new Error(message));
if (this.logger) {
this.logger.error(message);
}
if (this._throwHookErrors) {
throw new Error(message);
}
}
}
}
}
/**
* Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.
* @param {string} event - The event name
* @param {T[]} arguments_ - The arguments to pass to the hook
*/
async beforeHook(event, ...arguments_) {
await this.hook(`before:${event}`, ...arguments_);
}
/**
* Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.
* @param {string} event - The event name
* @param {T[]} arguments_ - The arguments to pass to the hook
*/
async afterHook(event, ...arguments_) {
await this.hook(`after:${event}`, ...arguments_);
}
/**
* Calls all handlers for a specific event. This is an alias for `hook` and is provided for
* compatibility with other libraries that use the `callHook` method.
* @param {string} event
* @param {T[]} arguments_
* @returns {Promise<void>}
*/
async callHook(event, ...arguments_) {
await this.hook(event, ...arguments_);
}
/**
* Gets all hooks for a specific event
* @param {string} event
* @returns {Hook[]}
*/
getHooks(event) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return void 0;
}
return this._hooks.get(event);
}
/**
* Removes all hooks
* @returns {void}
*/
clearHooks() {
this._hooks.clear();
}
};
})();
//# sourceMappingURL=index.global.js.map

@@ -1,2 +0,572 @@

var g=Object.defineProperty;var p=(n,t,e)=>t in n?g(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var i=(n,t,e)=>p(n,typeof t!="symbol"?t+"":t,e);var a=class{constructor(t){i(this,"_eventListeners");i(this,"_maxListeners");i(this,"_logger");i(this,"_throwOnEmitError",!1);this._eventListeners=new Map,this._maxListeners=100,this._logger=t?.logger,t?.throwOnEmitError!==void 0&&(this._throwOnEmitError=t.throwOnEmitError)}get logger(){return this._logger}set logger(t){this._logger=t}get throwOnEmitError(){return this._throwOnEmitError}set throwOnEmitError(t){this._throwOnEmitError=t}once(t,e){let r=(...s)=>{this.off(t,r),e(...s)};return this.on(t,r),this}listenerCount(t){if(t===void 0)return this.getAllListeners().length;let e=this._eventListeners.get(t);return e?e.length:0}eventNames(){return[...this._eventListeners.keys()]}rawListeners(t){return t===void 0?this.getAllListeners():this._eventListeners.get(t)??[]}prependListener(t,e){let r=this._eventListeners.get(t)??[];return r.unshift(e),this._eventListeners.set(t,r),this}prependOnceListener(t,e){let r=(...s)=>{this.off(t,r),e(...s)};return this.prependListener(t,r),this}maxListeners(){return this._maxListeners}addListener(t,e){return this.on(t,e),this}on(t,e){this._eventListeners.has(t)||this._eventListeners.set(t,[]);let r=this._eventListeners.get(t);return r&&(r.length>=this._maxListeners&&console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${r.length+1} ${t} listeners added. Use setMaxListeners() to increase limit.`),r.push(e)),this}removeListener(t,e){return this.off(t,e),this}off(t,e){let r=this._eventListeners.get(t)??[],s=r.indexOf(e);return s!==-1&&r.splice(s,1),r.length===0&&this._eventListeners.delete(t),this}emit(t,...e){let r=!1,s=this._eventListeners.get(t);if(s&&s.length>0)for(let o of s)o(...e),r=!0;if(t==="error"){let o=e[0]instanceof Error?e[0]:new Error(`${e[0]}`);if(this._throwOnEmitError&&!r)throw o}return r}listeners(t){return this._eventListeners.get(t)??[]}removeAllListeners(t){return t!==void 0?this._eventListeners.delete(t):this._eventListeners.clear(),this}setMaxListeners(t){this._maxListeners=t;for(let e of this._eventListeners.values())e.length>t&&e.splice(t)}getAllListeners(){let t=[];for(let e of this._eventListeners.values())t=[...t,...e];return t}};var h=class extends a{constructor(e){super({logger:e?.logger});i(this,"_hooks");i(this,"_throwHookErrors",!1);i(this,"_enforceBeforeAfter",!1);i(this,"_deprecatedHooks");i(this,"_allowDeprecated",!0);this._hooks=new Map,this._deprecatedHooks=e?.deprecatedHooks?new Map(e.deprecatedHooks):new Map,e?.throwHookErrors!==void 0&&(this._throwHookErrors=e.throwHookErrors),e?.enforceBeforeAfter!==void 0&&(this._enforceBeforeAfter=e.enforceBeforeAfter),e?.allowDeprecated!==void 0&&(this._allowDeprecated=e.allowDeprecated)}get hooks(){return this._hooks}get throwHookErrors(){return this._throwHookErrors}set throwHookErrors(e){this._throwHookErrors=e}get enforceBeforeAfter(){return this._enforceBeforeAfter}set enforceBeforeAfter(e){this._enforceBeforeAfter=e}get deprecatedHooks(){return this._deprecatedHooks}set deprecatedHooks(e){this._deprecatedHooks=e}get allowDeprecated(){return this._allowDeprecated}set allowDeprecated(e){this._allowDeprecated=e}validateHookName(e){if(this._enforceBeforeAfter){let r=e.trim().toLocaleLowerCase();if(!r.startsWith("before")&&!r.startsWith("after"))throw new Error(`Hook event "${e}" must start with "before" or "after" when enforceBeforeAfter is enabled`)}}checkDeprecatedHook(e){if(this._deprecatedHooks.has(e)){let r=this._deprecatedHooks.get(e),s=`Hook "${e}" is deprecated${r?`: ${r}`:""}`;return this.emit("warn",{hook:e,message:s}),this.logger?.warn&&this.logger.warn(s),this._allowDeprecated}return!0}onHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=this._hooks.get(e);s?s.push(r):this._hooks.set(e,[r])}onHookEntry(e){this.onHook(e.event,e.handler)}addHook(e,r){this.onHook(e,r)}onHooks(e){for(let r of e)this.onHook(r.event,r.handler)}prependHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=this._hooks.get(e);s?s.unshift(r):this._hooks.set(e,[r])}prependOnceHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=async(...o)=>(this.removeHook(e,s),r(...o));this.prependHook(e,s)}onceHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=async(...o)=>(this.removeHook(e,s),r(...o));this.onHook(e,s)}removeHook(e,r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=this._hooks.get(e);if(s){let o=s.indexOf(r);o!==-1&&s.splice(o,1)}}removeHooks(e){for(let r of e)this.removeHook(r.event,r.handler)}async hook(e,...r){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let s=this._hooks.get(e);if(s)for(let o of s)try{await o(...r)}catch(c){let l=`${e}: ${c.message}`;if(this.emit("error",new Error(l)),this.logger&&this.logger.error(l),this._throwHookErrors)throw new Error(l)}}async beforeHook(e,...r){await this.hook(`before:${e}`,...r)}async afterHook(e,...r){await this.hook(`after:${e}`,...r)}async callHook(e,...r){await this.hook(e,...r)}getHooks(e){if(this.validateHookName(e),!!this.checkDeprecatedHook(e))return this._hooks.get(e)}clearHooks(){this._hooks.clear()}};export{a as Eventified,h as Hookified};
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/eventified.ts
var Eventified = class {
constructor(options) {
__publicField(this, "_eventListeners");
__publicField(this, "_maxListeners");
__publicField(this, "_logger");
__publicField(this, "_throwOnEmitError", false);
this._eventListeners = /* @__PURE__ */ new Map();
this._maxListeners = 100;
this._logger = options?.logger;
if (options?.throwOnEmitError !== void 0) {
this._throwOnEmitError = options.throwOnEmitError;
}
}
/**
* Gets the logger
* @returns {Logger}
*/
get logger() {
return this._logger;
}
/**
* Sets the logger
* @param {Logger} logger
*/
set logger(logger) {
this._logger = logger;
}
/**
* Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
* @returns {boolean}
*/
get throwOnEmitError() {
return this._throwOnEmitError;
}
/**
* Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
* @param {boolean} value
*/
set throwOnEmitError(value) {
this._throwOnEmitError = value;
}
/**
* Adds a handler function for a specific event that will run only once
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
once(eventName, listener) {
const onceListener = (...arguments_) => {
this.off(eventName, onceListener);
listener(...arguments_);
};
this.on(eventName, onceListener);
return this;
}
/**
* Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners
* @param {string} eventName The event name. Not required
* @returns {number} The number of listeners
*/
listenerCount(eventName) {
if (eventName === void 0) {
return this.getAllListeners().length;
}
const listeners = this._eventListeners.get(eventName);
return listeners ? listeners.length : 0;
}
/**
* Gets an array of event names
* @returns {Array<string | symbol>} An array of event names
*/
eventNames() {
return [...this._eventListeners.keys()];
}
/**
* Gets an array of listeners for a specific event. If no event is provided, it returns all listeners
* @param {string} [event] (Optional) The event name
* @returns {EventListener[]} An array of listeners
*/
rawListeners(event) {
if (event === void 0) {
return this.getAllListeners();
}
return this._eventListeners.get(event) ?? [];
}
/**
* Prepends a listener to the beginning of the listeners array for the specified event
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
prependListener(eventName, listener) {
const listeners = this._eventListeners.get(eventName) ?? [];
listeners.unshift(listener);
this._eventListeners.set(eventName, listeners);
return this;
}
/**
* Prepends a one-time listener to the beginning of the listeners array for the specified event
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
prependOnceListener(eventName, listener) {
const onceListener = (...arguments_) => {
this.off(eventName, onceListener);
listener(...arguments_);
};
this.prependListener(eventName, onceListener);
return this;
}
/**
* Gets the maximum number of listeners that can be added for a single event
* @returns {number} The maximum number of listeners
*/
maxListeners() {
return this._maxListeners;
}
/**
* Adds a listener for a specific event. It is an alias for the on() method
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
addListener(event, listener) {
this.on(event, listener);
return this;
}
/**
* Adds a listener for a specific event
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
on(event, listener) {
if (!this._eventListeners.has(event)) {
this._eventListeners.set(event, []);
}
const listeners = this._eventListeners.get(event);
if (listeners) {
if (listeners.length >= this._maxListeners) {
console.warn(
`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
);
}
listeners.push(listener);
}
return this;
}
/**
* Removes a listener for a specific event. It is an alias for the off() method
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
removeListener(event, listener) {
this.off(event, listener);
return this;
}
/**
* Removes a listener for a specific event
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
off(event, listener) {
const listeners = this._eventListeners.get(event) ?? [];
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length === 0) {
this._eventListeners.delete(event);
}
return this;
}
/**
* Calls all listeners for a specific event
* @param {string | symbol} event
* @param arguments_ The arguments to pass to the listeners
* @returns {boolean} Returns true if the event had listeners, false otherwise
*/
emit(event, ...arguments_) {
let result = false;
const listeners = this._eventListeners.get(event);
if (listeners && listeners.length > 0) {
for (const listener of listeners) {
listener(...arguments_);
result = true;
}
}
if (event === "error") {
const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
if (this._throwOnEmitError && !result) {
throw error;
}
}
return result;
}
/**
* Gets all listeners for a specific event. If no event is provided, it returns all listeners
* @param {string} [event] (Optional) The event name
* @returns {EventListener[]} An array of listeners
*/
listeners(event) {
return this._eventListeners.get(event) ?? [];
}
/**
* Removes all listeners for a specific event. If no event is provided, it removes all listeners
* @param {string} [event] (Optional) The event name
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
removeAllListeners(event) {
if (event !== void 0) {
this._eventListeners.delete(event);
} else {
this._eventListeners.clear();
}
return this;
}
/**
* Sets the maximum number of listeners that can be added for a single event
* @param {number} n The maximum number of listeners
* @returns {void}
*/
setMaxListeners(n) {
this._maxListeners = n;
for (const listeners of this._eventListeners.values()) {
if (listeners.length > n) {
listeners.splice(n);
}
}
}
/**
* Gets all listeners
* @returns {EventListener[]} An array of listeners
*/
getAllListeners() {
let result = [];
for (const listeners of this._eventListeners.values()) {
result = [...result, ...listeners];
}
return result;
}
};
// src/index.ts
var Hookified = class extends Eventified {
constructor(options) {
super({ logger: options?.logger });
__publicField(this, "_hooks");
__publicField(this, "_throwHookErrors", false);
__publicField(this, "_enforceBeforeAfter", false);
__publicField(this, "_deprecatedHooks");
__publicField(this, "_allowDeprecated", true);
this._hooks = /* @__PURE__ */ new Map();
this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
if (options?.throwHookErrors !== void 0) {
this._throwHookErrors = options.throwHookErrors;
}
if (options?.enforceBeforeAfter !== void 0) {
this._enforceBeforeAfter = options.enforceBeforeAfter;
}
if (options?.allowDeprecated !== void 0) {
this._allowDeprecated = options.allowDeprecated;
}
}
/**
* Gets all hooks
* @returns {Map<string, Hook[]>}
*/
get hooks() {
return this._hooks;
}
/**
* Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
* @returns {boolean}
*/
get throwHookErrors() {
return this._throwHookErrors;
}
/**
* Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
* @param {boolean} value
*/
set throwHookErrors(value) {
this._throwHookErrors = value;
}
/**
* Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
* @returns {boolean}
* @default false
*/
get enforceBeforeAfter() {
return this._enforceBeforeAfter;
}
/**
* Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
* @param {boolean} value
*/
set enforceBeforeAfter(value) {
this._enforceBeforeAfter = value;
}
/**
* Gets the map of deprecated hook names to deprecation messages.
* @returns {Map<string, string>}
*/
get deprecatedHooks() {
return this._deprecatedHooks;
}
/**
* Sets the map of deprecated hook names to deprecation messages.
* @param {Map<string, string>} value
*/
set deprecatedHooks(value) {
this._deprecatedHooks = value;
}
/**
* Gets whether deprecated hooks are allowed to be registered and executed. Default is true.
* @returns {boolean}
*/
get allowDeprecated() {
return this._allowDeprecated;
}
/**
* Sets whether deprecated hooks are allowed to be registered and executed. Default is true.
* @param {boolean} value
*/
set allowDeprecated(value) {
this._allowDeprecated = value;
}
/**
* Validates hook event name if enforceBeforeAfter is enabled
* @param {string} event - The event name to validate
* @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
*/
validateHookName(event) {
if (this._enforceBeforeAfter) {
const eventValue = event.trim().toLocaleLowerCase();
if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
throw new Error(
`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
);
}
}
}
/**
* Checks if a hook is deprecated and emits a warning if it is
* @param {string} event - The event name to check
* @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
*/
checkDeprecatedHook(event) {
if (this._deprecatedHooks.has(event)) {
const message = this._deprecatedHooks.get(event);
const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
this.emit("warn", { hook: event, message: warningMessage });
if (this.logger?.warn) {
this.logger.warn(warningMessage);
}
return this._allowDeprecated;
}
return true;
}
/**
* Adds a handler function for a specific event
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
onHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
eventHandlers.push(handler);
} else {
this._hooks.set(event, [handler]);
}
}
/**
* Adds a handler function for a specific event that runs before all other handlers
* @param {HookEntry} hookEntry
* @returns {void}
*/
onHookEntry(hookEntry) {
this.onHook(hookEntry.event, hookEntry.handler);
}
/**
* Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
addHook(event, handler) {
this.onHook(event, handler);
}
/**
* Adds a handler function for a specific event
* @param {Array<HookEntry>} hooks
* @returns {void}
*/
onHooks(hooks) {
for (const hook of hooks) {
this.onHook(hook.event, hook.handler);
}
}
/**
* Adds a handler function for a specific event that runs before all other handlers
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
prependHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
eventHandlers.unshift(handler);
} else {
this._hooks.set(event, [handler]);
}
}
/**
* Adds a handler that only executes once for a specific event before all other handlers
* @param event
* @param handler
*/
prependOnceHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const hook = async (...arguments_) => {
this.removeHook(event, hook);
return handler(...arguments_);
};
this.prependHook(event, hook);
}
/**
* Adds a handler that only executes once for a specific event
* @param event
* @param handler
*/
onceHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const hook = async (...arguments_) => {
this.removeHook(event, hook);
return handler(...arguments_);
};
this.onHook(event, hook);
}
/**
* Removes a handler function for a specific event
* @param {string} event
* @param {Hook} handler
* @returns {void}
*/
removeHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
const index = eventHandlers.indexOf(handler);
if (index !== -1) {
eventHandlers.splice(index, 1);
}
}
}
/**
* Removes all handlers for a specific event
* @param {Array<HookEntry>} hooks
* @returns {void}
*/
removeHooks(hooks) {
for (const hook of hooks) {
this.removeHook(hook.event, hook.handler);
}
}
/**
* Calls all handlers for a specific event
* @param {string} event
* @param {T[]} arguments_
* @returns {Promise<void>}
*/
async hook(event, ...arguments_) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
for (const handler of eventHandlers) {
try {
await handler(...arguments_);
} catch (error) {
const message = `${event}: ${error.message}`;
this.emit("error", new Error(message));
if (this.logger) {
this.logger.error(message);
}
if (this._throwHookErrors) {
throw new Error(message);
}
}
}
}
}
/**
* Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.
* @param {string} event - The event name
* @param {T[]} arguments_ - The arguments to pass to the hook
*/
async beforeHook(event, ...arguments_) {
await this.hook(`before:${event}`, ...arguments_);
}
/**
* Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.
* @param {string} event - The event name
* @param {T[]} arguments_ - The arguments to pass to the hook
*/
async afterHook(event, ...arguments_) {
await this.hook(`after:${event}`, ...arguments_);
}
/**
* Calls all handlers for a specific event. This is an alias for `hook` and is provided for
* compatibility with other libraries that use the `callHook` method.
* @param {string} event
* @param {T[]} arguments_
* @returns {Promise<void>}
*/
async callHook(event, ...arguments_) {
await this.hook(event, ...arguments_);
}
/**
* Gets all hooks for a specific event
* @param {string} event
* @returns {Hook[]}
*/
getHooks(event) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return void 0;
}
return this._hooks.get(event);
}
/**
* Removes all hooks
* @returns {void}
*/
clearHooks() {
this._hooks.clear();
}
};
export {
Eventified,
Hookified
};
//# sourceMappingURL=index.js.map

@@ -1,1 +0,595 @@

"use strict";var l=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var m=(o,e)=>{for(var t in e)l(o,t,{get:e[t],enumerable:!0})},d=(o,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of g(e))!p.call(o,s)&&s!==t&&l(o,s,{get:()=>e[s],enumerable:!(r=c(e,s))||r.enumerable});return o};var f=o=>d(l({},"__esModule",{value:!0}),o);var u={};m(u,{Eventified:()=>i,Hookified:()=>h});module.exports=f(u);var i=class{_eventListeners;_maxListeners;_logger;_throwOnEmitError=!1;constructor(e){this._eventListeners=new Map,this._maxListeners=100,this._logger=e?.logger,e?.throwOnEmitError!==void 0&&(this._throwOnEmitError=e.throwOnEmitError)}get logger(){return this._logger}set logger(e){this._logger=e}get throwOnEmitError(){return this._throwOnEmitError}set throwOnEmitError(e){this._throwOnEmitError=e}once(e,t){let r=(...s)=>{this.off(e,r),t(...s)};return this.on(e,r),this}listenerCount(e){if(e===void 0)return this.getAllListeners().length;let t=this._eventListeners.get(e);return t?t.length:0}eventNames(){return[...this._eventListeners.keys()]}rawListeners(e){return e===void 0?this.getAllListeners():this._eventListeners.get(e)??[]}prependListener(e,t){let r=this._eventListeners.get(e)??[];return r.unshift(t),this._eventListeners.set(e,r),this}prependOnceListener(e,t){let r=(...s)=>{this.off(e,r),t(...s)};return this.prependListener(e,r),this}maxListeners(){return this._maxListeners}addListener(e,t){return this.on(e,t),this}on(e,t){this._eventListeners.has(e)||this._eventListeners.set(e,[]);let r=this._eventListeners.get(e);return r&&(r.length>=this._maxListeners&&console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${r.length+1} ${e} listeners added. Use setMaxListeners() to increase limit.`),r.push(t)),this}removeListener(e,t){return this.off(e,t),this}off(e,t){let r=this._eventListeners.get(e)??[],s=r.indexOf(t);return s!==-1&&r.splice(s,1),r.length===0&&this._eventListeners.delete(e),this}emit(e,...t){let r=!1,s=this._eventListeners.get(e);if(s&&s.length>0)for(let n of s)n(...t),r=!0;if(e==="error"){let n=t[0]instanceof Error?t[0]:new Error(`${t[0]}`);if(this._throwOnEmitError&&!r)throw n}return r}listeners(e){return this._eventListeners.get(e)??[]}removeAllListeners(e){return e!==void 0?this._eventListeners.delete(e):this._eventListeners.clear(),this}setMaxListeners(e){this._maxListeners=e;for(let t of this._eventListeners.values())t.length>e&&t.splice(e)}getAllListeners(){let e=[];for(let t of this._eventListeners.values())e=[...e,...t];return e}};var h=class extends i{_hooks;_throwHookErrors=!1;_enforceBeforeAfter=!1;_deprecatedHooks;_allowDeprecated=!0;constructor(e){super({logger:e?.logger}),this._hooks=new Map,this._deprecatedHooks=e?.deprecatedHooks?new Map(e.deprecatedHooks):new Map,e?.throwHookErrors!==void 0&&(this._throwHookErrors=e.throwHookErrors),e?.enforceBeforeAfter!==void 0&&(this._enforceBeforeAfter=e.enforceBeforeAfter),e?.allowDeprecated!==void 0&&(this._allowDeprecated=e.allowDeprecated)}get hooks(){return this._hooks}get throwHookErrors(){return this._throwHookErrors}set throwHookErrors(e){this._throwHookErrors=e}get enforceBeforeAfter(){return this._enforceBeforeAfter}set enforceBeforeAfter(e){this._enforceBeforeAfter=e}get deprecatedHooks(){return this._deprecatedHooks}set deprecatedHooks(e){this._deprecatedHooks=e}get allowDeprecated(){return this._allowDeprecated}set allowDeprecated(e){this._allowDeprecated=e}validateHookName(e){if(this._enforceBeforeAfter){let t=e.trim().toLocaleLowerCase();if(!t.startsWith("before")&&!t.startsWith("after"))throw new Error(`Hook event "${e}" must start with "before" or "after" when enforceBeforeAfter is enabled`)}}checkDeprecatedHook(e){if(this._deprecatedHooks.has(e)){let t=this._deprecatedHooks.get(e),r=`Hook "${e}" is deprecated${t?`: ${t}`:""}`;return this.emit("warn",{hook:e,message:r}),this.logger?.warn&&this.logger.warn(r),this._allowDeprecated}return!0}onHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);r?r.push(t):this._hooks.set(e,[t])}onHookEntry(e){this.onHook(e.event,e.handler)}addHook(e,t){this.onHook(e,t)}onHooks(e){for(let t of e)this.onHook(t.event,t.handler)}prependHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);r?r.unshift(t):this._hooks.set(e,[t])}prependOnceHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=async(...s)=>(this.removeHook(e,r),t(...s));this.prependHook(e,r)}onceHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=async(...s)=>(this.removeHook(e,r),t(...s));this.onHook(e,r)}removeHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);if(r){let s=r.indexOf(t);s!==-1&&r.splice(s,1)}}removeHooks(e){for(let t of e)this.removeHook(t.event,t.handler)}async hook(e,...t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);if(r)for(let s of r)try{await s(...t)}catch(n){let a=`${e}: ${n.message}`;if(this.emit("error",new Error(a)),this.logger&&this.logger.error(a),this._throwHookErrors)throw new Error(a)}}async beforeHook(e,...t){await this.hook(`before:${e}`,...t)}async afterHook(e,...t){await this.hook(`after:${e}`,...t)}async callHook(e,...t){await this.hook(e,...t)}getHooks(e){if(this.validateHookName(e),!!this.checkDeprecatedHook(e))return this._hooks.get(e)}clearHooks(){this._hooks.clear()}};0&&(module.exports={Eventified,Hookified});
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
Eventified: () => Eventified,
Hookified: () => Hookified
});
module.exports = __toCommonJS(index_exports);
// src/eventified.ts
var Eventified = class {
_eventListeners;
_maxListeners;
_logger;
_throwOnEmitError = false;
constructor(options) {
this._eventListeners = /* @__PURE__ */ new Map();
this._maxListeners = 100;
this._logger = options?.logger;
if (options?.throwOnEmitError !== void 0) {
this._throwOnEmitError = options.throwOnEmitError;
}
}
/**
* Gets the logger
* @returns {Logger}
*/
get logger() {
return this._logger;
}
/**
* Sets the logger
* @param {Logger} logger
*/
set logger(logger) {
this._logger = logger;
}
/**
* Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
* @returns {boolean}
*/
get throwOnEmitError() {
return this._throwOnEmitError;
}
/**
* Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
* @param {boolean} value
*/
set throwOnEmitError(value) {
this._throwOnEmitError = value;
}
/**
* Adds a handler function for a specific event that will run only once
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
once(eventName, listener) {
const onceListener = (...arguments_) => {
this.off(eventName, onceListener);
listener(...arguments_);
};
this.on(eventName, onceListener);
return this;
}
/**
* Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners
* @param {string} eventName The event name. Not required
* @returns {number} The number of listeners
*/
listenerCount(eventName) {
if (eventName === void 0) {
return this.getAllListeners().length;
}
const listeners = this._eventListeners.get(eventName);
return listeners ? listeners.length : 0;
}
/**
* Gets an array of event names
* @returns {Array<string | symbol>} An array of event names
*/
eventNames() {
return [...this._eventListeners.keys()];
}
/**
* Gets an array of listeners for a specific event. If no event is provided, it returns all listeners
* @param {string} [event] (Optional) The event name
* @returns {EventListener[]} An array of listeners
*/
rawListeners(event) {
if (event === void 0) {
return this.getAllListeners();
}
return this._eventListeners.get(event) ?? [];
}
/**
* Prepends a listener to the beginning of the listeners array for the specified event
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
prependListener(eventName, listener) {
const listeners = this._eventListeners.get(eventName) ?? [];
listeners.unshift(listener);
this._eventListeners.set(eventName, listeners);
return this;
}
/**
* Prepends a one-time listener to the beginning of the listeners array for the specified event
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
prependOnceListener(eventName, listener) {
const onceListener = (...arguments_) => {
this.off(eventName, onceListener);
listener(...arguments_);
};
this.prependListener(eventName, onceListener);
return this;
}
/**
* Gets the maximum number of listeners that can be added for a single event
* @returns {number} The maximum number of listeners
*/
maxListeners() {
return this._maxListeners;
}
/**
* Adds a listener for a specific event. It is an alias for the on() method
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
addListener(event, listener) {
this.on(event, listener);
return this;
}
/**
* Adds a listener for a specific event
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
on(event, listener) {
if (!this._eventListeners.has(event)) {
this._eventListeners.set(event, []);
}
const listeners = this._eventListeners.get(event);
if (listeners) {
if (listeners.length >= this._maxListeners) {
console.warn(
`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
);
}
listeners.push(listener);
}
return this;
}
/**
* Removes a listener for a specific event. It is an alias for the off() method
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
removeListener(event, listener) {
this.off(event, listener);
return this;
}
/**
* Removes a listener for a specific event
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
off(event, listener) {
const listeners = this._eventListeners.get(event) ?? [];
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length === 0) {
this._eventListeners.delete(event);
}
return this;
}
/**
* Calls all listeners for a specific event
* @param {string | symbol} event
* @param arguments_ The arguments to pass to the listeners
* @returns {boolean} Returns true if the event had listeners, false otherwise
*/
emit(event, ...arguments_) {
let result = false;
const listeners = this._eventListeners.get(event);
if (listeners && listeners.length > 0) {
for (const listener of listeners) {
listener(...arguments_);
result = true;
}
}
if (event === "error") {
const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
if (this._throwOnEmitError && !result) {
throw error;
}
}
return result;
}
/**
* Gets all listeners for a specific event. If no event is provided, it returns all listeners
* @param {string} [event] (Optional) The event name
* @returns {EventListener[]} An array of listeners
*/
listeners(event) {
return this._eventListeners.get(event) ?? [];
}
/**
* Removes all listeners for a specific event. If no event is provided, it removes all listeners
* @param {string} [event] (Optional) The event name
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
removeAllListeners(event) {
if (event !== void 0) {
this._eventListeners.delete(event);
} else {
this._eventListeners.clear();
}
return this;
}
/**
* Sets the maximum number of listeners that can be added for a single event
* @param {number} n The maximum number of listeners
* @returns {void}
*/
setMaxListeners(n) {
this._maxListeners = n;
for (const listeners of this._eventListeners.values()) {
if (listeners.length > n) {
listeners.splice(n);
}
}
}
/**
* Gets all listeners
* @returns {EventListener[]} An array of listeners
*/
getAllListeners() {
let result = [];
for (const listeners of this._eventListeners.values()) {
result = [...result, ...listeners];
}
return result;
}
};
// src/index.ts
var Hookified = class extends Eventified {
_hooks;
_throwHookErrors = false;
_enforceBeforeAfter = false;
_deprecatedHooks;
_allowDeprecated = true;
constructor(options) {
super({ logger: options?.logger });
this._hooks = /* @__PURE__ */ new Map();
this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
if (options?.throwHookErrors !== void 0) {
this._throwHookErrors = options.throwHookErrors;
}
if (options?.enforceBeforeAfter !== void 0) {
this._enforceBeforeAfter = options.enforceBeforeAfter;
}
if (options?.allowDeprecated !== void 0) {
this._allowDeprecated = options.allowDeprecated;
}
}
/**
* Gets all hooks
* @returns {Map<string, Hook[]>}
*/
get hooks() {
return this._hooks;
}
/**
* Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
* @returns {boolean}
*/
get throwHookErrors() {
return this._throwHookErrors;
}
/**
* Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
* @param {boolean} value
*/
set throwHookErrors(value) {
this._throwHookErrors = value;
}
/**
* Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
* @returns {boolean}
* @default false
*/
get enforceBeforeAfter() {
return this._enforceBeforeAfter;
}
/**
* Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
* @param {boolean} value
*/
set enforceBeforeAfter(value) {
this._enforceBeforeAfter = value;
}
/**
* Gets the map of deprecated hook names to deprecation messages.
* @returns {Map<string, string>}
*/
get deprecatedHooks() {
return this._deprecatedHooks;
}
/**
* Sets the map of deprecated hook names to deprecation messages.
* @param {Map<string, string>} value
*/
set deprecatedHooks(value) {
this._deprecatedHooks = value;
}
/**
* Gets whether deprecated hooks are allowed to be registered and executed. Default is true.
* @returns {boolean}
*/
get allowDeprecated() {
return this._allowDeprecated;
}
/**
* Sets whether deprecated hooks are allowed to be registered and executed. Default is true.
* @param {boolean} value
*/
set allowDeprecated(value) {
this._allowDeprecated = value;
}
/**
* Validates hook event name if enforceBeforeAfter is enabled
* @param {string} event - The event name to validate
* @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
*/
validateHookName(event) {
if (this._enforceBeforeAfter) {
const eventValue = event.trim().toLocaleLowerCase();
if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
throw new Error(
`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
);
}
}
}
/**
* Checks if a hook is deprecated and emits a warning if it is
* @param {string} event - The event name to check
* @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
*/
checkDeprecatedHook(event) {
if (this._deprecatedHooks.has(event)) {
const message = this._deprecatedHooks.get(event);
const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
this.emit("warn", { hook: event, message: warningMessage });
if (this.logger?.warn) {
this.logger.warn(warningMessage);
}
return this._allowDeprecated;
}
return true;
}
/**
* Adds a handler function for a specific event
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
onHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
eventHandlers.push(handler);
} else {
this._hooks.set(event, [handler]);
}
}
/**
* Adds a handler function for a specific event that runs before all other handlers
* @param {HookEntry} hookEntry
* @returns {void}
*/
onHookEntry(hookEntry) {
this.onHook(hookEntry.event, hookEntry.handler);
}
/**
* Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
addHook(event, handler) {
this.onHook(event, handler);
}
/**
* Adds a handler function for a specific event
* @param {Array<HookEntry>} hooks
* @returns {void}
*/
onHooks(hooks) {
for (const hook of hooks) {
this.onHook(hook.event, hook.handler);
}
}
/**
* Adds a handler function for a specific event that runs before all other handlers
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
prependHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
eventHandlers.unshift(handler);
} else {
this._hooks.set(event, [handler]);
}
}
/**
* Adds a handler that only executes once for a specific event before all other handlers
* @param event
* @param handler
*/
prependOnceHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const hook = async (...arguments_) => {
this.removeHook(event, hook);
return handler(...arguments_);
};
this.prependHook(event, hook);
}
/**
* Adds a handler that only executes once for a specific event
* @param event
* @param handler
*/
onceHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const hook = async (...arguments_) => {
this.removeHook(event, hook);
return handler(...arguments_);
};
this.onHook(event, hook);
}
/**
* Removes a handler function for a specific event
* @param {string} event
* @param {Hook} handler
* @returns {void}
*/
removeHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
const index = eventHandlers.indexOf(handler);
if (index !== -1) {
eventHandlers.splice(index, 1);
}
}
}
/**
* Removes all handlers for a specific event
* @param {Array<HookEntry>} hooks
* @returns {void}
*/
removeHooks(hooks) {
for (const hook of hooks) {
this.removeHook(hook.event, hook.handler);
}
}
/**
* Calls all handlers for a specific event
* @param {string} event
* @param {T[]} arguments_
* @returns {Promise<void>}
*/
async hook(event, ...arguments_) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
for (const handler of eventHandlers) {
try {
await handler(...arguments_);
} catch (error) {
const message = `${event}: ${error.message}`;
this.emit("error", new Error(message));
if (this.logger) {
this.logger.error(message);
}
if (this._throwHookErrors) {
throw new Error(message);
}
}
}
}
}
/**
* Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.
* @param {string} event - The event name
* @param {T[]} arguments_ - The arguments to pass to the hook
*/
async beforeHook(event, ...arguments_) {
await this.hook(`before:${event}`, ...arguments_);
}
/**
* Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.
* @param {string} event - The event name
* @param {T[]} arguments_ - The arguments to pass to the hook
*/
async afterHook(event, ...arguments_) {
await this.hook(`after:${event}`, ...arguments_);
}
/**
* Calls all handlers for a specific event. This is an alias for `hook` and is provided for
* compatibility with other libraries that use the `callHook` method.
* @param {string} event
* @param {T[]} arguments_
* @returns {Promise<void>}
*/
async callHook(event, ...arguments_) {
await this.hook(event, ...arguments_);
}
/**
* Gets all hooks for a specific event
* @param {string} event
* @returns {Hook[]}
*/
getHooks(event) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return void 0;
}
return this._hooks.get(event);
}
/**
* Removes all hooks
* @returns {void}
*/
clearHooks() {
this._hooks.clear();
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Eventified,
Hookified
});

@@ -1,1 +0,567 @@

var i=class{_eventListeners;_maxListeners;_logger;_throwOnEmitError=!1;constructor(e){this._eventListeners=new Map,this._maxListeners=100,this._logger=e?.logger,e?.throwOnEmitError!==void 0&&(this._throwOnEmitError=e.throwOnEmitError)}get logger(){return this._logger}set logger(e){this._logger=e}get throwOnEmitError(){return this._throwOnEmitError}set throwOnEmitError(e){this._throwOnEmitError=e}once(e,t){let r=(...s)=>{this.off(e,r),t(...s)};return this.on(e,r),this}listenerCount(e){if(e===void 0)return this.getAllListeners().length;let t=this._eventListeners.get(e);return t?t.length:0}eventNames(){return[...this._eventListeners.keys()]}rawListeners(e){return e===void 0?this.getAllListeners():this._eventListeners.get(e)??[]}prependListener(e,t){let r=this._eventListeners.get(e)??[];return r.unshift(t),this._eventListeners.set(e,r),this}prependOnceListener(e,t){let r=(...s)=>{this.off(e,r),t(...s)};return this.prependListener(e,r),this}maxListeners(){return this._maxListeners}addListener(e,t){return this.on(e,t),this}on(e,t){this._eventListeners.has(e)||this._eventListeners.set(e,[]);let r=this._eventListeners.get(e);return r&&(r.length>=this._maxListeners&&console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${r.length+1} ${e} listeners added. Use setMaxListeners() to increase limit.`),r.push(t)),this}removeListener(e,t){return this.off(e,t),this}off(e,t){let r=this._eventListeners.get(e)??[],s=r.indexOf(t);return s!==-1&&r.splice(s,1),r.length===0&&this._eventListeners.delete(e),this}emit(e,...t){let r=!1,s=this._eventListeners.get(e);if(s&&s.length>0)for(let o of s)o(...t),r=!0;if(e==="error"){let o=t[0]instanceof Error?t[0]:new Error(`${t[0]}`);if(this._throwOnEmitError&&!r)throw o}return r}listeners(e){return this._eventListeners.get(e)??[]}removeAllListeners(e){return e!==void 0?this._eventListeners.delete(e):this._eventListeners.clear(),this}setMaxListeners(e){this._maxListeners=e;for(let t of this._eventListeners.values())t.length>e&&t.splice(e)}getAllListeners(){let e=[];for(let t of this._eventListeners.values())e=[...e,...t];return e}};var a=class extends i{_hooks;_throwHookErrors=!1;_enforceBeforeAfter=!1;_deprecatedHooks;_allowDeprecated=!0;constructor(e){super({logger:e?.logger}),this._hooks=new Map,this._deprecatedHooks=e?.deprecatedHooks?new Map(e.deprecatedHooks):new Map,e?.throwHookErrors!==void 0&&(this._throwHookErrors=e.throwHookErrors),e?.enforceBeforeAfter!==void 0&&(this._enforceBeforeAfter=e.enforceBeforeAfter),e?.allowDeprecated!==void 0&&(this._allowDeprecated=e.allowDeprecated)}get hooks(){return this._hooks}get throwHookErrors(){return this._throwHookErrors}set throwHookErrors(e){this._throwHookErrors=e}get enforceBeforeAfter(){return this._enforceBeforeAfter}set enforceBeforeAfter(e){this._enforceBeforeAfter=e}get deprecatedHooks(){return this._deprecatedHooks}set deprecatedHooks(e){this._deprecatedHooks=e}get allowDeprecated(){return this._allowDeprecated}set allowDeprecated(e){this._allowDeprecated=e}validateHookName(e){if(this._enforceBeforeAfter){let t=e.trim().toLocaleLowerCase();if(!t.startsWith("before")&&!t.startsWith("after"))throw new Error(`Hook event "${e}" must start with "before" or "after" when enforceBeforeAfter is enabled`)}}checkDeprecatedHook(e){if(this._deprecatedHooks.has(e)){let t=this._deprecatedHooks.get(e),r=`Hook "${e}" is deprecated${t?`: ${t}`:""}`;return this.emit("warn",{hook:e,message:r}),this.logger?.warn&&this.logger.warn(r),this._allowDeprecated}return!0}onHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);r?r.push(t):this._hooks.set(e,[t])}onHookEntry(e){this.onHook(e.event,e.handler)}addHook(e,t){this.onHook(e,t)}onHooks(e){for(let t of e)this.onHook(t.event,t.handler)}prependHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);r?r.unshift(t):this._hooks.set(e,[t])}prependOnceHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=async(...s)=>(this.removeHook(e,r),t(...s));this.prependHook(e,r)}onceHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=async(...s)=>(this.removeHook(e,r),t(...s));this.onHook(e,r)}removeHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);if(r){let s=r.indexOf(t);s!==-1&&r.splice(s,1)}}removeHooks(e){for(let t of e)this.removeHook(t.event,t.handler)}async hook(e,...t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);if(r)for(let s of r)try{await s(...t)}catch(o){let n=`${e}: ${o.message}`;if(this.emit("error",new Error(n)),this.logger&&this.logger.error(n),this._throwHookErrors)throw new Error(n)}}async beforeHook(e,...t){await this.hook(`before:${e}`,...t)}async afterHook(e,...t){await this.hook(`after:${e}`,...t)}async callHook(e,...t){await this.hook(e,...t)}getHooks(e){if(this.validateHookName(e),!!this.checkDeprecatedHook(e))return this._hooks.get(e)}clearHooks(){this._hooks.clear()}};export{i as Eventified,a as Hookified};
// src/eventified.ts
var Eventified = class {
_eventListeners;
_maxListeners;
_logger;
_throwOnEmitError = false;
constructor(options) {
this._eventListeners = /* @__PURE__ */ new Map();
this._maxListeners = 100;
this._logger = options?.logger;
if (options?.throwOnEmitError !== void 0) {
this._throwOnEmitError = options.throwOnEmitError;
}
}
/**
* Gets the logger
* @returns {Logger}
*/
get logger() {
return this._logger;
}
/**
* Sets the logger
* @param {Logger} logger
*/
set logger(logger) {
this._logger = logger;
}
/**
* Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
* @returns {boolean}
*/
get throwOnEmitError() {
return this._throwOnEmitError;
}
/**
* Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
* @param {boolean} value
*/
set throwOnEmitError(value) {
this._throwOnEmitError = value;
}
/**
* Adds a handler function for a specific event that will run only once
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
once(eventName, listener) {
const onceListener = (...arguments_) => {
this.off(eventName, onceListener);
listener(...arguments_);
};
this.on(eventName, onceListener);
return this;
}
/**
* Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners
* @param {string} eventName The event name. Not required
* @returns {number} The number of listeners
*/
listenerCount(eventName) {
if (eventName === void 0) {
return this.getAllListeners().length;
}
const listeners = this._eventListeners.get(eventName);
return listeners ? listeners.length : 0;
}
/**
* Gets an array of event names
* @returns {Array<string | symbol>} An array of event names
*/
eventNames() {
return [...this._eventListeners.keys()];
}
/**
* Gets an array of listeners for a specific event. If no event is provided, it returns all listeners
* @param {string} [event] (Optional) The event name
* @returns {EventListener[]} An array of listeners
*/
rawListeners(event) {
if (event === void 0) {
return this.getAllListeners();
}
return this._eventListeners.get(event) ?? [];
}
/**
* Prepends a listener to the beginning of the listeners array for the specified event
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
prependListener(eventName, listener) {
const listeners = this._eventListeners.get(eventName) ?? [];
listeners.unshift(listener);
this._eventListeners.set(eventName, listeners);
return this;
}
/**
* Prepends a one-time listener to the beginning of the listeners array for the specified event
* @param {string | symbol} eventName
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
prependOnceListener(eventName, listener) {
const onceListener = (...arguments_) => {
this.off(eventName, onceListener);
listener(...arguments_);
};
this.prependListener(eventName, onceListener);
return this;
}
/**
* Gets the maximum number of listeners that can be added for a single event
* @returns {number} The maximum number of listeners
*/
maxListeners() {
return this._maxListeners;
}
/**
* Adds a listener for a specific event. It is an alias for the on() method
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
addListener(event, listener) {
this.on(event, listener);
return this;
}
/**
* Adds a listener for a specific event
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
on(event, listener) {
if (!this._eventListeners.has(event)) {
this._eventListeners.set(event, []);
}
const listeners = this._eventListeners.get(event);
if (listeners) {
if (listeners.length >= this._maxListeners) {
console.warn(
`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
);
}
listeners.push(listener);
}
return this;
}
/**
* Removes a listener for a specific event. It is an alias for the off() method
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
removeListener(event, listener) {
this.off(event, listener);
return this;
}
/**
* Removes a listener for a specific event
* @param {string | symbol} event
* @param {EventListener} listener
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
off(event, listener) {
const listeners = this._eventListeners.get(event) ?? [];
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length === 0) {
this._eventListeners.delete(event);
}
return this;
}
/**
* Calls all listeners for a specific event
* @param {string | symbol} event
* @param arguments_ The arguments to pass to the listeners
* @returns {boolean} Returns true if the event had listeners, false otherwise
*/
emit(event, ...arguments_) {
let result = false;
const listeners = this._eventListeners.get(event);
if (listeners && listeners.length > 0) {
for (const listener of listeners) {
listener(...arguments_);
result = true;
}
}
if (event === "error") {
const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
if (this._throwOnEmitError && !result) {
throw error;
}
}
return result;
}
/**
* Gets all listeners for a specific event. If no event is provided, it returns all listeners
* @param {string} [event] (Optional) The event name
* @returns {EventListener[]} An array of listeners
*/
listeners(event) {
return this._eventListeners.get(event) ?? [];
}
/**
* Removes all listeners for a specific event. If no event is provided, it removes all listeners
* @param {string} [event] (Optional) The event name
* @returns {IEventEmitter} returns the instance of the class for chaining
*/
removeAllListeners(event) {
if (event !== void 0) {
this._eventListeners.delete(event);
} else {
this._eventListeners.clear();
}
return this;
}
/**
* Sets the maximum number of listeners that can be added for a single event
* @param {number} n The maximum number of listeners
* @returns {void}
*/
setMaxListeners(n) {
this._maxListeners = n;
for (const listeners of this._eventListeners.values()) {
if (listeners.length > n) {
listeners.splice(n);
}
}
}
/**
* Gets all listeners
* @returns {EventListener[]} An array of listeners
*/
getAllListeners() {
let result = [];
for (const listeners of this._eventListeners.values()) {
result = [...result, ...listeners];
}
return result;
}
};
// src/index.ts
var Hookified = class extends Eventified {
_hooks;
_throwHookErrors = false;
_enforceBeforeAfter = false;
_deprecatedHooks;
_allowDeprecated = true;
constructor(options) {
super({ logger: options?.logger });
this._hooks = /* @__PURE__ */ new Map();
this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
if (options?.throwHookErrors !== void 0) {
this._throwHookErrors = options.throwHookErrors;
}
if (options?.enforceBeforeAfter !== void 0) {
this._enforceBeforeAfter = options.enforceBeforeAfter;
}
if (options?.allowDeprecated !== void 0) {
this._allowDeprecated = options.allowDeprecated;
}
}
/**
* Gets all hooks
* @returns {Map<string, Hook[]>}
*/
get hooks() {
return this._hooks;
}
/**
* Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
* @returns {boolean}
*/
get throwHookErrors() {
return this._throwHookErrors;
}
/**
* Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
* @param {boolean} value
*/
set throwHookErrors(value) {
this._throwHookErrors = value;
}
/**
* Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
* @returns {boolean}
* @default false
*/
get enforceBeforeAfter() {
return this._enforceBeforeAfter;
}
/**
* Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
* @param {boolean} value
*/
set enforceBeforeAfter(value) {
this._enforceBeforeAfter = value;
}
/**
* Gets the map of deprecated hook names to deprecation messages.
* @returns {Map<string, string>}
*/
get deprecatedHooks() {
return this._deprecatedHooks;
}
/**
* Sets the map of deprecated hook names to deprecation messages.
* @param {Map<string, string>} value
*/
set deprecatedHooks(value) {
this._deprecatedHooks = value;
}
/**
* Gets whether deprecated hooks are allowed to be registered and executed. Default is true.
* @returns {boolean}
*/
get allowDeprecated() {
return this._allowDeprecated;
}
/**
* Sets whether deprecated hooks are allowed to be registered and executed. Default is true.
* @param {boolean} value
*/
set allowDeprecated(value) {
this._allowDeprecated = value;
}
/**
* Validates hook event name if enforceBeforeAfter is enabled
* @param {string} event - The event name to validate
* @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
*/
validateHookName(event) {
if (this._enforceBeforeAfter) {
const eventValue = event.trim().toLocaleLowerCase();
if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
throw new Error(
`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
);
}
}
}
/**
* Checks if a hook is deprecated and emits a warning if it is
* @param {string} event - The event name to check
* @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
*/
checkDeprecatedHook(event) {
if (this._deprecatedHooks.has(event)) {
const message = this._deprecatedHooks.get(event);
const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
this.emit("warn", { hook: event, message: warningMessage });
if (this.logger?.warn) {
this.logger.warn(warningMessage);
}
return this._allowDeprecated;
}
return true;
}
/**
* Adds a handler function for a specific event
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
onHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
eventHandlers.push(handler);
} else {
this._hooks.set(event, [handler]);
}
}
/**
* Adds a handler function for a specific event that runs before all other handlers
* @param {HookEntry} hookEntry
* @returns {void}
*/
onHookEntry(hookEntry) {
this.onHook(hookEntry.event, hookEntry.handler);
}
/**
* Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
addHook(event, handler) {
this.onHook(event, handler);
}
/**
* Adds a handler function for a specific event
* @param {Array<HookEntry>} hooks
* @returns {void}
*/
onHooks(hooks) {
for (const hook of hooks) {
this.onHook(hook.event, hook.handler);
}
}
/**
* Adds a handler function for a specific event that runs before all other handlers
* @param {string} event
* @param {Hook} handler - this can be async or sync
* @returns {void}
*/
prependHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
eventHandlers.unshift(handler);
} else {
this._hooks.set(event, [handler]);
}
}
/**
* Adds a handler that only executes once for a specific event before all other handlers
* @param event
* @param handler
*/
prependOnceHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const hook = async (...arguments_) => {
this.removeHook(event, hook);
return handler(...arguments_);
};
this.prependHook(event, hook);
}
/**
* Adds a handler that only executes once for a specific event
* @param event
* @param handler
*/
onceHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const hook = async (...arguments_) => {
this.removeHook(event, hook);
return handler(...arguments_);
};
this.onHook(event, hook);
}
/**
* Removes a handler function for a specific event
* @param {string} event
* @param {Hook} handler
* @returns {void}
*/
removeHook(event, handler) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
const index = eventHandlers.indexOf(handler);
if (index !== -1) {
eventHandlers.splice(index, 1);
}
}
}
/**
* Removes all handlers for a specific event
* @param {Array<HookEntry>} hooks
* @returns {void}
*/
removeHooks(hooks) {
for (const hook of hooks) {
this.removeHook(hook.event, hook.handler);
}
}
/**
* Calls all handlers for a specific event
* @param {string} event
* @param {T[]} arguments_
* @returns {Promise<void>}
*/
async hook(event, ...arguments_) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return;
}
const eventHandlers = this._hooks.get(event);
if (eventHandlers) {
for (const handler of eventHandlers) {
try {
await handler(...arguments_);
} catch (error) {
const message = `${event}: ${error.message}`;
this.emit("error", new Error(message));
if (this.logger) {
this.logger.error(message);
}
if (this._throwHookErrors) {
throw new Error(message);
}
}
}
}
}
/**
* Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.
* @param {string} event - The event name
* @param {T[]} arguments_ - The arguments to pass to the hook
*/
async beforeHook(event, ...arguments_) {
await this.hook(`before:${event}`, ...arguments_);
}
/**
* Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.
* @param {string} event - The event name
* @param {T[]} arguments_ - The arguments to pass to the hook
*/
async afterHook(event, ...arguments_) {
await this.hook(`after:${event}`, ...arguments_);
}
/**
* Calls all handlers for a specific event. This is an alias for `hook` and is provided for
* compatibility with other libraries that use the `callHook` method.
* @param {string} event
* @param {T[]} arguments_
* @returns {Promise<void>}
*/
async callHook(event, ...arguments_) {
await this.hook(event, ...arguments_);
}
/**
* Gets all hooks for a specific event
* @param {string} event
* @returns {Hook[]}
*/
getHooks(event) {
this.validateHookName(event);
if (!this.checkDeprecatedHook(event)) {
return void 0;
}
return this._hooks.get(event);
}
/**
* Removes all hooks
* @returns {void}
*/
clearHooks() {
this._hooks.clear();
}
};
export {
Eventified,
Hookified
};
+7
-7
{
"name": "hookified",
"version": "1.12.1",
"version": "1.12.2",
"description": "Event Emitting and Middleware Hooks",

@@ -64,16 +64,16 @@ "type": "module",

"devDependencies": {
"@biomejs/biome": "^2.2.4",
"@biomejs/biome": "^2.2.6",
"@monstermann/tinybench-pretty-printer": "^0.2.0",
"@types/node": "^24.5.2",
"@types/node": "^24.7.2",
"@vitest/coverage-v8": "^3.2.4",
"docula": "^0.20.0",
"docula": "^0.30.0",
"emittery": "^1.2.0",
"eventemitter3": "^5.0.1",
"hookable": "^5.5.3",
"pino": "^9.10.0",
"pino": "^10.0.0",
"rimraf": "^6.0.1",
"tinybench": "^5.0.1",
"tsup": "^8.5.0",
"tsx": "^4.20.5",
"typescript": "^5.9.2",
"tsx": "^4.20.6",
"typescript": "^5.9.3",
"vitest": "^3.2.4"

@@ -80,0 +80,0 @@ },

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet