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