hookified 1.15.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +958 -707
- package/dist/browser/index.global.js +283 -155
- package/dist/browser/index.global.js.map +1 -1
- package/dist/browser/index.js +286 -156
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +289 -157
- package/dist/node/index.d.cts +206 -79
- package/dist/node/index.d.ts +206 -79
- package/dist/node/index.js +286 -156
- package/package.json +6 -6
package/dist/node/index.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
var Eventified = class {
|
|
3
3
|
_eventListeners;
|
|
4
4
|
_maxListeners;
|
|
5
|
-
|
|
5
|
+
_eventLogger;
|
|
6
6
|
_throwOnEmitError = false;
|
|
7
|
-
_throwOnEmptyListeners =
|
|
7
|
+
_throwOnEmptyListeners = true;
|
|
8
8
|
_errorEvent = "error";
|
|
9
9
|
constructor(options) {
|
|
10
10
|
this._eventListeners = /* @__PURE__ */ new Map();
|
|
11
|
-
this._maxListeners =
|
|
12
|
-
this.
|
|
11
|
+
this._maxListeners = 0;
|
|
12
|
+
this._eventLogger = options?.eventLogger;
|
|
13
13
|
if (options?.throwOnEmitError !== void 0) {
|
|
14
14
|
this._throwOnEmitError = options.throwOnEmitError;
|
|
15
15
|
}
|
|
@@ -18,18 +18,18 @@ var Eventified = class {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* Gets the logger
|
|
21
|
+
* Gets the event logger
|
|
22
22
|
* @returns {Logger}
|
|
23
23
|
*/
|
|
24
|
-
get
|
|
25
|
-
return this.
|
|
24
|
+
get eventLogger() {
|
|
25
|
+
return this._eventLogger;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Sets the logger
|
|
29
|
-
* @param {Logger}
|
|
28
|
+
* Sets the event logger
|
|
29
|
+
* @param {Logger} eventLogger
|
|
30
30
|
*/
|
|
31
|
-
set
|
|
32
|
-
this.
|
|
31
|
+
set eventLogger(eventLogger) {
|
|
32
|
+
this._eventLogger = eventLogger;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
|
|
@@ -158,7 +158,7 @@ var Eventified = class {
|
|
|
158
158
|
}
|
|
159
159
|
const listeners = this._eventListeners.get(event);
|
|
160
160
|
if (listeners) {
|
|
161
|
-
if (listeners.length >= this._maxListeners) {
|
|
161
|
+
if (this._maxListeners > 0 && listeners.length >= this._maxListeners) {
|
|
162
162
|
console.warn(
|
|
163
163
|
`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
|
|
164
164
|
);
|
|
@@ -209,6 +209,7 @@ var Eventified = class {
|
|
|
209
209
|
result = true;
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
+
this.sendToEventLogger(event, arguments_);
|
|
212
213
|
if (event === this._errorEvent) {
|
|
213
214
|
const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
|
|
214
215
|
if (this._throwOnEmitError && !result) {
|
|
@@ -219,7 +220,6 @@ var Eventified = class {
|
|
|
219
220
|
}
|
|
220
221
|
}
|
|
221
222
|
}
|
|
222
|
-
this.sendLog(event, arguments_);
|
|
223
223
|
return result;
|
|
224
224
|
}
|
|
225
225
|
/**
|
|
@@ -249,12 +249,7 @@ var Eventified = class {
|
|
|
249
249
|
* @returns {void}
|
|
250
250
|
*/
|
|
251
251
|
setMaxListeners(n) {
|
|
252
|
-
this._maxListeners = n;
|
|
253
|
-
for (const listeners of this._eventListeners.values()) {
|
|
254
|
-
if (listeners.length > n) {
|
|
255
|
-
listeners.splice(n);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
252
|
+
this._maxListeners = n < 0 ? 0 : n;
|
|
258
253
|
}
|
|
259
254
|
/**
|
|
260
255
|
* Gets all listeners
|
|
@@ -272,8 +267,8 @@ var Eventified = class {
|
|
|
272
267
|
* @param {string | symbol} eventName - The event name that determines the log level
|
|
273
268
|
* @param {unknown} data - The data to log
|
|
274
269
|
*/
|
|
275
|
-
|
|
276
|
-
if (!this.
|
|
270
|
+
sendToEventLogger(eventName, data) {
|
|
271
|
+
if (!this._eventLogger) {
|
|
277
272
|
return;
|
|
278
273
|
}
|
|
279
274
|
let message;
|
|
@@ -290,33 +285,101 @@ var Eventified = class {
|
|
|
290
285
|
}
|
|
291
286
|
switch (eventName) {
|
|
292
287
|
case "error": {
|
|
293
|
-
this.
|
|
288
|
+
this._eventLogger.error?.(message, { event: eventName, data });
|
|
294
289
|
break;
|
|
295
290
|
}
|
|
296
291
|
case "warn": {
|
|
297
|
-
this.
|
|
292
|
+
this._eventLogger.warn?.(message, { event: eventName, data });
|
|
298
293
|
break;
|
|
299
294
|
}
|
|
300
295
|
case "trace": {
|
|
301
|
-
this.
|
|
296
|
+
this._eventLogger.trace?.(message, { event: eventName, data });
|
|
302
297
|
break;
|
|
303
298
|
}
|
|
304
299
|
case "debug": {
|
|
305
|
-
this.
|
|
300
|
+
this._eventLogger.debug?.(message, { event: eventName, data });
|
|
306
301
|
break;
|
|
307
302
|
}
|
|
308
303
|
case "fatal": {
|
|
309
|
-
this.
|
|
304
|
+
this._eventLogger.fatal?.(message, { event: eventName, data });
|
|
310
305
|
break;
|
|
311
306
|
}
|
|
312
307
|
default: {
|
|
313
|
-
this.
|
|
308
|
+
this._eventLogger.info?.(message, { event: eventName, data });
|
|
314
309
|
break;
|
|
315
310
|
}
|
|
316
311
|
}
|
|
317
312
|
}
|
|
318
313
|
};
|
|
319
314
|
|
|
315
|
+
// src/hooks/hook.ts
|
|
316
|
+
var Hook = class {
|
|
317
|
+
id;
|
|
318
|
+
event;
|
|
319
|
+
handler;
|
|
320
|
+
/**
|
|
321
|
+
* Creates a new Hook instance
|
|
322
|
+
* @param {string} event - The event name for the hook
|
|
323
|
+
* @param {HookFn} handler - The handler function for the hook
|
|
324
|
+
* @param {string} [id] - Optional unique identifier for the hook
|
|
325
|
+
*/
|
|
326
|
+
constructor(event, handler, id) {
|
|
327
|
+
this.id = id;
|
|
328
|
+
this.event = event;
|
|
329
|
+
this.handler = handler;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// src/hooks/waterfall-hook.ts
|
|
334
|
+
var WaterfallHook = class {
|
|
335
|
+
id;
|
|
336
|
+
event;
|
|
337
|
+
handler;
|
|
338
|
+
hooks;
|
|
339
|
+
_finalHandler;
|
|
340
|
+
/**
|
|
341
|
+
* Creates a new WaterfallHook instance
|
|
342
|
+
* @param {string} event - The event name for the hook
|
|
343
|
+
* @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result
|
|
344
|
+
* @param {string} [id] - Optional unique identifier for the hook
|
|
345
|
+
*/
|
|
346
|
+
constructor(event, finalHandler, id) {
|
|
347
|
+
this.id = id;
|
|
348
|
+
this.event = event;
|
|
349
|
+
this.hooks = [];
|
|
350
|
+
this._finalHandler = finalHandler;
|
|
351
|
+
this.handler = async (...arguments_) => {
|
|
352
|
+
const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_;
|
|
353
|
+
const results = [];
|
|
354
|
+
for (const hook of this.hooks) {
|
|
355
|
+
const result = await hook({ initialArgs, results: [...results] });
|
|
356
|
+
results.push({ hook, result });
|
|
357
|
+
}
|
|
358
|
+
await this._finalHandler({ initialArgs, results: [...results] });
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Adds a hook function to the end of the waterfall chain
|
|
363
|
+
* @param {WaterfallHookFn} hook - The hook function to add
|
|
364
|
+
*/
|
|
365
|
+
addHook(hook) {
|
|
366
|
+
this.hooks.push(hook);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Removes a specific hook function from the waterfall chain
|
|
370
|
+
* @param {WaterfallHookFn} hook - The hook function to remove
|
|
371
|
+
* @returns {boolean} true if the hook was found and removed
|
|
372
|
+
*/
|
|
373
|
+
removeHook(hook) {
|
|
374
|
+
const index = this.hooks.indexOf(hook);
|
|
375
|
+
if (index !== -1) {
|
|
376
|
+
this.hooks.splice(index, 1);
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
320
383
|
// src/index.ts
|
|
321
384
|
var Hookified = class extends Eventified {
|
|
322
385
|
_hooks;
|
|
@@ -324,9 +387,10 @@ var Hookified = class extends Eventified {
|
|
|
324
387
|
_enforceBeforeAfter = false;
|
|
325
388
|
_deprecatedHooks;
|
|
326
389
|
_allowDeprecated = true;
|
|
390
|
+
_useHookClone = true;
|
|
327
391
|
constructor(options) {
|
|
328
392
|
super({
|
|
329
|
-
|
|
393
|
+
eventLogger: options?.eventLogger,
|
|
330
394
|
throwOnEmitError: options?.throwOnEmitError,
|
|
331
395
|
throwOnEmptyListeners: options?.throwOnEmptyListeners
|
|
332
396
|
});
|
|
@@ -334,8 +398,6 @@ var Hookified = class extends Eventified {
|
|
|
334
398
|
this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
|
|
335
399
|
if (options?.throwOnHookError !== void 0) {
|
|
336
400
|
this._throwOnHookError = options.throwOnHookError;
|
|
337
|
-
} else if (options?.throwHookErrors !== void 0) {
|
|
338
|
-
this._throwOnHookError = options.throwHookErrors;
|
|
339
401
|
}
|
|
340
402
|
if (options?.enforceBeforeAfter !== void 0) {
|
|
341
403
|
this._enforceBeforeAfter = options.enforceBeforeAfter;
|
|
@@ -343,30 +405,17 @@ var Hookified = class extends Eventified {
|
|
|
343
405
|
if (options?.allowDeprecated !== void 0) {
|
|
344
406
|
this._allowDeprecated = options.allowDeprecated;
|
|
345
407
|
}
|
|
408
|
+
if (options?.useHookClone !== void 0) {
|
|
409
|
+
this._useHookClone = options.useHookClone;
|
|
410
|
+
}
|
|
346
411
|
}
|
|
347
412
|
/**
|
|
348
413
|
* Gets all hooks
|
|
349
|
-
* @returns {Map<string,
|
|
414
|
+
* @returns {Map<string, IHook[]>}
|
|
350
415
|
*/
|
|
351
416
|
get hooks() {
|
|
352
417
|
return this._hooks;
|
|
353
418
|
}
|
|
354
|
-
/**
|
|
355
|
-
* Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
|
|
356
|
-
* @returns {boolean}
|
|
357
|
-
* @deprecated - this will be deprecated in version 2. Please use throwOnHookError.
|
|
358
|
-
*/
|
|
359
|
-
get throwHookErrors() {
|
|
360
|
-
return this._throwOnHookError;
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
|
|
364
|
-
* @param {boolean} value
|
|
365
|
-
* @deprecated - this will be deprecated in version 2. Please use throwOnHookError.
|
|
366
|
-
*/
|
|
367
|
-
set throwHookErrors(value) {
|
|
368
|
-
this._throwOnHookError = value;
|
|
369
|
-
}
|
|
370
419
|
/**
|
|
371
420
|
* Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
|
|
372
421
|
* @returns {boolean}
|
|
@@ -425,157 +474,153 @@ var Hookified = class extends Eventified {
|
|
|
425
474
|
this._allowDeprecated = value;
|
|
426
475
|
}
|
|
427
476
|
/**
|
|
428
|
-
*
|
|
429
|
-
* @
|
|
430
|
-
* @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
|
|
431
|
-
*/
|
|
432
|
-
validateHookName(event) {
|
|
433
|
-
if (this._enforceBeforeAfter) {
|
|
434
|
-
const eventValue = event.trim().toLocaleLowerCase();
|
|
435
|
-
if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
|
|
436
|
-
throw new Error(
|
|
437
|
-
`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* Checks if a hook is deprecated and emits a warning if it is
|
|
444
|
-
* @param {string} event - The event name to check
|
|
445
|
-
* @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
|
|
477
|
+
* Gets whether hook objects are cloned before storing. Default is true.
|
|
478
|
+
* @returns {boolean}
|
|
446
479
|
*/
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const message = this._deprecatedHooks.get(event);
|
|
450
|
-
const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
|
|
451
|
-
this.emit("warn", { hook: event, message: warningMessage });
|
|
452
|
-
return this._allowDeprecated;
|
|
453
|
-
}
|
|
454
|
-
return true;
|
|
480
|
+
get useHookClone() {
|
|
481
|
+
return this._useHookClone;
|
|
455
482
|
}
|
|
456
483
|
/**
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
* @param {
|
|
460
|
-
* @returns {void}
|
|
484
|
+
* Sets whether hook objects are cloned before storing. Default is true.
|
|
485
|
+
* When false, the original IHook reference is stored directly.
|
|
486
|
+
* @param {boolean} value
|
|
461
487
|
*/
|
|
462
|
-
|
|
463
|
-
this.
|
|
488
|
+
set useHookClone(value) {
|
|
489
|
+
this._useHookClone = value;
|
|
464
490
|
}
|
|
465
491
|
/**
|
|
466
|
-
* Adds a handler function for a specific event
|
|
467
|
-
* @
|
|
468
|
-
* @
|
|
492
|
+
* Adds a handler function for a specific event.
|
|
493
|
+
* If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead.
|
|
494
|
+
* To register multiple hooks at once, use {@link onHooks}.
|
|
495
|
+
* @param {IHook} hook - the hook containing event name and handler
|
|
496
|
+
* @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
|
|
497
|
+
* @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
|
|
469
498
|
*/
|
|
470
|
-
|
|
471
|
-
this.validateHookName(
|
|
472
|
-
if (!this.checkDeprecatedHook(
|
|
473
|
-
return;
|
|
499
|
+
onHook(hook, options) {
|
|
500
|
+
this.validateHookName(hook.event);
|
|
501
|
+
if (!this.checkDeprecatedHook(hook.event)) {
|
|
502
|
+
return void 0;
|
|
474
503
|
}
|
|
475
|
-
const
|
|
504
|
+
const shouldClone = options?.useHookClone ?? this._useHookClone;
|
|
505
|
+
const entry = shouldClone ? { id: hook.id, event: hook.event, handler: hook.handler } : hook;
|
|
506
|
+
entry.id = entry.id ?? crypto.randomUUID();
|
|
507
|
+
const eventHandlers = this._hooks.get(hook.event);
|
|
476
508
|
if (eventHandlers) {
|
|
477
|
-
eventHandlers.
|
|
509
|
+
const existingIndex = eventHandlers.findIndex((h) => h.id === entry.id);
|
|
510
|
+
if (existingIndex !== -1) {
|
|
511
|
+
eventHandlers[existingIndex] = entry;
|
|
512
|
+
} else {
|
|
513
|
+
const position = options?.position ?? "Bottom";
|
|
514
|
+
if (position === "Top") {
|
|
515
|
+
eventHandlers.unshift(entry);
|
|
516
|
+
} else if (position === "Bottom") {
|
|
517
|
+
eventHandlers.push(entry);
|
|
518
|
+
} else {
|
|
519
|
+
const index = Math.max(0, Math.min(position, eventHandlers.length));
|
|
520
|
+
eventHandlers.splice(index, 0, entry);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
478
523
|
} else {
|
|
479
|
-
this._hooks.set(
|
|
524
|
+
this._hooks.set(hook.event, [entry]);
|
|
480
525
|
}
|
|
526
|
+
return entry;
|
|
481
527
|
}
|
|
482
528
|
/**
|
|
483
529
|
* Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
|
|
484
|
-
* @param {string} event
|
|
485
|
-
* @param {
|
|
530
|
+
* @param {string} event - the event name
|
|
531
|
+
* @param {HookFn} handler - the handler function
|
|
486
532
|
* @returns {void}
|
|
487
533
|
*/
|
|
488
534
|
addHook(event, handler) {
|
|
489
|
-
this.
|
|
535
|
+
this.onHook({ event, handler });
|
|
490
536
|
}
|
|
491
537
|
/**
|
|
492
|
-
* Adds
|
|
493
|
-
* @param {Array<
|
|
538
|
+
* Adds handler functions for specific events
|
|
539
|
+
* @param {Array<IHook>} hooks
|
|
540
|
+
* @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
|
|
494
541
|
* @returns {void}
|
|
495
542
|
*/
|
|
496
|
-
onHooks(hooks) {
|
|
543
|
+
onHooks(hooks, options) {
|
|
497
544
|
for (const hook of hooks) {
|
|
498
|
-
this.onHook(hook
|
|
545
|
+
this.onHook(hook, options);
|
|
499
546
|
}
|
|
500
547
|
}
|
|
501
548
|
/**
|
|
502
|
-
* Adds a handler function for a specific event that runs before all other handlers
|
|
503
|
-
*
|
|
504
|
-
* @param {
|
|
505
|
-
* @
|
|
549
|
+
* Adds a handler function for a specific event that runs before all other handlers.
|
|
550
|
+
* Equivalent to calling `onHook(hook, { position: "Top" })`.
|
|
551
|
+
* @param {IHook} hook - the hook containing event name and handler
|
|
552
|
+
* @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)
|
|
553
|
+
* @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
|
|
506
554
|
*/
|
|
507
|
-
prependHook(
|
|
508
|
-
this.
|
|
509
|
-
if (!this.checkDeprecatedHook(event)) {
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
const eventHandlers = this._hooks.get(event);
|
|
513
|
-
if (eventHandlers) {
|
|
514
|
-
eventHandlers.unshift(handler);
|
|
515
|
-
} else {
|
|
516
|
-
this._hooks.set(event, [handler]);
|
|
517
|
-
}
|
|
555
|
+
prependHook(hook, options) {
|
|
556
|
+
return this.onHook(hook, { ...options, position: "Top" });
|
|
518
557
|
}
|
|
519
558
|
/**
|
|
520
|
-
* Adds a handler that only executes once for a specific event before all other handlers
|
|
521
|
-
*
|
|
522
|
-
* @param handler
|
|
559
|
+
* Adds a handler that only executes once for a specific event before all other handlers.
|
|
560
|
+
* Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`.
|
|
561
|
+
* @param {IHook} hook - the hook containing event name and handler
|
|
562
|
+
* @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)
|
|
563
|
+
* @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
|
|
523
564
|
*/
|
|
524
|
-
prependOnceHook(
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
const hook = async (...arguments_) => {
|
|
530
|
-
this.removeHook(event, hook);
|
|
531
|
-
return handler(...arguments_);
|
|
565
|
+
prependOnceHook(hook, options) {
|
|
566
|
+
const wrappedHandler = async (...arguments_) => {
|
|
567
|
+
this.removeHook({ event: hook.event, handler: wrappedHandler });
|
|
568
|
+
return hook.handler(...arguments_);
|
|
532
569
|
};
|
|
533
|
-
this.
|
|
570
|
+
return this.onHook(
|
|
571
|
+
{ id: hook.id, event: hook.event, handler: wrappedHandler },
|
|
572
|
+
{ ...options, position: "Top" }
|
|
573
|
+
);
|
|
534
574
|
}
|
|
535
575
|
/**
|
|
536
576
|
* Adds a handler that only executes once for a specific event
|
|
537
|
-
* @param event
|
|
538
|
-
* @param handler
|
|
577
|
+
* @param {IHook} hook - the hook containing event name and handler
|
|
539
578
|
*/
|
|
540
|
-
onceHook(
|
|
541
|
-
this.validateHookName(event);
|
|
542
|
-
if (!this.checkDeprecatedHook(event)) {
|
|
579
|
+
onceHook(hook) {
|
|
580
|
+
this.validateHookName(hook.event);
|
|
581
|
+
if (!this.checkDeprecatedHook(hook.event)) {
|
|
543
582
|
return;
|
|
544
583
|
}
|
|
545
|
-
const
|
|
546
|
-
this.removeHook(event,
|
|
547
|
-
return handler(...arguments_);
|
|
584
|
+
const wrappedHandler = async (...arguments_) => {
|
|
585
|
+
this.removeHook({ event: hook.event, handler: wrappedHandler });
|
|
586
|
+
return hook.handler(...arguments_);
|
|
548
587
|
};
|
|
549
|
-
this.onHook(event,
|
|
588
|
+
this.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler });
|
|
550
589
|
}
|
|
551
590
|
/**
|
|
552
591
|
* Removes a handler function for a specific event
|
|
553
|
-
* @param {
|
|
554
|
-
* @
|
|
555
|
-
* @returns {void}
|
|
592
|
+
* @param {IHook} hook - the hook containing event name and handler to remove
|
|
593
|
+
* @returns {IHook | undefined} the removed hook, or undefined if not found
|
|
556
594
|
*/
|
|
557
|
-
removeHook(
|
|
558
|
-
this.validateHookName(event);
|
|
559
|
-
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
const eventHandlers = this._hooks.get(event);
|
|
595
|
+
removeHook(hook) {
|
|
596
|
+
this.validateHookName(hook.event);
|
|
597
|
+
const eventHandlers = this._hooks.get(hook.event);
|
|
563
598
|
if (eventHandlers) {
|
|
564
|
-
const index = eventHandlers.
|
|
599
|
+
const index = eventHandlers.findIndex((h) => h.handler === hook.handler);
|
|
565
600
|
if (index !== -1) {
|
|
566
601
|
eventHandlers.splice(index, 1);
|
|
602
|
+
if (eventHandlers.length === 0) {
|
|
603
|
+
this._hooks.delete(hook.event);
|
|
604
|
+
}
|
|
605
|
+
return { event: hook.event, handler: hook.handler };
|
|
567
606
|
}
|
|
568
607
|
}
|
|
608
|
+
return void 0;
|
|
569
609
|
}
|
|
570
610
|
/**
|
|
571
|
-
* Removes
|
|
572
|
-
* @param {Array<
|
|
573
|
-
* @returns {
|
|
611
|
+
* Removes multiple hook handlers
|
|
612
|
+
* @param {Array<IHook>} hooks
|
|
613
|
+
* @returns {IHook[]} the hooks that were successfully removed
|
|
574
614
|
*/
|
|
575
615
|
removeHooks(hooks) {
|
|
616
|
+
const removed = [];
|
|
576
617
|
for (const hook of hooks) {
|
|
577
|
-
this.removeHook(hook
|
|
618
|
+
const result = this.removeHook(hook);
|
|
619
|
+
if (result) {
|
|
620
|
+
removed.push(result);
|
|
621
|
+
}
|
|
578
622
|
}
|
|
623
|
+
return removed;
|
|
579
624
|
}
|
|
580
625
|
/**
|
|
581
626
|
* Calls all handlers for a specific event
|
|
@@ -590,9 +635,9 @@ var Hookified = class extends Eventified {
|
|
|
590
635
|
}
|
|
591
636
|
const eventHandlers = this._hooks.get(event);
|
|
592
637
|
if (eventHandlers) {
|
|
593
|
-
for (const
|
|
638
|
+
for (const hook of [...eventHandlers]) {
|
|
594
639
|
try {
|
|
595
|
-
await handler(...arguments_);
|
|
640
|
+
await hook.handler(...arguments_);
|
|
596
641
|
} catch (error) {
|
|
597
642
|
const message = `${event}: ${error.message}`;
|
|
598
643
|
this.emit("error", new Error(message));
|
|
@@ -620,12 +665,12 @@ var Hookified = class extends Eventified {
|
|
|
620
665
|
}
|
|
621
666
|
const eventHandlers = this._hooks.get(event);
|
|
622
667
|
if (eventHandlers) {
|
|
623
|
-
for (const
|
|
624
|
-
if (handler.constructor.name === "AsyncFunction") {
|
|
668
|
+
for (const hook of [...eventHandlers]) {
|
|
669
|
+
if (hook.handler.constructor.name === "AsyncFunction") {
|
|
625
670
|
continue;
|
|
626
671
|
}
|
|
627
672
|
try {
|
|
628
|
-
handler(...arguments_);
|
|
673
|
+
hook.handler(...arguments_);
|
|
629
674
|
} catch (error) {
|
|
630
675
|
const message = `${event}: ${error.message}`;
|
|
631
676
|
this.emit("error", new Error(message));
|
|
@@ -665,15 +710,54 @@ var Hookified = class extends Eventified {
|
|
|
665
710
|
/**
|
|
666
711
|
* Gets all hooks for a specific event
|
|
667
712
|
* @param {string} event
|
|
668
|
-
* @returns {
|
|
713
|
+
* @returns {IHook[]}
|
|
669
714
|
*/
|
|
670
715
|
getHooks(event) {
|
|
671
716
|
this.validateHookName(event);
|
|
672
|
-
if (!this.checkDeprecatedHook(event)) {
|
|
673
|
-
return void 0;
|
|
674
|
-
}
|
|
675
717
|
return this._hooks.get(event);
|
|
676
718
|
}
|
|
719
|
+
/**
|
|
720
|
+
* Gets a specific hook by id, searching across all events
|
|
721
|
+
* @param {string} id - the hook id
|
|
722
|
+
* @returns {IHook | undefined} the hook if found, or undefined
|
|
723
|
+
*/
|
|
724
|
+
getHook(id) {
|
|
725
|
+
for (const eventHandlers of this._hooks.values()) {
|
|
726
|
+
const found = eventHandlers.find((h) => h.id === id);
|
|
727
|
+
if (found) {
|
|
728
|
+
return found;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return void 0;
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Removes one or more hooks by id, searching across all events
|
|
735
|
+
* @param {string | string[]} id - the hook id or array of hook ids to remove
|
|
736
|
+
* @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found
|
|
737
|
+
*/
|
|
738
|
+
removeHookById(id) {
|
|
739
|
+
if (Array.isArray(id)) {
|
|
740
|
+
const removed = [];
|
|
741
|
+
for (const singleId of id) {
|
|
742
|
+
const result = this.removeHookById(singleId);
|
|
743
|
+
if (result && !Array.isArray(result)) {
|
|
744
|
+
removed.push(result);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return removed;
|
|
748
|
+
}
|
|
749
|
+
for (const [event, eventHandlers] of this._hooks.entries()) {
|
|
750
|
+
const index = eventHandlers.findIndex((h) => h.id === id);
|
|
751
|
+
if (index !== -1) {
|
|
752
|
+
const [removed] = eventHandlers.splice(index, 1);
|
|
753
|
+
if (eventHandlers.length === 0) {
|
|
754
|
+
this._hooks.delete(event);
|
|
755
|
+
}
|
|
756
|
+
return removed;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return void 0;
|
|
760
|
+
}
|
|
677
761
|
/**
|
|
678
762
|
* Removes all hooks
|
|
679
763
|
* @returns {void}
|
|
@@ -681,9 +765,55 @@ var Hookified = class extends Eventified {
|
|
|
681
765
|
clearHooks() {
|
|
682
766
|
this._hooks.clear();
|
|
683
767
|
}
|
|
768
|
+
/**
|
|
769
|
+
* Removes all hooks for a specific event and returns the removed hooks.
|
|
770
|
+
* @param {string} event - The event name to remove hooks for.
|
|
771
|
+
* @returns {IHook[]} the hooks that were removed
|
|
772
|
+
*/
|
|
773
|
+
removeEventHooks(event) {
|
|
774
|
+
this.validateHookName(event);
|
|
775
|
+
const eventHandlers = this._hooks.get(event);
|
|
776
|
+
if (eventHandlers) {
|
|
777
|
+
const removed = [...eventHandlers];
|
|
778
|
+
this._hooks.delete(event);
|
|
779
|
+
return removed;
|
|
780
|
+
}
|
|
781
|
+
return [];
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Validates hook event name if enforceBeforeAfter is enabled
|
|
785
|
+
* @param {string} event - The event name to validate
|
|
786
|
+
* @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
|
|
787
|
+
*/
|
|
788
|
+
validateHookName(event) {
|
|
789
|
+
if (this._enforceBeforeAfter) {
|
|
790
|
+
const eventValue = event.trim().toLocaleLowerCase();
|
|
791
|
+
if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
|
|
792
|
+
throw new Error(
|
|
793
|
+
`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Checks if a hook is deprecated and emits a warning if it is
|
|
800
|
+
* @param {string} event - The event name to check
|
|
801
|
+
* @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
|
|
802
|
+
*/
|
|
803
|
+
checkDeprecatedHook(event) {
|
|
804
|
+
if (this._deprecatedHooks.has(event)) {
|
|
805
|
+
const message = this._deprecatedHooks.get(event);
|
|
806
|
+
const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
|
|
807
|
+
this.emit("warn", { hook: event, message: warningMessage });
|
|
808
|
+
return this._allowDeprecated;
|
|
809
|
+
}
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
684
812
|
};
|
|
685
813
|
export {
|
|
686
814
|
Eventified,
|
|
687
|
-
|
|
815
|
+
Hook,
|
|
816
|
+
Hookified,
|
|
817
|
+
WaterfallHook
|
|
688
818
|
};
|
|
689
819
|
/* v8 ignore next -- @preserve */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hookified",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Event Emitting and Middleware Hooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/node/index.js",
|
|
@@ -69,16 +69,16 @@
|
|
|
69
69
|
},
|
|
70
70
|
"homepage": "https://github.com/jaredwray/hookified#readme",
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@biomejs/biome": "^2.
|
|
72
|
+
"@biomejs/biome": "^2.4.4",
|
|
73
73
|
"@monstermann/tinybench-pretty-printer": "^0.3.0",
|
|
74
|
-
"@types/node": "^25.0
|
|
74
|
+
"@types/node": "^25.3.0",
|
|
75
75
|
"@vitest/coverage-v8": "^4.0.18",
|
|
76
|
-
"docula": "^0.
|
|
76
|
+
"docula": "^0.41.1",
|
|
77
77
|
"emittery": "^1.2.0",
|
|
78
78
|
"eventemitter3": "^5.0.4",
|
|
79
79
|
"hookable": "^6.0.1",
|
|
80
|
-
"pino": "^10.3.
|
|
81
|
-
"rimraf": "^6.1.
|
|
80
|
+
"pino": "^10.3.1",
|
|
81
|
+
"rimraf": "^6.1.3",
|
|
82
82
|
"tinybench": "^6.0.0",
|
|
83
83
|
"tsup": "^8.5.1",
|
|
84
84
|
"tsx": "^4.21.0",
|