+283
-155
@@ -12,9 +12,9 @@ "use strict"; | ||
| __publicField(this, "_maxListeners"); | ||
| __publicField(this, "_logger"); | ||
| __publicField(this, "_eventLogger"); | ||
| __publicField(this, "_throwOnEmitError", false); | ||
| __publicField(this, "_throwOnEmptyListeners", false); | ||
| __publicField(this, "_throwOnEmptyListeners", true); | ||
| __publicField(this, "_errorEvent", "error"); | ||
| this._eventListeners = /* @__PURE__ */ new Map(); | ||
| this._maxListeners = 100; | ||
| this._logger = options?.logger; | ||
| this._maxListeners = 0; | ||
| this._eventLogger = options?.eventLogger; | ||
| if (options?.throwOnEmitError !== void 0) { | ||
@@ -28,14 +28,14 @@ this._throwOnEmitError = options.throwOnEmitError; | ||
| /** | ||
| * Gets the logger | ||
| * Gets the event logger | ||
| * @returns {Logger} | ||
| */ | ||
| get logger() { | ||
| return this._logger; | ||
| get eventLogger() { | ||
| return this._eventLogger; | ||
| } | ||
| /** | ||
| * Sets the logger | ||
| * @param {Logger} logger | ||
| * Sets the event logger | ||
| * @param {Logger} eventLogger | ||
| */ | ||
| set logger(logger) { | ||
| this._logger = logger; | ||
| set eventLogger(eventLogger) { | ||
| this._eventLogger = eventLogger; | ||
| } | ||
@@ -169,3 +169,3 @@ /** | ||
| if (listeners) { | ||
| if (listeners.length >= this._maxListeners) { | ||
| if (this._maxListeners > 0 && listeners.length >= this._maxListeners) { | ||
| console.warn( | ||
@@ -221,2 +221,3 @@ `MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.` | ||
| } | ||
| this.sendToEventLogger(event, arguments_); | ||
| if (event === this._errorEvent) { | ||
@@ -232,3 +233,2 @@ const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`); | ||
| } | ||
| this.sendLog(event, arguments_); | ||
| return result; | ||
@@ -263,8 +263,3 @@ } | ||
| setMaxListeners(n) { | ||
| this._maxListeners = n; | ||
| for (const listeners of this._eventListeners.values()) { | ||
| if (listeners.length > n) { | ||
| listeners.splice(n); | ||
| } | ||
| } | ||
| this._maxListeners = n < 0 ? 0 : n; | ||
| } | ||
@@ -287,4 +282,4 @@ /** | ||
| */ | ||
| sendLog(eventName, data) { | ||
| if (!this._logger) { | ||
| sendToEventLogger(eventName, data) { | ||
| if (!this._eventLogger) { | ||
| return; | ||
@@ -306,23 +301,23 @@ } | ||
| case "error": { | ||
| this._logger.error?.(message, { event: eventName, data }); | ||
| this._eventLogger.error?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "warn": { | ||
| this._logger.warn?.(message, { event: eventName, data }); | ||
| this._eventLogger.warn?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "trace": { | ||
| this._logger.trace?.(message, { event: eventName, data }); | ||
| this._eventLogger.trace?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "debug": { | ||
| this._logger.debug?.(message, { event: eventName, data }); | ||
| this._eventLogger.debug?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "fatal": { | ||
| this._logger.fatal?.(message, { event: eventName, data }); | ||
| this._eventLogger.fatal?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| default: { | ||
| this._logger.info?.(message, { event: eventName, data }); | ||
| this._eventLogger.info?.(message, { event: eventName, data }); | ||
| break; | ||
@@ -334,2 +329,70 @@ } | ||
| // src/hooks/hook.ts | ||
| var Hook = class { | ||
| /** | ||
| * Creates a new Hook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {HookFn} handler - The handler function for the hook | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event, handler, id) { | ||
| __publicField(this, "id"); | ||
| __publicField(this, "event"); | ||
| __publicField(this, "handler"); | ||
| this.id = id; | ||
| this.event = event; | ||
| this.handler = handler; | ||
| } | ||
| }; | ||
| // src/hooks/waterfall-hook.ts | ||
| var WaterfallHook = class { | ||
| /** | ||
| * Creates a new WaterfallHook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event, finalHandler, id) { | ||
| __publicField(this, "id"); | ||
| __publicField(this, "event"); | ||
| __publicField(this, "handler"); | ||
| __publicField(this, "hooks"); | ||
| __publicField(this, "_finalHandler"); | ||
| this.id = id; | ||
| this.event = event; | ||
| this.hooks = []; | ||
| this._finalHandler = finalHandler; | ||
| this.handler = async (...arguments_) => { | ||
| const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_; | ||
| const results = []; | ||
| for (const hook of this.hooks) { | ||
| const result = await hook({ initialArgs, results: [...results] }); | ||
| results.push({ hook, result }); | ||
| } | ||
| await this._finalHandler({ initialArgs, results: [...results] }); | ||
| }; | ||
| } | ||
| /** | ||
| * Adds a hook function to the end of the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to add | ||
| */ | ||
| addHook(hook) { | ||
| this.hooks.push(hook); | ||
| } | ||
| /** | ||
| * Removes a specific hook function from the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to remove | ||
| * @returns {boolean} true if the hook was found and removed | ||
| */ | ||
| removeHook(hook) { | ||
| const index = this.hooks.indexOf(hook); | ||
| if (index !== -1) { | ||
| this.hooks.splice(index, 1); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| }; | ||
| // src/index.ts | ||
@@ -339,3 +402,3 @@ var Hookified = class extends Eventified { | ||
| super({ | ||
| logger: options?.logger, | ||
| eventLogger: options?.eventLogger, | ||
| throwOnEmitError: options?.throwOnEmitError, | ||
@@ -349,2 +412,3 @@ throwOnEmptyListeners: options?.throwOnEmptyListeners | ||
| __publicField(this, "_allowDeprecated", true); | ||
| __publicField(this, "_useHookClone", true); | ||
| this._hooks = /* @__PURE__ */ new Map(); | ||
@@ -354,4 +418,2 @@ this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map(); | ||
| this._throwOnHookError = options.throwOnHookError; | ||
| } else if (options?.throwHookErrors !== void 0) { | ||
| this._throwOnHookError = options.throwHookErrors; | ||
| } | ||
@@ -364,6 +426,9 @@ if (options?.enforceBeforeAfter !== void 0) { | ||
| } | ||
| if (options?.useHookClone !== void 0) { | ||
| this._useHookClone = options.useHookClone; | ||
| } | ||
| } | ||
| /** | ||
| * Gets all hooks | ||
| * @returns {Map<string, Hook[]>} | ||
| * @returns {Map<string, IHook[]>} | ||
| */ | ||
@@ -376,19 +441,3 @@ get hooks() { | ||
| * @returns {boolean} | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| get throwHookErrors() { | ||
| return this._throwOnHookError; | ||
| } | ||
| /** | ||
| * 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 | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| set throwHookErrors(value) { | ||
| this._throwOnHookError = value; | ||
| } | ||
| /** | ||
| * 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 throwOnHookError() { | ||
@@ -448,153 +497,149 @@ return this._throwOnHookError; | ||
| /** | ||
| * 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' | ||
| * Gets whether hook objects are cloned before storing. Default is true. | ||
| * @returns {boolean} | ||
| */ | ||
| 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` | ||
| ); | ||
| } | ||
| } | ||
| get useHookClone() { | ||
| return this._useHookClone; | ||
| } | ||
| /** | ||
| * 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 | ||
| * Sets whether hook objects are cloned before storing. Default is true. | ||
| * When false, the original IHook reference is stored directly. | ||
| * @param {boolean} value | ||
| */ | ||
| 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 }); | ||
| return this._allowDeprecated; | ||
| } | ||
| return true; | ||
| set useHookClone(value) { | ||
| this._useHookClone = value; | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler - this can be async or sync | ||
| * @returns {void} | ||
| * Adds a handler function for a specific event. | ||
| * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead. | ||
| * To register multiple hooks at once, use {@link onHooks}. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| onHook(event, handler) { | ||
| this.onHookEntry({ event, handler }); | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {HookEntry} hookEntry | ||
| * @returns {void} | ||
| */ | ||
| onHookEntry(hookEntry) { | ||
| this.validateHookName(hookEntry.event); | ||
| if (!this.checkDeprecatedHook(hookEntry.event)) { | ||
| return; | ||
| onHook(hook, options) { | ||
| this.validateHookName(hook.event); | ||
| if (!this.checkDeprecatedHook(hook.event)) { | ||
| return void 0; | ||
| } | ||
| const eventHandlers = this._hooks.get(hookEntry.event); | ||
| const shouldClone = options?.useHookClone ?? this._useHookClone; | ||
| const entry = shouldClone ? { id: hook.id, event: hook.event, handler: hook.handler } : hook; | ||
| entry.id = entry.id ?? crypto.randomUUID(); | ||
| const eventHandlers = this._hooks.get(hook.event); | ||
| if (eventHandlers) { | ||
| eventHandlers.push(hookEntry.handler); | ||
| const existingIndex = eventHandlers.findIndex((h) => h.id === entry.id); | ||
| if (existingIndex !== -1) { | ||
| eventHandlers[existingIndex] = entry; | ||
| } else { | ||
| const position = options?.position ?? "Bottom"; | ||
| if (position === "Top") { | ||
| eventHandlers.unshift(entry); | ||
| } else if (position === "Bottom") { | ||
| eventHandlers.push(entry); | ||
| } else { | ||
| const index = Math.max(0, Math.min(position, eventHandlers.length)); | ||
| eventHandlers.splice(index, 0, entry); | ||
| } | ||
| } | ||
| } else { | ||
| this._hooks.set(hookEntry.event, [hookEntry.handler]); | ||
| this._hooks.set(hook.event, [entry]); | ||
| } | ||
| return entry; | ||
| } | ||
| /** | ||
| * 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 | ||
| * @param {string} event - the event name | ||
| * @param {HookFn} handler - the handler function | ||
| * @returns {void} | ||
| */ | ||
| addHook(event, handler) { | ||
| this.onHookEntry({ event, handler }); | ||
| this.onHook({ event, handler }); | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * Adds handler functions for specific events | ||
| * @param {Array<IHook>} hooks | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {void} | ||
| */ | ||
| onHooks(hooks) { | ||
| onHooks(hooks, options) { | ||
| for (const hook of hooks) { | ||
| this.onHook(hook.event, hook.handler); | ||
| this.onHook(hook, options); | ||
| } | ||
| } | ||
| /** | ||
| * 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} | ||
| * Adds a handler function for a specific event that runs before all other handlers. | ||
| * Equivalent to calling `onHook(hook, { position: "Top" })`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| 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]); | ||
| } | ||
| prependHook(hook, options) { | ||
| return this.onHook(hook, { ...options, position: "Top" }); | ||
| } | ||
| /** | ||
| * Adds a handler that only executes once for a specific event before all other handlers | ||
| * @param event | ||
| * @param handler | ||
| * Adds a handler that only executes once for a specific event before all other handlers. | ||
| * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| prependOnceHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return; | ||
| } | ||
| const hook = async (...arguments_) => { | ||
| this.removeHook(event, hook); | ||
| return handler(...arguments_); | ||
| prependOnceHook(hook, options) { | ||
| const wrappedHandler = async (...arguments_) => { | ||
| this.removeHook({ event: hook.event, handler: wrappedHandler }); | ||
| return hook.handler(...arguments_); | ||
| }; | ||
| this.prependHook(event, hook); | ||
| return this.onHook( | ||
| { id: hook.id, event: hook.event, handler: wrappedHandler }, | ||
| { ...options, position: "Top" } | ||
| ); | ||
| } | ||
| /** | ||
| * Adds a handler that only executes once for a specific event | ||
| * @param event | ||
| * @param handler | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| */ | ||
| onceHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| onceHook(hook) { | ||
| this.validateHookName(hook.event); | ||
| if (!this.checkDeprecatedHook(hook.event)) { | ||
| return; | ||
| } | ||
| const hook = async (...arguments_) => { | ||
| this.removeHook(event, hook); | ||
| return handler(...arguments_); | ||
| const wrappedHandler = async (...arguments_) => { | ||
| this.removeHook({ event: hook.event, handler: wrappedHandler }); | ||
| return hook.handler(...arguments_); | ||
| }; | ||
| this.onHook(event, hook); | ||
| this.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler }); | ||
| } | ||
| /** | ||
| * Removes a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler | ||
| * @returns {void} | ||
| * @param {IHook} hook - the hook containing event name and handler to remove | ||
| * @returns {IHook | undefined} the removed hook, or undefined if not found | ||
| */ | ||
| removeHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return; | ||
| } | ||
| const eventHandlers = this._hooks.get(event); | ||
| removeHook(hook) { | ||
| this.validateHookName(hook.event); | ||
| const eventHandlers = this._hooks.get(hook.event); | ||
| if (eventHandlers) { | ||
| const index = eventHandlers.indexOf(handler); | ||
| const index = eventHandlers.findIndex((h) => h.handler === hook.handler); | ||
| if (index !== -1) { | ||
| eventHandlers.splice(index, 1); | ||
| if (eventHandlers.length === 0) { | ||
| this._hooks.delete(hook.event); | ||
| } | ||
| return { event: hook.event, handler: hook.handler }; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes all handlers for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * @returns {void} | ||
| * Removes multiple hook handlers | ||
| * @param {Array<IHook>} hooks | ||
| * @returns {IHook[]} the hooks that were successfully removed | ||
| */ | ||
| removeHooks(hooks) { | ||
| const removed = []; | ||
| for (const hook of hooks) { | ||
| this.removeHook(hook.event, hook.handler); | ||
| const result = this.removeHook(hook); | ||
| if (result) { | ||
| removed.push(result); | ||
| } | ||
| } | ||
| return removed; | ||
| } | ||
@@ -614,5 +659,5 @@ /** | ||
| if (eventHandlers) { | ||
| for (const handler of eventHandlers) { | ||
| for (const hook of [...eventHandlers]) { | ||
| try { | ||
| await handler(...arguments_); | ||
| await hook.handler(...arguments_); | ||
| } catch (error) { | ||
@@ -645,8 +690,8 @@ const message = `${event}: ${error.message}`; | ||
| if (eventHandlers) { | ||
| for (const handler of eventHandlers) { | ||
| if (handler.constructor.name === "AsyncFunction") { | ||
| for (const hook of [...eventHandlers]) { | ||
| if (hook.handler.constructor.name === "AsyncFunction") { | ||
| continue; | ||
| } | ||
| try { | ||
| handler(...arguments_); | ||
| hook.handler(...arguments_); | ||
| } catch (error) { | ||
@@ -691,12 +736,51 @@ const message = `${event}: ${error.message}`; | ||
| * @param {string} event | ||
| * @returns {Hook[]} | ||
| * @returns {IHook[]} | ||
| */ | ||
| getHooks(event) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return void 0; | ||
| } | ||
| return this._hooks.get(event); | ||
| } | ||
| /** | ||
| * Gets a specific hook by id, searching across all events | ||
| * @param {string} id - the hook id | ||
| * @returns {IHook | undefined} the hook if found, or undefined | ||
| */ | ||
| getHook(id) { | ||
| for (const eventHandlers of this._hooks.values()) { | ||
| const found = eventHandlers.find((h) => h.id === id); | ||
| if (found) { | ||
| return found; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes one or more hooks by id, searching across all events | ||
| * @param {string | string[]} id - the hook id or array of hook ids to remove | ||
| * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found | ||
| */ | ||
| removeHookById(id) { | ||
| if (Array.isArray(id)) { | ||
| const removed = []; | ||
| for (const singleId of id) { | ||
| const result = this.removeHookById(singleId); | ||
| if (result && !Array.isArray(result)) { | ||
| removed.push(result); | ||
| } | ||
| } | ||
| return removed; | ||
| } | ||
| for (const [event, eventHandlers] of this._hooks.entries()) { | ||
| const index = eventHandlers.findIndex((h) => h.id === id); | ||
| if (index !== -1) { | ||
| const [removed] = eventHandlers.splice(index, 1); | ||
| if (eventHandlers.length === 0) { | ||
| this._hooks.delete(event); | ||
| } | ||
| return removed; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes all hooks | ||
@@ -708,2 +792,46 @@ * @returns {void} | ||
| } | ||
| /** | ||
| * Removes all hooks for a specific event and returns the removed hooks. | ||
| * @param {string} event - The event name to remove hooks for. | ||
| * @returns {IHook[]} the hooks that were removed | ||
| */ | ||
| removeEventHooks(event) { | ||
| this.validateHookName(event); | ||
| const eventHandlers = this._hooks.get(event); | ||
| if (eventHandlers) { | ||
| const removed = [...eventHandlers]; | ||
| this._hooks.delete(event); | ||
| return removed; | ||
| } | ||
| return []; | ||
| } | ||
| /** | ||
| * 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 }); | ||
| return this._allowDeprecated; | ||
| } | ||
| return true; | ||
| } | ||
| }; | ||
@@ -710,0 +838,0 @@ })(); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/eventified.ts","../../src/index.ts"],"sourcesContent":["// biome-ignore-all lint/suspicious/noExplicitAny: this is for event emitter compatibility\nimport type {\n\tEventEmitterOptions,\n\tEventListener,\n\tIEventEmitter,\n\tLogger,\n} from \"./types.js\";\n\nexport type { EventEmitterOptions, EventListener, IEventEmitter };\n\nexport class Eventified implements IEventEmitter {\n\tprivate readonly _eventListeners: Map<string | symbol, EventListener[]>;\n\tprivate _maxListeners: number;\n\tprivate _logger?: Logger;\n\tprivate _throwOnEmitError = false;\n\tprivate _throwOnEmptyListeners = false;\n\tprivate _errorEvent = \"error\";\n\n\tconstructor(options?: EventEmitterOptions) {\n\t\tthis._eventListeners = new Map<string | symbol, EventListener[]>();\n\t\tthis._maxListeners = 100; // Default maximum number of listeners\n\n\t\tthis._logger = options?.logger;\n\n\t\tif (options?.throwOnEmitError !== undefined) {\n\t\t\tthis._throwOnEmitError = options.throwOnEmitError;\n\t\t}\n\n\t\tif (options?.throwOnEmptyListeners !== undefined) {\n\t\t\tthis._throwOnEmptyListeners = options.throwOnEmptyListeners;\n\t\t}\n\t}\n\n\t/**\n\t * Gets the logger\n\t * @returns {Logger}\n\t */\n\tpublic get logger(): Logger | undefined {\n\t\treturn this._logger;\n\t}\n\n\t/**\n\t * Sets the logger\n\t * @param {Logger} logger\n\t */\n\tpublic set logger(logger: Logger | undefined) {\n\t\tthis._logger = logger;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnEmitError(): boolean {\n\t\treturn this._throwOnEmitError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnEmitError(value: boolean) {\n\t\tthis._throwOnEmitError = value;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnEmptyListeners(): boolean {\n\t\treturn this._throwOnEmptyListeners;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnEmptyListeners(value: boolean) {\n\t\tthis._throwOnEmptyListeners = value;\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event that will run only once\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic once(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst onceListener: EventListener = (...arguments_: any[]) => {\n\t\t\tthis.off(eventName as string, onceListener);\n\t\t\tlistener(...arguments_);\n\t\t};\n\n\t\tthis.on(eventName as string, onceListener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners\n\t * @param {string} eventName The event name. Not required\n\t * @returns {number} The number of listeners\n\t */\n\tpublic listenerCount(eventName?: string | symbol): number {\n\t\tif (eventName === undefined) {\n\t\t\treturn this.getAllListeners().length;\n\t\t}\n\n\t\tconst listeners = this._eventListeners.get(eventName);\n\t\treturn listeners ? listeners.length : 0;\n\t}\n\n\t/**\n\t * Gets an array of event names\n\t * @returns {Array<string | symbol>} An array of event names\n\t */\n\tpublic eventNames(): Array<string | symbol> {\n\t\treturn [...this._eventListeners.keys()];\n\t}\n\n\t/**\n\t * Gets an array of listeners for a specific event. If no event is provided, it returns all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic rawListeners(event?: string | symbol): EventListener[] {\n\t\tif (event === undefined) {\n\t\t\treturn this.getAllListeners();\n\t\t}\n\n\t\treturn this._eventListeners.get(event) ?? [];\n\t}\n\n\t/**\n\t * Prepends a listener to the beginning of the listeners array for the specified event\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic prependListener(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst listeners = this._eventListeners.get(eventName) ?? [];\n\t\tlisteners.unshift(listener);\n\t\tthis._eventListeners.set(eventName, listeners);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Prepends a one-time listener to the beginning of the listeners array for the specified event\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic prependOnceListener(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst onceListener: EventListener = (...arguments_: any[]) => {\n\t\t\tthis.off(eventName as string, onceListener);\n\t\t\tlistener(...arguments_);\n\t\t};\n\n\t\tthis.prependListener(eventName as string, onceListener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the maximum number of listeners that can be added for a single event\n\t * @returns {number} The maximum number of listeners\n\t */\n\tpublic maxListeners(): number {\n\t\treturn this._maxListeners;\n\t}\n\n\t/**\n\t * Adds a listener for a specific event. It is an alias for the on() method\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic addListener(\n\t\tevent: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tthis.on(event, listener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a listener for a specific event\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic on(event: string | symbol, listener: EventListener): IEventEmitter {\n\t\tif (!this._eventListeners.has(event)) {\n\t\t\tthis._eventListeners.set(event, []);\n\t\t}\n\n\t\tconst listeners = this._eventListeners.get(event);\n\n\t\tif (listeners) {\n\t\t\tif (listeners.length >= this._maxListeners) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event as string} listeners added. Use setMaxListeners() to increase limit.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlisteners.push(listener);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a listener for a specific event. It is an alias for the off() method\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic removeListener(event: string, listener: EventListener): IEventEmitter {\n\t\tthis.off(event, listener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a listener for a specific event\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic off(event: string | symbol, listener: EventListener): IEventEmitter {\n\t\tconst listeners = this._eventListeners.get(event) ?? [];\n\t\tconst index = listeners.indexOf(listener);\n\t\tif (index !== -1) {\n\t\t\tlisteners.splice(index, 1);\n\t\t}\n\n\t\tif (listeners.length === 0) {\n\t\t\tthis._eventListeners.delete(event);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Calls all listeners for a specific event\n\t * @param {string | symbol} event\n\t * @param arguments_ The arguments to pass to the listeners\n\t * @returns {boolean} Returns true if the event had listeners, false otherwise\n\t */\n\tpublic emit(event: string | symbol, ...arguments_: any[]): boolean {\n\t\tlet result = false;\n\t\tconst listeners = this._eventListeners.get(event);\n\n\t\tif (listeners && listeners.length > 0) {\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener(...arguments_);\n\t\t\t\tresult = true;\n\t\t\t}\n\t\t}\n\n\t\tif (event === this._errorEvent) {\n\t\t\tconst error =\n\t\t\t\targuments_[0] instanceof Error\n\t\t\t\t\t? arguments_[0]\n\t\t\t\t\t: new Error(`${arguments_[0]}`);\n\n\t\t\tif (this._throwOnEmitError && !result) {\n\t\t\t\tthrow error;\n\t\t\t} else {\n\t\t\t\tif (\n\t\t\t\t\tthis.listeners(this._errorEvent).length === 0 &&\n\t\t\t\t\tthis._throwOnEmptyListeners === true\n\t\t\t\t) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// send it to the logger\n\t\tthis.sendLog(event, arguments_);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Gets all listeners for a specific event. If no event is provided, it returns all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic listeners(event: string | symbol): EventListener[] {\n\t\treturn this._eventListeners.get(event) ?? [];\n\t}\n\n\t/**\n\t * Removes all listeners for a specific event. If no event is provided, it removes all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic removeAllListeners(event?: string | symbol): IEventEmitter {\n\t\tif (event !== undefined) {\n\t\t\tthis._eventListeners.delete(event);\n\t\t} else {\n\t\t\tthis._eventListeners.clear();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the maximum number of listeners that can be added for a single event\n\t * @param {number} n The maximum number of listeners\n\t * @returns {void}\n\t */\n\tpublic setMaxListeners(n: number): void {\n\t\tthis._maxListeners = n;\n\t\tfor (const listeners of this._eventListeners.values()) {\n\t\t\tif (listeners.length > n) {\n\t\t\t\tlisteners.splice(n);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets all listeners\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic getAllListeners(): EventListener[] {\n\t\tlet result: EventListener[] = [];\n\t\tfor (const listeners of this._eventListeners.values()) {\n\t\t\tresult = [...result, ...listeners];\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Sends a log message using the configured logger based on the event name\n\t * @param {string | symbol} eventName - The event name that determines the log level\n\t * @param {unknown} data - The data to log\n\t */\n\tprivate sendLog(eventName: string | symbol, data: any): void {\n\t\tif (!this._logger) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet message: string;\n\t\t/* v8 ignore next -- @preserve */\n\t\tif (typeof data === \"string\") {\n\t\t\tmessage = data;\n\t\t} else if (\n\t\t\tArray.isArray(data) &&\n\t\t\tdata.length > 0 &&\n\t\t\tdata[0] instanceof Error\n\t\t) {\n\t\t\tmessage = data[0].message;\n\t\t\t/* v8 ignore next -- @preserve */\n\t\t} else if (data instanceof Error) {\n\t\t\tmessage = data.message;\n\t\t} else if (\n\t\t\tArray.isArray(data) &&\n\t\t\tdata.length > 0 &&\n\t\t\ttypeof data[0]?.message === \"string\"\n\t\t) {\n\t\t\tmessage = data[0].message;\n\t\t} else {\n\t\t\tmessage = JSON.stringify(data);\n\t\t}\n\n\t\tswitch (eventName) {\n\t\t\tcase \"error\": {\n\t\t\t\tthis._logger.error?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"warn\": {\n\t\t\t\tthis._logger.warn?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"trace\": {\n\t\t\t\tthis._logger.trace?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"debug\": {\n\t\t\t\tthis._logger.debug?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"fatal\": {\n\t\t\t\tthis._logger.fatal?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tthis._logger.info?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n","import { Eventified } from \"./eventified.js\";\nimport type { Hook, HookEntry, HookifiedOptions } from \"./types.js\";\n\nexport type { Hook, HookEntry, HookifiedOptions };\n\nexport class Hookified extends Eventified {\n\tprivate readonly _hooks: Map<string, Hook[]>;\n\tprivate _throwOnHookError = false;\n\tprivate _enforceBeforeAfter = false;\n\tprivate _deprecatedHooks: Map<string, string>;\n\tprivate _allowDeprecated = true;\n\n\tconstructor(options?: HookifiedOptions) {\n\t\tsuper({\n\t\t\tlogger: options?.logger,\n\t\t\tthrowOnEmitError: options?.throwOnEmitError,\n\t\t\tthrowOnEmptyListeners: options?.throwOnEmptyListeners,\n\t\t});\n\t\tthis._hooks = new Map();\n\t\tthis._deprecatedHooks = options?.deprecatedHooks\n\t\t\t? new Map(options.deprecatedHooks)\n\t\t\t: new Map();\n\n\t\tif (options?.throwOnHookError !== undefined) {\n\t\t\tthis._throwOnHookError = options.throwOnHookError;\n\t\t} else if (options?.throwHookErrors !== undefined) {\n\t\t\tthis._throwOnHookError = options.throwHookErrors;\n\t\t}\n\n\t\tif (options?.enforceBeforeAfter !== undefined) {\n\t\t\tthis._enforceBeforeAfter = options.enforceBeforeAfter;\n\t\t}\n\n\t\tif (options?.allowDeprecated !== undefined) {\n\t\t\tthis._allowDeprecated = options.allowDeprecated;\n\t\t}\n\t}\n\n\t/**\n\t * Gets all hooks\n\t * @returns {Map<string, Hook[]>}\n\t */\n\tpublic get hooks() {\n\t\treturn this._hooks;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t * @deprecated - this will be deprecated in version 2. Please use throwOnHookError.\n\t */\n\tpublic get throwHookErrors() {\n\t\treturn this._throwOnHookError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t * @deprecated - this will be deprecated in version 2. Please use throwOnHookError.\n\t */\n\tpublic set throwHookErrors(value) {\n\t\tthis._throwOnHookError = value;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnHookError() {\n\t\treturn this._throwOnHookError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnHookError(value) {\n\t\tthis._throwOnHookError = value;\n\t}\n\n\t/**\n\t * Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.\n\t * @returns {boolean}\n\t * @default false\n\t */\n\tpublic get enforceBeforeAfter() {\n\t\treturn this._enforceBeforeAfter;\n\t}\n\n\t/**\n\t * Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.\n\t * @param {boolean} value\n\t */\n\tpublic set enforceBeforeAfter(value) {\n\t\tthis._enforceBeforeAfter = value;\n\t}\n\n\t/**\n\t * Gets the map of deprecated hook names to deprecation messages.\n\t * @returns {Map<string, string>}\n\t */\n\tpublic get deprecatedHooks() {\n\t\treturn this._deprecatedHooks;\n\t}\n\n\t/**\n\t * Sets the map of deprecated hook names to deprecation messages.\n\t * @param {Map<string, string>} value\n\t */\n\tpublic set deprecatedHooks(value) {\n\t\tthis._deprecatedHooks = value;\n\t}\n\n\t/**\n\t * Gets whether deprecated hooks are allowed to be registered and executed. Default is true.\n\t * @returns {boolean}\n\t */\n\tpublic get allowDeprecated() {\n\t\treturn this._allowDeprecated;\n\t}\n\n\t/**\n\t * Sets whether deprecated hooks are allowed to be registered and executed. Default is true.\n\t * @param {boolean} value\n\t */\n\tpublic set allowDeprecated(value) {\n\t\tthis._allowDeprecated = value;\n\t}\n\n\t/**\n\t * Validates hook event name if enforceBeforeAfter is enabled\n\t * @param {string} event - The event name to validate\n\t * @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'\n\t */\n\tprivate validateHookName(event: string): void {\n\t\tif (this._enforceBeforeAfter) {\n\t\t\tconst eventValue = event.trim().toLocaleLowerCase();\n\t\t\tif (!eventValue.startsWith(\"before\") && !eventValue.startsWith(\"after\")) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Hook event \"${event}\" must start with \"before\" or \"after\" when enforceBeforeAfter is enabled`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks if a hook is deprecated and emits a warning if it is\n\t * @param {string} event - The event name to check\n\t * @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked\n\t */\n\tprivate checkDeprecatedHook(event: string): boolean {\n\t\tif (this._deprecatedHooks.has(event)) {\n\t\t\tconst message = this._deprecatedHooks.get(event);\n\t\t\tconst warningMessage = `Hook \"${event}\" is deprecated${message ? `: ${message}` : \"\"}`;\n\n\t\t\t// Emit deprecation warning event\n\t\t\tthis.emit(\"warn\", { hook: event, message: warningMessage });\n\n\t\t\t// Return false if deprecated hooks are not allowed\n\t\t\treturn this._allowDeprecated;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event\n\t * @param {string} event\n\t * @param {Hook} handler - this can be async or sync\n\t * @returns {void}\n\t */\n\tpublic onHook(event: string, handler: Hook) {\n\t\tthis.onHookEntry({ event, handler });\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event\n\t * @param {HookEntry} hookEntry\n\t * @returns {void}\n\t */\n\tpublic onHookEntry(hookEntry: HookEntry) {\n\t\tthis.validateHookName(hookEntry.event);\n\t\tif (!this.checkDeprecatedHook(hookEntry.event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(hookEntry.event);\n\t\tif (eventHandlers) {\n\t\t\teventHandlers.push(hookEntry.handler);\n\t\t} else {\n\t\t\tthis._hooks.set(hookEntry.event, [hookEntry.handler]);\n\t\t}\n\t}\n\n\t/**\n\t * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.\n\t * @param {string} event\n\t * @param {Hook} handler - this can be async or sync\n\t * @returns {void}\n\t */\n\tpublic addHook(event: string, handler: Hook) {\n\t\tthis.onHookEntry({ event, handler });\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event\n\t * @param {Array<HookEntry>} hooks\n\t * @returns {void}\n\t */\n\tpublic onHooks(hooks: HookEntry[]) {\n\t\tfor (const hook of hooks) {\n\t\t\tthis.onHook(hook.event, hook.handler);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event that runs before all other handlers\n\t * @param {string} event\n\t * @param {Hook} handler - this can be async or sync\n\t * @returns {void}\n\t */\n\tpublic prependHook(event: string, handler: Hook) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\teventHandlers.unshift(handler);\n\t\t} else {\n\t\t\tthis._hooks.set(event, [handler]);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a handler that only executes once for a specific event before all other handlers\n\t * @param event\n\t * @param handler\n\t */\n\tpublic prependOnceHook(event: string, handler: Hook) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tconst hook = async (...arguments_: any[]) => {\n\t\t\tthis.removeHook(event, hook);\n\t\t\treturn handler(...arguments_);\n\t\t};\n\n\t\tthis.prependHook(event, hook);\n\t}\n\n\t/**\n\t * Adds a handler that only executes once for a specific event\n\t * @param event\n\t * @param handler\n\t */\n\tpublic onceHook(event: string, handler: Hook) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tconst hook = async (...arguments_: any[]) => {\n\t\t\tthis.removeHook(event, hook);\n\t\t\treturn handler(...arguments_);\n\t\t};\n\n\t\tthis.onHook(event, hook);\n\t}\n\n\t/**\n\t * Removes a handler function for a specific event\n\t * @param {string} event\n\t * @param {Hook} handler\n\t * @returns {void}\n\t */\n\tpublic removeHook(event: string, handler: Hook) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip removal if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tconst index = eventHandlers.indexOf(handler);\n\t\t\tif (index !== -1) {\n\t\t\t\teventHandlers.splice(index, 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Removes all handlers for a specific event\n\t * @param {Array<HookEntry>} hooks\n\t * @returns {void}\n\t */\n\tpublic removeHooks(hooks: HookEntry[]) {\n\t\tfor (const hook of hooks) {\n\t\t\tthis.removeHook(hook.event, hook.handler);\n\t\t}\n\t}\n\n\t/**\n\t * Calls all handlers for a specific event\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {Promise<void>}\n\t */\n\tpublic async hook<T>(event: string, ...arguments_: T[]) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip execution if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tfor (const handler of eventHandlers) {\n\t\t\t\ttry {\n\t\t\t\t\tawait handler(...arguments_);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = `${event}: ${(error as Error).message}`;\n\t\t\t\t\tthis.emit(\"error\", new Error(message));\n\n\t\t\t\t\tif (this._throwOnHookError) {\n\t\t\t\t\t\tthrow new Error(message);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Calls all synchronous handlers for a specific event.\n\t * Async handlers (declared with `async` keyword) are silently skipped.\n\t *\n\t * Note: The `hook` method is preferred as it executes both sync and async functions.\n\t * Use `hookSync` only when you specifically need synchronous execution.\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {void}\n\t */\n\tpublic hookSync<T>(event: string, ...arguments_: T[]): void {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tfor (const handler of eventHandlers) {\n\t\t\t\t// Skip async functions silently\n\t\t\t\tif (handler.constructor.name === \"AsyncFunction\") {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\thandler(...arguments_);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = `${event}: ${(error as Error).message}`;\n\t\t\t\t\tthis.emit(\"error\", new Error(message));\n\n\t\t\t\t\tif (this._throwOnHookError) {\n\t\t\t\t\t\tthrow new Error(message);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.\n\t * @param {string} event - The event name\n\t * @param {T[]} arguments_ - The arguments to pass to the hook\n\t */\n\tpublic async beforeHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(`before:${event}`, ...arguments_);\n\t}\n\n\t/**\n\t * Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.\n\t * @param {string} event - The event name\n\t * @param {T[]} arguments_ - The arguments to pass to the hook\n\t */\n\tpublic async afterHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(`after:${event}`, ...arguments_);\n\t}\n\n\t/**\n\t * Calls all handlers for a specific event. This is an alias for `hook` and is provided for\n\t * compatibility with other libraries that use the `callHook` method.\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {Promise<void>}\n\t */\n\tpublic async callHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(event, ...arguments_);\n\t}\n\n\t/**\n\t * Gets all hooks for a specific event\n\t * @param {string} event\n\t * @returns {Hook[]}\n\t */\n\tpublic getHooks(event: string) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn undefined; // Return undefined if deprecated hooks are not allowed\n\t\t}\n\t\treturn this._hooks.get(event);\n\t}\n\n\t/**\n\t * Removes all hooks\n\t * @returns {void}\n\t */\n\tpublic clearHooks() {\n\t\tthis._hooks.clear();\n\t}\n}\n\nexport { Eventified } from \"./eventified.js\";\nexport type {\n\tEventEmitterOptions,\n\tEventListener,\n\tIEventEmitter,\n\tLogger,\n} from \"./types.js\";\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../../src/eventified.ts","../../src/hooks/hook.ts","../../src/hooks/waterfall-hook.ts","../../src/index.ts"],"sourcesContent":["// biome-ignore-all lint/suspicious/noExplicitAny: this is for event emitter compatibility\nimport type {\n\tEventEmitterOptions,\n\tEventListener,\n\tIEventEmitter,\n\tLogger,\n} from \"./types.js\";\n\nexport type { EventEmitterOptions, EventListener, IEventEmitter };\n\nexport class Eventified implements IEventEmitter {\n\tprivate readonly _eventListeners: Map<string | symbol, EventListener[]>;\n\tprivate _maxListeners: number;\n\tprivate _eventLogger?: Logger;\n\tprivate _throwOnEmitError = false;\n\tprivate _throwOnEmptyListeners = true;\n\tprivate _errorEvent = \"error\";\n\n\tconstructor(options?: EventEmitterOptions) {\n\t\tthis._eventListeners = new Map<string | symbol, EventListener[]>();\n\t\tthis._maxListeners = 0; // Default is 0 (unlimited)\n\n\t\tthis._eventLogger = options?.eventLogger;\n\n\t\tif (options?.throwOnEmitError !== undefined) {\n\t\t\tthis._throwOnEmitError = options.throwOnEmitError;\n\t\t}\n\n\t\tif (options?.throwOnEmptyListeners !== undefined) {\n\t\t\tthis._throwOnEmptyListeners = options.throwOnEmptyListeners;\n\t\t}\n\t}\n\n\t/**\n\t * Gets the event logger\n\t * @returns {Logger}\n\t */\n\tpublic get eventLogger(): Logger | undefined {\n\t\treturn this._eventLogger;\n\t}\n\n\t/**\n\t * Sets the event logger\n\t * @param {Logger} eventLogger\n\t */\n\tpublic set eventLogger(eventLogger: Logger | undefined) {\n\t\tthis._eventLogger = eventLogger;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnEmitError(): boolean {\n\t\treturn this._throwOnEmitError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnEmitError(value: boolean) {\n\t\tthis._throwOnEmitError = value;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnEmptyListeners(): boolean {\n\t\treturn this._throwOnEmptyListeners;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnEmptyListeners(value: boolean) {\n\t\tthis._throwOnEmptyListeners = value;\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event that will run only once\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic once(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst onceListener: EventListener = (...arguments_: any[]) => {\n\t\t\tthis.off(eventName as string, onceListener);\n\t\t\tlistener(...arguments_);\n\t\t};\n\n\t\tthis.on(eventName as string, onceListener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners\n\t * @param {string} eventName The event name. Not required\n\t * @returns {number} The number of listeners\n\t */\n\tpublic listenerCount(eventName?: string | symbol): number {\n\t\tif (eventName === undefined) {\n\t\t\treturn this.getAllListeners().length;\n\t\t}\n\n\t\tconst listeners = this._eventListeners.get(eventName);\n\t\treturn listeners ? listeners.length : 0;\n\t}\n\n\t/**\n\t * Gets an array of event names\n\t * @returns {Array<string | symbol>} An array of event names\n\t */\n\tpublic eventNames(): Array<string | symbol> {\n\t\treturn [...this._eventListeners.keys()];\n\t}\n\n\t/**\n\t * Gets an array of listeners for a specific event. If no event is provided, it returns all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic rawListeners(event?: string | symbol): EventListener[] {\n\t\tif (event === undefined) {\n\t\t\treturn this.getAllListeners();\n\t\t}\n\n\t\treturn this._eventListeners.get(event) ?? [];\n\t}\n\n\t/**\n\t * Prepends a listener to the beginning of the listeners array for the specified event\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic prependListener(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst listeners = this._eventListeners.get(eventName) ?? [];\n\t\tlisteners.unshift(listener);\n\t\tthis._eventListeners.set(eventName, listeners);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Prepends a one-time listener to the beginning of the listeners array for the specified event\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic prependOnceListener(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst onceListener: EventListener = (...arguments_: any[]) => {\n\t\t\tthis.off(eventName as string, onceListener);\n\t\t\tlistener(...arguments_);\n\t\t};\n\n\t\tthis.prependListener(eventName as string, onceListener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the maximum number of listeners that can be added for a single event\n\t * @returns {number} The maximum number of listeners\n\t */\n\tpublic maxListeners(): number {\n\t\treturn this._maxListeners;\n\t}\n\n\t/**\n\t * Adds a listener for a specific event. It is an alias for the on() method\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic addListener(\n\t\tevent: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tthis.on(event, listener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a listener for a specific event\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic on(event: string | symbol, listener: EventListener): IEventEmitter {\n\t\tif (!this._eventListeners.has(event)) {\n\t\t\tthis._eventListeners.set(event, []);\n\t\t}\n\n\t\tconst listeners = this._eventListeners.get(event);\n\n\t\tif (listeners) {\n\t\t\tif (this._maxListeners > 0 && listeners.length >= this._maxListeners) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event as string} listeners added. Use setMaxListeners() to increase limit.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlisteners.push(listener);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a listener for a specific event. It is an alias for the off() method\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic removeListener(event: string, listener: EventListener): IEventEmitter {\n\t\tthis.off(event, listener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a listener for a specific event\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic off(event: string | symbol, listener: EventListener): IEventEmitter {\n\t\tconst listeners = this._eventListeners.get(event) ?? [];\n\t\tconst index = listeners.indexOf(listener);\n\t\tif (index !== -1) {\n\t\t\tlisteners.splice(index, 1);\n\t\t}\n\n\t\tif (listeners.length === 0) {\n\t\t\tthis._eventListeners.delete(event);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Calls all listeners for a specific event\n\t * @param {string | symbol} event\n\t * @param arguments_ The arguments to pass to the listeners\n\t * @returns {boolean} Returns true if the event had listeners, false otherwise\n\t */\n\tpublic emit(event: string | symbol, ...arguments_: any[]): boolean {\n\t\tlet result = false;\n\t\tconst listeners = this._eventListeners.get(event);\n\n\t\tif (listeners && listeners.length > 0) {\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener(...arguments_);\n\t\t\t\tresult = true;\n\t\t\t}\n\t\t}\n\n\t\t// send it to the logger\n\t\tthis.sendToEventLogger(event, arguments_);\n\n\t\tif (event === this._errorEvent) {\n\t\t\tconst error =\n\t\t\t\targuments_[0] instanceof Error\n\t\t\t\t\t? arguments_[0]\n\t\t\t\t\t: new Error(`${arguments_[0]}`);\n\n\t\t\tif (this._throwOnEmitError && !result) {\n\t\t\t\tthrow error;\n\t\t\t} else {\n\t\t\t\tif (\n\t\t\t\t\tthis.listeners(this._errorEvent).length === 0 &&\n\t\t\t\t\tthis._throwOnEmptyListeners === true\n\t\t\t\t) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Gets all listeners for a specific event. If no event is provided, it returns all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic listeners(event: string | symbol): EventListener[] {\n\t\treturn this._eventListeners.get(event) ?? [];\n\t}\n\n\t/**\n\t * Removes all listeners for a specific event. If no event is provided, it removes all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic removeAllListeners(event?: string | symbol): IEventEmitter {\n\t\tif (event !== undefined) {\n\t\t\tthis._eventListeners.delete(event);\n\t\t} else {\n\t\t\tthis._eventListeners.clear();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the maximum number of listeners that can be added for a single event\n\t * @param {number} n The maximum number of listeners\n\t * @returns {void}\n\t */\n\tpublic setMaxListeners(n: number): void {\n\t\tthis._maxListeners = n < 0 ? 0 : n;\n\t}\n\n\t/**\n\t * Gets all listeners\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic getAllListeners(): EventListener[] {\n\t\tlet result: EventListener[] = [];\n\t\tfor (const listeners of this._eventListeners.values()) {\n\t\t\tresult = [...result, ...listeners];\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Sends a log message using the configured logger based on the event name\n\t * @param {string | symbol} eventName - The event name that determines the log level\n\t * @param {unknown} data - The data to log\n\t */\n\tprivate sendToEventLogger(eventName: string | symbol, data: any): void {\n\t\tif (!this._eventLogger) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet message: string;\n\t\t/* v8 ignore next -- @preserve */\n\t\tif (typeof data === \"string\") {\n\t\t\tmessage = data;\n\t\t} else if (\n\t\t\tArray.isArray(data) &&\n\t\t\tdata.length > 0 &&\n\t\t\tdata[0] instanceof Error\n\t\t) {\n\t\t\tmessage = data[0].message;\n\t\t\t/* v8 ignore next -- @preserve */\n\t\t} else if (data instanceof Error) {\n\t\t\tmessage = data.message;\n\t\t} else if (\n\t\t\tArray.isArray(data) &&\n\t\t\tdata.length > 0 &&\n\t\t\ttypeof data[0]?.message === \"string\"\n\t\t) {\n\t\t\tmessage = data[0].message;\n\t\t} else {\n\t\t\tmessage = JSON.stringify(data);\n\t\t}\n\n\t\tswitch (eventName) {\n\t\t\tcase \"error\": {\n\t\t\t\tthis._eventLogger.error?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"warn\": {\n\t\t\t\tthis._eventLogger.warn?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"trace\": {\n\t\t\t\tthis._eventLogger.trace?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"debug\": {\n\t\t\t\tthis._eventLogger.debug?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"fatal\": {\n\t\t\t\tthis._eventLogger.fatal?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tthis._eventLogger.info?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { HookFn, IHook } from \"../types.js\";\n\n/**\n * Concrete implementation of the IHook interface.\n * Provides a convenient class-based way to create hook entries.\n */\nexport class Hook implements IHook {\n\tpublic id?: string;\n\tpublic event: string;\n\tpublic handler: HookFn;\n\n\t/**\n\t * Creates a new Hook instance\n\t * @param {string} event - The event name for the hook\n\t * @param {HookFn} handler - The handler function for the hook\n\t * @param {string} [id] - Optional unique identifier for the hook\n\t */\n\tconstructor(event: string, handler: HookFn, id?: string) {\n\t\tthis.id = id;\n\t\tthis.event = event;\n\t\tthis.handler = handler;\n\t}\n}\n","import type {\n\tHookFn,\n\tIWaterfallHook,\n\tWaterfallHookFn,\n\tWaterfallHookResult,\n} from \"../types.js\";\n\n/**\n * A WaterfallHook chains multiple hook functions sequentially,\n * where each hook receives a context with the previous result,\n * initial arguments, and accumulated results. After all hooks\n * have executed, the final handler receives the transformed result.\n * Implements IHook for compatibility with Hookified.onHook().\n */\nexport class WaterfallHook implements IWaterfallHook {\n\tpublic id?: string;\n\tpublic event: string;\n\tpublic handler: HookFn;\n\tpublic hooks: WaterfallHookFn[];\n\n\tprivate readonly _finalHandler: WaterfallHookFn;\n\n\t/**\n\t * Creates a new WaterfallHook instance\n\t * @param {string} event - The event name for the hook\n\t * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result\n\t * @param {string} [id] - Optional unique identifier for the hook\n\t */\n\tconstructor(event: string, finalHandler: WaterfallHookFn, id?: string) {\n\t\tthis.id = id;\n\t\tthis.event = event;\n\t\tthis.hooks = [];\n\t\tthis._finalHandler = finalHandler;\n\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tthis.handler = async (...arguments_: any[]) => {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: waterfall result type varies through the chain\n\t\t\tconst initialArgs: any =\n\t\t\t\targuments_.length === 1 ? arguments_[0] : arguments_;\n\t\t\tconst results: WaterfallHookResult[] = [];\n\n\t\t\tfor (const hook of this.hooks) {\n\t\t\t\tconst result = await hook({ initialArgs, results: [...results] });\n\t\t\t\tresults.push({ hook, result });\n\t\t\t}\n\n\t\t\tawait this._finalHandler({ initialArgs, results: [...results] });\n\t\t};\n\t}\n\n\t/**\n\t * Adds a hook function to the end of the waterfall chain\n\t * @param {WaterfallHookFn} hook - The hook function to add\n\t */\n\tpublic addHook(hook: WaterfallHookFn): void {\n\t\tthis.hooks.push(hook);\n\t}\n\n\t/**\n\t * Removes a specific hook function from the waterfall chain\n\t * @param {WaterfallHookFn} hook - The hook function to remove\n\t * @returns {boolean} true if the hook was found and removed\n\t */\n\tpublic removeHook(hook: WaterfallHookFn): boolean {\n\t\tconst index = this.hooks.indexOf(hook);\n\t\tif (index !== -1) {\n\t\t\tthis.hooks.splice(index, 1);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n}\n","import { Eventified } from \"./eventified.js\";\nimport type {\n\tHookFn,\n\tHookifiedOptions,\n\tIHook,\n\tIWaterfallHook,\n\tOnHookOptions,\n\tPrependHookOptions,\n\tWaterfallHookContext,\n\tWaterfallHookFn,\n\tWaterfallHookResult,\n} from \"./types.js\";\n\nexport type {\n\tHookFn,\n\tHookifiedOptions,\n\tIHook,\n\tIWaterfallHook,\n\tOnHookOptions,\n\tPrependHookOptions,\n\tWaterfallHookContext,\n\tWaterfallHookFn,\n\tWaterfallHookResult,\n};\n\nexport class Hookified extends Eventified {\n\tprivate readonly _hooks: Map<string, IHook[]>;\n\tprivate _throwOnHookError = false;\n\tprivate _enforceBeforeAfter = false;\n\tprivate _deprecatedHooks: Map<string, string>;\n\tprivate _allowDeprecated = true;\n\tprivate _useHookClone = true;\n\n\tconstructor(options?: HookifiedOptions) {\n\t\tsuper({\n\t\t\teventLogger: options?.eventLogger,\n\t\t\tthrowOnEmitError: options?.throwOnEmitError,\n\t\t\tthrowOnEmptyListeners: options?.throwOnEmptyListeners,\n\t\t});\n\t\tthis._hooks = new Map();\n\t\tthis._deprecatedHooks = options?.deprecatedHooks\n\t\t\t? new Map(options.deprecatedHooks)\n\t\t\t: new Map();\n\n\t\tif (options?.throwOnHookError !== undefined) {\n\t\t\tthis._throwOnHookError = options.throwOnHookError;\n\t\t}\n\n\t\tif (options?.enforceBeforeAfter !== undefined) {\n\t\t\tthis._enforceBeforeAfter = options.enforceBeforeAfter;\n\t\t}\n\n\t\tif (options?.allowDeprecated !== undefined) {\n\t\t\tthis._allowDeprecated = options.allowDeprecated;\n\t\t}\n\n\t\tif (options?.useHookClone !== undefined) {\n\t\t\tthis._useHookClone = options.useHookClone;\n\t\t}\n\t}\n\n\t/**\n\t * Gets all hooks\n\t * @returns {Map<string, IHook[]>}\n\t */\n\tpublic get hooks() {\n\t\treturn this._hooks;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnHookError() {\n\t\treturn this._throwOnHookError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnHookError(value) {\n\t\tthis._throwOnHookError = value;\n\t}\n\n\t/**\n\t * Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.\n\t * @returns {boolean}\n\t * @default false\n\t */\n\tpublic get enforceBeforeAfter() {\n\t\treturn this._enforceBeforeAfter;\n\t}\n\n\t/**\n\t * Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.\n\t * @param {boolean} value\n\t */\n\tpublic set enforceBeforeAfter(value) {\n\t\tthis._enforceBeforeAfter = value;\n\t}\n\n\t/**\n\t * Gets the map of deprecated hook names to deprecation messages.\n\t * @returns {Map<string, string>}\n\t */\n\tpublic get deprecatedHooks() {\n\t\treturn this._deprecatedHooks;\n\t}\n\n\t/**\n\t * Sets the map of deprecated hook names to deprecation messages.\n\t * @param {Map<string, string>} value\n\t */\n\tpublic set deprecatedHooks(value) {\n\t\tthis._deprecatedHooks = value;\n\t}\n\n\t/**\n\t * Gets whether deprecated hooks are allowed to be registered and executed. Default is true.\n\t * @returns {boolean}\n\t */\n\tpublic get allowDeprecated() {\n\t\treturn this._allowDeprecated;\n\t}\n\n\t/**\n\t * Sets whether deprecated hooks are allowed to be registered and executed. Default is true.\n\t * @param {boolean} value\n\t */\n\tpublic set allowDeprecated(value) {\n\t\tthis._allowDeprecated = value;\n\t}\n\n\t/**\n\t * Gets whether hook objects are cloned before storing. Default is true.\n\t * @returns {boolean}\n\t */\n\tpublic get useHookClone() {\n\t\treturn this._useHookClone;\n\t}\n\n\t/**\n\t * Sets whether hook objects are cloned before storing. Default is true.\n\t * When false, the original IHook reference is stored directly.\n\t * @param {boolean} value\n\t */\n\tpublic set useHookClone(value) {\n\t\tthis._useHookClone = value;\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event.\n\t * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead.\n\t * To register multiple hooks at once, use {@link onHooks}.\n\t * @param {IHook} hook - the hook containing event name and handler\n\t * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)\n\t * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation\n\t */\n\tpublic onHook(hook: IHook, options?: OnHookOptions): IHook | undefined {\n\t\tthis.validateHookName(hook.event);\n\t\tif (!this.checkDeprecatedHook(hook.event)) {\n\t\t\treturn undefined; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\n\t\tconst shouldClone = options?.useHookClone ?? this._useHookClone;\n\t\tconst entry: IHook = shouldClone\n\t\t\t? { id: hook.id, event: hook.event, handler: hook.handler }\n\t\t\t: hook;\n\n\t\tentry.id = entry.id ?? crypto.randomUUID();\n\n\t\tconst eventHandlers = this._hooks.get(hook.event);\n\t\tif (eventHandlers) {\n\t\t\t// Check for duplicate id — replace in-place if found\n\t\t\tconst existingIndex = eventHandlers.findIndex((h) => h.id === entry.id);\n\t\t\tif (existingIndex !== -1) {\n\t\t\t\teventHandlers[existingIndex] = entry;\n\t\t\t} else {\n\t\t\t\tconst position = options?.position ?? \"Bottom\";\n\t\t\t\tif (position === \"Top\") {\n\t\t\t\t\teventHandlers.unshift(entry);\n\t\t\t\t} else if (position === \"Bottom\") {\n\t\t\t\t\teventHandlers.push(entry);\n\t\t\t\t} else {\n\t\t\t\t\tconst index = Math.max(0, Math.min(position, eventHandlers.length));\n\t\t\t\t\teventHandlers.splice(index, 0, entry);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis._hooks.set(hook.event, [entry]);\n\t\t}\n\n\t\treturn entry;\n\t}\n\n\t/**\n\t * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.\n\t * @param {string} event - the event name\n\t * @param {HookFn} handler - the handler function\n\t * @returns {void}\n\t */\n\tpublic addHook(event: string, handler: HookFn) {\n\t\tthis.onHook({ event, handler });\n\t}\n\n\t/**\n\t * Adds handler functions for specific events\n\t * @param {Array<IHook>} hooks\n\t * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)\n\t * @returns {void}\n\t */\n\tpublic onHooks(hooks: IHook[], options?: OnHookOptions) {\n\t\tfor (const hook of hooks) {\n\t\t\tthis.onHook(hook, options);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event that runs before all other handlers.\n\t * Equivalent to calling `onHook(hook, { position: \"Top\" })`.\n\t * @param {IHook} hook - the hook containing event name and handler\n\t * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)\n\t * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation\n\t */\n\tpublic prependHook(\n\t\thook: IHook,\n\t\toptions?: PrependHookOptions,\n\t): IHook | undefined {\n\t\treturn this.onHook(hook, { ...options, position: \"Top\" });\n\t}\n\n\t/**\n\t * Adds a handler that only executes once for a specific event before all other handlers.\n\t * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: \"Top\" }`.\n\t * @param {IHook} hook - the hook containing event name and handler\n\t * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)\n\t * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation\n\t */\n\tpublic prependOnceHook(\n\t\thook: IHook,\n\t\toptions?: PrependHookOptions,\n\t): IHook | undefined {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tconst wrappedHandler = async (...arguments_: any[]) => {\n\t\t\tthis.removeHook({ event: hook.event, handler: wrappedHandler });\n\t\t\treturn hook.handler(...arguments_);\n\t\t};\n\n\t\treturn this.onHook(\n\t\t\t{ id: hook.id, event: hook.event, handler: wrappedHandler },\n\t\t\t{ ...options, position: \"Top\" },\n\t\t);\n\t}\n\n\t/**\n\t * Adds a handler that only executes once for a specific event\n\t * @param {IHook} hook - the hook containing event name and handler\n\t */\n\tpublic onceHook(hook: IHook) {\n\t\tthis.validateHookName(hook.event);\n\t\tif (!this.checkDeprecatedHook(hook.event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tconst wrappedHandler = async (...arguments_: any[]) => {\n\t\t\tthis.removeHook({ event: hook.event, handler: wrappedHandler });\n\t\t\treturn hook.handler(...arguments_);\n\t\t};\n\n\t\tthis.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler });\n\t}\n\n\t/**\n\t * Removes a handler function for a specific event\n\t * @param {IHook} hook - the hook containing event name and handler to remove\n\t * @returns {IHook | undefined} the removed hook, or undefined if not found\n\t */\n\tpublic removeHook(hook: IHook): IHook | undefined {\n\t\tthis.validateHookName(hook.event);\n\t\tconst eventHandlers = this._hooks.get(hook.event);\n\t\tif (eventHandlers) {\n\t\t\tconst index = eventHandlers.findIndex((h) => h.handler === hook.handler);\n\t\t\tif (index !== -1) {\n\t\t\t\teventHandlers.splice(index, 1);\n\t\t\t\tif (eventHandlers.length === 0) {\n\t\t\t\t\tthis._hooks.delete(hook.event);\n\t\t\t\t}\n\n\t\t\t\treturn { event: hook.event, handler: hook.handler };\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Removes multiple hook handlers\n\t * @param {Array<IHook>} hooks\n\t * @returns {IHook[]} the hooks that were successfully removed\n\t */\n\tpublic removeHooks(hooks: IHook[]): IHook[] {\n\t\tconst removed: IHook[] = [];\n\t\tfor (const hook of hooks) {\n\t\t\tconst result = this.removeHook(hook);\n\t\t\tif (result) {\n\t\t\t\tremoved.push(result);\n\t\t\t}\n\t\t}\n\n\t\treturn removed;\n\t}\n\n\t/**\n\t * Calls all handlers for a specific event\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {Promise<void>}\n\t */\n\tpublic async hook<T>(event: string, ...arguments_: T[]) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip execution if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tfor (const hook of [...eventHandlers]) {\n\t\t\t\ttry {\n\t\t\t\t\tawait hook.handler(...arguments_);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = `${event}: ${(error as Error).message}`;\n\t\t\t\t\tthis.emit(\"error\", new Error(message));\n\n\t\t\t\t\tif (this._throwOnHookError) {\n\t\t\t\t\t\tthrow new Error(message);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Calls all synchronous handlers for a specific event.\n\t * Async handlers (declared with `async` keyword) are silently skipped.\n\t *\n\t * Note: The `hook` method is preferred as it executes both sync and async functions.\n\t * Use `hookSync` only when you specifically need synchronous execution.\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {void}\n\t */\n\tpublic hookSync<T>(event: string, ...arguments_: T[]): void {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tfor (const hook of [...eventHandlers]) {\n\t\t\t\t// Skip async functions silently\n\t\t\t\tif (hook.handler.constructor.name === \"AsyncFunction\") {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\thook.handler(...arguments_);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = `${event}: ${(error as Error).message}`;\n\t\t\t\t\tthis.emit(\"error\", new Error(message));\n\n\t\t\t\t\tif (this._throwOnHookError) {\n\t\t\t\t\t\tthrow new Error(message);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.\n\t * @param {string} event - The event name\n\t * @param {T[]} arguments_ - The arguments to pass to the hook\n\t */\n\tpublic async beforeHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(`before:${event}`, ...arguments_);\n\t}\n\n\t/**\n\t * Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.\n\t * @param {string} event - The event name\n\t * @param {T[]} arguments_ - The arguments to pass to the hook\n\t */\n\tpublic async afterHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(`after:${event}`, ...arguments_);\n\t}\n\n\t/**\n\t * Calls all handlers for a specific event. This is an alias for `hook` and is provided for\n\t * compatibility with other libraries that use the `callHook` method.\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {Promise<void>}\n\t */\n\tpublic async callHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(event, ...arguments_);\n\t}\n\n\t/**\n\t * Gets all hooks for a specific event\n\t * @param {string} event\n\t * @returns {IHook[]}\n\t */\n\tpublic getHooks(event: string) {\n\t\tthis.validateHookName(event);\n\t\treturn this._hooks.get(event);\n\t}\n\n\t/**\n\t * Gets a specific hook by id, searching across all events\n\t * @param {string} id - the hook id\n\t * @returns {IHook | undefined} the hook if found, or undefined\n\t */\n\tpublic getHook(id: string): IHook | undefined {\n\t\tfor (const eventHandlers of this._hooks.values()) {\n\t\t\tconst found = eventHandlers.find((h) => h.id === id);\n\t\t\tif (found) {\n\t\t\t\treturn found;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Removes one or more hooks by id, searching across all events\n\t * @param {string | string[]} id - the hook id or array of hook ids to remove\n\t * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found\n\t */\n\tpublic removeHookById(id: string | string[]): IHook | IHook[] | undefined {\n\t\tif (Array.isArray(id)) {\n\t\t\tconst removed: IHook[] = [];\n\t\t\tfor (const singleId of id) {\n\t\t\t\tconst result = this.removeHookById(singleId);\n\t\t\t\tif (result && !Array.isArray(result)) {\n\t\t\t\t\tremoved.push(result);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn removed;\n\t\t}\n\n\t\tfor (const [event, eventHandlers] of this._hooks.entries()) {\n\t\t\tconst index = eventHandlers.findIndex((h) => h.id === id);\n\t\t\tif (index !== -1) {\n\t\t\t\tconst [removed] = eventHandlers.splice(index, 1);\n\t\t\t\tif (eventHandlers.length === 0) {\n\t\t\t\t\tthis._hooks.delete(event);\n\t\t\t\t}\n\n\t\t\t\treturn removed;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Removes all hooks\n\t * @returns {void}\n\t */\n\tpublic clearHooks() {\n\t\tthis._hooks.clear();\n\t}\n\n\t/**\n\t * Removes all hooks for a specific event and returns the removed hooks.\n\t * @param {string} event - The event name to remove hooks for.\n\t * @returns {IHook[]} the hooks that were removed\n\t */\n\tpublic removeEventHooks(event: string): IHook[] {\n\t\tthis.validateHookName(event);\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tconst removed = [...eventHandlers];\n\t\t\tthis._hooks.delete(event);\n\t\t\treturn removed;\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Validates hook event name if enforceBeforeAfter is enabled\n\t * @param {string} event - The event name to validate\n\t * @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'\n\t */\n\tprivate validateHookName(event: string): void {\n\t\tif (this._enforceBeforeAfter) {\n\t\t\tconst eventValue = event.trim().toLocaleLowerCase();\n\t\t\tif (!eventValue.startsWith(\"before\") && !eventValue.startsWith(\"after\")) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Hook event \"${event}\" must start with \"before\" or \"after\" when enforceBeforeAfter is enabled`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks if a hook is deprecated and emits a warning if it is\n\t * @param {string} event - The event name to check\n\t * @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked\n\t */\n\tprivate checkDeprecatedHook(event: string): boolean {\n\t\tif (this._deprecatedHooks.has(event)) {\n\t\t\tconst message = this._deprecatedHooks.get(event);\n\t\t\tconst warningMessage = `Hook \"${event}\" is deprecated${message ? `: ${message}` : \"\"}`;\n\n\t\t\t// Emit deprecation warning event\n\t\t\tthis.emit(\"warn\", { hook: event, message: warningMessage });\n\n\t\t\t// Return false if deprecated hooks are not allowed\n\t\t\treturn this._allowDeprecated;\n\t\t}\n\t\treturn true;\n\t}\n}\n\nexport { Eventified } from \"./eventified.js\";\nexport { Hook } from \"./hooks/hook.js\";\nexport { WaterfallHook } from \"./hooks/waterfall-hook.js\";\nexport type {\n\tEventEmitterOptions,\n\tEventListener,\n\tIEventEmitter,\n\tLogger,\n} from \"./types.js\";\n"],"mappings":"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","names":[]} |
+286
-156
@@ -10,9 +10,9 @@ var __defProp = Object.defineProperty; | ||
| __publicField(this, "_maxListeners"); | ||
| __publicField(this, "_logger"); | ||
| __publicField(this, "_eventLogger"); | ||
| __publicField(this, "_throwOnEmitError", false); | ||
| __publicField(this, "_throwOnEmptyListeners", false); | ||
| __publicField(this, "_throwOnEmptyListeners", true); | ||
| __publicField(this, "_errorEvent", "error"); | ||
| this._eventListeners = /* @__PURE__ */ new Map(); | ||
| this._maxListeners = 100; | ||
| this._logger = options?.logger; | ||
| this._maxListeners = 0; | ||
| this._eventLogger = options?.eventLogger; | ||
| if (options?.throwOnEmitError !== void 0) { | ||
@@ -26,14 +26,14 @@ this._throwOnEmitError = options.throwOnEmitError; | ||
| /** | ||
| * Gets the logger | ||
| * Gets the event logger | ||
| * @returns {Logger} | ||
| */ | ||
| get logger() { | ||
| return this._logger; | ||
| get eventLogger() { | ||
| return this._eventLogger; | ||
| } | ||
| /** | ||
| * Sets the logger | ||
| * @param {Logger} logger | ||
| * Sets the event logger | ||
| * @param {Logger} eventLogger | ||
| */ | ||
| set logger(logger) { | ||
| this._logger = logger; | ||
| set eventLogger(eventLogger) { | ||
| this._eventLogger = eventLogger; | ||
| } | ||
@@ -167,3 +167,3 @@ /** | ||
| if (listeners) { | ||
| if (listeners.length >= this._maxListeners) { | ||
| if (this._maxListeners > 0 && listeners.length >= this._maxListeners) { | ||
| console.warn( | ||
@@ -219,2 +219,3 @@ `MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.` | ||
| } | ||
| this.sendToEventLogger(event, arguments_); | ||
| if (event === this._errorEvent) { | ||
@@ -230,3 +231,2 @@ const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`); | ||
| } | ||
| this.sendLog(event, arguments_); | ||
| return result; | ||
@@ -261,8 +261,3 @@ } | ||
| setMaxListeners(n) { | ||
| this._maxListeners = n; | ||
| for (const listeners of this._eventListeners.values()) { | ||
| if (listeners.length > n) { | ||
| listeners.splice(n); | ||
| } | ||
| } | ||
| this._maxListeners = n < 0 ? 0 : n; | ||
| } | ||
@@ -285,4 +280,4 @@ /** | ||
| */ | ||
| sendLog(eventName, data) { | ||
| if (!this._logger) { | ||
| sendToEventLogger(eventName, data) { | ||
| if (!this._eventLogger) { | ||
| return; | ||
@@ -304,23 +299,23 @@ } | ||
| case "error": { | ||
| this._logger.error?.(message, { event: eventName, data }); | ||
| this._eventLogger.error?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "warn": { | ||
| this._logger.warn?.(message, { event: eventName, data }); | ||
| this._eventLogger.warn?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "trace": { | ||
| this._logger.trace?.(message, { event: eventName, data }); | ||
| this._eventLogger.trace?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "debug": { | ||
| this._logger.debug?.(message, { event: eventName, data }); | ||
| this._eventLogger.debug?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "fatal": { | ||
| this._logger.fatal?.(message, { event: eventName, data }); | ||
| this._eventLogger.fatal?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| default: { | ||
| this._logger.info?.(message, { event: eventName, data }); | ||
| this._eventLogger.info?.(message, { event: eventName, data }); | ||
| break; | ||
@@ -332,2 +327,70 @@ } | ||
| // src/hooks/hook.ts | ||
| var Hook = class { | ||
| /** | ||
| * Creates a new Hook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {HookFn} handler - The handler function for the hook | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event, handler, id) { | ||
| __publicField(this, "id"); | ||
| __publicField(this, "event"); | ||
| __publicField(this, "handler"); | ||
| this.id = id; | ||
| this.event = event; | ||
| this.handler = handler; | ||
| } | ||
| }; | ||
| // src/hooks/waterfall-hook.ts | ||
| var WaterfallHook = class { | ||
| /** | ||
| * Creates a new WaterfallHook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event, finalHandler, id) { | ||
| __publicField(this, "id"); | ||
| __publicField(this, "event"); | ||
| __publicField(this, "handler"); | ||
| __publicField(this, "hooks"); | ||
| __publicField(this, "_finalHandler"); | ||
| this.id = id; | ||
| this.event = event; | ||
| this.hooks = []; | ||
| this._finalHandler = finalHandler; | ||
| this.handler = async (...arguments_) => { | ||
| const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_; | ||
| const results = []; | ||
| for (const hook of this.hooks) { | ||
| const result = await hook({ initialArgs, results: [...results] }); | ||
| results.push({ hook, result }); | ||
| } | ||
| await this._finalHandler({ initialArgs, results: [...results] }); | ||
| }; | ||
| } | ||
| /** | ||
| * Adds a hook function to the end of the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to add | ||
| */ | ||
| addHook(hook) { | ||
| this.hooks.push(hook); | ||
| } | ||
| /** | ||
| * Removes a specific hook function from the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to remove | ||
| * @returns {boolean} true if the hook was found and removed | ||
| */ | ||
| removeHook(hook) { | ||
| const index = this.hooks.indexOf(hook); | ||
| if (index !== -1) { | ||
| this.hooks.splice(index, 1); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| }; | ||
| // src/index.ts | ||
@@ -337,3 +400,3 @@ var Hookified = class extends Eventified { | ||
| super({ | ||
| logger: options?.logger, | ||
| eventLogger: options?.eventLogger, | ||
| throwOnEmitError: options?.throwOnEmitError, | ||
@@ -347,2 +410,3 @@ throwOnEmptyListeners: options?.throwOnEmptyListeners | ||
| __publicField(this, "_allowDeprecated", true); | ||
| __publicField(this, "_useHookClone", true); | ||
| this._hooks = /* @__PURE__ */ new Map(); | ||
@@ -352,4 +416,2 @@ this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map(); | ||
| this._throwOnHookError = options.throwOnHookError; | ||
| } else if (options?.throwHookErrors !== void 0) { | ||
| this._throwOnHookError = options.throwHookErrors; | ||
| } | ||
@@ -362,6 +424,9 @@ if (options?.enforceBeforeAfter !== void 0) { | ||
| } | ||
| if (options?.useHookClone !== void 0) { | ||
| this._useHookClone = options.useHookClone; | ||
| } | ||
| } | ||
| /** | ||
| * Gets all hooks | ||
| * @returns {Map<string, Hook[]>} | ||
| * @returns {Map<string, IHook[]>} | ||
| */ | ||
@@ -374,19 +439,3 @@ get hooks() { | ||
| * @returns {boolean} | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| get throwHookErrors() { | ||
| return this._throwOnHookError; | ||
| } | ||
| /** | ||
| * 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 | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| set throwHookErrors(value) { | ||
| this._throwOnHookError = value; | ||
| } | ||
| /** | ||
| * 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 throwOnHookError() { | ||
@@ -446,153 +495,149 @@ return this._throwOnHookError; | ||
| /** | ||
| * 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' | ||
| * Gets whether hook objects are cloned before storing. Default is true. | ||
| * @returns {boolean} | ||
| */ | ||
| 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` | ||
| ); | ||
| } | ||
| } | ||
| get useHookClone() { | ||
| return this._useHookClone; | ||
| } | ||
| /** | ||
| * 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 | ||
| * Sets whether hook objects are cloned before storing. Default is true. | ||
| * When false, the original IHook reference is stored directly. | ||
| * @param {boolean} value | ||
| */ | ||
| 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 }); | ||
| return this._allowDeprecated; | ||
| } | ||
| return true; | ||
| set useHookClone(value) { | ||
| this._useHookClone = value; | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler - this can be async or sync | ||
| * @returns {void} | ||
| * Adds a handler function for a specific event. | ||
| * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead. | ||
| * To register multiple hooks at once, use {@link onHooks}. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| onHook(event, handler) { | ||
| this.onHookEntry({ event, handler }); | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {HookEntry} hookEntry | ||
| * @returns {void} | ||
| */ | ||
| onHookEntry(hookEntry) { | ||
| this.validateHookName(hookEntry.event); | ||
| if (!this.checkDeprecatedHook(hookEntry.event)) { | ||
| return; | ||
| onHook(hook, options) { | ||
| this.validateHookName(hook.event); | ||
| if (!this.checkDeprecatedHook(hook.event)) { | ||
| return void 0; | ||
| } | ||
| const eventHandlers = this._hooks.get(hookEntry.event); | ||
| const shouldClone = options?.useHookClone ?? this._useHookClone; | ||
| const entry = shouldClone ? { id: hook.id, event: hook.event, handler: hook.handler } : hook; | ||
| entry.id = entry.id ?? crypto.randomUUID(); | ||
| const eventHandlers = this._hooks.get(hook.event); | ||
| if (eventHandlers) { | ||
| eventHandlers.push(hookEntry.handler); | ||
| const existingIndex = eventHandlers.findIndex((h) => h.id === entry.id); | ||
| if (existingIndex !== -1) { | ||
| eventHandlers[existingIndex] = entry; | ||
| } else { | ||
| const position = options?.position ?? "Bottom"; | ||
| if (position === "Top") { | ||
| eventHandlers.unshift(entry); | ||
| } else if (position === "Bottom") { | ||
| eventHandlers.push(entry); | ||
| } else { | ||
| const index = Math.max(0, Math.min(position, eventHandlers.length)); | ||
| eventHandlers.splice(index, 0, entry); | ||
| } | ||
| } | ||
| } else { | ||
| this._hooks.set(hookEntry.event, [hookEntry.handler]); | ||
| this._hooks.set(hook.event, [entry]); | ||
| } | ||
| return entry; | ||
| } | ||
| /** | ||
| * 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 | ||
| * @param {string} event - the event name | ||
| * @param {HookFn} handler - the handler function | ||
| * @returns {void} | ||
| */ | ||
| addHook(event, handler) { | ||
| this.onHookEntry({ event, handler }); | ||
| this.onHook({ event, handler }); | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * Adds handler functions for specific events | ||
| * @param {Array<IHook>} hooks | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {void} | ||
| */ | ||
| onHooks(hooks) { | ||
| onHooks(hooks, options) { | ||
| for (const hook of hooks) { | ||
| this.onHook(hook.event, hook.handler); | ||
| this.onHook(hook, options); | ||
| } | ||
| } | ||
| /** | ||
| * 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} | ||
| * Adds a handler function for a specific event that runs before all other handlers. | ||
| * Equivalent to calling `onHook(hook, { position: "Top" })`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| 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]); | ||
| } | ||
| prependHook(hook, options) { | ||
| return this.onHook(hook, { ...options, position: "Top" }); | ||
| } | ||
| /** | ||
| * Adds a handler that only executes once for a specific event before all other handlers | ||
| * @param event | ||
| * @param handler | ||
| * Adds a handler that only executes once for a specific event before all other handlers. | ||
| * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| prependOnceHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return; | ||
| } | ||
| const hook = async (...arguments_) => { | ||
| this.removeHook(event, hook); | ||
| return handler(...arguments_); | ||
| prependOnceHook(hook, options) { | ||
| const wrappedHandler = async (...arguments_) => { | ||
| this.removeHook({ event: hook.event, handler: wrappedHandler }); | ||
| return hook.handler(...arguments_); | ||
| }; | ||
| this.prependHook(event, hook); | ||
| return this.onHook( | ||
| { id: hook.id, event: hook.event, handler: wrappedHandler }, | ||
| { ...options, position: "Top" } | ||
| ); | ||
| } | ||
| /** | ||
| * Adds a handler that only executes once for a specific event | ||
| * @param event | ||
| * @param handler | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| */ | ||
| onceHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| onceHook(hook) { | ||
| this.validateHookName(hook.event); | ||
| if (!this.checkDeprecatedHook(hook.event)) { | ||
| return; | ||
| } | ||
| const hook = async (...arguments_) => { | ||
| this.removeHook(event, hook); | ||
| return handler(...arguments_); | ||
| const wrappedHandler = async (...arguments_) => { | ||
| this.removeHook({ event: hook.event, handler: wrappedHandler }); | ||
| return hook.handler(...arguments_); | ||
| }; | ||
| this.onHook(event, hook); | ||
| this.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler }); | ||
| } | ||
| /** | ||
| * Removes a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler | ||
| * @returns {void} | ||
| * @param {IHook} hook - the hook containing event name and handler to remove | ||
| * @returns {IHook | undefined} the removed hook, or undefined if not found | ||
| */ | ||
| removeHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return; | ||
| } | ||
| const eventHandlers = this._hooks.get(event); | ||
| removeHook(hook) { | ||
| this.validateHookName(hook.event); | ||
| const eventHandlers = this._hooks.get(hook.event); | ||
| if (eventHandlers) { | ||
| const index = eventHandlers.indexOf(handler); | ||
| const index = eventHandlers.findIndex((h) => h.handler === hook.handler); | ||
| if (index !== -1) { | ||
| eventHandlers.splice(index, 1); | ||
| if (eventHandlers.length === 0) { | ||
| this._hooks.delete(hook.event); | ||
| } | ||
| return { event: hook.event, handler: hook.handler }; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes all handlers for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * @returns {void} | ||
| * Removes multiple hook handlers | ||
| * @param {Array<IHook>} hooks | ||
| * @returns {IHook[]} the hooks that were successfully removed | ||
| */ | ||
| removeHooks(hooks) { | ||
| const removed = []; | ||
| for (const hook of hooks) { | ||
| this.removeHook(hook.event, hook.handler); | ||
| const result = this.removeHook(hook); | ||
| if (result) { | ||
| removed.push(result); | ||
| } | ||
| } | ||
| return removed; | ||
| } | ||
@@ -612,5 +657,5 @@ /** | ||
| if (eventHandlers) { | ||
| for (const handler of eventHandlers) { | ||
| for (const hook of [...eventHandlers]) { | ||
| try { | ||
| await handler(...arguments_); | ||
| await hook.handler(...arguments_); | ||
| } catch (error) { | ||
@@ -643,8 +688,8 @@ const message = `${event}: ${error.message}`; | ||
| if (eventHandlers) { | ||
| for (const handler of eventHandlers) { | ||
| if (handler.constructor.name === "AsyncFunction") { | ||
| for (const hook of [...eventHandlers]) { | ||
| if (hook.handler.constructor.name === "AsyncFunction") { | ||
| continue; | ||
| } | ||
| try { | ||
| handler(...arguments_); | ||
| hook.handler(...arguments_); | ||
| } catch (error) { | ||
@@ -689,12 +734,51 @@ const message = `${event}: ${error.message}`; | ||
| * @param {string} event | ||
| * @returns {Hook[]} | ||
| * @returns {IHook[]} | ||
| */ | ||
| getHooks(event) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return void 0; | ||
| } | ||
| return this._hooks.get(event); | ||
| } | ||
| /** | ||
| * Gets a specific hook by id, searching across all events | ||
| * @param {string} id - the hook id | ||
| * @returns {IHook | undefined} the hook if found, or undefined | ||
| */ | ||
| getHook(id) { | ||
| for (const eventHandlers of this._hooks.values()) { | ||
| const found = eventHandlers.find((h) => h.id === id); | ||
| if (found) { | ||
| return found; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes one or more hooks by id, searching across all events | ||
| * @param {string | string[]} id - the hook id or array of hook ids to remove | ||
| * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found | ||
| */ | ||
| removeHookById(id) { | ||
| if (Array.isArray(id)) { | ||
| const removed = []; | ||
| for (const singleId of id) { | ||
| const result = this.removeHookById(singleId); | ||
| if (result && !Array.isArray(result)) { | ||
| removed.push(result); | ||
| } | ||
| } | ||
| return removed; | ||
| } | ||
| for (const [event, eventHandlers] of this._hooks.entries()) { | ||
| const index = eventHandlers.findIndex((h) => h.id === id); | ||
| if (index !== -1) { | ||
| const [removed] = eventHandlers.splice(index, 1); | ||
| if (eventHandlers.length === 0) { | ||
| this._hooks.delete(event); | ||
| } | ||
| return removed; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes all hooks | ||
@@ -706,8 +790,54 @@ * @returns {void} | ||
| } | ||
| /** | ||
| * Removes all hooks for a specific event and returns the removed hooks. | ||
| * @param {string} event - The event name to remove hooks for. | ||
| * @returns {IHook[]} the hooks that were removed | ||
| */ | ||
| removeEventHooks(event) { | ||
| this.validateHookName(event); | ||
| const eventHandlers = this._hooks.get(event); | ||
| if (eventHandlers) { | ||
| const removed = [...eventHandlers]; | ||
| this._hooks.delete(event); | ||
| return removed; | ||
| } | ||
| return []; | ||
| } | ||
| /** | ||
| * 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 }); | ||
| return this._allowDeprecated; | ||
| } | ||
| return true; | ||
| } | ||
| }; | ||
| export { | ||
| Eventified, | ||
| Hookified | ||
| Hook, | ||
| Hookified, | ||
| WaterfallHook | ||
| }; | ||
| /* v8 ignore next -- @preserve */ | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/eventified.ts","../../src/index.ts"],"sourcesContent":["// biome-ignore-all lint/suspicious/noExplicitAny: this is for event emitter compatibility\nimport type {\n\tEventEmitterOptions,\n\tEventListener,\n\tIEventEmitter,\n\tLogger,\n} from \"./types.js\";\n\nexport type { EventEmitterOptions, EventListener, IEventEmitter };\n\nexport class Eventified implements IEventEmitter {\n\tprivate readonly _eventListeners: Map<string | symbol, EventListener[]>;\n\tprivate _maxListeners: number;\n\tprivate _logger?: Logger;\n\tprivate _throwOnEmitError = false;\n\tprivate _throwOnEmptyListeners = false;\n\tprivate _errorEvent = \"error\";\n\n\tconstructor(options?: EventEmitterOptions) {\n\t\tthis._eventListeners = new Map<string | symbol, EventListener[]>();\n\t\tthis._maxListeners = 100; // Default maximum number of listeners\n\n\t\tthis._logger = options?.logger;\n\n\t\tif (options?.throwOnEmitError !== undefined) {\n\t\t\tthis._throwOnEmitError = options.throwOnEmitError;\n\t\t}\n\n\t\tif (options?.throwOnEmptyListeners !== undefined) {\n\t\t\tthis._throwOnEmptyListeners = options.throwOnEmptyListeners;\n\t\t}\n\t}\n\n\t/**\n\t * Gets the logger\n\t * @returns {Logger}\n\t */\n\tpublic get logger(): Logger | undefined {\n\t\treturn this._logger;\n\t}\n\n\t/**\n\t * Sets the logger\n\t * @param {Logger} logger\n\t */\n\tpublic set logger(logger: Logger | undefined) {\n\t\tthis._logger = logger;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnEmitError(): boolean {\n\t\treturn this._throwOnEmitError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnEmitError(value: boolean) {\n\t\tthis._throwOnEmitError = value;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnEmptyListeners(): boolean {\n\t\treturn this._throwOnEmptyListeners;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnEmptyListeners(value: boolean) {\n\t\tthis._throwOnEmptyListeners = value;\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event that will run only once\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic once(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst onceListener: EventListener = (...arguments_: any[]) => {\n\t\t\tthis.off(eventName as string, onceListener);\n\t\t\tlistener(...arguments_);\n\t\t};\n\n\t\tthis.on(eventName as string, onceListener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners\n\t * @param {string} eventName The event name. Not required\n\t * @returns {number} The number of listeners\n\t */\n\tpublic listenerCount(eventName?: string | symbol): number {\n\t\tif (eventName === undefined) {\n\t\t\treturn this.getAllListeners().length;\n\t\t}\n\n\t\tconst listeners = this._eventListeners.get(eventName);\n\t\treturn listeners ? listeners.length : 0;\n\t}\n\n\t/**\n\t * Gets an array of event names\n\t * @returns {Array<string | symbol>} An array of event names\n\t */\n\tpublic eventNames(): Array<string | symbol> {\n\t\treturn [...this._eventListeners.keys()];\n\t}\n\n\t/**\n\t * Gets an array of listeners for a specific event. If no event is provided, it returns all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic rawListeners(event?: string | symbol): EventListener[] {\n\t\tif (event === undefined) {\n\t\t\treturn this.getAllListeners();\n\t\t}\n\n\t\treturn this._eventListeners.get(event) ?? [];\n\t}\n\n\t/**\n\t * Prepends a listener to the beginning of the listeners array for the specified event\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic prependListener(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst listeners = this._eventListeners.get(eventName) ?? [];\n\t\tlisteners.unshift(listener);\n\t\tthis._eventListeners.set(eventName, listeners);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Prepends a one-time listener to the beginning of the listeners array for the specified event\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic prependOnceListener(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst onceListener: EventListener = (...arguments_: any[]) => {\n\t\t\tthis.off(eventName as string, onceListener);\n\t\t\tlistener(...arguments_);\n\t\t};\n\n\t\tthis.prependListener(eventName as string, onceListener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the maximum number of listeners that can be added for a single event\n\t * @returns {number} The maximum number of listeners\n\t */\n\tpublic maxListeners(): number {\n\t\treturn this._maxListeners;\n\t}\n\n\t/**\n\t * Adds a listener for a specific event. It is an alias for the on() method\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic addListener(\n\t\tevent: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tthis.on(event, listener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a listener for a specific event\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic on(event: string | symbol, listener: EventListener): IEventEmitter {\n\t\tif (!this._eventListeners.has(event)) {\n\t\t\tthis._eventListeners.set(event, []);\n\t\t}\n\n\t\tconst listeners = this._eventListeners.get(event);\n\n\t\tif (listeners) {\n\t\t\tif (listeners.length >= this._maxListeners) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event as string} listeners added. Use setMaxListeners() to increase limit.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlisteners.push(listener);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a listener for a specific event. It is an alias for the off() method\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic removeListener(event: string, listener: EventListener): IEventEmitter {\n\t\tthis.off(event, listener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a listener for a specific event\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic off(event: string | symbol, listener: EventListener): IEventEmitter {\n\t\tconst listeners = this._eventListeners.get(event) ?? [];\n\t\tconst index = listeners.indexOf(listener);\n\t\tif (index !== -1) {\n\t\t\tlisteners.splice(index, 1);\n\t\t}\n\n\t\tif (listeners.length === 0) {\n\t\t\tthis._eventListeners.delete(event);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Calls all listeners for a specific event\n\t * @param {string | symbol} event\n\t * @param arguments_ The arguments to pass to the listeners\n\t * @returns {boolean} Returns true if the event had listeners, false otherwise\n\t */\n\tpublic emit(event: string | symbol, ...arguments_: any[]): boolean {\n\t\tlet result = false;\n\t\tconst listeners = this._eventListeners.get(event);\n\n\t\tif (listeners && listeners.length > 0) {\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener(...arguments_);\n\t\t\t\tresult = true;\n\t\t\t}\n\t\t}\n\n\t\tif (event === this._errorEvent) {\n\t\t\tconst error =\n\t\t\t\targuments_[0] instanceof Error\n\t\t\t\t\t? arguments_[0]\n\t\t\t\t\t: new Error(`${arguments_[0]}`);\n\n\t\t\tif (this._throwOnEmitError && !result) {\n\t\t\t\tthrow error;\n\t\t\t} else {\n\t\t\t\tif (\n\t\t\t\t\tthis.listeners(this._errorEvent).length === 0 &&\n\t\t\t\t\tthis._throwOnEmptyListeners === true\n\t\t\t\t) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// send it to the logger\n\t\tthis.sendLog(event, arguments_);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Gets all listeners for a specific event. If no event is provided, it returns all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic listeners(event: string | symbol): EventListener[] {\n\t\treturn this._eventListeners.get(event) ?? [];\n\t}\n\n\t/**\n\t * Removes all listeners for a specific event. If no event is provided, it removes all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic removeAllListeners(event?: string | symbol): IEventEmitter {\n\t\tif (event !== undefined) {\n\t\t\tthis._eventListeners.delete(event);\n\t\t} else {\n\t\t\tthis._eventListeners.clear();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the maximum number of listeners that can be added for a single event\n\t * @param {number} n The maximum number of listeners\n\t * @returns {void}\n\t */\n\tpublic setMaxListeners(n: number): void {\n\t\tthis._maxListeners = n;\n\t\tfor (const listeners of this._eventListeners.values()) {\n\t\t\tif (listeners.length > n) {\n\t\t\t\tlisteners.splice(n);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets all listeners\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic getAllListeners(): EventListener[] {\n\t\tlet result: EventListener[] = [];\n\t\tfor (const listeners of this._eventListeners.values()) {\n\t\t\tresult = [...result, ...listeners];\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Sends a log message using the configured logger based on the event name\n\t * @param {string | symbol} eventName - The event name that determines the log level\n\t * @param {unknown} data - The data to log\n\t */\n\tprivate sendLog(eventName: string | symbol, data: any): void {\n\t\tif (!this._logger) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet message: string;\n\t\t/* v8 ignore next -- @preserve */\n\t\tif (typeof data === \"string\") {\n\t\t\tmessage = data;\n\t\t} else if (\n\t\t\tArray.isArray(data) &&\n\t\t\tdata.length > 0 &&\n\t\t\tdata[0] instanceof Error\n\t\t) {\n\t\t\tmessage = data[0].message;\n\t\t\t/* v8 ignore next -- @preserve */\n\t\t} else if (data instanceof Error) {\n\t\t\tmessage = data.message;\n\t\t} else if (\n\t\t\tArray.isArray(data) &&\n\t\t\tdata.length > 0 &&\n\t\t\ttypeof data[0]?.message === \"string\"\n\t\t) {\n\t\t\tmessage = data[0].message;\n\t\t} else {\n\t\t\tmessage = JSON.stringify(data);\n\t\t}\n\n\t\tswitch (eventName) {\n\t\t\tcase \"error\": {\n\t\t\t\tthis._logger.error?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"warn\": {\n\t\t\t\tthis._logger.warn?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"trace\": {\n\t\t\t\tthis._logger.trace?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"debug\": {\n\t\t\t\tthis._logger.debug?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"fatal\": {\n\t\t\t\tthis._logger.fatal?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tthis._logger.info?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n","import { Eventified } from \"./eventified.js\";\nimport type { Hook, HookEntry, HookifiedOptions } from \"./types.js\";\n\nexport type { Hook, HookEntry, HookifiedOptions };\n\nexport class Hookified extends Eventified {\n\tprivate readonly _hooks: Map<string, Hook[]>;\n\tprivate _throwOnHookError = false;\n\tprivate _enforceBeforeAfter = false;\n\tprivate _deprecatedHooks: Map<string, string>;\n\tprivate _allowDeprecated = true;\n\n\tconstructor(options?: HookifiedOptions) {\n\t\tsuper({\n\t\t\tlogger: options?.logger,\n\t\t\tthrowOnEmitError: options?.throwOnEmitError,\n\t\t\tthrowOnEmptyListeners: options?.throwOnEmptyListeners,\n\t\t});\n\t\tthis._hooks = new Map();\n\t\tthis._deprecatedHooks = options?.deprecatedHooks\n\t\t\t? new Map(options.deprecatedHooks)\n\t\t\t: new Map();\n\n\t\tif (options?.throwOnHookError !== undefined) {\n\t\t\tthis._throwOnHookError = options.throwOnHookError;\n\t\t} else if (options?.throwHookErrors !== undefined) {\n\t\t\tthis._throwOnHookError = options.throwHookErrors;\n\t\t}\n\n\t\tif (options?.enforceBeforeAfter !== undefined) {\n\t\t\tthis._enforceBeforeAfter = options.enforceBeforeAfter;\n\t\t}\n\n\t\tif (options?.allowDeprecated !== undefined) {\n\t\t\tthis._allowDeprecated = options.allowDeprecated;\n\t\t}\n\t}\n\n\t/**\n\t * Gets all hooks\n\t * @returns {Map<string, Hook[]>}\n\t */\n\tpublic get hooks() {\n\t\treturn this._hooks;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t * @deprecated - this will be deprecated in version 2. Please use throwOnHookError.\n\t */\n\tpublic get throwHookErrors() {\n\t\treturn this._throwOnHookError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t * @deprecated - this will be deprecated in version 2. Please use throwOnHookError.\n\t */\n\tpublic set throwHookErrors(value) {\n\t\tthis._throwOnHookError = value;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnHookError() {\n\t\treturn this._throwOnHookError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnHookError(value) {\n\t\tthis._throwOnHookError = value;\n\t}\n\n\t/**\n\t * Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.\n\t * @returns {boolean}\n\t * @default false\n\t */\n\tpublic get enforceBeforeAfter() {\n\t\treturn this._enforceBeforeAfter;\n\t}\n\n\t/**\n\t * Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.\n\t * @param {boolean} value\n\t */\n\tpublic set enforceBeforeAfter(value) {\n\t\tthis._enforceBeforeAfter = value;\n\t}\n\n\t/**\n\t * Gets the map of deprecated hook names to deprecation messages.\n\t * @returns {Map<string, string>}\n\t */\n\tpublic get deprecatedHooks() {\n\t\treturn this._deprecatedHooks;\n\t}\n\n\t/**\n\t * Sets the map of deprecated hook names to deprecation messages.\n\t * @param {Map<string, string>} value\n\t */\n\tpublic set deprecatedHooks(value) {\n\t\tthis._deprecatedHooks = value;\n\t}\n\n\t/**\n\t * Gets whether deprecated hooks are allowed to be registered and executed. Default is true.\n\t * @returns {boolean}\n\t */\n\tpublic get allowDeprecated() {\n\t\treturn this._allowDeprecated;\n\t}\n\n\t/**\n\t * Sets whether deprecated hooks are allowed to be registered and executed. Default is true.\n\t * @param {boolean} value\n\t */\n\tpublic set allowDeprecated(value) {\n\t\tthis._allowDeprecated = value;\n\t}\n\n\t/**\n\t * Validates hook event name if enforceBeforeAfter is enabled\n\t * @param {string} event - The event name to validate\n\t * @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'\n\t */\n\tprivate validateHookName(event: string): void {\n\t\tif (this._enforceBeforeAfter) {\n\t\t\tconst eventValue = event.trim().toLocaleLowerCase();\n\t\t\tif (!eventValue.startsWith(\"before\") && !eventValue.startsWith(\"after\")) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Hook event \"${event}\" must start with \"before\" or \"after\" when enforceBeforeAfter is enabled`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks if a hook is deprecated and emits a warning if it is\n\t * @param {string} event - The event name to check\n\t * @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked\n\t */\n\tprivate checkDeprecatedHook(event: string): boolean {\n\t\tif (this._deprecatedHooks.has(event)) {\n\t\t\tconst message = this._deprecatedHooks.get(event);\n\t\t\tconst warningMessage = `Hook \"${event}\" is deprecated${message ? `: ${message}` : \"\"}`;\n\n\t\t\t// Emit deprecation warning event\n\t\t\tthis.emit(\"warn\", { hook: event, message: warningMessage });\n\n\t\t\t// Return false if deprecated hooks are not allowed\n\t\t\treturn this._allowDeprecated;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event\n\t * @param {string} event\n\t * @param {Hook} handler - this can be async or sync\n\t * @returns {void}\n\t */\n\tpublic onHook(event: string, handler: Hook) {\n\t\tthis.onHookEntry({ event, handler });\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event\n\t * @param {HookEntry} hookEntry\n\t * @returns {void}\n\t */\n\tpublic onHookEntry(hookEntry: HookEntry) {\n\t\tthis.validateHookName(hookEntry.event);\n\t\tif (!this.checkDeprecatedHook(hookEntry.event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(hookEntry.event);\n\t\tif (eventHandlers) {\n\t\t\teventHandlers.push(hookEntry.handler);\n\t\t} else {\n\t\t\tthis._hooks.set(hookEntry.event, [hookEntry.handler]);\n\t\t}\n\t}\n\n\t/**\n\t * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.\n\t * @param {string} event\n\t * @param {Hook} handler - this can be async or sync\n\t * @returns {void}\n\t */\n\tpublic addHook(event: string, handler: Hook) {\n\t\tthis.onHookEntry({ event, handler });\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event\n\t * @param {Array<HookEntry>} hooks\n\t * @returns {void}\n\t */\n\tpublic onHooks(hooks: HookEntry[]) {\n\t\tfor (const hook of hooks) {\n\t\t\tthis.onHook(hook.event, hook.handler);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event that runs before all other handlers\n\t * @param {string} event\n\t * @param {Hook} handler - this can be async or sync\n\t * @returns {void}\n\t */\n\tpublic prependHook(event: string, handler: Hook) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\teventHandlers.unshift(handler);\n\t\t} else {\n\t\t\tthis._hooks.set(event, [handler]);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a handler that only executes once for a specific event before all other handlers\n\t * @param event\n\t * @param handler\n\t */\n\tpublic prependOnceHook(event: string, handler: Hook) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tconst hook = async (...arguments_: any[]) => {\n\t\t\tthis.removeHook(event, hook);\n\t\t\treturn handler(...arguments_);\n\t\t};\n\n\t\tthis.prependHook(event, hook);\n\t}\n\n\t/**\n\t * Adds a handler that only executes once for a specific event\n\t * @param event\n\t * @param handler\n\t */\n\tpublic onceHook(event: string, handler: Hook) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tconst hook = async (...arguments_: any[]) => {\n\t\t\tthis.removeHook(event, hook);\n\t\t\treturn handler(...arguments_);\n\t\t};\n\n\t\tthis.onHook(event, hook);\n\t}\n\n\t/**\n\t * Removes a handler function for a specific event\n\t * @param {string} event\n\t * @param {Hook} handler\n\t * @returns {void}\n\t */\n\tpublic removeHook(event: string, handler: Hook) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip removal if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tconst index = eventHandlers.indexOf(handler);\n\t\t\tif (index !== -1) {\n\t\t\t\teventHandlers.splice(index, 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Removes all handlers for a specific event\n\t * @param {Array<HookEntry>} hooks\n\t * @returns {void}\n\t */\n\tpublic removeHooks(hooks: HookEntry[]) {\n\t\tfor (const hook of hooks) {\n\t\t\tthis.removeHook(hook.event, hook.handler);\n\t\t}\n\t}\n\n\t/**\n\t * Calls all handlers for a specific event\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {Promise<void>}\n\t */\n\tpublic async hook<T>(event: string, ...arguments_: T[]) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip execution if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tfor (const handler of eventHandlers) {\n\t\t\t\ttry {\n\t\t\t\t\tawait handler(...arguments_);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = `${event}: ${(error as Error).message}`;\n\t\t\t\t\tthis.emit(\"error\", new Error(message));\n\n\t\t\t\t\tif (this._throwOnHookError) {\n\t\t\t\t\t\tthrow new Error(message);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Calls all synchronous handlers for a specific event.\n\t * Async handlers (declared with `async` keyword) are silently skipped.\n\t *\n\t * Note: The `hook` method is preferred as it executes both sync and async functions.\n\t * Use `hookSync` only when you specifically need synchronous execution.\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {void}\n\t */\n\tpublic hookSync<T>(event: string, ...arguments_: T[]): void {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tfor (const handler of eventHandlers) {\n\t\t\t\t// Skip async functions silently\n\t\t\t\tif (handler.constructor.name === \"AsyncFunction\") {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\thandler(...arguments_);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = `${event}: ${(error as Error).message}`;\n\t\t\t\t\tthis.emit(\"error\", new Error(message));\n\n\t\t\t\t\tif (this._throwOnHookError) {\n\t\t\t\t\t\tthrow new Error(message);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.\n\t * @param {string} event - The event name\n\t * @param {T[]} arguments_ - The arguments to pass to the hook\n\t */\n\tpublic async beforeHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(`before:${event}`, ...arguments_);\n\t}\n\n\t/**\n\t * Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.\n\t * @param {string} event - The event name\n\t * @param {T[]} arguments_ - The arguments to pass to the hook\n\t */\n\tpublic async afterHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(`after:${event}`, ...arguments_);\n\t}\n\n\t/**\n\t * Calls all handlers for a specific event. This is an alias for `hook` and is provided for\n\t * compatibility with other libraries that use the `callHook` method.\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {Promise<void>}\n\t */\n\tpublic async callHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(event, ...arguments_);\n\t}\n\n\t/**\n\t * Gets all hooks for a specific event\n\t * @param {string} event\n\t * @returns {Hook[]}\n\t */\n\tpublic getHooks(event: string) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn undefined; // Return undefined if deprecated hooks are not allowed\n\t\t}\n\t\treturn this._hooks.get(event);\n\t}\n\n\t/**\n\t * Removes all hooks\n\t * @returns {void}\n\t */\n\tpublic clearHooks() {\n\t\tthis._hooks.clear();\n\t}\n}\n\nexport { Eventified } from \"./eventified.js\";\nexport type {\n\tEventEmitterOptions,\n\tEventListener,\n\tIEventEmitter,\n\tLogger,\n} from \"./types.js\";\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../../src/eventified.ts","../../src/hooks/hook.ts","../../src/hooks/waterfall-hook.ts","../../src/index.ts"],"sourcesContent":["// biome-ignore-all lint/suspicious/noExplicitAny: this is for event emitter compatibility\nimport type {\n\tEventEmitterOptions,\n\tEventListener,\n\tIEventEmitter,\n\tLogger,\n} from \"./types.js\";\n\nexport type { EventEmitterOptions, EventListener, IEventEmitter };\n\nexport class Eventified implements IEventEmitter {\n\tprivate readonly _eventListeners: Map<string | symbol, EventListener[]>;\n\tprivate _maxListeners: number;\n\tprivate _eventLogger?: Logger;\n\tprivate _throwOnEmitError = false;\n\tprivate _throwOnEmptyListeners = true;\n\tprivate _errorEvent = \"error\";\n\n\tconstructor(options?: EventEmitterOptions) {\n\t\tthis._eventListeners = new Map<string | symbol, EventListener[]>();\n\t\tthis._maxListeners = 0; // Default is 0 (unlimited)\n\n\t\tthis._eventLogger = options?.eventLogger;\n\n\t\tif (options?.throwOnEmitError !== undefined) {\n\t\t\tthis._throwOnEmitError = options.throwOnEmitError;\n\t\t}\n\n\t\tif (options?.throwOnEmptyListeners !== undefined) {\n\t\t\tthis._throwOnEmptyListeners = options.throwOnEmptyListeners;\n\t\t}\n\t}\n\n\t/**\n\t * Gets the event logger\n\t * @returns {Logger}\n\t */\n\tpublic get eventLogger(): Logger | undefined {\n\t\treturn this._eventLogger;\n\t}\n\n\t/**\n\t * Sets the event logger\n\t * @param {Logger} eventLogger\n\t */\n\tpublic set eventLogger(eventLogger: Logger | undefined) {\n\t\tthis._eventLogger = eventLogger;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnEmitError(): boolean {\n\t\treturn this._throwOnEmitError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnEmitError(value: boolean) {\n\t\tthis._throwOnEmitError = value;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnEmptyListeners(): boolean {\n\t\treturn this._throwOnEmptyListeners;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnEmptyListeners(value: boolean) {\n\t\tthis._throwOnEmptyListeners = value;\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event that will run only once\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic once(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst onceListener: EventListener = (...arguments_: any[]) => {\n\t\t\tthis.off(eventName as string, onceListener);\n\t\t\tlistener(...arguments_);\n\t\t};\n\n\t\tthis.on(eventName as string, onceListener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners\n\t * @param {string} eventName The event name. Not required\n\t * @returns {number} The number of listeners\n\t */\n\tpublic listenerCount(eventName?: string | symbol): number {\n\t\tif (eventName === undefined) {\n\t\t\treturn this.getAllListeners().length;\n\t\t}\n\n\t\tconst listeners = this._eventListeners.get(eventName);\n\t\treturn listeners ? listeners.length : 0;\n\t}\n\n\t/**\n\t * Gets an array of event names\n\t * @returns {Array<string | symbol>} An array of event names\n\t */\n\tpublic eventNames(): Array<string | symbol> {\n\t\treturn [...this._eventListeners.keys()];\n\t}\n\n\t/**\n\t * Gets an array of listeners for a specific event. If no event is provided, it returns all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic rawListeners(event?: string | symbol): EventListener[] {\n\t\tif (event === undefined) {\n\t\t\treturn this.getAllListeners();\n\t\t}\n\n\t\treturn this._eventListeners.get(event) ?? [];\n\t}\n\n\t/**\n\t * Prepends a listener to the beginning of the listeners array for the specified event\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic prependListener(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst listeners = this._eventListeners.get(eventName) ?? [];\n\t\tlisteners.unshift(listener);\n\t\tthis._eventListeners.set(eventName, listeners);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Prepends a one-time listener to the beginning of the listeners array for the specified event\n\t * @param {string | symbol} eventName\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic prependOnceListener(\n\t\teventName: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tconst onceListener: EventListener = (...arguments_: any[]) => {\n\t\t\tthis.off(eventName as string, onceListener);\n\t\t\tlistener(...arguments_);\n\t\t};\n\n\t\tthis.prependListener(eventName as string, onceListener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the maximum number of listeners that can be added for a single event\n\t * @returns {number} The maximum number of listeners\n\t */\n\tpublic maxListeners(): number {\n\t\treturn this._maxListeners;\n\t}\n\n\t/**\n\t * Adds a listener for a specific event. It is an alias for the on() method\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic addListener(\n\t\tevent: string | symbol,\n\t\tlistener: EventListener,\n\t): IEventEmitter {\n\t\tthis.on(event, listener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a listener for a specific event\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic on(event: string | symbol, listener: EventListener): IEventEmitter {\n\t\tif (!this._eventListeners.has(event)) {\n\t\t\tthis._eventListeners.set(event, []);\n\t\t}\n\n\t\tconst listeners = this._eventListeners.get(event);\n\n\t\tif (listeners) {\n\t\t\tif (this._maxListeners > 0 && listeners.length >= this._maxListeners) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event as string} listeners added. Use setMaxListeners() to increase limit.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlisteners.push(listener);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a listener for a specific event. It is an alias for the off() method\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic removeListener(event: string, listener: EventListener): IEventEmitter {\n\t\tthis.off(event, listener);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a listener for a specific event\n\t * @param {string | symbol} event\n\t * @param {EventListener} listener\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic off(event: string | symbol, listener: EventListener): IEventEmitter {\n\t\tconst listeners = this._eventListeners.get(event) ?? [];\n\t\tconst index = listeners.indexOf(listener);\n\t\tif (index !== -1) {\n\t\t\tlisteners.splice(index, 1);\n\t\t}\n\n\t\tif (listeners.length === 0) {\n\t\t\tthis._eventListeners.delete(event);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Calls all listeners for a specific event\n\t * @param {string | symbol} event\n\t * @param arguments_ The arguments to pass to the listeners\n\t * @returns {boolean} Returns true if the event had listeners, false otherwise\n\t */\n\tpublic emit(event: string | symbol, ...arguments_: any[]): boolean {\n\t\tlet result = false;\n\t\tconst listeners = this._eventListeners.get(event);\n\n\t\tif (listeners && listeners.length > 0) {\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener(...arguments_);\n\t\t\t\tresult = true;\n\t\t\t}\n\t\t}\n\n\t\t// send it to the logger\n\t\tthis.sendToEventLogger(event, arguments_);\n\n\t\tif (event === this._errorEvent) {\n\t\t\tconst error =\n\t\t\t\targuments_[0] instanceof Error\n\t\t\t\t\t? arguments_[0]\n\t\t\t\t\t: new Error(`${arguments_[0]}`);\n\n\t\t\tif (this._throwOnEmitError && !result) {\n\t\t\t\tthrow error;\n\t\t\t} else {\n\t\t\t\tif (\n\t\t\t\t\tthis.listeners(this._errorEvent).length === 0 &&\n\t\t\t\t\tthis._throwOnEmptyListeners === true\n\t\t\t\t) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Gets all listeners for a specific event. If no event is provided, it returns all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic listeners(event: string | symbol): EventListener[] {\n\t\treturn this._eventListeners.get(event) ?? [];\n\t}\n\n\t/**\n\t * Removes all listeners for a specific event. If no event is provided, it removes all listeners\n\t * @param {string} [event] (Optional) The event name\n\t * @returns {IEventEmitter} returns the instance of the class for chaining\n\t */\n\tpublic removeAllListeners(event?: string | symbol): IEventEmitter {\n\t\tif (event !== undefined) {\n\t\t\tthis._eventListeners.delete(event);\n\t\t} else {\n\t\t\tthis._eventListeners.clear();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the maximum number of listeners that can be added for a single event\n\t * @param {number} n The maximum number of listeners\n\t * @returns {void}\n\t */\n\tpublic setMaxListeners(n: number): void {\n\t\tthis._maxListeners = n < 0 ? 0 : n;\n\t}\n\n\t/**\n\t * Gets all listeners\n\t * @returns {EventListener[]} An array of listeners\n\t */\n\tpublic getAllListeners(): EventListener[] {\n\t\tlet result: EventListener[] = [];\n\t\tfor (const listeners of this._eventListeners.values()) {\n\t\t\tresult = [...result, ...listeners];\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Sends a log message using the configured logger based on the event name\n\t * @param {string | symbol} eventName - The event name that determines the log level\n\t * @param {unknown} data - The data to log\n\t */\n\tprivate sendToEventLogger(eventName: string | symbol, data: any): void {\n\t\tif (!this._eventLogger) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet message: string;\n\t\t/* v8 ignore next -- @preserve */\n\t\tif (typeof data === \"string\") {\n\t\t\tmessage = data;\n\t\t} else if (\n\t\t\tArray.isArray(data) &&\n\t\t\tdata.length > 0 &&\n\t\t\tdata[0] instanceof Error\n\t\t) {\n\t\t\tmessage = data[0].message;\n\t\t\t/* v8 ignore next -- @preserve */\n\t\t} else if (data instanceof Error) {\n\t\t\tmessage = data.message;\n\t\t} else if (\n\t\t\tArray.isArray(data) &&\n\t\t\tdata.length > 0 &&\n\t\t\ttypeof data[0]?.message === \"string\"\n\t\t) {\n\t\t\tmessage = data[0].message;\n\t\t} else {\n\t\t\tmessage = JSON.stringify(data);\n\t\t}\n\n\t\tswitch (eventName) {\n\t\t\tcase \"error\": {\n\t\t\t\tthis._eventLogger.error?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"warn\": {\n\t\t\t\tthis._eventLogger.warn?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"trace\": {\n\t\t\t\tthis._eventLogger.trace?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"debug\": {\n\t\t\t\tthis._eventLogger.debug?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"fatal\": {\n\t\t\t\tthis._eventLogger.fatal?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tthis._eventLogger.info?.(message, { event: eventName, data });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { HookFn, IHook } from \"../types.js\";\n\n/**\n * Concrete implementation of the IHook interface.\n * Provides a convenient class-based way to create hook entries.\n */\nexport class Hook implements IHook {\n\tpublic id?: string;\n\tpublic event: string;\n\tpublic handler: HookFn;\n\n\t/**\n\t * Creates a new Hook instance\n\t * @param {string} event - The event name for the hook\n\t * @param {HookFn} handler - The handler function for the hook\n\t * @param {string} [id] - Optional unique identifier for the hook\n\t */\n\tconstructor(event: string, handler: HookFn, id?: string) {\n\t\tthis.id = id;\n\t\tthis.event = event;\n\t\tthis.handler = handler;\n\t}\n}\n","import type {\n\tHookFn,\n\tIWaterfallHook,\n\tWaterfallHookFn,\n\tWaterfallHookResult,\n} from \"../types.js\";\n\n/**\n * A WaterfallHook chains multiple hook functions sequentially,\n * where each hook receives a context with the previous result,\n * initial arguments, and accumulated results. After all hooks\n * have executed, the final handler receives the transformed result.\n * Implements IHook for compatibility with Hookified.onHook().\n */\nexport class WaterfallHook implements IWaterfallHook {\n\tpublic id?: string;\n\tpublic event: string;\n\tpublic handler: HookFn;\n\tpublic hooks: WaterfallHookFn[];\n\n\tprivate readonly _finalHandler: WaterfallHookFn;\n\n\t/**\n\t * Creates a new WaterfallHook instance\n\t * @param {string} event - The event name for the hook\n\t * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result\n\t * @param {string} [id] - Optional unique identifier for the hook\n\t */\n\tconstructor(event: string, finalHandler: WaterfallHookFn, id?: string) {\n\t\tthis.id = id;\n\t\tthis.event = event;\n\t\tthis.hooks = [];\n\t\tthis._finalHandler = finalHandler;\n\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tthis.handler = async (...arguments_: any[]) => {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: waterfall result type varies through the chain\n\t\t\tconst initialArgs: any =\n\t\t\t\targuments_.length === 1 ? arguments_[0] : arguments_;\n\t\t\tconst results: WaterfallHookResult[] = [];\n\n\t\t\tfor (const hook of this.hooks) {\n\t\t\t\tconst result = await hook({ initialArgs, results: [...results] });\n\t\t\t\tresults.push({ hook, result });\n\t\t\t}\n\n\t\t\tawait this._finalHandler({ initialArgs, results: [...results] });\n\t\t};\n\t}\n\n\t/**\n\t * Adds a hook function to the end of the waterfall chain\n\t * @param {WaterfallHookFn} hook - The hook function to add\n\t */\n\tpublic addHook(hook: WaterfallHookFn): void {\n\t\tthis.hooks.push(hook);\n\t}\n\n\t/**\n\t * Removes a specific hook function from the waterfall chain\n\t * @param {WaterfallHookFn} hook - The hook function to remove\n\t * @returns {boolean} true if the hook was found and removed\n\t */\n\tpublic removeHook(hook: WaterfallHookFn): boolean {\n\t\tconst index = this.hooks.indexOf(hook);\n\t\tif (index !== -1) {\n\t\t\tthis.hooks.splice(index, 1);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n}\n","import { Eventified } from \"./eventified.js\";\nimport type {\n\tHookFn,\n\tHookifiedOptions,\n\tIHook,\n\tIWaterfallHook,\n\tOnHookOptions,\n\tPrependHookOptions,\n\tWaterfallHookContext,\n\tWaterfallHookFn,\n\tWaterfallHookResult,\n} from \"./types.js\";\n\nexport type {\n\tHookFn,\n\tHookifiedOptions,\n\tIHook,\n\tIWaterfallHook,\n\tOnHookOptions,\n\tPrependHookOptions,\n\tWaterfallHookContext,\n\tWaterfallHookFn,\n\tWaterfallHookResult,\n};\n\nexport class Hookified extends Eventified {\n\tprivate readonly _hooks: Map<string, IHook[]>;\n\tprivate _throwOnHookError = false;\n\tprivate _enforceBeforeAfter = false;\n\tprivate _deprecatedHooks: Map<string, string>;\n\tprivate _allowDeprecated = true;\n\tprivate _useHookClone = true;\n\n\tconstructor(options?: HookifiedOptions) {\n\t\tsuper({\n\t\t\teventLogger: options?.eventLogger,\n\t\t\tthrowOnEmitError: options?.throwOnEmitError,\n\t\t\tthrowOnEmptyListeners: options?.throwOnEmptyListeners,\n\t\t});\n\t\tthis._hooks = new Map();\n\t\tthis._deprecatedHooks = options?.deprecatedHooks\n\t\t\t? new Map(options.deprecatedHooks)\n\t\t\t: new Map();\n\n\t\tif (options?.throwOnHookError !== undefined) {\n\t\t\tthis._throwOnHookError = options.throwOnHookError;\n\t\t}\n\n\t\tif (options?.enforceBeforeAfter !== undefined) {\n\t\t\tthis._enforceBeforeAfter = options.enforceBeforeAfter;\n\t\t}\n\n\t\tif (options?.allowDeprecated !== undefined) {\n\t\t\tthis._allowDeprecated = options.allowDeprecated;\n\t\t}\n\n\t\tif (options?.useHookClone !== undefined) {\n\t\t\tthis._useHookClone = options.useHookClone;\n\t\t}\n\t}\n\n\t/**\n\t * Gets all hooks\n\t * @returns {Map<string, IHook[]>}\n\t */\n\tpublic get hooks() {\n\t\treturn this._hooks;\n\t}\n\n\t/**\n\t * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @returns {boolean}\n\t */\n\tpublic get throwOnHookError() {\n\t\treturn this._throwOnHookError;\n\t}\n\n\t/**\n\t * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.\n\t * @param {boolean} value\n\t */\n\tpublic set throwOnHookError(value) {\n\t\tthis._throwOnHookError = value;\n\t}\n\n\t/**\n\t * Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.\n\t * @returns {boolean}\n\t * @default false\n\t */\n\tpublic get enforceBeforeAfter() {\n\t\treturn this._enforceBeforeAfter;\n\t}\n\n\t/**\n\t * Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.\n\t * @param {boolean} value\n\t */\n\tpublic set enforceBeforeAfter(value) {\n\t\tthis._enforceBeforeAfter = value;\n\t}\n\n\t/**\n\t * Gets the map of deprecated hook names to deprecation messages.\n\t * @returns {Map<string, string>}\n\t */\n\tpublic get deprecatedHooks() {\n\t\treturn this._deprecatedHooks;\n\t}\n\n\t/**\n\t * Sets the map of deprecated hook names to deprecation messages.\n\t * @param {Map<string, string>} value\n\t */\n\tpublic set deprecatedHooks(value) {\n\t\tthis._deprecatedHooks = value;\n\t}\n\n\t/**\n\t * Gets whether deprecated hooks are allowed to be registered and executed. Default is true.\n\t * @returns {boolean}\n\t */\n\tpublic get allowDeprecated() {\n\t\treturn this._allowDeprecated;\n\t}\n\n\t/**\n\t * Sets whether deprecated hooks are allowed to be registered and executed. Default is true.\n\t * @param {boolean} value\n\t */\n\tpublic set allowDeprecated(value) {\n\t\tthis._allowDeprecated = value;\n\t}\n\n\t/**\n\t * Gets whether hook objects are cloned before storing. Default is true.\n\t * @returns {boolean}\n\t */\n\tpublic get useHookClone() {\n\t\treturn this._useHookClone;\n\t}\n\n\t/**\n\t * Sets whether hook objects are cloned before storing. Default is true.\n\t * When false, the original IHook reference is stored directly.\n\t * @param {boolean} value\n\t */\n\tpublic set useHookClone(value) {\n\t\tthis._useHookClone = value;\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event.\n\t * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead.\n\t * To register multiple hooks at once, use {@link onHooks}.\n\t * @param {IHook} hook - the hook containing event name and handler\n\t * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)\n\t * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation\n\t */\n\tpublic onHook(hook: IHook, options?: OnHookOptions): IHook | undefined {\n\t\tthis.validateHookName(hook.event);\n\t\tif (!this.checkDeprecatedHook(hook.event)) {\n\t\t\treturn undefined; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\n\t\tconst shouldClone = options?.useHookClone ?? this._useHookClone;\n\t\tconst entry: IHook = shouldClone\n\t\t\t? { id: hook.id, event: hook.event, handler: hook.handler }\n\t\t\t: hook;\n\n\t\tentry.id = entry.id ?? crypto.randomUUID();\n\n\t\tconst eventHandlers = this._hooks.get(hook.event);\n\t\tif (eventHandlers) {\n\t\t\t// Check for duplicate id — replace in-place if found\n\t\t\tconst existingIndex = eventHandlers.findIndex((h) => h.id === entry.id);\n\t\t\tif (existingIndex !== -1) {\n\t\t\t\teventHandlers[existingIndex] = entry;\n\t\t\t} else {\n\t\t\t\tconst position = options?.position ?? \"Bottom\";\n\t\t\t\tif (position === \"Top\") {\n\t\t\t\t\teventHandlers.unshift(entry);\n\t\t\t\t} else if (position === \"Bottom\") {\n\t\t\t\t\teventHandlers.push(entry);\n\t\t\t\t} else {\n\t\t\t\t\tconst index = Math.max(0, Math.min(position, eventHandlers.length));\n\t\t\t\t\teventHandlers.splice(index, 0, entry);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis._hooks.set(hook.event, [entry]);\n\t\t}\n\n\t\treturn entry;\n\t}\n\n\t/**\n\t * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.\n\t * @param {string} event - the event name\n\t * @param {HookFn} handler - the handler function\n\t * @returns {void}\n\t */\n\tpublic addHook(event: string, handler: HookFn) {\n\t\tthis.onHook({ event, handler });\n\t}\n\n\t/**\n\t * Adds handler functions for specific events\n\t * @param {Array<IHook>} hooks\n\t * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)\n\t * @returns {void}\n\t */\n\tpublic onHooks(hooks: IHook[], options?: OnHookOptions) {\n\t\tfor (const hook of hooks) {\n\t\t\tthis.onHook(hook, options);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a handler function for a specific event that runs before all other handlers.\n\t * Equivalent to calling `onHook(hook, { position: \"Top\" })`.\n\t * @param {IHook} hook - the hook containing event name and handler\n\t * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)\n\t * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation\n\t */\n\tpublic prependHook(\n\t\thook: IHook,\n\t\toptions?: PrependHookOptions,\n\t): IHook | undefined {\n\t\treturn this.onHook(hook, { ...options, position: \"Top\" });\n\t}\n\n\t/**\n\t * Adds a handler that only executes once for a specific event before all other handlers.\n\t * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: \"Top\" }`.\n\t * @param {IHook} hook - the hook containing event name and handler\n\t * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)\n\t * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation\n\t */\n\tpublic prependOnceHook(\n\t\thook: IHook,\n\t\toptions?: PrependHookOptions,\n\t): IHook | undefined {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tconst wrappedHandler = async (...arguments_: any[]) => {\n\t\t\tthis.removeHook({ event: hook.event, handler: wrappedHandler });\n\t\t\treturn hook.handler(...arguments_);\n\t\t};\n\n\t\treturn this.onHook(\n\t\t\t{ id: hook.id, event: hook.event, handler: wrappedHandler },\n\t\t\t{ ...options, position: \"Top\" },\n\t\t);\n\t}\n\n\t/**\n\t * Adds a handler that only executes once for a specific event\n\t * @param {IHook} hook - the hook containing event name and handler\n\t */\n\tpublic onceHook(hook: IHook) {\n\t\tthis.validateHookName(hook.event);\n\t\tif (!this.checkDeprecatedHook(hook.event)) {\n\t\t\treturn; // Skip registration if deprecated hooks are not allowed\n\t\t}\n\t\t// biome-ignore lint/suspicious/noExplicitAny: this is for any parameter compatibility\n\t\tconst wrappedHandler = async (...arguments_: any[]) => {\n\t\t\tthis.removeHook({ event: hook.event, handler: wrappedHandler });\n\t\t\treturn hook.handler(...arguments_);\n\t\t};\n\n\t\tthis.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler });\n\t}\n\n\t/**\n\t * Removes a handler function for a specific event\n\t * @param {IHook} hook - the hook containing event name and handler to remove\n\t * @returns {IHook | undefined} the removed hook, or undefined if not found\n\t */\n\tpublic removeHook(hook: IHook): IHook | undefined {\n\t\tthis.validateHookName(hook.event);\n\t\tconst eventHandlers = this._hooks.get(hook.event);\n\t\tif (eventHandlers) {\n\t\t\tconst index = eventHandlers.findIndex((h) => h.handler === hook.handler);\n\t\t\tif (index !== -1) {\n\t\t\t\teventHandlers.splice(index, 1);\n\t\t\t\tif (eventHandlers.length === 0) {\n\t\t\t\t\tthis._hooks.delete(hook.event);\n\t\t\t\t}\n\n\t\t\t\treturn { event: hook.event, handler: hook.handler };\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Removes multiple hook handlers\n\t * @param {Array<IHook>} hooks\n\t * @returns {IHook[]} the hooks that were successfully removed\n\t */\n\tpublic removeHooks(hooks: IHook[]): IHook[] {\n\t\tconst removed: IHook[] = [];\n\t\tfor (const hook of hooks) {\n\t\t\tconst result = this.removeHook(hook);\n\t\t\tif (result) {\n\t\t\t\tremoved.push(result);\n\t\t\t}\n\t\t}\n\n\t\treturn removed;\n\t}\n\n\t/**\n\t * Calls all handlers for a specific event\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {Promise<void>}\n\t */\n\tpublic async hook<T>(event: string, ...arguments_: T[]) {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn; // Skip execution if deprecated hooks are not allowed\n\t\t}\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tfor (const hook of [...eventHandlers]) {\n\t\t\t\ttry {\n\t\t\t\t\tawait hook.handler(...arguments_);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = `${event}: ${(error as Error).message}`;\n\t\t\t\t\tthis.emit(\"error\", new Error(message));\n\n\t\t\t\t\tif (this._throwOnHookError) {\n\t\t\t\t\t\tthrow new Error(message);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Calls all synchronous handlers for a specific event.\n\t * Async handlers (declared with `async` keyword) are silently skipped.\n\t *\n\t * Note: The `hook` method is preferred as it executes both sync and async functions.\n\t * Use `hookSync` only when you specifically need synchronous execution.\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {void}\n\t */\n\tpublic hookSync<T>(event: string, ...arguments_: T[]): void {\n\t\tthis.validateHookName(event);\n\t\tif (!this.checkDeprecatedHook(event)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tfor (const hook of [...eventHandlers]) {\n\t\t\t\t// Skip async functions silently\n\t\t\t\tif (hook.handler.constructor.name === \"AsyncFunction\") {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\thook.handler(...arguments_);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = `${event}: ${(error as Error).message}`;\n\t\t\t\t\tthis.emit(\"error\", new Error(message));\n\n\t\t\t\t\tif (this._throwOnHookError) {\n\t\t\t\t\t\tthrow new Error(message);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.\n\t * @param {string} event - The event name\n\t * @param {T[]} arguments_ - The arguments to pass to the hook\n\t */\n\tpublic async beforeHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(`before:${event}`, ...arguments_);\n\t}\n\n\t/**\n\t * Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.\n\t * @param {string} event - The event name\n\t * @param {T[]} arguments_ - The arguments to pass to the hook\n\t */\n\tpublic async afterHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(`after:${event}`, ...arguments_);\n\t}\n\n\t/**\n\t * Calls all handlers for a specific event. This is an alias for `hook` and is provided for\n\t * compatibility with other libraries that use the `callHook` method.\n\t * @param {string} event\n\t * @param {T[]} arguments_\n\t * @returns {Promise<void>}\n\t */\n\tpublic async callHook<T>(event: string, ...arguments_: T[]) {\n\t\tawait this.hook(event, ...arguments_);\n\t}\n\n\t/**\n\t * Gets all hooks for a specific event\n\t * @param {string} event\n\t * @returns {IHook[]}\n\t */\n\tpublic getHooks(event: string) {\n\t\tthis.validateHookName(event);\n\t\treturn this._hooks.get(event);\n\t}\n\n\t/**\n\t * Gets a specific hook by id, searching across all events\n\t * @param {string} id - the hook id\n\t * @returns {IHook | undefined} the hook if found, or undefined\n\t */\n\tpublic getHook(id: string): IHook | undefined {\n\t\tfor (const eventHandlers of this._hooks.values()) {\n\t\t\tconst found = eventHandlers.find((h) => h.id === id);\n\t\t\tif (found) {\n\t\t\t\treturn found;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Removes one or more hooks by id, searching across all events\n\t * @param {string | string[]} id - the hook id or array of hook ids to remove\n\t * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found\n\t */\n\tpublic removeHookById(id: string | string[]): IHook | IHook[] | undefined {\n\t\tif (Array.isArray(id)) {\n\t\t\tconst removed: IHook[] = [];\n\t\t\tfor (const singleId of id) {\n\t\t\t\tconst result = this.removeHookById(singleId);\n\t\t\t\tif (result && !Array.isArray(result)) {\n\t\t\t\t\tremoved.push(result);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn removed;\n\t\t}\n\n\t\tfor (const [event, eventHandlers] of this._hooks.entries()) {\n\t\t\tconst index = eventHandlers.findIndex((h) => h.id === id);\n\t\t\tif (index !== -1) {\n\t\t\t\tconst [removed] = eventHandlers.splice(index, 1);\n\t\t\t\tif (eventHandlers.length === 0) {\n\t\t\t\t\tthis._hooks.delete(event);\n\t\t\t\t}\n\n\t\t\t\treturn removed;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Removes all hooks\n\t * @returns {void}\n\t */\n\tpublic clearHooks() {\n\t\tthis._hooks.clear();\n\t}\n\n\t/**\n\t * Removes all hooks for a specific event and returns the removed hooks.\n\t * @param {string} event - The event name to remove hooks for.\n\t * @returns {IHook[]} the hooks that were removed\n\t */\n\tpublic removeEventHooks(event: string): IHook[] {\n\t\tthis.validateHookName(event);\n\t\tconst eventHandlers = this._hooks.get(event);\n\t\tif (eventHandlers) {\n\t\t\tconst removed = [...eventHandlers];\n\t\t\tthis._hooks.delete(event);\n\t\t\treturn removed;\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Validates hook event name if enforceBeforeAfter is enabled\n\t * @param {string} event - The event name to validate\n\t * @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'\n\t */\n\tprivate validateHookName(event: string): void {\n\t\tif (this._enforceBeforeAfter) {\n\t\t\tconst eventValue = event.trim().toLocaleLowerCase();\n\t\t\tif (!eventValue.startsWith(\"before\") && !eventValue.startsWith(\"after\")) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Hook event \"${event}\" must start with \"before\" or \"after\" when enforceBeforeAfter is enabled`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks if a hook is deprecated and emits a warning if it is\n\t * @param {string} event - The event name to check\n\t * @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked\n\t */\n\tprivate checkDeprecatedHook(event: string): boolean {\n\t\tif (this._deprecatedHooks.has(event)) {\n\t\t\tconst message = this._deprecatedHooks.get(event);\n\t\t\tconst warningMessage = `Hook \"${event}\" is deprecated${message ? `: ${message}` : \"\"}`;\n\n\t\t\t// Emit deprecation warning event\n\t\t\tthis.emit(\"warn\", { hook: event, message: warningMessage });\n\n\t\t\t// Return false if deprecated hooks are not allowed\n\t\t\treturn this._allowDeprecated;\n\t\t}\n\t\treturn true;\n\t}\n}\n\nexport { Eventified } from \"./eventified.js\";\nexport { Hook } from \"./hooks/hook.js\";\nexport { WaterfallHook } from \"./hooks/waterfall-hook.js\";\nexport type {\n\tEventEmitterOptions,\n\tEventListener,\n\tIEventEmitter,\n\tLogger,\n} from \"./types.js\";\n"],"mappings":"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","names":[]} |
+289
-157
@@ -24,3 +24,5 @@ "use strict"; | ||
| Eventified: () => Eventified, | ||
| Hookified: () => Hookified | ||
| Hook: () => Hook, | ||
| Hookified: () => Hookified, | ||
| WaterfallHook: () => WaterfallHook | ||
| }); | ||
@@ -33,10 +35,10 @@ module.exports = __toCommonJS(index_exports); | ||
| _maxListeners; | ||
| _logger; | ||
| _eventLogger; | ||
| _throwOnEmitError = false; | ||
| _throwOnEmptyListeners = false; | ||
| _throwOnEmptyListeners = true; | ||
| _errorEvent = "error"; | ||
| constructor(options) { | ||
| this._eventListeners = /* @__PURE__ */ new Map(); | ||
| this._maxListeners = 100; | ||
| this._logger = options?.logger; | ||
| this._maxListeners = 0; | ||
| this._eventLogger = options?.eventLogger; | ||
| if (options?.throwOnEmitError !== void 0) { | ||
@@ -50,14 +52,14 @@ this._throwOnEmitError = options.throwOnEmitError; | ||
| /** | ||
| * Gets the logger | ||
| * Gets the event logger | ||
| * @returns {Logger} | ||
| */ | ||
| get logger() { | ||
| return this._logger; | ||
| get eventLogger() { | ||
| return this._eventLogger; | ||
| } | ||
| /** | ||
| * Sets the logger | ||
| * @param {Logger} logger | ||
| * Sets the event logger | ||
| * @param {Logger} eventLogger | ||
| */ | ||
| set logger(logger) { | ||
| this._logger = logger; | ||
| set eventLogger(eventLogger) { | ||
| this._eventLogger = eventLogger; | ||
| } | ||
@@ -191,3 +193,3 @@ /** | ||
| if (listeners) { | ||
| if (listeners.length >= this._maxListeners) { | ||
| if (this._maxListeners > 0 && listeners.length >= this._maxListeners) { | ||
| console.warn( | ||
@@ -243,2 +245,3 @@ `MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.` | ||
| } | ||
| this.sendToEventLogger(event, arguments_); | ||
| if (event === this._errorEvent) { | ||
@@ -254,3 +257,2 @@ const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`); | ||
| } | ||
| this.sendLog(event, arguments_); | ||
| return result; | ||
@@ -285,8 +287,3 @@ } | ||
| setMaxListeners(n) { | ||
| this._maxListeners = n; | ||
| for (const listeners of this._eventListeners.values()) { | ||
| if (listeners.length > n) { | ||
| listeners.splice(n); | ||
| } | ||
| } | ||
| this._maxListeners = n < 0 ? 0 : n; | ||
| } | ||
@@ -309,4 +306,4 @@ /** | ||
| */ | ||
| sendLog(eventName, data) { | ||
| if (!this._logger) { | ||
| sendToEventLogger(eventName, data) { | ||
| if (!this._eventLogger) { | ||
| return; | ||
@@ -328,23 +325,23 @@ } | ||
| case "error": { | ||
| this._logger.error?.(message, { event: eventName, data }); | ||
| this._eventLogger.error?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "warn": { | ||
| this._logger.warn?.(message, { event: eventName, data }); | ||
| this._eventLogger.warn?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "trace": { | ||
| this._logger.trace?.(message, { event: eventName, data }); | ||
| this._eventLogger.trace?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "debug": { | ||
| this._logger.debug?.(message, { event: eventName, data }); | ||
| this._eventLogger.debug?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "fatal": { | ||
| this._logger.fatal?.(message, { event: eventName, data }); | ||
| this._eventLogger.fatal?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| default: { | ||
| this._logger.info?.(message, { event: eventName, data }); | ||
| this._eventLogger.info?.(message, { event: eventName, data }); | ||
| break; | ||
@@ -356,2 +353,70 @@ } | ||
| // src/hooks/hook.ts | ||
| var Hook = class { | ||
| id; | ||
| event; | ||
| handler; | ||
| /** | ||
| * Creates a new Hook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {HookFn} handler - The handler function for the hook | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event, handler, id) { | ||
| this.id = id; | ||
| this.event = event; | ||
| this.handler = handler; | ||
| } | ||
| }; | ||
| // src/hooks/waterfall-hook.ts | ||
| var WaterfallHook = class { | ||
| id; | ||
| event; | ||
| handler; | ||
| hooks; | ||
| _finalHandler; | ||
| /** | ||
| * Creates a new WaterfallHook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event, finalHandler, id) { | ||
| this.id = id; | ||
| this.event = event; | ||
| this.hooks = []; | ||
| this._finalHandler = finalHandler; | ||
| this.handler = async (...arguments_) => { | ||
| const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_; | ||
| const results = []; | ||
| for (const hook of this.hooks) { | ||
| const result = await hook({ initialArgs, results: [...results] }); | ||
| results.push({ hook, result }); | ||
| } | ||
| await this._finalHandler({ initialArgs, results: [...results] }); | ||
| }; | ||
| } | ||
| /** | ||
| * Adds a hook function to the end of the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to add | ||
| */ | ||
| addHook(hook) { | ||
| this.hooks.push(hook); | ||
| } | ||
| /** | ||
| * Removes a specific hook function from the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to remove | ||
| * @returns {boolean} true if the hook was found and removed | ||
| */ | ||
| removeHook(hook) { | ||
| const index = this.hooks.indexOf(hook); | ||
| if (index !== -1) { | ||
| this.hooks.splice(index, 1); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| }; | ||
| // src/index.ts | ||
@@ -364,5 +429,6 @@ var Hookified = class extends Eventified { | ||
| _allowDeprecated = true; | ||
| _useHookClone = true; | ||
| constructor(options) { | ||
| super({ | ||
| logger: options?.logger, | ||
| eventLogger: options?.eventLogger, | ||
| throwOnEmitError: options?.throwOnEmitError, | ||
@@ -375,4 +441,2 @@ throwOnEmptyListeners: options?.throwOnEmptyListeners | ||
| this._throwOnHookError = options.throwOnHookError; | ||
| } else if (options?.throwHookErrors !== void 0) { | ||
| this._throwOnHookError = options.throwHookErrors; | ||
| } | ||
@@ -385,6 +449,9 @@ if (options?.enforceBeforeAfter !== void 0) { | ||
| } | ||
| if (options?.useHookClone !== void 0) { | ||
| this._useHookClone = options.useHookClone; | ||
| } | ||
| } | ||
| /** | ||
| * Gets all hooks | ||
| * @returns {Map<string, Hook[]>} | ||
| * @returns {Map<string, IHook[]>} | ||
| */ | ||
@@ -397,19 +464,3 @@ get hooks() { | ||
| * @returns {boolean} | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| get throwHookErrors() { | ||
| return this._throwOnHookError; | ||
| } | ||
| /** | ||
| * 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 | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| set throwHookErrors(value) { | ||
| this._throwOnHookError = value; | ||
| } | ||
| /** | ||
| * 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 throwOnHookError() { | ||
@@ -469,153 +520,149 @@ return this._throwOnHookError; | ||
| /** | ||
| * 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' | ||
| * Gets whether hook objects are cloned before storing. Default is true. | ||
| * @returns {boolean} | ||
| */ | ||
| 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` | ||
| ); | ||
| } | ||
| } | ||
| get useHookClone() { | ||
| return this._useHookClone; | ||
| } | ||
| /** | ||
| * 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 | ||
| * Sets whether hook objects are cloned before storing. Default is true. | ||
| * When false, the original IHook reference is stored directly. | ||
| * @param {boolean} value | ||
| */ | ||
| 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 }); | ||
| return this._allowDeprecated; | ||
| } | ||
| return true; | ||
| set useHookClone(value) { | ||
| this._useHookClone = value; | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler - this can be async or sync | ||
| * @returns {void} | ||
| * Adds a handler function for a specific event. | ||
| * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead. | ||
| * To register multiple hooks at once, use {@link onHooks}. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| onHook(event, handler) { | ||
| this.onHookEntry({ event, handler }); | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {HookEntry} hookEntry | ||
| * @returns {void} | ||
| */ | ||
| onHookEntry(hookEntry) { | ||
| this.validateHookName(hookEntry.event); | ||
| if (!this.checkDeprecatedHook(hookEntry.event)) { | ||
| return; | ||
| onHook(hook, options) { | ||
| this.validateHookName(hook.event); | ||
| if (!this.checkDeprecatedHook(hook.event)) { | ||
| return void 0; | ||
| } | ||
| const eventHandlers = this._hooks.get(hookEntry.event); | ||
| const shouldClone = options?.useHookClone ?? this._useHookClone; | ||
| const entry = shouldClone ? { id: hook.id, event: hook.event, handler: hook.handler } : hook; | ||
| entry.id = entry.id ?? crypto.randomUUID(); | ||
| const eventHandlers = this._hooks.get(hook.event); | ||
| if (eventHandlers) { | ||
| eventHandlers.push(hookEntry.handler); | ||
| const existingIndex = eventHandlers.findIndex((h) => h.id === entry.id); | ||
| if (existingIndex !== -1) { | ||
| eventHandlers[existingIndex] = entry; | ||
| } else { | ||
| const position = options?.position ?? "Bottom"; | ||
| if (position === "Top") { | ||
| eventHandlers.unshift(entry); | ||
| } else if (position === "Bottom") { | ||
| eventHandlers.push(entry); | ||
| } else { | ||
| const index = Math.max(0, Math.min(position, eventHandlers.length)); | ||
| eventHandlers.splice(index, 0, entry); | ||
| } | ||
| } | ||
| } else { | ||
| this._hooks.set(hookEntry.event, [hookEntry.handler]); | ||
| this._hooks.set(hook.event, [entry]); | ||
| } | ||
| return entry; | ||
| } | ||
| /** | ||
| * 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 | ||
| * @param {string} event - the event name | ||
| * @param {HookFn} handler - the handler function | ||
| * @returns {void} | ||
| */ | ||
| addHook(event, handler) { | ||
| this.onHookEntry({ event, handler }); | ||
| this.onHook({ event, handler }); | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * Adds handler functions for specific events | ||
| * @param {Array<IHook>} hooks | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {void} | ||
| */ | ||
| onHooks(hooks) { | ||
| onHooks(hooks, options) { | ||
| for (const hook of hooks) { | ||
| this.onHook(hook.event, hook.handler); | ||
| this.onHook(hook, options); | ||
| } | ||
| } | ||
| /** | ||
| * 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} | ||
| * Adds a handler function for a specific event that runs before all other handlers. | ||
| * Equivalent to calling `onHook(hook, { position: "Top" })`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| 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]); | ||
| } | ||
| prependHook(hook, options) { | ||
| return this.onHook(hook, { ...options, position: "Top" }); | ||
| } | ||
| /** | ||
| * Adds a handler that only executes once for a specific event before all other handlers | ||
| * @param event | ||
| * @param handler | ||
| * Adds a handler that only executes once for a specific event before all other handlers. | ||
| * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| prependOnceHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return; | ||
| } | ||
| const hook = async (...arguments_) => { | ||
| this.removeHook(event, hook); | ||
| return handler(...arguments_); | ||
| prependOnceHook(hook, options) { | ||
| const wrappedHandler = async (...arguments_) => { | ||
| this.removeHook({ event: hook.event, handler: wrappedHandler }); | ||
| return hook.handler(...arguments_); | ||
| }; | ||
| this.prependHook(event, hook); | ||
| return this.onHook( | ||
| { id: hook.id, event: hook.event, handler: wrappedHandler }, | ||
| { ...options, position: "Top" } | ||
| ); | ||
| } | ||
| /** | ||
| * Adds a handler that only executes once for a specific event | ||
| * @param event | ||
| * @param handler | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| */ | ||
| onceHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| onceHook(hook) { | ||
| this.validateHookName(hook.event); | ||
| if (!this.checkDeprecatedHook(hook.event)) { | ||
| return; | ||
| } | ||
| const hook = async (...arguments_) => { | ||
| this.removeHook(event, hook); | ||
| return handler(...arguments_); | ||
| const wrappedHandler = async (...arguments_) => { | ||
| this.removeHook({ event: hook.event, handler: wrappedHandler }); | ||
| return hook.handler(...arguments_); | ||
| }; | ||
| this.onHook(event, hook); | ||
| this.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler }); | ||
| } | ||
| /** | ||
| * Removes a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler | ||
| * @returns {void} | ||
| * @param {IHook} hook - the hook containing event name and handler to remove | ||
| * @returns {IHook | undefined} the removed hook, or undefined if not found | ||
| */ | ||
| removeHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return; | ||
| } | ||
| const eventHandlers = this._hooks.get(event); | ||
| removeHook(hook) { | ||
| this.validateHookName(hook.event); | ||
| const eventHandlers = this._hooks.get(hook.event); | ||
| if (eventHandlers) { | ||
| const index = eventHandlers.indexOf(handler); | ||
| const index = eventHandlers.findIndex((h) => h.handler === hook.handler); | ||
| if (index !== -1) { | ||
| eventHandlers.splice(index, 1); | ||
| if (eventHandlers.length === 0) { | ||
| this._hooks.delete(hook.event); | ||
| } | ||
| return { event: hook.event, handler: hook.handler }; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes all handlers for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * @returns {void} | ||
| * Removes multiple hook handlers | ||
| * @param {Array<IHook>} hooks | ||
| * @returns {IHook[]} the hooks that were successfully removed | ||
| */ | ||
| removeHooks(hooks) { | ||
| const removed = []; | ||
| for (const hook of hooks) { | ||
| this.removeHook(hook.event, hook.handler); | ||
| const result = this.removeHook(hook); | ||
| if (result) { | ||
| removed.push(result); | ||
| } | ||
| } | ||
| return removed; | ||
| } | ||
@@ -635,5 +682,5 @@ /** | ||
| if (eventHandlers) { | ||
| for (const handler of eventHandlers) { | ||
| for (const hook of [...eventHandlers]) { | ||
| try { | ||
| await handler(...arguments_); | ||
| await hook.handler(...arguments_); | ||
| } catch (error) { | ||
@@ -666,8 +713,8 @@ const message = `${event}: ${error.message}`; | ||
| if (eventHandlers) { | ||
| for (const handler of eventHandlers) { | ||
| if (handler.constructor.name === "AsyncFunction") { | ||
| for (const hook of [...eventHandlers]) { | ||
| if (hook.handler.constructor.name === "AsyncFunction") { | ||
| continue; | ||
| } | ||
| try { | ||
| handler(...arguments_); | ||
| hook.handler(...arguments_); | ||
| } catch (error) { | ||
@@ -712,12 +759,51 @@ const message = `${event}: ${error.message}`; | ||
| * @param {string} event | ||
| * @returns {Hook[]} | ||
| * @returns {IHook[]} | ||
| */ | ||
| getHooks(event) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return void 0; | ||
| } | ||
| return this._hooks.get(event); | ||
| } | ||
| /** | ||
| * Gets a specific hook by id, searching across all events | ||
| * @param {string} id - the hook id | ||
| * @returns {IHook | undefined} the hook if found, or undefined | ||
| */ | ||
| getHook(id) { | ||
| for (const eventHandlers of this._hooks.values()) { | ||
| const found = eventHandlers.find((h) => h.id === id); | ||
| if (found) { | ||
| return found; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes one or more hooks by id, searching across all events | ||
| * @param {string | string[]} id - the hook id or array of hook ids to remove | ||
| * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found | ||
| */ | ||
| removeHookById(id) { | ||
| if (Array.isArray(id)) { | ||
| const removed = []; | ||
| for (const singleId of id) { | ||
| const result = this.removeHookById(singleId); | ||
| if (result && !Array.isArray(result)) { | ||
| removed.push(result); | ||
| } | ||
| } | ||
| return removed; | ||
| } | ||
| for (const [event, eventHandlers] of this._hooks.entries()) { | ||
| const index = eventHandlers.findIndex((h) => h.id === id); | ||
| if (index !== -1) { | ||
| const [removed] = eventHandlers.splice(index, 1); | ||
| if (eventHandlers.length === 0) { | ||
| this._hooks.delete(event); | ||
| } | ||
| return removed; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes all hooks | ||
@@ -729,2 +815,46 @@ * @returns {void} | ||
| } | ||
| /** | ||
| * Removes all hooks for a specific event and returns the removed hooks. | ||
| * @param {string} event - The event name to remove hooks for. | ||
| * @returns {IHook[]} the hooks that were removed | ||
| */ | ||
| removeEventHooks(event) { | ||
| this.validateHookName(event); | ||
| const eventHandlers = this._hooks.get(event); | ||
| if (eventHandlers) { | ||
| const removed = [...eventHandlers]; | ||
| this._hooks.delete(event); | ||
| return removed; | ||
| } | ||
| return []; | ||
| } | ||
| /** | ||
| * 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 }); | ||
| return this._allowDeprecated; | ||
| } | ||
| return true; | ||
| } | ||
| }; | ||
@@ -734,4 +864,6 @@ // Annotate the CommonJS export names for ESM import in node: | ||
| Eventified, | ||
| Hookified | ||
| Hook, | ||
| Hookified, | ||
| WaterfallHook | ||
| }); | ||
| /* v8 ignore next -- @preserve */ |
+206
-79
@@ -11,5 +11,5 @@ type Logger = { | ||
| /** | ||
| * Logger instance for logging errors. | ||
| * Logger instance for logging events. | ||
| */ | ||
| logger?: Logger; | ||
| eventLogger?: Logger; | ||
| /** | ||
@@ -21,3 +21,3 @@ * Whether to throw an error when emit 'error' and there are no listeners. Default is false and only emits an error event. | ||
| * Whether to throw on 'error' when there are no listeners. This is the standard functionality in EventEmitter | ||
| * @default false - in v2 this will be set to true by default | ||
| * @default true | ||
| */ | ||
@@ -171,5 +171,9 @@ throwOnEmptyListeners?: boolean; | ||
| type EventListener = (...arguments_: any[]) => void; | ||
| type Hook = (...arguments_: any[]) => Promise<void> | void; | ||
| type HookEntry = { | ||
| type HookFn = (...arguments_: any[]) => Promise<void> | void; | ||
| interface IHook { | ||
| /** | ||
| * Unique identifier for the hook. Auto-generated via crypto.randomUUID() if not provided. | ||
| */ | ||
| id?: string; | ||
| /** | ||
| * The event name for the hook | ||
@@ -181,11 +185,61 @@ */ | ||
| */ | ||
| handler: Hook; | ||
| handler: HookFn; | ||
| } | ||
| type WaterfallHookResult = { | ||
| /** | ||
| * The hook function that produced this result | ||
| */ | ||
| hook: WaterfallHookFn; | ||
| /** | ||
| * The value returned by the hook | ||
| */ | ||
| result: any; | ||
| }; | ||
| type HookifiedOptions = { | ||
| type WaterfallHookContext = { | ||
| /** | ||
| * Whether an error should be thrown when a hook throws an error. Default is false and only emits an error event. | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| * The original arguments passed to the waterfall execution, before any hooks processed them. | ||
| */ | ||
| throwHookErrors?: boolean; | ||
| initialArgs: any; | ||
| /** | ||
| * Array of results from previous hooks in execution order, each containing the hook and its result. | ||
| * Empty for the first hook. | ||
| */ | ||
| results: WaterfallHookResult[]; | ||
| }; | ||
| type WaterfallHookFn = (context: WaterfallHookContext) => Promise<any> | any; | ||
| interface IWaterfallHook extends IHook { | ||
| /** | ||
| * Array of hook functions that are called sequentially. | ||
| * Each hook receives a WaterfallHookContext with initialArgs and results. | ||
| */ | ||
| hooks: WaterfallHookFn[]; | ||
| /** | ||
| * Adds a hook function to the end of the waterfall chain | ||
| */ | ||
| addHook(hook: WaterfallHookFn): void; | ||
| /** | ||
| * Removes a specific hook function from the waterfall chain | ||
| */ | ||
| removeHook(hook: WaterfallHookFn): boolean; | ||
| } | ||
| type OnHookOptions = { | ||
| /** | ||
| * Per-call override for useHookClone. | ||
| * When true, hook objects are cloned before storing. | ||
| * When false, the original IHook reference is stored directly. | ||
| * When undefined, falls back to the instance-level useHookClone setting. | ||
| * @type {boolean} | ||
| */ | ||
| useHookClone?: boolean; | ||
| /** | ||
| * Controls where the hook is inserted in the handlers array. | ||
| * - "Top": Insert at the beginning (index 0), before all existing handlers. | ||
| * - "Bottom": Append to the end, after all existing handlers. This is the default. | ||
| * - number: Insert at a specific index. Values are clamped to the array bounds. | ||
| */ | ||
| position?: "Top" | "Bottom" | number; | ||
| }; | ||
| type PrependHookOptions = Omit<OnHookOptions, "position">; | ||
| type HookifiedOptions = { | ||
| /** | ||
| * Whether an error should be thrown when a hook throws an error. Default is false and only emits an error event. | ||
@@ -212,2 +266,9 @@ */ | ||
| allowDeprecated?: boolean; | ||
| /** | ||
| * Whether to clone hook objects before storing. Default is true. | ||
| * When false, the original IHook reference is stored directly. | ||
| * @type {boolean} | ||
| * @default true | ||
| */ | ||
| useHookClone?: boolean; | ||
| } & EventEmitterOptions; | ||
@@ -218,3 +279,3 @@ | ||
| private _maxListeners; | ||
| private _logger?; | ||
| private _eventLogger?; | ||
| private _throwOnEmitError; | ||
@@ -225,11 +286,11 @@ private _throwOnEmptyListeners; | ||
| /** | ||
| * Gets the logger | ||
| * Gets the event logger | ||
| * @returns {Logger} | ||
| */ | ||
| get logger(): Logger | undefined; | ||
| get eventLogger(): Logger | undefined; | ||
| /** | ||
| * Sets the logger | ||
| * @param {Logger} logger | ||
| * Sets the event logger | ||
| * @param {Logger} eventLogger | ||
| */ | ||
| set logger(logger: Logger | undefined); | ||
| set eventLogger(eventLogger: Logger | undefined); | ||
| /** | ||
@@ -361,5 +422,55 @@ * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event. | ||
| */ | ||
| private sendLog; | ||
| private sendToEventLogger; | ||
| } | ||
| /** | ||
| * Concrete implementation of the IHook interface. | ||
| * Provides a convenient class-based way to create hook entries. | ||
| */ | ||
| declare class Hook implements IHook { | ||
| id?: string; | ||
| event: string; | ||
| handler: HookFn; | ||
| /** | ||
| * Creates a new Hook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {HookFn} handler - The handler function for the hook | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event: string, handler: HookFn, id?: string); | ||
| } | ||
| /** | ||
| * A WaterfallHook chains multiple hook functions sequentially, | ||
| * where each hook receives a context with the previous result, | ||
| * initial arguments, and accumulated results. After all hooks | ||
| * have executed, the final handler receives the transformed result. | ||
| * Implements IHook for compatibility with Hookified.onHook(). | ||
| */ | ||
| declare class WaterfallHook implements IWaterfallHook { | ||
| id?: string; | ||
| event: string; | ||
| handler: HookFn; | ||
| hooks: WaterfallHookFn[]; | ||
| private readonly _finalHandler; | ||
| /** | ||
| * Creates a new WaterfallHook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event: string, finalHandler: WaterfallHookFn, id?: string); | ||
| /** | ||
| * Adds a hook function to the end of the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to add | ||
| */ | ||
| addHook(hook: WaterfallHookFn): void; | ||
| /** | ||
| * Removes a specific hook function from the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to remove | ||
| * @returns {boolean} true if the hook was found and removed | ||
| */ | ||
| removeHook(hook: WaterfallHookFn): boolean; | ||
| } | ||
| declare class Hookified extends Eventified { | ||
@@ -371,24 +482,13 @@ private readonly _hooks; | ||
| private _allowDeprecated; | ||
| private _useHookClone; | ||
| constructor(options?: HookifiedOptions); | ||
| /** | ||
| * Gets all hooks | ||
| * @returns {Map<string, Hook[]>} | ||
| * @returns {Map<string, IHook[]>} | ||
| */ | ||
| get hooks(): Map<string, Hook[]>; | ||
| get hooks(): Map<string, IHook[]>; | ||
| /** | ||
| * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event. | ||
| * @returns {boolean} | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| get throwHookErrors(): boolean; | ||
| /** | ||
| * 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 | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| set throwHookErrors(value: boolean); | ||
| /** | ||
| * 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 throwOnHookError(): boolean; | ||
@@ -432,71 +532,68 @@ /** | ||
| /** | ||
| * 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' | ||
| * Gets whether hook objects are cloned before storing. Default is true. | ||
| * @returns {boolean} | ||
| */ | ||
| private validateHookName; | ||
| get useHookClone(): boolean; | ||
| /** | ||
| * 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 | ||
| * Sets whether hook objects are cloned before storing. Default is true. | ||
| * When false, the original IHook reference is stored directly. | ||
| * @param {boolean} value | ||
| */ | ||
| private checkDeprecatedHook; | ||
| set useHookClone(value: boolean); | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler - this can be async or sync | ||
| * @returns {void} | ||
| * Adds a handler function for a specific event. | ||
| * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead. | ||
| * To register multiple hooks at once, use {@link onHooks}. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| onHook(event: string, handler: Hook): void; | ||
| onHook(hook: IHook, options?: OnHookOptions): IHook | undefined; | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {HookEntry} hookEntry | ||
| * @returns {void} | ||
| */ | ||
| onHookEntry(hookEntry: HookEntry): void; | ||
| /** | ||
| * 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 | ||
| * @param {string} event - the event name | ||
| * @param {HookFn} handler - the handler function | ||
| * @returns {void} | ||
| */ | ||
| addHook(event: string, handler: Hook): void; | ||
| addHook(event: string, handler: HookFn): void; | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * Adds handler functions for specific events | ||
| * @param {Array<IHook>} hooks | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {void} | ||
| */ | ||
| onHooks(hooks: HookEntry[]): void; | ||
| onHooks(hooks: IHook[], options?: OnHookOptions): void; | ||
| /** | ||
| * 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} | ||
| * Adds a handler function for a specific event that runs before all other handlers. | ||
| * Equivalent to calling `onHook(hook, { position: "Top" })`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| prependHook(event: string, handler: Hook): void; | ||
| prependHook(hook: IHook, options?: PrependHookOptions): IHook | undefined; | ||
| /** | ||
| * Adds a handler that only executes once for a specific event before all other handlers | ||
| * @param event | ||
| * @param handler | ||
| * Adds a handler that only executes once for a specific event before all other handlers. | ||
| * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| prependOnceHook(event: string, handler: Hook): void; | ||
| prependOnceHook(hook: IHook, options?: PrependHookOptions): IHook | undefined; | ||
| /** | ||
| * Adds a handler that only executes once for a specific event | ||
| * @param event | ||
| * @param handler | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| */ | ||
| onceHook(event: string, handler: Hook): void; | ||
| onceHook(hook: IHook): void; | ||
| /** | ||
| * Removes a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler | ||
| * @returns {void} | ||
| * @param {IHook} hook - the hook containing event name and handler to remove | ||
| * @returns {IHook | undefined} the removed hook, or undefined if not found | ||
| */ | ||
| removeHook(event: string, handler: Hook): void; | ||
| removeHook(hook: IHook): IHook | undefined; | ||
| /** | ||
| * Removes all handlers for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * @returns {void} | ||
| * Removes multiple hook handlers | ||
| * @param {Array<IHook>} hooks | ||
| * @returns {IHook[]} the hooks that were successfully removed | ||
| */ | ||
| removeHooks(hooks: HookEntry[]): void; | ||
| removeHooks(hooks: IHook[]): IHook[]; | ||
| /** | ||
@@ -543,6 +640,18 @@ * Calls all handlers for a specific event | ||
| * @param {string} event | ||
| * @returns {Hook[]} | ||
| * @returns {IHook[]} | ||
| */ | ||
| getHooks(event: string): Hook[] | undefined; | ||
| getHooks(event: string): IHook[] | undefined; | ||
| /** | ||
| * Gets a specific hook by id, searching across all events | ||
| * @param {string} id - the hook id | ||
| * @returns {IHook | undefined} the hook if found, or undefined | ||
| */ | ||
| getHook(id: string): IHook | undefined; | ||
| /** | ||
| * Removes one or more hooks by id, searching across all events | ||
| * @param {string | string[]} id - the hook id or array of hook ids to remove | ||
| * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found | ||
| */ | ||
| removeHookById(id: string | string[]): IHook | IHook[] | undefined; | ||
| /** | ||
| * Removes all hooks | ||
@@ -552,4 +661,22 @@ * @returns {void} | ||
| clearHooks(): void; | ||
| /** | ||
| * Removes all hooks for a specific event and returns the removed hooks. | ||
| * @param {string} event - The event name to remove hooks for. | ||
| * @returns {IHook[]} the hooks that were removed | ||
| */ | ||
| removeEventHooks(event: string): IHook[]; | ||
| /** | ||
| * 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' | ||
| */ | ||
| private validateHookName; | ||
| /** | ||
| * 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 | ||
| */ | ||
| private checkDeprecatedHook; | ||
| } | ||
| export { type EventEmitterOptions, type EventListener, Eventified, type Hook, type HookEntry, Hookified, type HookifiedOptions, type IEventEmitter, type Logger }; | ||
| export { type EventEmitterOptions, type EventListener, Eventified, Hook, type HookFn, Hookified, type HookifiedOptions, type IEventEmitter, type IHook, type IWaterfallHook, type Logger, type OnHookOptions, type PrependHookOptions, WaterfallHook, type WaterfallHookContext, type WaterfallHookFn, type WaterfallHookResult }; |
+206
-79
@@ -11,5 +11,5 @@ type Logger = { | ||
| /** | ||
| * Logger instance for logging errors. | ||
| * Logger instance for logging events. | ||
| */ | ||
| logger?: Logger; | ||
| eventLogger?: Logger; | ||
| /** | ||
@@ -21,3 +21,3 @@ * Whether to throw an error when emit 'error' and there are no listeners. Default is false and only emits an error event. | ||
| * Whether to throw on 'error' when there are no listeners. This is the standard functionality in EventEmitter | ||
| * @default false - in v2 this will be set to true by default | ||
| * @default true | ||
| */ | ||
@@ -171,5 +171,9 @@ throwOnEmptyListeners?: boolean; | ||
| type EventListener = (...arguments_: any[]) => void; | ||
| type Hook = (...arguments_: any[]) => Promise<void> | void; | ||
| type HookEntry = { | ||
| type HookFn = (...arguments_: any[]) => Promise<void> | void; | ||
| interface IHook { | ||
| /** | ||
| * Unique identifier for the hook. Auto-generated via crypto.randomUUID() if not provided. | ||
| */ | ||
| id?: string; | ||
| /** | ||
| * The event name for the hook | ||
@@ -181,11 +185,61 @@ */ | ||
| */ | ||
| handler: Hook; | ||
| handler: HookFn; | ||
| } | ||
| type WaterfallHookResult = { | ||
| /** | ||
| * The hook function that produced this result | ||
| */ | ||
| hook: WaterfallHookFn; | ||
| /** | ||
| * The value returned by the hook | ||
| */ | ||
| result: any; | ||
| }; | ||
| type HookifiedOptions = { | ||
| type WaterfallHookContext = { | ||
| /** | ||
| * Whether an error should be thrown when a hook throws an error. Default is false and only emits an error event. | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| * The original arguments passed to the waterfall execution, before any hooks processed them. | ||
| */ | ||
| throwHookErrors?: boolean; | ||
| initialArgs: any; | ||
| /** | ||
| * Array of results from previous hooks in execution order, each containing the hook and its result. | ||
| * Empty for the first hook. | ||
| */ | ||
| results: WaterfallHookResult[]; | ||
| }; | ||
| type WaterfallHookFn = (context: WaterfallHookContext) => Promise<any> | any; | ||
| interface IWaterfallHook extends IHook { | ||
| /** | ||
| * Array of hook functions that are called sequentially. | ||
| * Each hook receives a WaterfallHookContext with initialArgs and results. | ||
| */ | ||
| hooks: WaterfallHookFn[]; | ||
| /** | ||
| * Adds a hook function to the end of the waterfall chain | ||
| */ | ||
| addHook(hook: WaterfallHookFn): void; | ||
| /** | ||
| * Removes a specific hook function from the waterfall chain | ||
| */ | ||
| removeHook(hook: WaterfallHookFn): boolean; | ||
| } | ||
| type OnHookOptions = { | ||
| /** | ||
| * Per-call override for useHookClone. | ||
| * When true, hook objects are cloned before storing. | ||
| * When false, the original IHook reference is stored directly. | ||
| * When undefined, falls back to the instance-level useHookClone setting. | ||
| * @type {boolean} | ||
| */ | ||
| useHookClone?: boolean; | ||
| /** | ||
| * Controls where the hook is inserted in the handlers array. | ||
| * - "Top": Insert at the beginning (index 0), before all existing handlers. | ||
| * - "Bottom": Append to the end, after all existing handlers. This is the default. | ||
| * - number: Insert at a specific index. Values are clamped to the array bounds. | ||
| */ | ||
| position?: "Top" | "Bottom" | number; | ||
| }; | ||
| type PrependHookOptions = Omit<OnHookOptions, "position">; | ||
| type HookifiedOptions = { | ||
| /** | ||
| * Whether an error should be thrown when a hook throws an error. Default is false and only emits an error event. | ||
@@ -212,2 +266,9 @@ */ | ||
| allowDeprecated?: boolean; | ||
| /** | ||
| * Whether to clone hook objects before storing. Default is true. | ||
| * When false, the original IHook reference is stored directly. | ||
| * @type {boolean} | ||
| * @default true | ||
| */ | ||
| useHookClone?: boolean; | ||
| } & EventEmitterOptions; | ||
@@ -218,3 +279,3 @@ | ||
| private _maxListeners; | ||
| private _logger?; | ||
| private _eventLogger?; | ||
| private _throwOnEmitError; | ||
@@ -225,11 +286,11 @@ private _throwOnEmptyListeners; | ||
| /** | ||
| * Gets the logger | ||
| * Gets the event logger | ||
| * @returns {Logger} | ||
| */ | ||
| get logger(): Logger | undefined; | ||
| get eventLogger(): Logger | undefined; | ||
| /** | ||
| * Sets the logger | ||
| * @param {Logger} logger | ||
| * Sets the event logger | ||
| * @param {Logger} eventLogger | ||
| */ | ||
| set logger(logger: Logger | undefined); | ||
| set eventLogger(eventLogger: Logger | undefined); | ||
| /** | ||
@@ -361,5 +422,55 @@ * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event. | ||
| */ | ||
| private sendLog; | ||
| private sendToEventLogger; | ||
| } | ||
| /** | ||
| * Concrete implementation of the IHook interface. | ||
| * Provides a convenient class-based way to create hook entries. | ||
| */ | ||
| declare class Hook implements IHook { | ||
| id?: string; | ||
| event: string; | ||
| handler: HookFn; | ||
| /** | ||
| * Creates a new Hook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {HookFn} handler - The handler function for the hook | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event: string, handler: HookFn, id?: string); | ||
| } | ||
| /** | ||
| * A WaterfallHook chains multiple hook functions sequentially, | ||
| * where each hook receives a context with the previous result, | ||
| * initial arguments, and accumulated results. After all hooks | ||
| * have executed, the final handler receives the transformed result. | ||
| * Implements IHook for compatibility with Hookified.onHook(). | ||
| */ | ||
| declare class WaterfallHook implements IWaterfallHook { | ||
| id?: string; | ||
| event: string; | ||
| handler: HookFn; | ||
| hooks: WaterfallHookFn[]; | ||
| private readonly _finalHandler; | ||
| /** | ||
| * Creates a new WaterfallHook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event: string, finalHandler: WaterfallHookFn, id?: string); | ||
| /** | ||
| * Adds a hook function to the end of the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to add | ||
| */ | ||
| addHook(hook: WaterfallHookFn): void; | ||
| /** | ||
| * Removes a specific hook function from the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to remove | ||
| * @returns {boolean} true if the hook was found and removed | ||
| */ | ||
| removeHook(hook: WaterfallHookFn): boolean; | ||
| } | ||
| declare class Hookified extends Eventified { | ||
@@ -371,24 +482,13 @@ private readonly _hooks; | ||
| private _allowDeprecated; | ||
| private _useHookClone; | ||
| constructor(options?: HookifiedOptions); | ||
| /** | ||
| * Gets all hooks | ||
| * @returns {Map<string, Hook[]>} | ||
| * @returns {Map<string, IHook[]>} | ||
| */ | ||
| get hooks(): Map<string, Hook[]>; | ||
| get hooks(): Map<string, IHook[]>; | ||
| /** | ||
| * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event. | ||
| * @returns {boolean} | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| get throwHookErrors(): boolean; | ||
| /** | ||
| * 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 | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| set throwHookErrors(value: boolean); | ||
| /** | ||
| * 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 throwOnHookError(): boolean; | ||
@@ -432,71 +532,68 @@ /** | ||
| /** | ||
| * 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' | ||
| * Gets whether hook objects are cloned before storing. Default is true. | ||
| * @returns {boolean} | ||
| */ | ||
| private validateHookName; | ||
| get useHookClone(): boolean; | ||
| /** | ||
| * 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 | ||
| * Sets whether hook objects are cloned before storing. Default is true. | ||
| * When false, the original IHook reference is stored directly. | ||
| * @param {boolean} value | ||
| */ | ||
| private checkDeprecatedHook; | ||
| set useHookClone(value: boolean); | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler - this can be async or sync | ||
| * @returns {void} | ||
| * Adds a handler function for a specific event. | ||
| * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead. | ||
| * To register multiple hooks at once, use {@link onHooks}. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| onHook(event: string, handler: Hook): void; | ||
| onHook(hook: IHook, options?: OnHookOptions): IHook | undefined; | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {HookEntry} hookEntry | ||
| * @returns {void} | ||
| */ | ||
| onHookEntry(hookEntry: HookEntry): void; | ||
| /** | ||
| * 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 | ||
| * @param {string} event - the event name | ||
| * @param {HookFn} handler - the handler function | ||
| * @returns {void} | ||
| */ | ||
| addHook(event: string, handler: Hook): void; | ||
| addHook(event: string, handler: HookFn): void; | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * Adds handler functions for specific events | ||
| * @param {Array<IHook>} hooks | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {void} | ||
| */ | ||
| onHooks(hooks: HookEntry[]): void; | ||
| onHooks(hooks: IHook[], options?: OnHookOptions): void; | ||
| /** | ||
| * 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} | ||
| * Adds a handler function for a specific event that runs before all other handlers. | ||
| * Equivalent to calling `onHook(hook, { position: "Top" })`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| prependHook(event: string, handler: Hook): void; | ||
| prependHook(hook: IHook, options?: PrependHookOptions): IHook | undefined; | ||
| /** | ||
| * Adds a handler that only executes once for a specific event before all other handlers | ||
| * @param event | ||
| * @param handler | ||
| * Adds a handler that only executes once for a specific event before all other handlers. | ||
| * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| prependOnceHook(event: string, handler: Hook): void; | ||
| prependOnceHook(hook: IHook, options?: PrependHookOptions): IHook | undefined; | ||
| /** | ||
| * Adds a handler that only executes once for a specific event | ||
| * @param event | ||
| * @param handler | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| */ | ||
| onceHook(event: string, handler: Hook): void; | ||
| onceHook(hook: IHook): void; | ||
| /** | ||
| * Removes a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler | ||
| * @returns {void} | ||
| * @param {IHook} hook - the hook containing event name and handler to remove | ||
| * @returns {IHook | undefined} the removed hook, or undefined if not found | ||
| */ | ||
| removeHook(event: string, handler: Hook): void; | ||
| removeHook(hook: IHook): IHook | undefined; | ||
| /** | ||
| * Removes all handlers for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * @returns {void} | ||
| * Removes multiple hook handlers | ||
| * @param {Array<IHook>} hooks | ||
| * @returns {IHook[]} the hooks that were successfully removed | ||
| */ | ||
| removeHooks(hooks: HookEntry[]): void; | ||
| removeHooks(hooks: IHook[]): IHook[]; | ||
| /** | ||
@@ -543,6 +640,18 @@ * Calls all handlers for a specific event | ||
| * @param {string} event | ||
| * @returns {Hook[]} | ||
| * @returns {IHook[]} | ||
| */ | ||
| getHooks(event: string): Hook[] | undefined; | ||
| getHooks(event: string): IHook[] | undefined; | ||
| /** | ||
| * Gets a specific hook by id, searching across all events | ||
| * @param {string} id - the hook id | ||
| * @returns {IHook | undefined} the hook if found, or undefined | ||
| */ | ||
| getHook(id: string): IHook | undefined; | ||
| /** | ||
| * Removes one or more hooks by id, searching across all events | ||
| * @param {string | string[]} id - the hook id or array of hook ids to remove | ||
| * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found | ||
| */ | ||
| removeHookById(id: string | string[]): IHook | IHook[] | undefined; | ||
| /** | ||
| * Removes all hooks | ||
@@ -552,4 +661,22 @@ * @returns {void} | ||
| clearHooks(): void; | ||
| /** | ||
| * Removes all hooks for a specific event and returns the removed hooks. | ||
| * @param {string} event - The event name to remove hooks for. | ||
| * @returns {IHook[]} the hooks that were removed | ||
| */ | ||
| removeEventHooks(event: string): IHook[]; | ||
| /** | ||
| * 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' | ||
| */ | ||
| private validateHookName; | ||
| /** | ||
| * 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 | ||
| */ | ||
| private checkDeprecatedHook; | ||
| } | ||
| export { type EventEmitterOptions, type EventListener, Eventified, type Hook, type HookEntry, Hookified, type HookifiedOptions, type IEventEmitter, type Logger }; | ||
| export { type EventEmitterOptions, type EventListener, Eventified, Hook, type HookFn, Hookified, type HookifiedOptions, type IEventEmitter, type IHook, type IWaterfallHook, type Logger, type OnHookOptions, type PrependHookOptions, WaterfallHook, type WaterfallHookContext, type WaterfallHookFn, type WaterfallHookResult }; |
+286
-156
@@ -5,10 +5,10 @@ // src/eventified.ts | ||
| _maxListeners; | ||
| _logger; | ||
| _eventLogger; | ||
| _throwOnEmitError = false; | ||
| _throwOnEmptyListeners = false; | ||
| _throwOnEmptyListeners = true; | ||
| _errorEvent = "error"; | ||
| constructor(options) { | ||
| this._eventListeners = /* @__PURE__ */ new Map(); | ||
| this._maxListeners = 100; | ||
| this._logger = options?.logger; | ||
| this._maxListeners = 0; | ||
| this._eventLogger = options?.eventLogger; | ||
| if (options?.throwOnEmitError !== void 0) { | ||
@@ -22,14 +22,14 @@ this._throwOnEmitError = options.throwOnEmitError; | ||
| /** | ||
| * Gets the logger | ||
| * Gets the event logger | ||
| * @returns {Logger} | ||
| */ | ||
| get logger() { | ||
| return this._logger; | ||
| get eventLogger() { | ||
| return this._eventLogger; | ||
| } | ||
| /** | ||
| * Sets the logger | ||
| * @param {Logger} logger | ||
| * Sets the event logger | ||
| * @param {Logger} eventLogger | ||
| */ | ||
| set logger(logger) { | ||
| this._logger = logger; | ||
| set eventLogger(eventLogger) { | ||
| this._eventLogger = eventLogger; | ||
| } | ||
@@ -163,3 +163,3 @@ /** | ||
| if (listeners) { | ||
| if (listeners.length >= this._maxListeners) { | ||
| if (this._maxListeners > 0 && listeners.length >= this._maxListeners) { | ||
| console.warn( | ||
@@ -215,2 +215,3 @@ `MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.` | ||
| } | ||
| this.sendToEventLogger(event, arguments_); | ||
| if (event === this._errorEvent) { | ||
@@ -226,3 +227,2 @@ const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`); | ||
| } | ||
| this.sendLog(event, arguments_); | ||
| return result; | ||
@@ -257,8 +257,3 @@ } | ||
| setMaxListeners(n) { | ||
| this._maxListeners = n; | ||
| for (const listeners of this._eventListeners.values()) { | ||
| if (listeners.length > n) { | ||
| listeners.splice(n); | ||
| } | ||
| } | ||
| this._maxListeners = n < 0 ? 0 : n; | ||
| } | ||
@@ -281,4 +276,4 @@ /** | ||
| */ | ||
| sendLog(eventName, data) { | ||
| if (!this._logger) { | ||
| sendToEventLogger(eventName, data) { | ||
| if (!this._eventLogger) { | ||
| return; | ||
@@ -300,23 +295,23 @@ } | ||
| case "error": { | ||
| this._logger.error?.(message, { event: eventName, data }); | ||
| this._eventLogger.error?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "warn": { | ||
| this._logger.warn?.(message, { event: eventName, data }); | ||
| this._eventLogger.warn?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "trace": { | ||
| this._logger.trace?.(message, { event: eventName, data }); | ||
| this._eventLogger.trace?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "debug": { | ||
| this._logger.debug?.(message, { event: eventName, data }); | ||
| this._eventLogger.debug?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| case "fatal": { | ||
| this._logger.fatal?.(message, { event: eventName, data }); | ||
| this._eventLogger.fatal?.(message, { event: eventName, data }); | ||
| break; | ||
| } | ||
| default: { | ||
| this._logger.info?.(message, { event: eventName, data }); | ||
| this._eventLogger.info?.(message, { event: eventName, data }); | ||
| break; | ||
@@ -328,2 +323,70 @@ } | ||
| // src/hooks/hook.ts | ||
| var Hook = class { | ||
| id; | ||
| event; | ||
| handler; | ||
| /** | ||
| * Creates a new Hook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {HookFn} handler - The handler function for the hook | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event, handler, id) { | ||
| this.id = id; | ||
| this.event = event; | ||
| this.handler = handler; | ||
| } | ||
| }; | ||
| // src/hooks/waterfall-hook.ts | ||
| var WaterfallHook = class { | ||
| id; | ||
| event; | ||
| handler; | ||
| hooks; | ||
| _finalHandler; | ||
| /** | ||
| * Creates a new WaterfallHook instance | ||
| * @param {string} event - The event name for the hook | ||
| * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result | ||
| * @param {string} [id] - Optional unique identifier for the hook | ||
| */ | ||
| constructor(event, finalHandler, id) { | ||
| this.id = id; | ||
| this.event = event; | ||
| this.hooks = []; | ||
| this._finalHandler = finalHandler; | ||
| this.handler = async (...arguments_) => { | ||
| const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_; | ||
| const results = []; | ||
| for (const hook of this.hooks) { | ||
| const result = await hook({ initialArgs, results: [...results] }); | ||
| results.push({ hook, result }); | ||
| } | ||
| await this._finalHandler({ initialArgs, results: [...results] }); | ||
| }; | ||
| } | ||
| /** | ||
| * Adds a hook function to the end of the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to add | ||
| */ | ||
| addHook(hook) { | ||
| this.hooks.push(hook); | ||
| } | ||
| /** | ||
| * Removes a specific hook function from the waterfall chain | ||
| * @param {WaterfallHookFn} hook - The hook function to remove | ||
| * @returns {boolean} true if the hook was found and removed | ||
| */ | ||
| removeHook(hook) { | ||
| const index = this.hooks.indexOf(hook); | ||
| if (index !== -1) { | ||
| this.hooks.splice(index, 1); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| }; | ||
| // src/index.ts | ||
@@ -336,5 +399,6 @@ var Hookified = class extends Eventified { | ||
| _allowDeprecated = true; | ||
| _useHookClone = true; | ||
| constructor(options) { | ||
| super({ | ||
| logger: options?.logger, | ||
| eventLogger: options?.eventLogger, | ||
| throwOnEmitError: options?.throwOnEmitError, | ||
@@ -347,4 +411,2 @@ throwOnEmptyListeners: options?.throwOnEmptyListeners | ||
| this._throwOnHookError = options.throwOnHookError; | ||
| } else if (options?.throwHookErrors !== void 0) { | ||
| this._throwOnHookError = options.throwHookErrors; | ||
| } | ||
@@ -357,6 +419,9 @@ if (options?.enforceBeforeAfter !== void 0) { | ||
| } | ||
| if (options?.useHookClone !== void 0) { | ||
| this._useHookClone = options.useHookClone; | ||
| } | ||
| } | ||
| /** | ||
| * Gets all hooks | ||
| * @returns {Map<string, Hook[]>} | ||
| * @returns {Map<string, IHook[]>} | ||
| */ | ||
@@ -369,19 +434,3 @@ get hooks() { | ||
| * @returns {boolean} | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| get throwHookErrors() { | ||
| return this._throwOnHookError; | ||
| } | ||
| /** | ||
| * 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 | ||
| * @deprecated - this will be deprecated in version 2. Please use throwOnHookError. | ||
| */ | ||
| set throwHookErrors(value) { | ||
| this._throwOnHookError = value; | ||
| } | ||
| /** | ||
| * 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 throwOnHookError() { | ||
@@ -441,153 +490,149 @@ return this._throwOnHookError; | ||
| /** | ||
| * 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' | ||
| * Gets whether hook objects are cloned before storing. Default is true. | ||
| * @returns {boolean} | ||
| */ | ||
| 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` | ||
| ); | ||
| } | ||
| } | ||
| get useHookClone() { | ||
| return this._useHookClone; | ||
| } | ||
| /** | ||
| * 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 | ||
| * Sets whether hook objects are cloned before storing. Default is true. | ||
| * When false, the original IHook reference is stored directly. | ||
| * @param {boolean} value | ||
| */ | ||
| 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 }); | ||
| return this._allowDeprecated; | ||
| } | ||
| return true; | ||
| set useHookClone(value) { | ||
| this._useHookClone = value; | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler - this can be async or sync | ||
| * @returns {void} | ||
| * Adds a handler function for a specific event. | ||
| * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead. | ||
| * To register multiple hooks at once, use {@link onHooks}. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| onHook(event, handler) { | ||
| this.onHookEntry({ event, handler }); | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {HookEntry} hookEntry | ||
| * @returns {void} | ||
| */ | ||
| onHookEntry(hookEntry) { | ||
| this.validateHookName(hookEntry.event); | ||
| if (!this.checkDeprecatedHook(hookEntry.event)) { | ||
| return; | ||
| onHook(hook, options) { | ||
| this.validateHookName(hook.event); | ||
| if (!this.checkDeprecatedHook(hook.event)) { | ||
| return void 0; | ||
| } | ||
| const eventHandlers = this._hooks.get(hookEntry.event); | ||
| const shouldClone = options?.useHookClone ?? this._useHookClone; | ||
| const entry = shouldClone ? { id: hook.id, event: hook.event, handler: hook.handler } : hook; | ||
| entry.id = entry.id ?? crypto.randomUUID(); | ||
| const eventHandlers = this._hooks.get(hook.event); | ||
| if (eventHandlers) { | ||
| eventHandlers.push(hookEntry.handler); | ||
| const existingIndex = eventHandlers.findIndex((h) => h.id === entry.id); | ||
| if (existingIndex !== -1) { | ||
| eventHandlers[existingIndex] = entry; | ||
| } else { | ||
| const position = options?.position ?? "Bottom"; | ||
| if (position === "Top") { | ||
| eventHandlers.unshift(entry); | ||
| } else if (position === "Bottom") { | ||
| eventHandlers.push(entry); | ||
| } else { | ||
| const index = Math.max(0, Math.min(position, eventHandlers.length)); | ||
| eventHandlers.splice(index, 0, entry); | ||
| } | ||
| } | ||
| } else { | ||
| this._hooks.set(hookEntry.event, [hookEntry.handler]); | ||
| this._hooks.set(hook.event, [entry]); | ||
| } | ||
| return entry; | ||
| } | ||
| /** | ||
| * 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 | ||
| * @param {string} event - the event name | ||
| * @param {HookFn} handler - the handler function | ||
| * @returns {void} | ||
| */ | ||
| addHook(event, handler) { | ||
| this.onHookEntry({ event, handler }); | ||
| this.onHook({ event, handler }); | ||
| } | ||
| /** | ||
| * Adds a handler function for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * Adds handler functions for specific events | ||
| * @param {Array<IHook>} hooks | ||
| * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position) | ||
| * @returns {void} | ||
| */ | ||
| onHooks(hooks) { | ||
| onHooks(hooks, options) { | ||
| for (const hook of hooks) { | ||
| this.onHook(hook.event, hook.handler); | ||
| this.onHook(hook, options); | ||
| } | ||
| } | ||
| /** | ||
| * 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} | ||
| * Adds a handler function for a specific event that runs before all other handlers. | ||
| * Equivalent to calling `onHook(hook, { position: "Top" })`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| 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]); | ||
| } | ||
| prependHook(hook, options) { | ||
| return this.onHook(hook, { ...options, position: "Top" }); | ||
| } | ||
| /** | ||
| * Adds a handler that only executes once for a specific event before all other handlers | ||
| * @param event | ||
| * @param handler | ||
| * Adds a handler that only executes once for a specific event before all other handlers. | ||
| * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`. | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override) | ||
| * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation | ||
| */ | ||
| prependOnceHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return; | ||
| } | ||
| const hook = async (...arguments_) => { | ||
| this.removeHook(event, hook); | ||
| return handler(...arguments_); | ||
| prependOnceHook(hook, options) { | ||
| const wrappedHandler = async (...arguments_) => { | ||
| this.removeHook({ event: hook.event, handler: wrappedHandler }); | ||
| return hook.handler(...arguments_); | ||
| }; | ||
| this.prependHook(event, hook); | ||
| return this.onHook( | ||
| { id: hook.id, event: hook.event, handler: wrappedHandler }, | ||
| { ...options, position: "Top" } | ||
| ); | ||
| } | ||
| /** | ||
| * Adds a handler that only executes once for a specific event | ||
| * @param event | ||
| * @param handler | ||
| * @param {IHook} hook - the hook containing event name and handler | ||
| */ | ||
| onceHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| onceHook(hook) { | ||
| this.validateHookName(hook.event); | ||
| if (!this.checkDeprecatedHook(hook.event)) { | ||
| return; | ||
| } | ||
| const hook = async (...arguments_) => { | ||
| this.removeHook(event, hook); | ||
| return handler(...arguments_); | ||
| const wrappedHandler = async (...arguments_) => { | ||
| this.removeHook({ event: hook.event, handler: wrappedHandler }); | ||
| return hook.handler(...arguments_); | ||
| }; | ||
| this.onHook(event, hook); | ||
| this.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler }); | ||
| } | ||
| /** | ||
| * Removes a handler function for a specific event | ||
| * @param {string} event | ||
| * @param {Hook} handler | ||
| * @returns {void} | ||
| * @param {IHook} hook - the hook containing event name and handler to remove | ||
| * @returns {IHook | undefined} the removed hook, or undefined if not found | ||
| */ | ||
| removeHook(event, handler) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return; | ||
| } | ||
| const eventHandlers = this._hooks.get(event); | ||
| removeHook(hook) { | ||
| this.validateHookName(hook.event); | ||
| const eventHandlers = this._hooks.get(hook.event); | ||
| if (eventHandlers) { | ||
| const index = eventHandlers.indexOf(handler); | ||
| const index = eventHandlers.findIndex((h) => h.handler === hook.handler); | ||
| if (index !== -1) { | ||
| eventHandlers.splice(index, 1); | ||
| if (eventHandlers.length === 0) { | ||
| this._hooks.delete(hook.event); | ||
| } | ||
| return { event: hook.event, handler: hook.handler }; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes all handlers for a specific event | ||
| * @param {Array<HookEntry>} hooks | ||
| * @returns {void} | ||
| * Removes multiple hook handlers | ||
| * @param {Array<IHook>} hooks | ||
| * @returns {IHook[]} the hooks that were successfully removed | ||
| */ | ||
| removeHooks(hooks) { | ||
| const removed = []; | ||
| for (const hook of hooks) { | ||
| this.removeHook(hook.event, hook.handler); | ||
| const result = this.removeHook(hook); | ||
| if (result) { | ||
| removed.push(result); | ||
| } | ||
| } | ||
| return removed; | ||
| } | ||
@@ -607,5 +652,5 @@ /** | ||
| if (eventHandlers) { | ||
| for (const handler of eventHandlers) { | ||
| for (const hook of [...eventHandlers]) { | ||
| try { | ||
| await handler(...arguments_); | ||
| await hook.handler(...arguments_); | ||
| } catch (error) { | ||
@@ -638,8 +683,8 @@ const message = `${event}: ${error.message}`; | ||
| if (eventHandlers) { | ||
| for (const handler of eventHandlers) { | ||
| if (handler.constructor.name === "AsyncFunction") { | ||
| for (const hook of [...eventHandlers]) { | ||
| if (hook.handler.constructor.name === "AsyncFunction") { | ||
| continue; | ||
| } | ||
| try { | ||
| handler(...arguments_); | ||
| hook.handler(...arguments_); | ||
| } catch (error) { | ||
@@ -684,12 +729,51 @@ const message = `${event}: ${error.message}`; | ||
| * @param {string} event | ||
| * @returns {Hook[]} | ||
| * @returns {IHook[]} | ||
| */ | ||
| getHooks(event) { | ||
| this.validateHookName(event); | ||
| if (!this.checkDeprecatedHook(event)) { | ||
| return void 0; | ||
| } | ||
| return this._hooks.get(event); | ||
| } | ||
| /** | ||
| * Gets a specific hook by id, searching across all events | ||
| * @param {string} id - the hook id | ||
| * @returns {IHook | undefined} the hook if found, or undefined | ||
| */ | ||
| getHook(id) { | ||
| for (const eventHandlers of this._hooks.values()) { | ||
| const found = eventHandlers.find((h) => h.id === id); | ||
| if (found) { | ||
| return found; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes one or more hooks by id, searching across all events | ||
| * @param {string | string[]} id - the hook id or array of hook ids to remove | ||
| * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found | ||
| */ | ||
| removeHookById(id) { | ||
| if (Array.isArray(id)) { | ||
| const removed = []; | ||
| for (const singleId of id) { | ||
| const result = this.removeHookById(singleId); | ||
| if (result && !Array.isArray(result)) { | ||
| removed.push(result); | ||
| } | ||
| } | ||
| return removed; | ||
| } | ||
| for (const [event, eventHandlers] of this._hooks.entries()) { | ||
| const index = eventHandlers.findIndex((h) => h.id === id); | ||
| if (index !== -1) { | ||
| const [removed] = eventHandlers.splice(index, 1); | ||
| if (eventHandlers.length === 0) { | ||
| this._hooks.delete(event); | ||
| } | ||
| return removed; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| /** | ||
| * Removes all hooks | ||
@@ -701,7 +785,53 @@ * @returns {void} | ||
| } | ||
| /** | ||
| * Removes all hooks for a specific event and returns the removed hooks. | ||
| * @param {string} event - The event name to remove hooks for. | ||
| * @returns {IHook[]} the hooks that were removed | ||
| */ | ||
| removeEventHooks(event) { | ||
| this.validateHookName(event); | ||
| const eventHandlers = this._hooks.get(event); | ||
| if (eventHandlers) { | ||
| const removed = [...eventHandlers]; | ||
| this._hooks.delete(event); | ||
| return removed; | ||
| } | ||
| return []; | ||
| } | ||
| /** | ||
| * 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 }); | ||
| return this._allowDeprecated; | ||
| } | ||
| return true; | ||
| } | ||
| }; | ||
| export { | ||
| Eventified, | ||
| Hookified | ||
| Hook, | ||
| Hookified, | ||
| WaterfallHook | ||
| }; | ||
| /* v8 ignore next -- @preserve */ |
+6
-6
| { | ||
| "name": "hookified", | ||
| "version": "1.15.1", | ||
| "version": "2.0.0", | ||
| "description": "Event Emitting and Middleware Hooks", | ||
@@ -72,12 +72,12 @@ "type": "module", | ||
| "devDependencies": { | ||
| "@biomejs/biome": "^2.3.13", | ||
| "@biomejs/biome": "^2.4.4", | ||
| "@monstermann/tinybench-pretty-printer": "^0.3.0", | ||
| "@types/node": "^25.0.3", | ||
| "@types/node": "^25.3.0", | ||
| "@vitest/coverage-v8": "^4.0.18", | ||
| "docula": "^0.40.0", | ||
| "docula": "^0.41.1", | ||
| "emittery": "^1.2.0", | ||
| "eventemitter3": "^5.0.4", | ||
| "hookable": "^6.0.1", | ||
| "pino": "^10.3.0", | ||
| "rimraf": "^6.1.2", | ||
| "pino": "^10.3.1", | ||
| "rimraf": "^6.1.3", | ||
| "tinybench": "^6.0.0", | ||
@@ -84,0 +84,0 @@ "tsup": "^8.5.1", |
+956
-705
@@ -7,3 +7,3 @@  | ||
| [](https://github.com/jaredwray/hookified/blob/master/LICENSE) | ||
| [](https://codecov.io/gh/jaredwray/hookified) | ||
| [](https://codecov.io/gh/jaredwray/hookified) | ||
| [](https://npmjs.com/package/hookified) | ||
@@ -23,3 +23,4 @@ [](https://www.jsdelivr.com/package/npm/hookified) | ||
| - Control deprecated hook execution with `allowDeprecated` | ||
| - No package dependencies and only 200KB in size | ||
| - WaterfallHook for sequential data transformation pipelines | ||
| - No package dependencies and only 250KB in size | ||
| - Fast and Efficient with [Benchmarks](#benchmarks) | ||
@@ -31,26 +32,33 @@ - Maintained on a regular basis! | ||
| - [Usage](#usage) | ||
| - [Migrating from v1 to v2](#migrating-from-v1-to-v2) | ||
| - [Using it in the Browser](#using-it-in-the-browser) | ||
| - [Hooks](#hooks) | ||
| - [Standard Hook](#standard-hook) | ||
| - [Waterfall Hook](#waterfallhook) | ||
| - [API - Hooks](#api---hooks) | ||
| - [.throwOnHookError](#throwhookerror) | ||
| - [.logger](#logger) | ||
| - [.allowDeprecated](#allowdeprecated) | ||
| - [.deprecatedHooks](#deprecatedhooks) | ||
| - [.enforceBeforeAfter](#enforcebeforeafter) | ||
| - [.deprecatedHooks](#deprecatedhooks) | ||
| - [.allowDeprecated](#allowdeprecated) | ||
| - [.onHook(eventName, handler)](#onhookeventname-handler) | ||
| - [.onHookEntry(hookEntry)](#onhookentryhookentry) | ||
| - [.addHook(eventName, handler)](#addhookeventname-handler) | ||
| - [.onHooks(Array)](#onhooksarray) | ||
| - [.onceHook(eventName, handler)](#oncehookeventname-handler) | ||
| - [.prependHook(eventName, handler)](#prependhookeventname-handler) | ||
| - [.prependOnceHook(eventName, handler)](#prependoncehookeventname-handler) | ||
| - [.removeHook(eventName)](#removehookeventname) | ||
| - [.removeHooks(Array)](#removehooksarray) | ||
| - [.eventLogger](#eventlogger) | ||
| - [.hooks](#hooks-1) | ||
| - [.throwOnHookError](#throwOnHookError) | ||
| - [.useHookClone](#usehookclone) | ||
| - [.addHook(event, handler)](#addhookevent-handler) | ||
| - [.afterHook(eventName, ...args)](#afterhookeventname-args) | ||
| - [.beforeHook(eventName, ...args)](#beforehookeventname-args) | ||
| - [.callHook(eventName, ...args)](#callhookeventname-args) | ||
| - [.clearHooks()](#clearhooks) | ||
| - [.getHook(id)](#gethookid) | ||
| - [.getHooks(eventName)](#gethookseventname) | ||
| - [.hook(eventName, ...args)](#hookeventname-args) | ||
| - [.callHook(eventName, ...args)](#callhookeventname-args) | ||
| - [.beforeHook(eventName, ...args)](#beforehookeventname-args) | ||
| - [.afterHook(eventName, ...args)](#afterhookeventname-args) | ||
| - [.hookSync(eventName, ...args)](#hooksync-eventname-args) | ||
| - [.hooks](#hooks) | ||
| - [.getHooks(eventName)](#gethookseventname) | ||
| - [.clearHooks(eventName)](#clearhookeventname) | ||
| - [.onHook(hook, options?)](#onhookhook-options) | ||
| - [.onHooks(Array, options?)](#onhooksarray-options) | ||
| - [.onceHook(hook)](#oncehookhook) | ||
| - [.prependHook(hook, options?)](#prependhookhook-options) | ||
| - [.prependOnceHook(hook, options?)](#prependoncehookhook-options) | ||
| - [.removeEventHooks(eventName)](#removeeventhookseventname) | ||
| - [.removeHook(hook)](#removehookhook) | ||
| - [.removeHookById(id)](#removehookbyidid) | ||
| - [.removeHooks(Array)](#removehooksarray) | ||
| - [API - Events](#api---events) | ||
@@ -93,7 +101,7 @@ - [.throwOnEmitError](#throwonemitterror) | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); //using Emittery | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| //with hooks you can pass data in and if they are subscribed via onHook they can modify the data | ||
| async myMethodWithHooks() Promise<any> { | ||
| async myMethodWithHooks(): Promise<any> { | ||
| let data = { some: 'data' }; | ||
@@ -118,3 +126,3 @@ // do something | ||
| async myMethodWithHooks() Promise<any> { | ||
| async myMethodWithHooks(): Promise<any> { | ||
| let data = { some: 'data' }; | ||
@@ -142,7 +150,7 @@ let data2 = { some: 'data2' }; | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); //using Emittery | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| //with hooks you can pass data in and if they are subscribed via onHook they can modify the data | ||
| async myMethodWithHooks() Promise<any> { | ||
| async myMethodWithHooks(): Promise<any> { | ||
| let data = { some: 'data' }; | ||
@@ -169,7 +177,7 @@ // do something | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); //using Emittery | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| //with hooks you can pass data in and if they are subscribed via onHook they can modify the data | ||
| async myMethodWithHooks() Promise<any> { | ||
| async myMethodWithHooks(): Promise<any> { | ||
| let data = { some: 'data' }; | ||
@@ -185,15 +193,25 @@ // do something | ||
| # API - Hooks | ||
| # Hooks | ||
| ## .throwOnHookError | ||
| ## Standard Hook | ||
| If set to true, errors thrown in hooks will be thrown. If set to false, errors will be only emitted. | ||
| The `Hook` class provides a convenient way to create hook entries. It implements the `IHook` interface. | ||
| The `IHook` interface has the following properties: | ||
| | Property | Type | Required | Description | | ||
| |----------|------|----------|-------------| | ||
| | `id` | `string` | No | Unique identifier for the hook. Auto-generated via `crypto.randomUUID()` if not provided. | | ||
| | `event` | `string` | Yes | The event name for the hook. | | ||
| | `handler` | `HookFn` | Yes | The handler function for the hook. | | ||
| When a hook is registered, it is assigned an `id` (auto-generated if not provided). The `id` can be used to look up or remove hooks via `getHook` and `removeHookById`. If you register a hook with the same `id` on the same event, it will replace the existing hook in-place (preserving its position). | ||
| **Using the `Hook` class:** | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| import { Hook, Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super({ throwOnHookError: true }); | ||
| } | ||
| constructor() { super(); } | ||
| } | ||
@@ -203,122 +221,94 @@ | ||
| console.log(myClass.throwOnHookError); // true. because it is set in super | ||
| // Without id (auto-generated) | ||
| const hook = new Hook('before:save', async (data) => { | ||
| data.validated = true; | ||
| }); | ||
| try { | ||
| myClass.onHook('error-event', async () => { | ||
| throw new Error('error'); | ||
| }); | ||
| // With id | ||
| const hook2 = new Hook('after:save', async (data) => { | ||
| console.log('saved'); | ||
| }, 'my-after-save-hook'); | ||
| await myClass.hook('error-event'); | ||
| } catch (error) { | ||
| console.log(error.message); // error | ||
| } | ||
| // Register with onHook | ||
| myClass.onHook(hook); | ||
| myClass.throwOnHookError = false; | ||
| console.log(myClass.throwOnHookError); // false | ||
| // Or register multiple hooks with onHooks | ||
| const hooks = [ | ||
| new Hook('before:save', async (data) => { data.validated = true; }), | ||
| new Hook('after:save', async (data) => { console.log('saved'); }), | ||
| ]; | ||
| myClass.onHooks(hooks); | ||
| // Remove hooks | ||
| myClass.removeHooks(hooks); | ||
| ``` | ||
| ## .logger | ||
| If set, errors thrown in hooks will be logged to the logger. If not set, errors will be only emitted. | ||
| **Using plain TypeScript with the `IHook` interface:** | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| import pino from 'pino'; | ||
| ```typescript | ||
| import { Hookified, type IHook } from 'hookified'; | ||
| const logger = pino(); // create a logger instance that is compatible with Logger type | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super({ logger }); | ||
| } | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| return data; | ||
| } | ||
| constructor() { super(); } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.onHook('before:myMethod2', async () => { | ||
| throw new Error('error'); | ||
| }); | ||
| // when you call before:myMethod2 it will log the error to the logger | ||
| await myClass.hook('before:myMethod2'); | ||
| const hook: IHook = { | ||
| id: 'my-validation-hook', // optional — auto-generated if omitted | ||
| event: 'before:save', | ||
| handler: async (data) => { | ||
| data.validated = true; | ||
| }, | ||
| }; | ||
| const stored = myClass.onHook(hook); | ||
| console.log(stored?.id); // 'my-validation-hook' | ||
| // Later, remove by id | ||
| myClass.removeHookById('my-validation-hook'); | ||
| ``` | ||
| ## .enforceBeforeAfter | ||
| ## Waterfall Hook | ||
| If set to true, enforces that all hook names must start with 'before' or 'after'. This is useful for maintaining consistent hook naming conventions in your application. Default is false. | ||
| The `WaterfallHook` class chains multiple hook functions sequentially in a waterfall pipeline. Each hook receives a context containing the original arguments and the accumulated results from all previous hooks. It implements the `IHook` interface, so it integrates directly with `Hookified.onHook()`. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| The `WaterfallHookContext` has the following properties: | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super({ enforceBeforeAfter: true }); | ||
| } | ||
| } | ||
| | Property | Type | Description | | ||
| |----------|------|-------------| | ||
| | `initialArgs` | `any` | The original arguments passed to the waterfall execution. | | ||
| | `results` | `WaterfallHookResult[]` | Array of `{ hook, result }` entries from previous hooks. Empty for the first hook. | | ||
| const myClass = new MyClass(); | ||
| **Basic usage:** | ||
| console.log(myClass.enforceBeforeAfter); // true | ||
| ```javascript | ||
| import { WaterfallHook } from 'hookified'; | ||
| // These will work fine | ||
| myClass.onHook('beforeSave', async () => { | ||
| console.log('Before save hook'); | ||
| const wh = new WaterfallHook('process', ({ results, initialArgs }) => { | ||
| // Final handler receives all accumulated results | ||
| const lastResult = results[results.length - 1].result; | ||
| console.log('Final:', lastResult); | ||
| }); | ||
| myClass.onHook('afterSave', async () => { | ||
| console.log('After save hook'); | ||
| // Add transformation hooks to the pipeline | ||
| wh.addHook(({ initialArgs }) => { | ||
| return initialArgs + 1; // 5 -> 6 | ||
| }); | ||
| myClass.onHook('before:validation', async () => { | ||
| console.log('Before validation hook'); | ||
| wh.addHook(({ results }) => { | ||
| return results[results.length - 1].result * 2; // 6 -> 12 | ||
| }); | ||
| // This will throw an error | ||
| try { | ||
| myClass.onHook('customEvent', async () => { | ||
| console.log('This will not work'); | ||
| }); | ||
| } catch (error) { | ||
| console.log(error.message); // Hook event "customEvent" must start with "before" or "after" when enforceBeforeAfter is enabled | ||
| } | ||
| // You can also change it dynamically | ||
| myClass.enforceBeforeAfter = false; | ||
| myClass.onHook('customEvent', async () => { | ||
| console.log('This will work now'); | ||
| }); | ||
| // Execute the waterfall by calling handler directly | ||
| await wh.handler(5); // Final: 12 | ||
| ``` | ||
| The validation applies to all hook-related methods: | ||
| - `onHook()`, `addHook()`, `onHookEntry()`, `onHooks()` | ||
| - `prependHook()`, `onceHook()`, `prependOnceHook()` | ||
| - `hook()`, `callHook()` | ||
| - `getHooks()`, `removeHook()`, `removeHooks()` | ||
| **Integrating with Hookified via `onHook()`:** | ||
| Note: The `beforeHook()` and `afterHook()` helper methods automatically generate proper hook names and work regardless of the `enforceBeforeAfter` setting. | ||
| ## .deprecatedHooks | ||
| A Map of deprecated hook names to deprecation messages. When a deprecated hook is used, a warning will be emitted via the 'warn' event and logged to the logger (if available). Default is an empty Map. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| import { Hookified, WaterfallHook } from 'hookified'; | ||
| // Define deprecated hooks with custom messages | ||
| const deprecatedHooks = new Map([ | ||
| ['oldHook', 'Use newHook instead'], | ||
| ['legacyMethod', 'This hook will be removed in v2.0'], | ||
| ['deprecatedFeature', ''] // Empty message - will just say "deprecated" | ||
| ]); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super({ deprecatedHooks }); | ||
| } | ||
| constructor() { super(); } | ||
| } | ||
@@ -328,46 +318,48 @@ | ||
| console.log(myClass.deprecatedHooks); // Map with deprecated hooks | ||
| // Listen for deprecation warnings | ||
| myClass.on('warn', (event) => { | ||
| console.log(`Deprecation warning: ${event.message}`); | ||
| // event.hook contains the hook name | ||
| // event.message contains the full warning message | ||
| const wh = new WaterfallHook('save', ({ results }) => { | ||
| const data = results[results.length - 1].result; | ||
| console.log('Saved:', data); | ||
| }); | ||
| // Using a deprecated hook will emit warnings | ||
| myClass.onHook('oldHook', () => { | ||
| console.log('This hook is deprecated'); | ||
| wh.addHook(({ initialArgs }) => { | ||
| return { ...initialArgs, validated: true }; | ||
| }); | ||
| // Output: Hook "oldHook" is deprecated: Use newHook instead | ||
| // Using a deprecated hook with empty message | ||
| myClass.onHook('deprecatedFeature', () => { | ||
| console.log('This hook is deprecated'); | ||
| wh.addHook(({ results }) => { | ||
| return { ...results[results.length - 1].result, timestamp: Date.now() }; | ||
| }); | ||
| // Output: Hook "deprecatedFeature" is deprecated | ||
| // You can also set deprecated hooks dynamically | ||
| myClass.deprecatedHooks.set('anotherOldHook', 'Please migrate to the new API'); | ||
| // Register with Hookified — works because WaterfallHook implements IHook | ||
| myClass.onHook(wh); | ||
| // Works with logger if provided | ||
| import pino from 'pino'; | ||
| const logger = pino(); | ||
| // When hook() fires, the full waterfall pipeline executes | ||
| await myClass.hook('save', { name: 'test' }); | ||
| // Saved: { name: 'test', validated: true, timestamp: ... } | ||
| ``` | ||
| const myClassWithLogger = new Hookified({ | ||
| deprecatedHooks, | ||
| logger | ||
| }); | ||
| **Managing hooks:** | ||
| // Deprecation warnings will be logged to logger.warn | ||
| ```javascript | ||
| const wh = new WaterfallHook('process', ({ results }) => results); | ||
| const myHook = ({ initialArgs }) => initialArgs + 1; | ||
| wh.addHook(myHook); | ||
| // Remove a hook by reference | ||
| wh.removeHook(myHook); // returns true | ||
| // Access the hooks array | ||
| console.log(wh.hooks.length); // 0 | ||
| ``` | ||
| The deprecation warning system applies to all hook-related methods: | ||
| - Registration: `onHook()`, `addHook()`, `onHookEntry()`, `onHooks()`, `prependHook()`, `onceHook()`, `prependOnceHook()` | ||
| - Execution: `hook()`, `callHook()` | ||
| - Management: `getHooks()`, `removeHook()`, `removeHooks()` | ||
| # API - Hooks | ||
| Deprecation warnings are emitted in two ways: | ||
| 1. **Event**: A 'warn' event is emitted with `{ hook: string, message: string }` | ||
| 2. **Logger**: Logged to `logger.warn()` if a logger is configured and has a `warn` method | ||
| > All examples below assume the following setup unless otherwise noted: | ||
| > ```javascript | ||
| > import { Hookified } from 'hookified'; | ||
| > class MyClass extends Hookified { | ||
| > constructor(options) { super(options); } | ||
| > } | ||
| > const myClass = new MyClass(); | ||
| > ``` | ||
@@ -401,5 +393,5 @@ ## .allowDeprecated | ||
| // Try to register a deprecated hook - will emit warning but not register | ||
| myClass.onHook('oldHook', () => { | ||
| myClass.onHook({ event: 'oldHook', handler: () => { | ||
| console.log('This will never execute'); | ||
| }); | ||
| }}); | ||
| // Output: Warning: Hook "oldHook" is deprecated: Use newHook instead | ||
@@ -416,5 +408,5 @@ | ||
| // Non-deprecated hooks work normally | ||
| myClass.onHook('validHook', () => { | ||
| myClass.onHook({ event: 'validHook', handler: () => { | ||
| console.log('This works fine'); | ||
| }); | ||
| }}); | ||
@@ -427,5 +419,5 @@ console.log(myClass.getHooks('validHook')); // [handler function] | ||
| // Now deprecated hooks can be registered and executed | ||
| myClass.onHook('oldHook', () => { | ||
| myClass.onHook({ event: 'oldHook', handler: () => { | ||
| console.log('Now this works'); | ||
| }); | ||
| }}); | ||
@@ -437,4 +429,4 @@ console.log(myClass.getHooks('oldHook')); // [handler function] | ||
| - **Registration**: All hook registration methods (`onHook`, `addHook`, `prependHook`, etc.) will emit warnings but skip registration | ||
| - **Execution**: Hook execution methods (`hook`, `callHook`) will emit warnings but skip execution | ||
| - **Management**: Hook management methods (`getHooks`, `removeHook`) will emit warnings and return undefined/skip operations | ||
| - **Execution**: Hook execution methods (`hook`, `callHook`) will emit warnings but skip execution | ||
| - **Removal/Reading**: `removeHook`, `removeHooks`, and `getHooks` always work regardless of deprecation status | ||
| - **Warnings**: Deprecation warnings are always emitted regardless of `allowDeprecated` setting | ||
@@ -448,5 +440,5 @@ | ||
| ## .onHook(eventName, handler) | ||
| ## .deprecatedHooks | ||
| Subscribe to a hook event. | ||
| A Map of deprecated hook names to deprecation messages. When a deprecated hook is used, a warning will be emitted via the 'warn' event and logged to the logger (if available). Default is an empty Map. | ||
@@ -456,60 +448,67 @@ ```javascript | ||
| // Define deprecated hooks with custom messages | ||
| const deprecatedHooks = new Map([ | ||
| ['oldHook', 'Use newHook instead'], | ||
| ['legacyMethod', 'This hook will be removed in v2.0'], | ||
| ['deprecatedFeature', ''] // Empty message - will just say "deprecated" | ||
| ]); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| super({ deprecatedHooks }); | ||
| } | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| return data; | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.onHook('before:myMethod2', async (data) => { | ||
| data.some = 'new data'; | ||
| }); | ||
| ``` | ||
| ## .onHookEntry(hookEntry) | ||
| console.log(myClass.deprecatedHooks); // Map with deprecated hooks | ||
| This allows you to create a hook with the `HookEntry` type which includes the event and handler. This is useful for creating hooks with a single object. | ||
| // Listen for deprecation warnings | ||
| myClass.on('warn', (event) => { | ||
| console.log(`Deprecation warning: ${event.message}`); | ||
| // event.hook contains the hook name | ||
| // event.message contains the full warning message | ||
| }); | ||
| ```javascript | ||
| import { Hookified, HookEntry } from 'hookified'; | ||
| // Using a deprecated hook will emit warnings | ||
| myClass.onHook({ event: 'oldHook', handler: () => { | ||
| console.log('This hook is deprecated'); | ||
| }}); | ||
| // Output: Hook "oldHook" is deprecated: Use newHook instead | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| // Using a deprecated hook with empty message | ||
| myClass.onHook({ event: 'deprecatedFeature', handler: () => { | ||
| console.log('This hook is deprecated'); | ||
| }}); | ||
| // Output: Hook "deprecatedFeature" is deprecated | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| // You can also set deprecated hooks dynamically | ||
| myClass.deprecatedHooks.set('anotherOldHook', 'Please migrate to the new API'); | ||
| return data; | ||
| } | ||
| } | ||
| // Works with logger if provided | ||
| import pino from 'pino'; | ||
| const logger = pino(); | ||
| const myClass = new MyClass(); | ||
| myClass.onHookEntry({ | ||
| event: 'before:myMethod2', | ||
| handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }, | ||
| const myClassWithLogger = new Hookified({ | ||
| deprecatedHooks, | ||
| eventLogger: logger | ||
| }); | ||
| // Deprecation warnings will be logged to logger.warn | ||
| ``` | ||
| ## .addHook(eventName, handler) | ||
| The deprecation warning system applies to the following hook-related methods: | ||
| - Registration: `onHook()`, `addHook()`, `onHooks()`, `prependHook()`, `onceHook()`, `prependOnceHook()` | ||
| - Execution: `hook()`, `callHook()` | ||
| This is an alias for `.onHook(eventName, handler)` for backwards compatibility. | ||
| Note: `getHooks()`, `removeHook()`, and `removeHooks()` do not check for deprecated hooks and always operate normally. | ||
| ## .onHooks(Array) | ||
| Deprecation warnings are emitted in two ways: | ||
| 1. **Event**: A 'warn' event is emitted with `{ hook: string, message: string }` | ||
| 2. **Logger**: Logged to `eventLogger.warn()` if an `eventLogger` is configured and has a `warn` method | ||
| Subscribe to multiple hook events at once | ||
| ## .enforceBeforeAfter | ||
| If set to true, enforces that all hook names must start with 'before' or 'after'. This is useful for maintaining consistent hook naming conventions in your application. Default is false. | ||
| ```javascript | ||
@@ -520,427 +519,430 @@ import { Hookified } from 'hookified'; | ||
| constructor() { | ||
| super(); | ||
| super({ enforceBeforeAfter: true }); | ||
| } | ||
| } | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| await this.hook('before:myMethodWithHooks', data); | ||
| // do something here with the data | ||
| data.some = 'new data'; | ||
| const myClass = new MyClass(); | ||
| await this.hook('after:myMethodWithHooks', data); | ||
| console.log(myClass.enforceBeforeAfter); // true | ||
| return data; | ||
| } | ||
| // These will work fine | ||
| myClass.onHook({ event: 'beforeSave', handler: async () => { | ||
| console.log('Before save hook'); | ||
| }}); | ||
| myClass.onHook({ event: 'afterSave', handler: async () => { | ||
| console.log('After save hook'); | ||
| }}); | ||
| myClass.onHook({ event: 'before:validation', handler: async () => { | ||
| console.log('Before validation hook'); | ||
| }}); | ||
| // This will throw an error | ||
| try { | ||
| myClass.onHook({ event: 'customEvent', handler: async () => { | ||
| console.log('This will not work'); | ||
| }}); | ||
| } catch (error) { | ||
| console.log(error.message); // Hook event "customEvent" must start with "before" or "after" when enforceBeforeAfter is enabled | ||
| } | ||
| const myClass = new MyClass(); | ||
| const hooks = [ | ||
| { | ||
| event: 'before:myMethodWithHooks', | ||
| handler: async (data) => { | ||
| data.some = 'new data1'; | ||
| }, | ||
| }, | ||
| { | ||
| event: 'after:myMethodWithHooks', | ||
| handler: async (data) => { | ||
| data.some = 'new data2'; | ||
| }, | ||
| }, | ||
| ]; | ||
| // You can also change it dynamically | ||
| myClass.enforceBeforeAfter = false; | ||
| myClass.onHook({ event: 'customEvent', handler: async () => { | ||
| console.log('This will work now'); | ||
| }}); | ||
| ``` | ||
| ## .onceHook(eventName, handler) | ||
| The validation applies to all hook-related methods: | ||
| - `onHook()`, `addHook()`, `onHooks()` | ||
| - `prependHook()`, `onceHook()`, `prependOnceHook()` | ||
| - `hook()`, `callHook()` | ||
| - `getHooks()`, `removeHook()`, `removeHooks()` | ||
| Subscribe to a hook event once. | ||
| Note: The `beforeHook()` and `afterHook()` helper methods automatically generate proper hook names and work regardless of the `enforceBeforeAfter` setting. | ||
| ## .eventLogger | ||
| If set, errors thrown in hooks will be logged to the logger. If not set, errors will be only emitted. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| import pino from 'pino'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| const myClass = new MyClass({ eventLogger: pino() }); | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async () => { | ||
| throw new Error('error'); | ||
| }}); | ||
| return data; | ||
| } | ||
| } | ||
| // when you call before:myMethod2 it will log the error to the logger | ||
| await myClass.hook('before:myMethod2'); | ||
| ``` | ||
| const myClass = new MyClass(); | ||
| ## .hooks | ||
| myClass.onHookOnce('before:myMethod2', async (data) => { | ||
| Get all hooks. Returns a `Map<string, IHook[]>` where each key is an event name and the value is an array of `IHook` objects. | ||
| ```javascript | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }); | ||
| }}); | ||
| myClass.myMethodWithHooks(); | ||
| console.log(myClass.hooks.length); // 0 | ||
| console.log(myClass.hooks); // Map { 'before:myMethod2' => [{ event: 'before:myMethod2', handler: [Function] }] } | ||
| ``` | ||
| ## .prependHook(eventName, handler) | ||
| ## .throwOnHookError | ||
| Subscribe to a hook event before all other hooks. | ||
| If set to true, errors thrown in hooks will be thrown. If set to false, errors will be only emitted. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| const myClass = new MyClass({ throwOnHookError: true }); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| console.log(myClass.throwOnHookError); // true | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| try { | ||
| myClass.onHook({ event: 'error-event', handler: async () => { | ||
| throw new Error('error'); | ||
| }}); | ||
| return data; | ||
| } | ||
| await myClass.hook('error-event'); | ||
| } catch (error) { | ||
| console.log(error.message); // error | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.onHook('before:myMethod2', async (data) => { | ||
| data.some = 'new data'; | ||
| }); | ||
| myClass.preHook('before:myMethod2', async (data) => { | ||
| data.some = 'will run before new data'; | ||
| }); | ||
| myClass.throwOnHookError = false; | ||
| console.log(myClass.throwOnHookError); // false | ||
| ``` | ||
| ## .prependOnceHook(eventName, handler) | ||
| ## .useHookClone | ||
| Subscribe to a hook event before all other hooks. After it is used once it will be removed. | ||
| Controls whether hook objects are cloned before storing internally. Default is `true`. When `true`, a shallow copy of the `IHook` object is stored, preventing external mutation from affecting registered hooks. When `false`, the original reference is stored directly. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| const myClass = new MyClass({ useHookClone: false }); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| const hook = { event: 'before:save', handler: async (data) => {} }; | ||
| myClass.onHook(hook); | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| // With useHookClone: false, the stored hook is the same reference | ||
| const storedHooks = myClass.getHooks('before:save'); | ||
| console.log(storedHooks[0] === hook); // true | ||
| return data; | ||
| } | ||
| } | ||
| // You can dynamically change the setting | ||
| myClass.useHookClone = true; | ||
| ``` | ||
| const myClass = new MyClass(); | ||
| myClass.onHook('before:myMethod2', async (data) => { | ||
| ## .addHook(event, handler) | ||
| This is an alias for `.onHook()` that takes an event name and handler function directly. | ||
| ```javascript | ||
| myClass.addHook('before:myMethod2', async (data) => { | ||
| data.some = 'new data'; | ||
| }); | ||
| myClass.preHook('before:myMethod2', async (data) => { | ||
| data.some = 'will run before new data'; | ||
| }); | ||
| ``` | ||
| ## .removeHook(eventName) | ||
| ## .afterHook(eventName, ...args) | ||
| Unsubscribe from a hook event. | ||
| This is a helper function that will prepend a hook name with `after:`. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| // Inside your class method — the event name will be `after:myMethod2` | ||
| await this.afterHook('myMethod2', data); | ||
| ``` | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| ## .beforeHook(eventName, ...args) | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| This is a helper function that will prepend a hook name with `before:`. | ||
| return data; | ||
| } | ||
| } | ||
| ```javascript | ||
| // Inside your class method — the event name will be `before:myMethod2` | ||
| await this.beforeHook('myMethod2', data); | ||
| ``` | ||
| const myClass = new MyClass(); | ||
| const handler = async (data) => { | ||
| data.some = 'new data'; | ||
| }; | ||
| ## .callHook(eventName, ...args) | ||
| myClass.onHook('before:myMethod2', handler); | ||
| This is an alias for `.hook(eventName, ...args)` for backwards compatibility. | ||
| myClass.removeHook('before:myMethod2', handler); | ||
| ``` | ||
| ## .clearHooks() | ||
| ## .removeHooks(Array) | ||
| Unsubscribe from multiple hooks. | ||
| Clear all hooks across all events. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }}); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| myClass.clearHooks(); | ||
| ``` | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| await this.hook('before:myMethodWithHooks', data); | ||
| // do something | ||
| data.some = 'new data'; | ||
| await this.hook('after:myMethodWithHooks', data); | ||
| ## .getHook(id) | ||
| return data; | ||
| } | ||
| } | ||
| Get a specific hook by `id`, searching across all events. Returns the `IHook` if found, or `undefined`. | ||
| ```javascript | ||
| const myClass = new MyClass(); | ||
| const hooks = [ | ||
| { | ||
| event: 'before:myMethodWithHooks', | ||
| handler: async (data) => { | ||
| data.some = 'new data1'; | ||
| }, | ||
| }, | ||
| { | ||
| event: 'after:myMethodWithHooks', | ||
| handler: async (data) => { | ||
| data.some = 'new data2'; | ||
| }, | ||
| }, | ||
| ]; | ||
| myClass.onHooks(hooks); | ||
| myClass.onHook({ | ||
| id: 'my-hook', | ||
| event: 'before:save', | ||
| handler: async (data) => { data.validated = true; }, | ||
| }); | ||
| // remove all hooks | ||
| myClass.removeHook(hooks); | ||
| const hook = myClass.getHook('my-hook'); | ||
| console.log(hook?.id); // 'my-hook' | ||
| console.log(hook?.event); // 'before:save' | ||
| console.log(hook?.handler); // [Function] | ||
| ``` | ||
| ## .hook(eventName, ...args) | ||
| ## .getHooks(eventName) | ||
| Run a hook event. | ||
| Get all hooks for an event. Returns an `IHook[]` array, or `undefined` if no hooks are registered for the event. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }}); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| console.log(myClass.getHooks('before:myMethod2')); // [{ event: 'before:myMethod2', handler: [Function] }] | ||
| ``` | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| ## .hook(eventName, ...args) | ||
| return data; | ||
| } | ||
| } | ||
| Run a hook event. | ||
| ```javascript | ||
| // Inside your class method | ||
| await this.hook('before:myMethod2', data); | ||
| ``` | ||
| in this example we are passing multiple arguments to the hook: | ||
| You can pass multiple arguments to the hook: | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| // Inside your class method | ||
| await this.hook('before:myMethod2', data, data2); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| // The handler receives all arguments | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async (data, data2) => { | ||
| data.some = 'new data'; | ||
| data2.some = 'new data2'; | ||
| }}); | ||
| ``` | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| let data2 = { some: 'data2' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data, data2); | ||
| ## .hookSync(eventName, ...args) | ||
| return data; | ||
| } | ||
| } | ||
| Run a hook event synchronously. Async handlers (functions declared with `async` keyword) are silently skipped and only synchronous handlers are executed. | ||
| const myClass = new MyClass(); | ||
| > **Note:** The `.hook()` method is preferred as it executes both sync and async functions. Use `.hookSync()` only when you specifically need synchronous execution. | ||
| myClass.onHook('before:myMethod2', async (data, data2) => { | ||
| data.some = 'new data'; | ||
| data2.some = 'new data2'; | ||
| }); | ||
| ```javascript | ||
| // This sync handler will execute | ||
| myClass.onHook({ event: 'before:myMethod', handler: (data) => { | ||
| data.some = 'modified'; | ||
| }}); | ||
| await myClass.myMethodWithHooks(); | ||
| // This async handler will be silently skipped | ||
| myClass.onHook({ event: 'before:myMethod', handler: async (data) => { | ||
| data.some = 'will not run'; | ||
| }}); | ||
| // Inside your class method | ||
| this.hookSync('before:myMethod', data); // Only sync handler runs | ||
| ``` | ||
| ## .callHook(eventName, ...args) | ||
| ## .onHook(hook, options?) | ||
| This is an alias for `.hook(eventName, ...args)` for backwards compatibility. | ||
| Subscribe to a hook event. Takes an `IHook` object and an optional `OnHookOptions` object. Returns the stored `IHook` (with `id` assigned), or `undefined` if the hook was blocked by deprecation. The returned reference is the exact object stored internally, which is useful for later removal with `.removeHook()` or `.removeHookById()`. To register multiple hooks at once, use `.onHooks()`. | ||
| ## .beforeHook(eventName, ...args) | ||
| If the hook has an `id`, it will be used as-is. If not, a UUID is auto-generated via `crypto.randomUUID()`. If a hook with the same `id` already exists on the same event, it will be **replaced in-place** (preserving its position in the array). | ||
| This is a helper function that will prepend a hook name with `before:`. | ||
| **Options (`OnHookOptions`)**: | ||
| - `useHookClone` (boolean, optional) — Per-call override for the instance-level `useHookClone` setting. When `true`, the hook object is cloned before storing. When `false`, the original reference is stored directly. When omitted, falls back to the instance-level setting. | ||
| - `position` (`"Top"` | `"Bottom"` | `number`, optional) — Controls where the hook is inserted in the handlers array. `"Top"` inserts at the beginning, `"Bottom"` appends to the end (default). A number inserts at that index, clamped to the array bounds. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| // Single hook — returns the stored IHook with id | ||
| const stored = myClass.onHook({ | ||
| event: 'before:myMethod2', | ||
| handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }, | ||
| }); | ||
| console.log(stored.id); // auto-generated UUID | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| // With a custom id | ||
| const stored2 = myClass.onHook({ | ||
| id: 'my-validation', | ||
| event: 'before:save', | ||
| handler: async (data) => { data.validated = true; }, | ||
| }); | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // the event name will be `before:myMethod2` | ||
| await this.beforeHook('myMethod2', data); | ||
| // Replace hook by registering with the same id | ||
| myClass.onHook({ | ||
| id: 'my-validation', | ||
| event: 'before:save', | ||
| handler: async (data) => { data.validated = true; data.extra = true; }, | ||
| }); | ||
| // Only one hook with id 'my-validation' exists, at the same position | ||
| return data; | ||
| } | ||
| } | ||
| ``` | ||
| // Remove by id | ||
| myClass.removeHookById('my-validation'); | ||
| ## .afterHook(eventName, ...args) | ||
| // Use the returned reference to remove the hook later | ||
| myClass.removeHook(stored); | ||
| This is a helper function that will prepend a hook name with `after:`. | ||
| // Override useHookClone per-call — store original reference even though instance default is true | ||
| const hook = { event: 'before:save', handler: async (data) => {} }; | ||
| myClass.onHook(hook, { useHookClone: false }); | ||
| console.log(myClass.getHooks('before:save')[0] === hook); // true | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| // Insert at the top of the handlers array | ||
| myClass.onHook({ event: 'before:save', handler: async (data) => {} }, { position: 'Top' }); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // the event name will be `after:myMethod2` | ||
| await this.afterHook('myMethod2', data); | ||
| return data; | ||
| } | ||
| } | ||
| // Insert at a specific index | ||
| myClass.onHook({ event: 'before:save', handler: async (data) => {} }, { position: 1 }); | ||
| ``` | ||
| ## .hookSync(eventName, ...args) | ||
| ## .onHooks(Array, options?) | ||
| Run a hook event synchronously. Async handlers (functions declared with `async` keyword) are silently skipped and only synchronous handlers are executed. | ||
| Subscribe to multiple hook events at once. Takes an array of `IHook` objects and an optional `OnHookOptions` object that is applied to each hook. | ||
| > **Note:** The `.hook()` method is preferred as it executes both sync and async functions. Use `.hookSync()` only when you specifically need synchronous execution. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| const hooks = [ | ||
| { | ||
| event: 'before:myMethodWithHooks', | ||
| handler: async (data) => { | ||
| data.some = 'new data1'; | ||
| }, | ||
| }, | ||
| { | ||
| event: 'after:myMethodWithHooks', | ||
| handler: async (data) => { | ||
| data.some = 'new data2'; | ||
| }, | ||
| }, | ||
| ]; | ||
| myClass.onHooks(hooks); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| // With options — insert all hooks at the top | ||
| myClass.onHooks(hooks, { position: 'Top' }); | ||
| myMethodWithSyncHooks() { | ||
| let data = { some: 'data' }; | ||
| // Only synchronous handlers will execute | ||
| this.hookSync('before:myMethod', data); | ||
| // With options — skip cloning for all hooks in this batch | ||
| myClass.onHooks(hooks, { useHookClone: false }); | ||
| ``` | ||
| return data; | ||
| } | ||
| } | ||
| ## .onceHook(hook) | ||
| const myClass = new MyClass(); | ||
| Subscribe to a hook event once. Takes an `IHook` object with `event` and `handler` properties. After the handler is called once, it is automatically removed. | ||
| // This sync handler will execute | ||
| myClass.onHook('before:myMethod', (data) => { | ||
| data.some = 'modified'; | ||
| }); | ||
| ```javascript | ||
| myClass.onceHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }}); | ||
| // This async handler will be silently skipped | ||
| myClass.onHook('before:myMethod', async (data) => { | ||
| data.some = 'will not run'; | ||
| }); | ||
| myClass.myMethodWithSyncHooks(); // Only sync handler runs | ||
| await myClass.hook('before:myMethod2', data); // handler runs once then is removed | ||
| console.log(myClass.hooks.size); // 0 | ||
| ``` | ||
| ## .hooks | ||
| ## .prependHook(hook, options?) | ||
| Get all hooks. | ||
| Subscribe to a hook event before all other hooks. Takes an `IHook` object with `event` and `handler` properties. Returns the stored `IHook` (with generated `id`), or `undefined` if blocked by deprecation. Equivalent to calling `onHook(hook, { position: "Top" })`. | ||
| An optional `PrependHookOptions` object can be passed with: | ||
| - `useHookClone` (boolean) — per-call override for hook cloning behavior | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }}); | ||
| myClass.prependHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'will run before new data'; | ||
| }}); | ||
| ``` | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| ## .prependOnceHook(hook, options?) | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| Subscribe to a hook event before all other hooks. Takes an `IHook` object with `event` and `handler` properties. After the handler is called once, it is automatically removed. Returns the stored `IHook` (with generated `id`), or `undefined` if blocked by deprecation. | ||
| return data; | ||
| } | ||
| } | ||
| An optional `PrependHookOptions` object can be passed with: | ||
| - `useHookClone` (boolean) — per-call override for hook cloning behavior | ||
| const myClass = new MyClass(); | ||
| myClass.onHook('before:myMethod2', async (data) => { | ||
| ```javascript | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }); | ||
| console.log(myClass.hooks); | ||
| }}); | ||
| myClass.prependOnceHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'will run before new data'; | ||
| }}); | ||
| ``` | ||
| ## .getHooks(eventName) | ||
| ## .removeEventHooks(eventName) | ||
| Get all hooks for an event. | ||
| Removes all hooks for a specific event and returns the removed hooks as an `IHook[]` array. Returns an empty array if no hooks are registered for the event. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'new data'; | ||
| }}); | ||
| myClass.onHook({ event: 'before:myMethod2', handler: async (data) => { | ||
| data.some = 'more data'; | ||
| }}); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| // Remove all hooks for a specific event | ||
| const removed = myClass.removeEventHooks('before:myMethod2'); | ||
| console.log(removed.length); // 2 | ||
| ``` | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| ## .removeHook(hook) | ||
| return data; | ||
| } | ||
| } | ||
| Unsubscribe a handler from a hook event. Takes an `IHook` object with `event` and `handler` properties. Returns the removed hook as an `IHook` object, or `undefined` if the handler was not found. | ||
| const myClass = new MyClass(); | ||
| myClass.onHook('before:myMethod2', async (data) => { | ||
| ```javascript | ||
| const handler = async (data) => { | ||
| data.some = 'new data'; | ||
| }); | ||
| }; | ||
| console.log(myClass.getHooks('before:myMethod2')); | ||
| myClass.onHook({ event: 'before:myMethod2', handler }); | ||
| const removed = myClass.removeHook({ event: 'before:myMethod2', handler }); | ||
| console.log(removed); // { event: 'before:myMethod2', handler: [Function] } | ||
| ``` | ||
| ## .clearHooks(eventName) | ||
| ## .removeHookById(id) | ||
| Clear all hooks for an event. | ||
| Remove one or more hooks by `id`, searching across all events. Accepts a single `string` or an array of `string` ids. | ||
| - **Single id**: Returns the removed `IHook`, or `undefined` if not found. | ||
| - **Array of ids**: Returns an `IHook[]` array of the hooks that were successfully removed. | ||
| When the last hook for an event is removed, the event key is cleaned up. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| const myClass = new MyClass(); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| myClass.onHook({ id: 'hook-a', event: 'before:save', handler: async () => {} }); | ||
| myClass.onHook({ id: 'hook-b', event: 'after:save', handler: async () => {} }); | ||
| myClass.onHook({ id: 'hook-c', event: 'before:save', handler: async () => {} }); | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| // Remove a single hook by id | ||
| const removed = myClass.removeHookById('hook-a'); | ||
| console.log(removed?.id); // 'hook-a' | ||
| return data; | ||
| } | ||
| } | ||
| // Remove multiple hooks by ids | ||
| const removedMany = myClass.removeHookById(['hook-b', 'hook-c']); | ||
| console.log(removedMany.length); // 2 | ||
| ``` | ||
| const myClass = new MyClass(); | ||
| ## .removeHooks(Array) | ||
| myClass.onHook('before:myMethod2', async (data) => { | ||
| data.some = 'new data'; | ||
| }); | ||
| Unsubscribe from multiple hooks. Returns an array of the hooks that were successfully removed. | ||
| myClass.clearHooks('before:myMethod2'); | ||
| ```javascript | ||
| const hooks = [ | ||
| { event: 'before:save', handler: async (data) => { data.some = 'new data1'; } }, | ||
| { event: 'after:save', handler: async (data) => { data.some = 'new data2'; } }, | ||
| ]; | ||
| myClass.onHooks(hooks); | ||
| const removed = myClass.removeHooks(hooks); | ||
| console.log(removed.length); // 2 | ||
| ``` | ||
@@ -950,21 +952,26 @@ | ||
| > All examples below assume the following setup unless otherwise noted: | ||
| > ```javascript | ||
| > import { Hookified } from 'hookified'; | ||
| > class MyClass extends Hookified { | ||
| > constructor(options) { super(options); } | ||
| > } | ||
| > const myClass = new MyClass(); | ||
| > ``` | ||
| ## .throwOnEmitError | ||
| If set to true, errors emitted as `error` will be thrown if there are no listeners. If set to false, errors will be only emitted. | ||
| If set to true, errors emitted as `error` will always be thrown, even if there are listeners. If set to false (default), errors will only be emitted to listeners. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| const myClass = new MyClass({ throwOnEmitError: true }); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| myClass.on('error', (err) => { | ||
| console.log('listener received:', err.message); | ||
| }); | ||
| async myMethodWithHooks() Promise<any> { | ||
| let data = { some: 'data' }; | ||
| // do something | ||
| await this.hook('before:myMethod2', data); | ||
| return data; | ||
| } | ||
| try { | ||
| myClass.emit('error', new Error('This will throw despite having a listener')); | ||
| } catch (error) { | ||
| console.log(error.message); // This will throw despite having a listener | ||
| } | ||
@@ -975,17 +982,9 @@ ``` | ||
| If set to true, errors will be thrown when emitting an `error` event with no listeners. This follows the standard Node.js EventEmitter behavior. Default is false. In version 2, this will be set to true by default. | ||
| If set to true, errors will be thrown when emitting an `error` event with no listeners. This follows the standard Node.js EventEmitter behavior. Default is `true`. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| const myClass = new MyClass({ throwOnEmptyListeners: true }); | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super({ throwOnEmptyListeners: true }); | ||
| } | ||
| } | ||
| console.log(myClass.throwOnEmptyListeners); // true (default) | ||
| const myClass = new MyClass(); | ||
| console.log(myClass.throwOnEmptyListeners); // true | ||
| // This will throw because there are no error listeners | ||
@@ -1021,16 +1020,2 @@ try { | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.on('message', (message) => { | ||
@@ -1046,22 +1031,8 @@ console.log(message); | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.on('message', (message) => { | ||
| const handler = (message) => { | ||
| console.log(message); | ||
| }); | ||
| }; | ||
| myClass.off('message', (message) => { | ||
| console.log(message); | ||
| }); | ||
| myClass.on('message', handler); | ||
| myClass.off('message', handler); | ||
| ``` | ||
@@ -1074,13 +1045,3 @@ | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| } | ||
| myClass.emit('message', 'Hello World'); | ||
| ``` | ||
@@ -1093,16 +1054,2 @@ | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.on('message', (message) => { | ||
@@ -1120,16 +1067,2 @@ console.log(message); | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.on('message', (message) => { | ||
@@ -1144,19 +1077,5 @@ console.log(message); | ||
| Set the maximum number of listeners and will truncate if there are already too many. | ||
| Set the maximum number of listeners for a single event. Default is `0` (unlimited). Negative values are treated as `0`. Setting to `0` disables the limit and the warning. When the limit is exceeded, a `MaxListenersExceededWarning` is emitted via `console.warn` but the listener is still added. This matches standard Node.js EventEmitter behavior. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| async myMethodEmittingEvent() { | ||
| this.emit('message', 'Hello World'); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.setMaxListeners(1); | ||
@@ -1170,5 +1089,5 @@ | ||
| console.log(message); | ||
| }); // this will not be added and console warning | ||
| }); // warning emitted but listener is still added | ||
| console.log(myClass.listenerCount('message')); // 1 | ||
| console.log(myClass.listenerCount('message')); // 2 | ||
| ``` | ||
@@ -1181,12 +1100,2 @@ | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.once('message', (message) => { | ||
@@ -1196,5 +1105,4 @@ console.log(message); | ||
| myClass.emit('message', 'Hello World'); | ||
| myClass.emit('message', 'Hello World'); // this will not be called | ||
| myClass.emit('message', 'Hello World'); // handler runs | ||
| myClass.emit('message', 'Hello World'); // handler does not run | ||
| ``` | ||
@@ -1207,12 +1115,2 @@ | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.prependListener('message', (message) => { | ||
@@ -1228,12 +1126,2 @@ console.log(message); | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.prependOnceListener('message', (message) => { | ||
@@ -1251,12 +1139,2 @@ console.log(message); | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.on('message', (message) => { | ||
@@ -1266,3 +1144,3 @@ console.log(message); | ||
| console.log(myClass.eventNames()); | ||
| console.log(myClass.eventNames()); // ['message'] | ||
| ``` | ||
@@ -1272,15 +1150,5 @@ | ||
| Get the count of listeners for an event or all events if evenName not provided. | ||
| Get the count of listeners for an event or all events if eventName not provided. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.on('message', (message) => { | ||
@@ -1295,15 +1163,5 @@ console.log(message); | ||
| Get all listeners for an event or all events if evenName not provided. | ||
| Get all listeners for an event or all events if eventName not provided. | ||
| ```javascript | ||
| import { Hookified } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { | ||
| super(); | ||
| } | ||
| } | ||
| const myClass = new MyClass(); | ||
| myClass.on('message', (message) => { | ||
@@ -1318,16 +1176,16 @@ console.log(message); | ||
| Hookified integrates logging directly into the event system. When a logger is configured, all emitted events are automatically logged to the appropriate log level based on the event name. | ||
| Hookified integrates logging directly into the event system. When an `eventLogger` is configured, all emitted events are automatically logged to the appropriate log level based on the event name. | ||
| ## How It Works | ||
| When you emit an event, Hookified automatically sends the event data to the configured logger using the appropriate log method: | ||
| When you emit an event, Hookified automatically sends the event data to the configured `eventLogger` using the appropriate log method: | ||
| | Event Name | Logger Method | | ||
| |------------|---------------| | ||
| | `error` | `logger.error()` | | ||
| | `warn` | `logger.warn()` | | ||
| | `debug` | `logger.debug()` | | ||
| | `trace` | `logger.trace()` | | ||
| | `fatal` | `logger.fatal()` | | ||
| | Any other | `logger.info()` | | ||
| | `error` | `eventLogger.error()` | | ||
| | `warn` | `eventLogger.warn()` | | ||
| | `debug` | `eventLogger.debug()` | | ||
| | `trace` | `eventLogger.trace()` | | ||
| | `fatal` | `eventLogger.fatal()` | | ||
| | Any other | `eventLogger.info()` | | ||
@@ -1363,3 +1221,3 @@ The logger receives two arguments: | ||
| constructor() { | ||
| super({ logger }); | ||
| super({ eventLogger: logger }); | ||
| } | ||
@@ -1390,10 +1248,10 @@ | ||
| You can also set or change the logger after instantiation: | ||
| You can also set or change the eventLogger after instantiation: | ||
| ```javascript | ||
| const service = new MyService(); | ||
| service.logger = pino({ level: 'debug' }); | ||
| service.eventLogger = pino({ level: 'debug' }); | ||
| // Or remove the logger | ||
| service.logger = undefined; | ||
| // Or remove the eventLogger | ||
| service.eventLogger = undefined; | ||
| ``` | ||
@@ -1407,6 +1265,6 @@ | ||
| | name | summary | ops/sec | time/op | margin | samples | | ||
| |-----------------------|:---------:|----------:|----------:|:--------:|----------:| | ||
| | Hookified (v1.15.1) | 🥇 | 5M | 199ns | ±0.01% | 5M | | ||
| | Hookable (v6.0.1) | -62% | 2M | 578ns | ±0.01% | 2M | | ||
| | name | summary | ops/sec | time/op | margin | samples | | ||
| |----------------------|:---------:|----------:|----------:|:--------:|----------:| | ||
| | Hookified (v2.0.0) | 🥇 | 5M | 214ns | ±0.01% | 5M | | ||
| | Hookable (v6.0.1) | -59% | 2M | 567ns | ±0.01% | 2M | | ||
@@ -1419,27 +1277,424 @@ ## Emits | ||
| |---------------------------|:---------:|----------:|----------:|:--------:|----------:| | ||
| | EventEmitter3 (v5.0.4) | 🥇 | 14M | 85ns | ±0.02% | 12M | | ||
| | Hookified (v1.15.1) | -6.9% | 13M | 88ns | ±0.02% | 11M | | ||
| | EventEmitter (v24.11.1) | -9.5% | 13M | 89ns | ±0.02% | 11M | | ||
| | Emittery (v1.2.0) | -92% | 1M | 993ns | ±0.01% | 1M | | ||
| | EventEmitter3 (v5.0.4) | 🥇 | 14M | 82ns | ±0.02% | 12M | | ||
| | Hookified (v2.0.0) | -6.9% | 13M | 97ns | ±0.02% | 10M | | ||
| | EventEmitter (v24.11.1) | -7.2% | 13M | 83ns | ±0.02% | 12M | | ||
| | Emittery (v1.2.0) | -92% | 1M | 1µs | ±0.01% | 979K || | ||
| _Note: the `EventEmitter` version is Nodejs versioning._ | ||
| # How to Contribute | ||
| # Migrating from v1 to v2 | ||
| Hookified is written in TypeScript and tests are written in `vitest`. To run the tests, use the following command: | ||
| ## Quick Guide | ||
| To setup the environment and run the tests: | ||
| v2 overhauls hook storage to use `IHook` objects instead of raw functions. This enables hook IDs, ordering via position, cloning control, and new hook types like `WaterfallHook`. The main change most users will notice is that `onHook` now takes an `IHook` object instead of positional arguments: | ||
| ```bash | ||
| pnpm i && pnpm test | ||
| ```typescript | ||
| // v1 — positional arguments | ||
| hookified.onHook('before:save', async (data) => {}); | ||
| // v2 — IHook object (or use addHook for positional args) | ||
| hookified.onHook({ event: 'before:save', handler: async (data) => {} }); | ||
| hookified.addHook('before:save', async (data) => {}); // still works | ||
| ``` | ||
| Note that we are using `pnpm` as our package manager. If you don't have it installed, you can install it globally with: | ||
| **Other common changes:** | ||
| ```bash | ||
| npm install -g pnpm | ||
| | v1 | v2 | | ||
| |---|---| | ||
| | `throwHookErrors` | `throwOnHookError` | | ||
| | `logger` | `eventLogger` | | ||
| | `onHookEntry(hook)` | `onHook(hook)` | | ||
| | `HookEntry` type | `IHook` interface | | ||
| | `Hook` type (fn) | `HookFn` type | | ||
| | `getHooks()` returns `HookFn[]` | `getHooks()` returns `IHook[]` | | ||
| | `removeHook(event, handler)` | `removeHook({ event, handler })` | | ||
| See below for full details on each change. | ||
| **[Breaking Changes](#breaking-changes)** | ||
| - [`throwHookErrors` removed — use `throwOnHookError` instead](#throwhookerrors-removed--use-throwonhookerror-instead) | ||
| - [`throwOnEmptyListeners` now defaults to `true`](#throwonemptylisteners-now-defaults-to-true) | ||
| - [`logger` renamed to `eventLogger`](#logger-renamed-to-eventlogger) | ||
| - [`maxListeners` default changed from `100` to `0` (unlimited) and no longer truncates](#maxlisteners-default-changed-from-100-to-0-unlimited-and-no-longer-truncates) | ||
| - [`onHookEntry` removed — use `onHook` instead](#onhookentry-removed--use-onhook-instead) | ||
| - [`onHook` signature changed](#onhook-signature-changed) | ||
| - [`HookEntry` type and `Hook` type removed](#hookentry-type-and-hook-type-removed) | ||
| - [`removeHook` and `removeHooks` now return removed hooks](#removehook-and-removehooks-now-return-removed-hooks) | ||
| - [`removeHook`, `removeHooks`, and `getHooks` no longer check for deprecated hooks](#removehook-removehooks-and-gethooks-no-longer-check-for-deprecated-hooks) | ||
| - [Internal hook storage now uses `IHook` objects](#internal-hook-storage-now-uses-ihook-objects) | ||
| - [`onceHook`, `prependHook`, `prependOnceHook`, and `removeHook` now take `IHook`](#oncehook-prependhook-prependoncehook-and-removehook-now-take-ihook) | ||
| - [`onHook` now returns the stored hook](#onhook-now-returns-the-stored-hook) | ||
| **[New Features](#new-features)** | ||
| - [standard `Hook` class now available](#standard-hook) | ||
| - [`WaterfallHook` class for sequential data transformation pipelines](#waterfallhook-class) | ||
| - [`useHookClone` option](#usehookclone-option) | ||
| - [`onHook` now accepts `OnHookOptions`](#onhook-now-accepts-onhookoptions) | ||
| - [`IHook` now has an `id` property](#ihook-now-has-an-id-property) | ||
| - [`removeEventHooks` method](#removeeventhooks-method) | ||
| ## Breaking Changes | ||
| | Change | Summary | | ||
| |---|---| | ||
| | `throwHookErrors` | Renamed to `throwOnHookError` | | ||
| | `throwOnEmptyListeners` | Default changed from `false` to `true` | | ||
| | `logger` | Renamed to `eventLogger` | | ||
| | `maxListeners` | Default changed from `100` to `0` (unlimited), no longer truncates | | ||
| | `onHookEntry` | Removed — use `onHook` instead | | ||
| | `onHook` signature | Now takes `IHook` object instead of `(event, handler)` | | ||
| | `HookEntry` / `Hook` types | Replaced with `IHook` / `HookFn` | | ||
| | `removeHook` / `removeHooks` | Now return removed hooks; no longer check deprecated status | | ||
| | Internal hook storage | Uses `IHook` objects instead of raw functions | | ||
| | `onceHook`, `prependHook`, etc. | Now take `IHook` instead of `(event, handler)` | | ||
| | `onHook` return | Now returns stored `IHook` (was `void`) | | ||
| ### `throwHookErrors` removed — use `throwOnHookError` instead | ||
| The deprecated `throwHookErrors` option and property has been removed. Use `throwOnHookError` instead. | ||
| **Before (v1):** | ||
| ```javascript | ||
| super({ throwHookErrors: true }); | ||
| myClass.throwHookErrors = false; | ||
| ``` | ||
| To contribute follow the [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md). | ||
| **After (v2):** | ||
| ```javascript | ||
| super({ throwOnHookError: true }); | ||
| myClass.throwOnHookError = false; | ||
| ``` | ||
| ### `throwOnEmptyListeners` now defaults to `true` | ||
| The `throwOnEmptyListeners` option now defaults to `true`, matching standard Node.js EventEmitter behavior. Previously it defaulted to `false`. If you emit an `error` event with no listeners registered, an error will now be thrown by default. | ||
| **Before (v1):** | ||
| ```javascript | ||
| const myClass = new MyClass(); // throwOnEmptyListeners defaults to false | ||
| myClass.emit('error', new Error('No throw')); // silently ignored | ||
| ``` | ||
| **After (v2):** | ||
| ```javascript | ||
| const myClass = new MyClass(); // throwOnEmptyListeners defaults to true | ||
| myClass.emit('error', new Error('This will throw')); // throws! | ||
| // To restore v1 behavior: | ||
| const myClass2 = new MyClass({ throwOnEmptyListeners: false }); | ||
| ``` | ||
| ### `logger` renamed to `eventLogger` | ||
| The `logger` option and property has been renamed to `eventLogger` to avoid conflicts with other logger properties in your classes. | ||
| **Before (v1):** | ||
| ```javascript | ||
| super({ logger }); | ||
| myClass.logger = pino({ level: 'debug' }); | ||
| ``` | ||
| **After (v2):** | ||
| ```javascript | ||
| super({ eventLogger: logger }); | ||
| myClass.eventLogger = pino({ level: 'debug' }); | ||
| ``` | ||
| ### `maxListeners` default changed from `100` to `0` (unlimited) and no longer truncates | ||
| The default maximum number of listeners has changed from `100` to `0` (unlimited). The `MaxListenersExceededWarning` will no longer be emitted unless you explicitly set a limit via `setMaxListeners()`. Additionally, `setMaxListeners()` no longer truncates existing listeners — it only sets the warning threshold, matching standard Node.js EventEmitter behavior. | ||
| **Before (v1):** | ||
| ```javascript | ||
| const myClass = new MyClass(); // maxListeners defaults to 100 | ||
| // Warning emitted after adding 100+ listeners to the same event | ||
| // setMaxListeners() would truncate existing listeners exceeding the limit | ||
| ``` | ||
| **After (v2):** | ||
| ```javascript | ||
| const myClass = new MyClass(); // maxListeners defaults to 0 (unlimited) | ||
| // No warning — unlimited listeners allowed | ||
| // setMaxListeners() only sets warning threshold, never removes listeners | ||
| // To restore v1 warning behavior: | ||
| myClass.setMaxListeners(100); | ||
| ``` | ||
| ### `onHookEntry` removed — use `onHook` instead | ||
| The `onHookEntry` method has been removed. Use `onHook` which now accepts an `IHook` object (or array of `IHook`) directly. | ||
| **Before (v1):** | ||
| ```typescript | ||
| hookified.onHookEntry({ event: 'before:save', handler: async (data) => {} }); | ||
| ``` | ||
| **After (v2):** | ||
| ```typescript | ||
| hookified.onHook({ event: 'before:save', handler: async (data) => {} }); | ||
| ``` | ||
| ### `onHook` signature changed | ||
| `onHook` no longer accepts positional `(event, handler)` arguments. It now takes a single `IHook` object or `Hook` class instance. Use `addHook(event, handler)` if you prefer positional arguments. Use `onHooks()` for bulk registration. | ||
| **Before (v1):** | ||
| ```typescript | ||
| hookified.onHook('before:save', async (data) => {}); | ||
| ``` | ||
| **After (v2):** | ||
| ```typescript | ||
| // Using IHook object | ||
| hookified.onHook({ event: 'before:save', handler: async (data) => {} }); | ||
| // For multiple hooks, use onHooks | ||
| hookified.onHooks([ | ||
| { event: 'before:save', handler: async (data) => {} }, | ||
| { event: 'after:save', handler: async (data) => {} }, | ||
| ]); | ||
| // Or use addHook for positional args | ||
| hookified.addHook('before:save', async (data) => {}); | ||
| ``` | ||
| ### `HookEntry` type and `Hook` type removed | ||
| The `HookEntry` type has been removed and replaced with the `IHook` interface. The `Hook` type (function type) has been renamed to `HookFn`. | ||
| **Before (v1):** | ||
| ```typescript | ||
| import type { HookEntry, Hook } from 'hookified'; | ||
| const hook: HookEntry = { event: 'before:save', handler: async () => {} }; | ||
| const myHook: Hook = async (data) => {}; | ||
| ``` | ||
| **After (v2):** | ||
| ```typescript | ||
| import type { IHook, HookFn } from 'hookified'; | ||
| const hook: IHook = { event: 'before:save', handler: async () => {} }; | ||
| const myHook: HookFn = async (data) => {}; | ||
| ``` | ||
| ### `removeHook` and `removeHooks` now return removed hooks | ||
| `removeHook` now returns the removed hook as an `IHook` object (or `undefined` if not found). `removeHooks` now returns an `IHook[]` array of the hooks that were successfully removed. Previously both returned `void`. | ||
| **Before (v1):** | ||
| ```typescript | ||
| hookified.removeHook('before:save', handler); // void | ||
| hookified.removeHooks(hooks); // void | ||
| ``` | ||
| **After (v2):** | ||
| ```typescript | ||
| const removed = hookified.removeHook({ event: 'before:save', handler }); // IHook | undefined | ||
| const removedHooks = hookified.removeHooks(hooks); // IHook[] | ||
| ``` | ||
| ### `removeHook`, `removeHooks`, and `getHooks` no longer check for deprecated hooks | ||
| Previously, `removeHook`, `removeHooks`, and `getHooks` would skip their operation and emit a deprecation warning when called with a deprecated hook name and `allowDeprecated` was `false`. This made it impossible to clean up or inspect deprecated hooks. These methods now always operate regardless of deprecation status. | ||
| ### Internal hook storage now uses `IHook` objects | ||
| The internal `_hooks` map now stores full `IHook` objects (`Map<string, IHook[]>`) instead of raw handler functions (`Map<string, HookFn[]>`). This means `.hooks` returns `Map<string, IHook[]>` and `.getHooks()` returns `IHook[] | undefined`. | ||
| **Before (v1):** | ||
| ```typescript | ||
| const hooks = myClass.getHooks('before:save'); // HookFn[] | ||
| hooks[0](data); // direct function call | ||
| ``` | ||
| **After (v2):** | ||
| ```typescript | ||
| const hooks = myClass.getHooks('before:save'); // IHook[] | ||
| hooks[0].handler(data); // access .handler property | ||
| hooks[0].event; // 'before:save' | ||
| ``` | ||
| ### `onceHook`, `prependHook`, `prependOnceHook`, and `removeHook` now take `IHook` | ||
| These methods now accept an `IHook` object instead of separate `(event, handler)` arguments. | ||
| **Before (v1):** | ||
| ```typescript | ||
| hookified.onceHook('before:save', async (data) => {}); | ||
| hookified.prependHook('before:save', async (data) => {}); | ||
| hookified.prependOnceHook('before:save', async (data) => {}); | ||
| hookified.removeHook('before:save', handler); | ||
| ``` | ||
| **After (v2):** | ||
| ```typescript | ||
| hookified.onceHook({ event: 'before:save', handler: async (data) => {} }); | ||
| hookified.prependHook({ event: 'before:save', handler: async (data) => {} }); | ||
| hookified.prependOnceHook({ event: 'before:save', handler: async (data) => {} }); | ||
| hookified.removeHook({ event: 'before:save', handler }); | ||
| ``` | ||
| ### `onHook` now returns the stored hook | ||
| `onHook` now returns the stored `IHook` object (or `undefined` if blocked by deprecation). Previously it returned `void`. The returned reference is the exact object stored internally, making it easy to later remove with `removeHook()`. | ||
| **Before (v1):** | ||
| ```typescript | ||
| hookified.onHook({ event: 'before:save', handler }); // void | ||
| ``` | ||
| **After (v2):** | ||
| ```typescript | ||
| const stored = hookified.onHook({ event: 'before:save', handler }); // IHook | undefined | ||
| hookified.removeHook(stored); // exact reference match | ||
| ``` | ||
| ## New Features | ||
| ### `Hook` class | ||
| A new `Hook` class is available for creating hook entries. It implements the `IHook` interface and can be used anywhere `IHook` is accepted. | ||
| ```typescript | ||
| import { Hook } from 'hookified'; | ||
| const hook = new Hook('before:save', async (data) => { | ||
| data.validated = true; | ||
| }); | ||
| myClass.onHook(hook); | ||
| ``` | ||
| ### `WaterfallHook` class | ||
| A new `WaterfallHook` class is available for creating sequential data transformation pipelines. It implements the `IHook` interface and integrates directly with `Hookified.onHook()`. Each hook in the chain receives a `WaterfallHookContext` with `initialArgs` (the original arguments) and `results` (an array of `{ hook, result }` entries from all previous hooks). | ||
| ```typescript | ||
| import { Hookified, WaterfallHook } from 'hookified'; | ||
| class MyClass extends Hookified { | ||
| constructor() { super(); } | ||
| } | ||
| const myClass = new MyClass(); | ||
| const wh = new WaterfallHook('save', ({ results }) => { | ||
| const data = results[results.length - 1].result; | ||
| console.log('Saved:', data); | ||
| }); | ||
| wh.addHook(({ initialArgs }) => { | ||
| return { ...initialArgs, validated: true }; | ||
| }); | ||
| wh.addHook(({ results }) => { | ||
| return { ...results[results.length - 1].result, timestamp: Date.now() }; | ||
| }); | ||
| myClass.onHook(wh); | ||
| await myClass.hook('save', { name: 'test' }); | ||
| // Saved: { name: 'test', validated: true, timestamp: ... } | ||
| ``` | ||
| See the [Waterfall Hook](#waterfallhook) section for full documentation. | ||
| ### `useHookClone` option | ||
| A new `useHookClone` option (default `true`) controls whether hook objects are shallow-cloned before storing. When enabled, external mutation of a registered hook object won't affect the internal state. Set to `false` to store the original reference for performance or when you need reference equality. | ||
| ```typescript | ||
| class MyClass extends Hookified { | ||
| constructor() { super({ useHookClone: false }); } | ||
| } | ||
| ``` | ||
| ### `onHook` now accepts `OnHookOptions` | ||
| `onHook` now accepts an optional second parameter of type `OnHookOptions`. This allows you to override the instance-level `useHookClone` setting and control hook positioning on a per-call basis. | ||
| ```typescript | ||
| // Override useHookClone for this specific call | ||
| hookified.onHook({ event: 'before:save', handler }, { useHookClone: false }); | ||
| // Insert at the top of the handlers array instead of the end | ||
| hookified.onHook({ event: 'before:save', handler }, { position: 'Top' }); | ||
| // Insert at a specific index | ||
| hookified.onHook({ event: 'before:save', handler }, { position: 1 }); | ||
| ``` | ||
| ### `IHook` now has an `id` property | ||
| Every hook now has an optional `id` property. If not provided, a UUID is auto-generated via `crypto.randomUUID()`. The `id` enables easier lookups and removal via the new `getHook(id)` and `removeHookById(id)` methods, which search across all events. | ||
| Registering a hook with the same `id` on the same event replaces the existing hook in-place (preserving its position). | ||
| ```typescript | ||
| // With custom id | ||
| const stored = hookified.onHook({ | ||
| id: 'my-validation', | ||
| event: 'before:save', | ||
| handler: async (data) => { data.validated = true; }, | ||
| }); | ||
| // Without id — auto-generated | ||
| const stored2 = hookified.onHook({ | ||
| event: 'before:save', | ||
| handler: async (data) => {}, | ||
| }); | ||
| console.log(stored2.id); // e.g. '550e8400-e29b-41d4-a716-446655440000' | ||
| // Look up by id (searches all events) | ||
| const hook = hookified.getHook('my-validation'); | ||
| // Remove by id (searches all events) | ||
| hookified.removeHookById('my-validation'); | ||
| // Remove multiple by ids | ||
| hookified.removeHookById(['hook-a', 'hook-b']); | ||
| ``` | ||
| The `Hook` class also accepts an optional `id` parameter: | ||
| ```typescript | ||
| const hook = new Hook('before:save', handler, 'my-custom-id'); | ||
| ``` | ||
| ### `removeEventHooks` method | ||
| A new `removeEventHooks(event)` method removes all hooks for a specific event and returns the removed hooks as an `IHook[]` array. | ||
| ```typescript | ||
| const removed = hookified.removeEventHooks('before:save'); | ||
| console.log(removed.length); // number of hooks removed | ||
| ``` | ||
| # How to Contribute | ||
| Hookified is written in TypeScript and tests are written with `vitest`. To setup the environment and run the tests: | ||
| ```bash | ||
@@ -1460,5 +1715,1 @@ pnpm i && pnpm test | ||
| [MIT & © Jared Wray](LICENSE) | ||
311704
30.14%3961
19.2%1676
17.61%