hookified 1.12.1 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,612 @@
1
- var i=class{_eventListeners;_maxListeners;_logger;_throwOnEmitError=!1;constructor(e){this._eventListeners=new Map,this._maxListeners=100,this._logger=e?.logger,e?.throwOnEmitError!==void 0&&(this._throwOnEmitError=e.throwOnEmitError)}get logger(){return this._logger}set logger(e){this._logger=e}get throwOnEmitError(){return this._throwOnEmitError}set throwOnEmitError(e){this._throwOnEmitError=e}once(e,t){let r=(...s)=>{this.off(e,r),t(...s)};return this.on(e,r),this}listenerCount(e){if(e===void 0)return this.getAllListeners().length;let t=this._eventListeners.get(e);return t?t.length:0}eventNames(){return[...this._eventListeners.keys()]}rawListeners(e){return e===void 0?this.getAllListeners():this._eventListeners.get(e)??[]}prependListener(e,t){let r=this._eventListeners.get(e)??[];return r.unshift(t),this._eventListeners.set(e,r),this}prependOnceListener(e,t){let r=(...s)=>{this.off(e,r),t(...s)};return this.prependListener(e,r),this}maxListeners(){return this._maxListeners}addListener(e,t){return this.on(e,t),this}on(e,t){this._eventListeners.has(e)||this._eventListeners.set(e,[]);let r=this._eventListeners.get(e);return r&&(r.length>=this._maxListeners&&console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${r.length+1} ${e} listeners added. Use setMaxListeners() to increase limit.`),r.push(t)),this}removeListener(e,t){return this.off(e,t),this}off(e,t){let r=this._eventListeners.get(e)??[],s=r.indexOf(t);return s!==-1&&r.splice(s,1),r.length===0&&this._eventListeners.delete(e),this}emit(e,...t){let r=!1,s=this._eventListeners.get(e);if(s&&s.length>0)for(let o of s)o(...t),r=!0;if(e==="error"){let o=t[0]instanceof Error?t[0]:new Error(`${t[0]}`);if(this._throwOnEmitError&&!r)throw o}return r}listeners(e){return this._eventListeners.get(e)??[]}removeAllListeners(e){return e!==void 0?this._eventListeners.delete(e):this._eventListeners.clear(),this}setMaxListeners(e){this._maxListeners=e;for(let t of this._eventListeners.values())t.length>e&&t.splice(e)}getAllListeners(){let e=[];for(let t of this._eventListeners.values())e=[...e,...t];return e}};var a=class extends i{_hooks;_throwHookErrors=!1;_enforceBeforeAfter=!1;_deprecatedHooks;_allowDeprecated=!0;constructor(e){super({logger:e?.logger}),this._hooks=new Map,this._deprecatedHooks=e?.deprecatedHooks?new Map(e.deprecatedHooks):new Map,e?.throwHookErrors!==void 0&&(this._throwHookErrors=e.throwHookErrors),e?.enforceBeforeAfter!==void 0&&(this._enforceBeforeAfter=e.enforceBeforeAfter),e?.allowDeprecated!==void 0&&(this._allowDeprecated=e.allowDeprecated)}get hooks(){return this._hooks}get throwHookErrors(){return this._throwHookErrors}set throwHookErrors(e){this._throwHookErrors=e}get enforceBeforeAfter(){return this._enforceBeforeAfter}set enforceBeforeAfter(e){this._enforceBeforeAfter=e}get deprecatedHooks(){return this._deprecatedHooks}set deprecatedHooks(e){this._deprecatedHooks=e}get allowDeprecated(){return this._allowDeprecated}set allowDeprecated(e){this._allowDeprecated=e}validateHookName(e){if(this._enforceBeforeAfter){let t=e.trim().toLocaleLowerCase();if(!t.startsWith("before")&&!t.startsWith("after"))throw new Error(`Hook event "${e}" must start with "before" or "after" when enforceBeforeAfter is enabled`)}}checkDeprecatedHook(e){if(this._deprecatedHooks.has(e)){let t=this._deprecatedHooks.get(e),r=`Hook "${e}" is deprecated${t?`: ${t}`:""}`;return this.emit("warn",{hook:e,message:r}),this.logger?.warn&&this.logger.warn(r),this._allowDeprecated}return!0}onHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);r?r.push(t):this._hooks.set(e,[t])}onHookEntry(e){this.onHook(e.event,e.handler)}addHook(e,t){this.onHook(e,t)}onHooks(e){for(let t of e)this.onHook(t.event,t.handler)}prependHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);r?r.unshift(t):this._hooks.set(e,[t])}prependOnceHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=async(...s)=>(this.removeHook(e,r),t(...s));this.prependHook(e,r)}onceHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=async(...s)=>(this.removeHook(e,r),t(...s));this.onHook(e,r)}removeHook(e,t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);if(r){let s=r.indexOf(t);s!==-1&&r.splice(s,1)}}removeHooks(e){for(let t of e)this.removeHook(t.event,t.handler)}async hook(e,...t){if(this.validateHookName(e),!this.checkDeprecatedHook(e))return;let r=this._hooks.get(e);if(r)for(let s of r)try{await s(...t)}catch(o){let n=`${e}: ${o.message}`;if(this.emit("error",new Error(n)),this.logger&&this.logger.error(n),this._throwHookErrors)throw new Error(n)}}async beforeHook(e,...t){await this.hook(`before:${e}`,...t)}async afterHook(e,...t){await this.hook(`after:${e}`,...t)}async callHook(e,...t){await this.hook(e,...t)}getHooks(e){if(this.validateHookName(e),!!this.checkDeprecatedHook(e))return this._hooks.get(e)}clearHooks(){this._hooks.clear()}};export{i as Eventified,a as Hookified};
1
+ // src/eventified.ts
2
+ var Eventified = class {
3
+ _eventListeners;
4
+ _maxListeners;
5
+ _logger;
6
+ _throwOnEmitError = false;
7
+ _throwOnEmptyListeners = false;
8
+ _errorEvent = "error";
9
+ constructor(options) {
10
+ this._eventListeners = /* @__PURE__ */ new Map();
11
+ this._maxListeners = 100;
12
+ this._logger = options?.logger;
13
+ if (options?.throwOnEmitError !== void 0) {
14
+ this._throwOnEmitError = options.throwOnEmitError;
15
+ }
16
+ if (options?.throwOnEmptyListeners !== void 0) {
17
+ this._throwOnEmptyListeners = options.throwOnEmptyListeners;
18
+ }
19
+ }
20
+ /**
21
+ * Gets the logger
22
+ * @returns {Logger}
23
+ */
24
+ get logger() {
25
+ return this._logger;
26
+ }
27
+ /**
28
+ * Sets the logger
29
+ * @param {Logger} logger
30
+ */
31
+ set logger(logger) {
32
+ this._logger = logger;
33
+ }
34
+ /**
35
+ * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
36
+ * @returns {boolean}
37
+ */
38
+ get throwOnEmitError() {
39
+ return this._throwOnEmitError;
40
+ }
41
+ /**
42
+ * Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
43
+ * @param {boolean} value
44
+ */
45
+ set throwOnEmitError(value) {
46
+ this._throwOnEmitError = value;
47
+ }
48
+ /**
49
+ * Gets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.
50
+ * @returns {boolean}
51
+ */
52
+ get throwOnEmptyListeners() {
53
+ return this._throwOnEmptyListeners;
54
+ }
55
+ /**
56
+ * Sets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.
57
+ * @param {boolean} value
58
+ */
59
+ set throwOnEmptyListeners(value) {
60
+ this._throwOnEmptyListeners = value;
61
+ }
62
+ /**
63
+ * Adds a handler function for a specific event that will run only once
64
+ * @param {string | symbol} eventName
65
+ * @param {EventListener} listener
66
+ * @returns {IEventEmitter} returns the instance of the class for chaining
67
+ */
68
+ once(eventName, listener) {
69
+ const onceListener = (...arguments_) => {
70
+ this.off(eventName, onceListener);
71
+ listener(...arguments_);
72
+ };
73
+ this.on(eventName, onceListener);
74
+ return this;
75
+ }
76
+ /**
77
+ * Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners
78
+ * @param {string} eventName The event name. Not required
79
+ * @returns {number} The number of listeners
80
+ */
81
+ listenerCount(eventName) {
82
+ if (eventName === void 0) {
83
+ return this.getAllListeners().length;
84
+ }
85
+ const listeners = this._eventListeners.get(eventName);
86
+ return listeners ? listeners.length : 0;
87
+ }
88
+ /**
89
+ * Gets an array of event names
90
+ * @returns {Array<string | symbol>} An array of event names
91
+ */
92
+ eventNames() {
93
+ return [...this._eventListeners.keys()];
94
+ }
95
+ /**
96
+ * Gets an array of listeners for a specific event. If no event is provided, it returns all listeners
97
+ * @param {string} [event] (Optional) The event name
98
+ * @returns {EventListener[]} An array of listeners
99
+ */
100
+ rawListeners(event) {
101
+ if (event === void 0) {
102
+ return this.getAllListeners();
103
+ }
104
+ return this._eventListeners.get(event) ?? [];
105
+ }
106
+ /**
107
+ * Prepends a listener to the beginning of the listeners array for the specified event
108
+ * @param {string | symbol} eventName
109
+ * @param {EventListener} listener
110
+ * @returns {IEventEmitter} returns the instance of the class for chaining
111
+ */
112
+ prependListener(eventName, listener) {
113
+ const listeners = this._eventListeners.get(eventName) ?? [];
114
+ listeners.unshift(listener);
115
+ this._eventListeners.set(eventName, listeners);
116
+ return this;
117
+ }
118
+ /**
119
+ * Prepends a one-time listener to the beginning of the listeners array for the specified event
120
+ * @param {string | symbol} eventName
121
+ * @param {EventListener} listener
122
+ * @returns {IEventEmitter} returns the instance of the class for chaining
123
+ */
124
+ prependOnceListener(eventName, listener) {
125
+ const onceListener = (...arguments_) => {
126
+ this.off(eventName, onceListener);
127
+ listener(...arguments_);
128
+ };
129
+ this.prependListener(eventName, onceListener);
130
+ return this;
131
+ }
132
+ /**
133
+ * Gets the maximum number of listeners that can be added for a single event
134
+ * @returns {number} The maximum number of listeners
135
+ */
136
+ maxListeners() {
137
+ return this._maxListeners;
138
+ }
139
+ /**
140
+ * Adds a listener for a specific event. It is an alias for the on() method
141
+ * @param {string | symbol} event
142
+ * @param {EventListener} listener
143
+ * @returns {IEventEmitter} returns the instance of the class for chaining
144
+ */
145
+ addListener(event, listener) {
146
+ this.on(event, listener);
147
+ return this;
148
+ }
149
+ /**
150
+ * Adds a listener for a specific event
151
+ * @param {string | symbol} event
152
+ * @param {EventListener} listener
153
+ * @returns {IEventEmitter} returns the instance of the class for chaining
154
+ */
155
+ on(event, listener) {
156
+ if (!this._eventListeners.has(event)) {
157
+ this._eventListeners.set(event, []);
158
+ }
159
+ const listeners = this._eventListeners.get(event);
160
+ if (listeners) {
161
+ if (listeners.length >= this._maxListeners) {
162
+ console.warn(
163
+ `MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
164
+ );
165
+ }
166
+ listeners.push(listener);
167
+ }
168
+ return this;
169
+ }
170
+ /**
171
+ * Removes a listener for a specific event. It is an alias for the off() method
172
+ * @param {string | symbol} event
173
+ * @param {EventListener} listener
174
+ * @returns {IEventEmitter} returns the instance of the class for chaining
175
+ */
176
+ removeListener(event, listener) {
177
+ this.off(event, listener);
178
+ return this;
179
+ }
180
+ /**
181
+ * Removes a listener for a specific event
182
+ * @param {string | symbol} event
183
+ * @param {EventListener} listener
184
+ * @returns {IEventEmitter} returns the instance of the class for chaining
185
+ */
186
+ off(event, listener) {
187
+ const listeners = this._eventListeners.get(event) ?? [];
188
+ const index = listeners.indexOf(listener);
189
+ if (index !== -1) {
190
+ listeners.splice(index, 1);
191
+ }
192
+ if (listeners.length === 0) {
193
+ this._eventListeners.delete(event);
194
+ }
195
+ return this;
196
+ }
197
+ /**
198
+ * Calls all listeners for a specific event
199
+ * @param {string | symbol} event
200
+ * @param arguments_ The arguments to pass to the listeners
201
+ * @returns {boolean} Returns true if the event had listeners, false otherwise
202
+ */
203
+ emit(event, ...arguments_) {
204
+ let result = false;
205
+ const listeners = this._eventListeners.get(event);
206
+ if (listeners && listeners.length > 0) {
207
+ for (const listener of listeners) {
208
+ listener(...arguments_);
209
+ result = true;
210
+ }
211
+ }
212
+ if (event === this._errorEvent) {
213
+ const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
214
+ if (this._throwOnEmitError && !result) {
215
+ throw error;
216
+ } else {
217
+ if (this.listeners(this._errorEvent).length === 0 && this._throwOnEmptyListeners === true) {
218
+ throw error;
219
+ }
220
+ }
221
+ }
222
+ return result;
223
+ }
224
+ /**
225
+ * Gets all listeners for a specific event. If no event is provided, it returns all listeners
226
+ * @param {string} [event] (Optional) The event name
227
+ * @returns {EventListener[]} An array of listeners
228
+ */
229
+ listeners(event) {
230
+ return this._eventListeners.get(event) ?? [];
231
+ }
232
+ /**
233
+ * Removes all listeners for a specific event. If no event is provided, it removes all listeners
234
+ * @param {string} [event] (Optional) The event name
235
+ * @returns {IEventEmitter} returns the instance of the class for chaining
236
+ */
237
+ removeAllListeners(event) {
238
+ if (event !== void 0) {
239
+ this._eventListeners.delete(event);
240
+ } else {
241
+ this._eventListeners.clear();
242
+ }
243
+ return this;
244
+ }
245
+ /**
246
+ * Sets the maximum number of listeners that can be added for a single event
247
+ * @param {number} n The maximum number of listeners
248
+ * @returns {void}
249
+ */
250
+ setMaxListeners(n) {
251
+ this._maxListeners = n;
252
+ for (const listeners of this._eventListeners.values()) {
253
+ if (listeners.length > n) {
254
+ listeners.splice(n);
255
+ }
256
+ }
257
+ }
258
+ /**
259
+ * Gets all listeners
260
+ * @returns {EventListener[]} An array of listeners
261
+ */
262
+ getAllListeners() {
263
+ let result = [];
264
+ for (const listeners of this._eventListeners.values()) {
265
+ result = [...result, ...listeners];
266
+ }
267
+ return result;
268
+ }
269
+ };
270
+
271
+ // src/index.ts
272
+ var Hookified = class extends Eventified {
273
+ _hooks;
274
+ _throwOnHookError = false;
275
+ _enforceBeforeAfter = false;
276
+ _deprecatedHooks;
277
+ _allowDeprecated = true;
278
+ constructor(options) {
279
+ super({
280
+ logger: options?.logger,
281
+ throwOnEmitError: options?.throwOnEmitError,
282
+ throwOnEmptyListeners: options?.throwOnEmptyListeners
283
+ });
284
+ this._hooks = /* @__PURE__ */ new Map();
285
+ this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
286
+ if (options?.throwOnHookError !== void 0) {
287
+ this._throwOnHookError = options.throwOnHookError;
288
+ } else if (options?.throwHookErrors !== void 0) {
289
+ this._throwOnHookError = options.throwHookErrors;
290
+ }
291
+ if (options?.enforceBeforeAfter !== void 0) {
292
+ this._enforceBeforeAfter = options.enforceBeforeAfter;
293
+ }
294
+ if (options?.allowDeprecated !== void 0) {
295
+ this._allowDeprecated = options.allowDeprecated;
296
+ }
297
+ }
298
+ /**
299
+ * Gets all hooks
300
+ * @returns {Map<string, Hook[]>}
301
+ */
302
+ get hooks() {
303
+ return this._hooks;
304
+ }
305
+ /**
306
+ * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
307
+ * @returns {boolean}
308
+ * @deprecated - this will be deprecated in version 2. Please use throwOnHookError.
309
+ */
310
+ get throwHookErrors() {
311
+ return this._throwOnHookError;
312
+ }
313
+ /**
314
+ * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
315
+ * @param {boolean} value
316
+ * @deprecated - this will be deprecated in version 2. Please use throwOnHookError.
317
+ */
318
+ set throwHookErrors(value) {
319
+ this._throwOnHookError = value;
320
+ }
321
+ /**
322
+ * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
323
+ * @returns {boolean}
324
+ */
325
+ get throwOnHookError() {
326
+ return this._throwOnHookError;
327
+ }
328
+ /**
329
+ * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
330
+ * @param {boolean} value
331
+ */
332
+ set throwOnHookError(value) {
333
+ this._throwOnHookError = value;
334
+ }
335
+ /**
336
+ * Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
337
+ * @returns {boolean}
338
+ * @default false
339
+ */
340
+ get enforceBeforeAfter() {
341
+ return this._enforceBeforeAfter;
342
+ }
343
+ /**
344
+ * Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
345
+ * @param {boolean} value
346
+ */
347
+ set enforceBeforeAfter(value) {
348
+ this._enforceBeforeAfter = value;
349
+ }
350
+ /**
351
+ * Gets the map of deprecated hook names to deprecation messages.
352
+ * @returns {Map<string, string>}
353
+ */
354
+ get deprecatedHooks() {
355
+ return this._deprecatedHooks;
356
+ }
357
+ /**
358
+ * Sets the map of deprecated hook names to deprecation messages.
359
+ * @param {Map<string, string>} value
360
+ */
361
+ set deprecatedHooks(value) {
362
+ this._deprecatedHooks = value;
363
+ }
364
+ /**
365
+ * Gets whether deprecated hooks are allowed to be registered and executed. Default is true.
366
+ * @returns {boolean}
367
+ */
368
+ get allowDeprecated() {
369
+ return this._allowDeprecated;
370
+ }
371
+ /**
372
+ * Sets whether deprecated hooks are allowed to be registered and executed. Default is true.
373
+ * @param {boolean} value
374
+ */
375
+ set allowDeprecated(value) {
376
+ this._allowDeprecated = value;
377
+ }
378
+ /**
379
+ * Validates hook event name if enforceBeforeAfter is enabled
380
+ * @param {string} event - The event name to validate
381
+ * @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
382
+ */
383
+ validateHookName(event) {
384
+ if (this._enforceBeforeAfter) {
385
+ const eventValue = event.trim().toLocaleLowerCase();
386
+ if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
387
+ throw new Error(
388
+ `Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
389
+ );
390
+ }
391
+ }
392
+ }
393
+ /**
394
+ * Checks if a hook is deprecated and emits a warning if it is
395
+ * @param {string} event - The event name to check
396
+ * @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
397
+ */
398
+ checkDeprecatedHook(event) {
399
+ if (this._deprecatedHooks.has(event)) {
400
+ const message = this._deprecatedHooks.get(event);
401
+ const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
402
+ this.emit("warn", { hook: event, message: warningMessage });
403
+ if (this.logger?.warn) {
404
+ this.logger.warn(warningMessage);
405
+ }
406
+ return this._allowDeprecated;
407
+ }
408
+ return true;
409
+ }
410
+ /**
411
+ * Adds a handler function for a specific event
412
+ * @param {string} event
413
+ * @param {Hook} handler - this can be async or sync
414
+ * @returns {void}
415
+ */
416
+ onHook(event, handler) {
417
+ this.validateHookName(event);
418
+ if (!this.checkDeprecatedHook(event)) {
419
+ return;
420
+ }
421
+ const eventHandlers = this._hooks.get(event);
422
+ if (eventHandlers) {
423
+ eventHandlers.push(handler);
424
+ } else {
425
+ this._hooks.set(event, [handler]);
426
+ }
427
+ }
428
+ /**
429
+ * Adds a handler function for a specific event that runs before all other handlers
430
+ * @param {HookEntry} hookEntry
431
+ * @returns {void}
432
+ */
433
+ onHookEntry(hookEntry) {
434
+ this.onHook(hookEntry.event, hookEntry.handler);
435
+ }
436
+ /**
437
+ * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
438
+ * @param {string} event
439
+ * @param {Hook} handler - this can be async or sync
440
+ * @returns {void}
441
+ */
442
+ addHook(event, handler) {
443
+ this.onHook(event, handler);
444
+ }
445
+ /**
446
+ * Adds a handler function for a specific event
447
+ * @param {Array<HookEntry>} hooks
448
+ * @returns {void}
449
+ */
450
+ onHooks(hooks) {
451
+ for (const hook of hooks) {
452
+ this.onHook(hook.event, hook.handler);
453
+ }
454
+ }
455
+ /**
456
+ * Adds a handler function for a specific event that runs before all other handlers
457
+ * @param {string} event
458
+ * @param {Hook} handler - this can be async or sync
459
+ * @returns {void}
460
+ */
461
+ prependHook(event, handler) {
462
+ this.validateHookName(event);
463
+ if (!this.checkDeprecatedHook(event)) {
464
+ return;
465
+ }
466
+ const eventHandlers = this._hooks.get(event);
467
+ if (eventHandlers) {
468
+ eventHandlers.unshift(handler);
469
+ } else {
470
+ this._hooks.set(event, [handler]);
471
+ }
472
+ }
473
+ /**
474
+ * Adds a handler that only executes once for a specific event before all other handlers
475
+ * @param event
476
+ * @param handler
477
+ */
478
+ prependOnceHook(event, handler) {
479
+ this.validateHookName(event);
480
+ if (!this.checkDeprecatedHook(event)) {
481
+ return;
482
+ }
483
+ const hook = async (...arguments_) => {
484
+ this.removeHook(event, hook);
485
+ return handler(...arguments_);
486
+ };
487
+ this.prependHook(event, hook);
488
+ }
489
+ /**
490
+ * Adds a handler that only executes once for a specific event
491
+ * @param event
492
+ * @param handler
493
+ */
494
+ onceHook(event, handler) {
495
+ this.validateHookName(event);
496
+ if (!this.checkDeprecatedHook(event)) {
497
+ return;
498
+ }
499
+ const hook = async (...arguments_) => {
500
+ this.removeHook(event, hook);
501
+ return handler(...arguments_);
502
+ };
503
+ this.onHook(event, hook);
504
+ }
505
+ /**
506
+ * Removes a handler function for a specific event
507
+ * @param {string} event
508
+ * @param {Hook} handler
509
+ * @returns {void}
510
+ */
511
+ removeHook(event, handler) {
512
+ this.validateHookName(event);
513
+ if (!this.checkDeprecatedHook(event)) {
514
+ return;
515
+ }
516
+ const eventHandlers = this._hooks.get(event);
517
+ if (eventHandlers) {
518
+ const index = eventHandlers.indexOf(handler);
519
+ if (index !== -1) {
520
+ eventHandlers.splice(index, 1);
521
+ }
522
+ }
523
+ }
524
+ /**
525
+ * Removes all handlers for a specific event
526
+ * @param {Array<HookEntry>} hooks
527
+ * @returns {void}
528
+ */
529
+ removeHooks(hooks) {
530
+ for (const hook of hooks) {
531
+ this.removeHook(hook.event, hook.handler);
532
+ }
533
+ }
534
+ /**
535
+ * Calls all handlers for a specific event
536
+ * @param {string} event
537
+ * @param {T[]} arguments_
538
+ * @returns {Promise<void>}
539
+ */
540
+ async hook(event, ...arguments_) {
541
+ this.validateHookName(event);
542
+ if (!this.checkDeprecatedHook(event)) {
543
+ return;
544
+ }
545
+ const eventHandlers = this._hooks.get(event);
546
+ if (eventHandlers) {
547
+ for (const handler of eventHandlers) {
548
+ try {
549
+ await handler(...arguments_);
550
+ } catch (error) {
551
+ const message = `${event}: ${error.message}`;
552
+ this.emit("error", new Error(message));
553
+ if (this.logger) {
554
+ this.logger.error(message);
555
+ }
556
+ if (this._throwOnHookError) {
557
+ throw new Error(message);
558
+ }
559
+ }
560
+ }
561
+ }
562
+ }
563
+ /**
564
+ * Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.
565
+ * @param {string} event - The event name
566
+ * @param {T[]} arguments_ - The arguments to pass to the hook
567
+ */
568
+ async beforeHook(event, ...arguments_) {
569
+ await this.hook(`before:${event}`, ...arguments_);
570
+ }
571
+ /**
572
+ * Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.
573
+ * @param {string} event - The event name
574
+ * @param {T[]} arguments_ - The arguments to pass to the hook
575
+ */
576
+ async afterHook(event, ...arguments_) {
577
+ await this.hook(`after:${event}`, ...arguments_);
578
+ }
579
+ /**
580
+ * Calls all handlers for a specific event. This is an alias for `hook` and is provided for
581
+ * compatibility with other libraries that use the `callHook` method.
582
+ * @param {string} event
583
+ * @param {T[]} arguments_
584
+ * @returns {Promise<void>}
585
+ */
586
+ async callHook(event, ...arguments_) {
587
+ await this.hook(event, ...arguments_);
588
+ }
589
+ /**
590
+ * Gets all hooks for a specific event
591
+ * @param {string} event
592
+ * @returns {Hook[]}
593
+ */
594
+ getHooks(event) {
595
+ this.validateHookName(event);
596
+ if (!this.checkDeprecatedHook(event)) {
597
+ return void 0;
598
+ }
599
+ return this._hooks.get(event);
600
+ }
601
+ /**
602
+ * Removes all hooks
603
+ * @returns {void}
604
+ */
605
+ clearHooks() {
606
+ this._hooks.clear();
607
+ }
608
+ };
609
+ export {
610
+ Eventified,
611
+ Hookified
612
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hookified",
3
- "version": "1.12.1",
3
+ "version": "1.13.0",
4
4
  "description": "Event Emitting and Middleware Hooks",
5
5
  "type": "module",
6
6
  "main": "dist/node/index.cjs",
@@ -61,21 +61,21 @@
61
61
  },
62
62
  "homepage": "https://github.com/jaredwray/hookified#readme",
63
63
  "devDependencies": {
64
- "@biomejs/biome": "^2.2.4",
64
+ "@biomejs/biome": "^2.3.5",
65
65
  "@monstermann/tinybench-pretty-printer": "^0.2.0",
66
- "@types/node": "^24.5.2",
67
- "@vitest/coverage-v8": "^3.2.4",
68
- "docula": "^0.20.0",
66
+ "@types/node": "^24.10.0",
67
+ "@vitest/coverage-v8": "^4.0.8",
68
+ "docula": "^0.31.0",
69
69
  "emittery": "^1.2.0",
70
70
  "eventemitter3": "^5.0.1",
71
71
  "hookable": "^5.5.3",
72
- "pino": "^9.10.0",
73
- "rimraf": "^6.0.1",
74
- "tinybench": "^5.0.1",
72
+ "pino": "^10.1.0",
73
+ "rimraf": "^6.1.0",
74
+ "tinybench": "^5.1.0",
75
75
  "tsup": "^8.5.0",
76
- "tsx": "^4.20.5",
77
- "typescript": "^5.9.2",
78
- "vitest": "^3.2.4"
76
+ "tsx": "^4.20.6",
77
+ "typescript": "^5.9.3",
78
+ "vitest": "^4.0.8"
79
79
  },
80
80
  "files": [
81
81
  "dist",