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.cjs
CHANGED
|
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Eventified: () => Eventified,
|
|
24
|
-
|
|
24
|
+
Hook: () => Hook,
|
|
25
|
+
Hookified: () => Hookified,
|
|
26
|
+
WaterfallHook: () => WaterfallHook
|
|
25
27
|
});
|
|
26
28
|
module.exports = __toCommonJS(index_exports);
|
|
27
29
|
|
|
@@ -29,14 +31,14 @@ module.exports = __toCommonJS(index_exports);
|
|
|
29
31
|
var Eventified = class {
|
|
30
32
|
_eventListeners;
|
|
31
33
|
_maxListeners;
|
|
32
|
-
|
|
34
|
+
_eventLogger;
|
|
33
35
|
_throwOnEmitError = false;
|
|
34
|
-
_throwOnEmptyListeners =
|
|
36
|
+
_throwOnEmptyListeners = true;
|
|
35
37
|
_errorEvent = "error";
|
|
36
38
|
constructor(options) {
|
|
37
39
|
this._eventListeners = /* @__PURE__ */ new Map();
|
|
38
|
-
this._maxListeners =
|
|
39
|
-
this.
|
|
40
|
+
this._maxListeners = 0;
|
|
41
|
+
this._eventLogger = options?.eventLogger;
|
|
40
42
|
if (options?.throwOnEmitError !== void 0) {
|
|
41
43
|
this._throwOnEmitError = options.throwOnEmitError;
|
|
42
44
|
}
|
|
@@ -45,18 +47,18 @@ var Eventified = class {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
48
|
-
* Gets the logger
|
|
50
|
+
* Gets the event logger
|
|
49
51
|
* @returns {Logger}
|
|
50
52
|
*/
|
|
51
|
-
get
|
|
52
|
-
return this.
|
|
53
|
+
get eventLogger() {
|
|
54
|
+
return this._eventLogger;
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
|
-
* Sets the logger
|
|
56
|
-
* @param {Logger}
|
|
57
|
+
* Sets the event logger
|
|
58
|
+
* @param {Logger} eventLogger
|
|
57
59
|
*/
|
|
58
|
-
set
|
|
59
|
-
this.
|
|
60
|
+
set eventLogger(eventLogger) {
|
|
61
|
+
this._eventLogger = eventLogger;
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
62
64
|
* Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
|
|
@@ -185,7 +187,7 @@ var Eventified = class {
|
|
|
185
187
|
}
|
|
186
188
|
const listeners = this._eventListeners.get(event);
|
|
187
189
|
if (listeners) {
|
|
188
|
-
if (listeners.length >= this._maxListeners) {
|
|
190
|
+
if (this._maxListeners > 0 && listeners.length >= this._maxListeners) {
|
|
189
191
|
console.warn(
|
|
190
192
|
`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
|
|
191
193
|
);
|
|
@@ -236,6 +238,7 @@ var Eventified = class {
|
|
|
236
238
|
result = true;
|
|
237
239
|
}
|
|
238
240
|
}
|
|
241
|
+
this.sendToEventLogger(event, arguments_);
|
|
239
242
|
if (event === this._errorEvent) {
|
|
240
243
|
const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
|
|
241
244
|
if (this._throwOnEmitError && !result) {
|
|
@@ -246,7 +249,6 @@ var Eventified = class {
|
|
|
246
249
|
}
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
|
-
this.sendLog(event, arguments_);
|
|
250
252
|
return result;
|
|
251
253
|
}
|
|
252
254
|
/**
|
|
@@ -276,12 +278,7 @@ var Eventified = class {
|
|
|
276
278
|
* @returns {void}
|
|
277
279
|
*/
|
|
278
280
|
setMaxListeners(n) {
|
|
279
|
-
this._maxListeners = n;
|
|
280
|
-
for (const listeners of this._eventListeners.values()) {
|
|
281
|
-
if (listeners.length > n) {
|
|
282
|
-
listeners.splice(n);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
281
|
+
this._maxListeners = n < 0 ? 0 : n;
|
|
285
282
|
}
|
|
286
283
|
/**
|
|
287
284
|
* Gets all listeners
|
|
@@ -299,8 +296,8 @@ var Eventified = class {
|
|
|
299
296
|
* @param {string | symbol} eventName - The event name that determines the log level
|
|
300
297
|
* @param {unknown} data - The data to log
|
|
301
298
|
*/
|
|
302
|
-
|
|
303
|
-
if (!this.
|
|
299
|
+
sendToEventLogger(eventName, data) {
|
|
300
|
+
if (!this._eventLogger) {
|
|
304
301
|
return;
|
|
305
302
|
}
|
|
306
303
|
let message;
|
|
@@ -317,33 +314,101 @@ var Eventified = class {
|
|
|
317
314
|
}
|
|
318
315
|
switch (eventName) {
|
|
319
316
|
case "error": {
|
|
320
|
-
this.
|
|
317
|
+
this._eventLogger.error?.(message, { event: eventName, data });
|
|
321
318
|
break;
|
|
322
319
|
}
|
|
323
320
|
case "warn": {
|
|
324
|
-
this.
|
|
321
|
+
this._eventLogger.warn?.(message, { event: eventName, data });
|
|
325
322
|
break;
|
|
326
323
|
}
|
|
327
324
|
case "trace": {
|
|
328
|
-
this.
|
|
325
|
+
this._eventLogger.trace?.(message, { event: eventName, data });
|
|
329
326
|
break;
|
|
330
327
|
}
|
|
331
328
|
case "debug": {
|
|
332
|
-
this.
|
|
329
|
+
this._eventLogger.debug?.(message, { event: eventName, data });
|
|
333
330
|
break;
|
|
334
331
|
}
|
|
335
332
|
case "fatal": {
|
|
336
|
-
this.
|
|
333
|
+
this._eventLogger.fatal?.(message, { event: eventName, data });
|
|
337
334
|
break;
|
|
338
335
|
}
|
|
339
336
|
default: {
|
|
340
|
-
this.
|
|
337
|
+
this._eventLogger.info?.(message, { event: eventName, data });
|
|
341
338
|
break;
|
|
342
339
|
}
|
|
343
340
|
}
|
|
344
341
|
}
|
|
345
342
|
};
|
|
346
343
|
|
|
344
|
+
// src/hooks/hook.ts
|
|
345
|
+
var Hook = class {
|
|
346
|
+
id;
|
|
347
|
+
event;
|
|
348
|
+
handler;
|
|
349
|
+
/**
|
|
350
|
+
* Creates a new Hook instance
|
|
351
|
+
* @param {string} event - The event name for the hook
|
|
352
|
+
* @param {HookFn} handler - The handler function for the hook
|
|
353
|
+
* @param {string} [id] - Optional unique identifier for the hook
|
|
354
|
+
*/
|
|
355
|
+
constructor(event, handler, id) {
|
|
356
|
+
this.id = id;
|
|
357
|
+
this.event = event;
|
|
358
|
+
this.handler = handler;
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// src/hooks/waterfall-hook.ts
|
|
363
|
+
var WaterfallHook = class {
|
|
364
|
+
id;
|
|
365
|
+
event;
|
|
366
|
+
handler;
|
|
367
|
+
hooks;
|
|
368
|
+
_finalHandler;
|
|
369
|
+
/**
|
|
370
|
+
* Creates a new WaterfallHook instance
|
|
371
|
+
* @param {string} event - The event name for the hook
|
|
372
|
+
* @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result
|
|
373
|
+
* @param {string} [id] - Optional unique identifier for the hook
|
|
374
|
+
*/
|
|
375
|
+
constructor(event, finalHandler, id) {
|
|
376
|
+
this.id = id;
|
|
377
|
+
this.event = event;
|
|
378
|
+
this.hooks = [];
|
|
379
|
+
this._finalHandler = finalHandler;
|
|
380
|
+
this.handler = async (...arguments_) => {
|
|
381
|
+
const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_;
|
|
382
|
+
const results = [];
|
|
383
|
+
for (const hook of this.hooks) {
|
|
384
|
+
const result = await hook({ initialArgs, results: [...results] });
|
|
385
|
+
results.push({ hook, result });
|
|
386
|
+
}
|
|
387
|
+
await this._finalHandler({ initialArgs, results: [...results] });
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Adds a hook function to the end of the waterfall chain
|
|
392
|
+
* @param {WaterfallHookFn} hook - The hook function to add
|
|
393
|
+
*/
|
|
394
|
+
addHook(hook) {
|
|
395
|
+
this.hooks.push(hook);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Removes a specific hook function from the waterfall chain
|
|
399
|
+
* @param {WaterfallHookFn} hook - The hook function to remove
|
|
400
|
+
* @returns {boolean} true if the hook was found and removed
|
|
401
|
+
*/
|
|
402
|
+
removeHook(hook) {
|
|
403
|
+
const index = this.hooks.indexOf(hook);
|
|
404
|
+
if (index !== -1) {
|
|
405
|
+
this.hooks.splice(index, 1);
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
|
|
347
412
|
// src/index.ts
|
|
348
413
|
var Hookified = class extends Eventified {
|
|
349
414
|
_hooks;
|
|
@@ -351,9 +416,10 @@ var Hookified = class extends Eventified {
|
|
|
351
416
|
_enforceBeforeAfter = false;
|
|
352
417
|
_deprecatedHooks;
|
|
353
418
|
_allowDeprecated = true;
|
|
419
|
+
_useHookClone = true;
|
|
354
420
|
constructor(options) {
|
|
355
421
|
super({
|
|
356
|
-
|
|
422
|
+
eventLogger: options?.eventLogger,
|
|
357
423
|
throwOnEmitError: options?.throwOnEmitError,
|
|
358
424
|
throwOnEmptyListeners: options?.throwOnEmptyListeners
|
|
359
425
|
});
|
|
@@ -361,8 +427,6 @@ var Hookified = class extends Eventified {
|
|
|
361
427
|
this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
|
|
362
428
|
if (options?.throwOnHookError !== void 0) {
|
|
363
429
|
this._throwOnHookError = options.throwOnHookError;
|
|
364
|
-
} else if (options?.throwHookErrors !== void 0) {
|
|
365
|
-
this._throwOnHookError = options.throwHookErrors;
|
|
366
430
|
}
|
|
367
431
|
if (options?.enforceBeforeAfter !== void 0) {
|
|
368
432
|
this._enforceBeforeAfter = options.enforceBeforeAfter;
|
|
@@ -370,30 +434,17 @@ var Hookified = class extends Eventified {
|
|
|
370
434
|
if (options?.allowDeprecated !== void 0) {
|
|
371
435
|
this._allowDeprecated = options.allowDeprecated;
|
|
372
436
|
}
|
|
437
|
+
if (options?.useHookClone !== void 0) {
|
|
438
|
+
this._useHookClone = options.useHookClone;
|
|
439
|
+
}
|
|
373
440
|
}
|
|
374
441
|
/**
|
|
375
442
|
* Gets all hooks
|
|
376
|
-
* @returns {Map<string,
|
|
443
|
+
* @returns {Map<string, IHook[]>}
|
|
377
444
|
*/
|
|
378
445
|
get hooks() {
|
|
379
446
|
return this._hooks;
|
|
380
447
|
}
|
|
381
|
-
/**
|
|
382
|
-
* Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
|
|
383
|
-
* @returns {boolean}
|
|
384
|
-
* @deprecated - this will be deprecated in version 2. Please use throwOnHookError.
|
|
385
|
-
*/
|
|
386
|
-
get throwHookErrors() {
|
|
387
|
-
return this._throwOnHookError;
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
|
|
391
|
-
* @param {boolean} value
|
|
392
|
-
* @deprecated - this will be deprecated in version 2. Please use throwOnHookError.
|
|
393
|
-
*/
|
|
394
|
-
set throwHookErrors(value) {
|
|
395
|
-
this._throwOnHookError = value;
|
|
396
|
-
}
|
|
397
448
|
/**
|
|
398
449
|
* Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
|
|
399
450
|
* @returns {boolean}
|
|
@@ -452,157 +503,153 @@ var Hookified = class extends Eventified {
|
|
|
452
503
|
this._allowDeprecated = value;
|
|
453
504
|
}
|
|
454
505
|
/**
|
|
455
|
-
*
|
|
456
|
-
* @
|
|
457
|
-
* @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
|
|
458
|
-
*/
|
|
459
|
-
validateHookName(event) {
|
|
460
|
-
if (this._enforceBeforeAfter) {
|
|
461
|
-
const eventValue = event.trim().toLocaleLowerCase();
|
|
462
|
-
if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
|
|
463
|
-
throw new Error(
|
|
464
|
-
`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Checks if a hook is deprecated and emits a warning if it is
|
|
471
|
-
* @param {string} event - The event name to check
|
|
472
|
-
* @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
|
|
506
|
+
* Gets whether hook objects are cloned before storing. Default is true.
|
|
507
|
+
* @returns {boolean}
|
|
473
508
|
*/
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const message = this._deprecatedHooks.get(event);
|
|
477
|
-
const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
|
|
478
|
-
this.emit("warn", { hook: event, message: warningMessage });
|
|
479
|
-
return this._allowDeprecated;
|
|
480
|
-
}
|
|
481
|
-
return true;
|
|
509
|
+
get useHookClone() {
|
|
510
|
+
return this._useHookClone;
|
|
482
511
|
}
|
|
483
512
|
/**
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* @param {
|
|
487
|
-
* @returns {void}
|
|
513
|
+
* Sets whether hook objects are cloned before storing. Default is true.
|
|
514
|
+
* When false, the original IHook reference is stored directly.
|
|
515
|
+
* @param {boolean} value
|
|
488
516
|
*/
|
|
489
|
-
|
|
490
|
-
this.
|
|
517
|
+
set useHookClone(value) {
|
|
518
|
+
this._useHookClone = value;
|
|
491
519
|
}
|
|
492
520
|
/**
|
|
493
|
-
* Adds a handler function for a specific event
|
|
494
|
-
* @
|
|
495
|
-
* @
|
|
521
|
+
* Adds a handler function for a specific event.
|
|
522
|
+
* If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead.
|
|
523
|
+
* To register multiple hooks at once, use {@link onHooks}.
|
|
524
|
+
* @param {IHook} hook - the hook containing event name and handler
|
|
525
|
+
* @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
|
|
526
|
+
* @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
|
|
496
527
|
*/
|
|
497
|
-
|
|
498
|
-
this.validateHookName(
|
|
499
|
-
if (!this.checkDeprecatedHook(
|
|
500
|
-
return;
|
|
528
|
+
onHook(hook, options) {
|
|
529
|
+
this.validateHookName(hook.event);
|
|
530
|
+
if (!this.checkDeprecatedHook(hook.event)) {
|
|
531
|
+
return void 0;
|
|
501
532
|
}
|
|
502
|
-
const
|
|
533
|
+
const shouldClone = options?.useHookClone ?? this._useHookClone;
|
|
534
|
+
const entry = shouldClone ? { id: hook.id, event: hook.event, handler: hook.handler } : hook;
|
|
535
|
+
entry.id = entry.id ?? crypto.randomUUID();
|
|
536
|
+
const eventHandlers = this._hooks.get(hook.event);
|
|
503
537
|
if (eventHandlers) {
|
|
504
|
-
eventHandlers.
|
|
538
|
+
const existingIndex = eventHandlers.findIndex((h) => h.id === entry.id);
|
|
539
|
+
if (existingIndex !== -1) {
|
|
540
|
+
eventHandlers[existingIndex] = entry;
|
|
541
|
+
} else {
|
|
542
|
+
const position = options?.position ?? "Bottom";
|
|
543
|
+
if (position === "Top") {
|
|
544
|
+
eventHandlers.unshift(entry);
|
|
545
|
+
} else if (position === "Bottom") {
|
|
546
|
+
eventHandlers.push(entry);
|
|
547
|
+
} else {
|
|
548
|
+
const index = Math.max(0, Math.min(position, eventHandlers.length));
|
|
549
|
+
eventHandlers.splice(index, 0, entry);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
505
552
|
} else {
|
|
506
|
-
this._hooks.set(
|
|
553
|
+
this._hooks.set(hook.event, [entry]);
|
|
507
554
|
}
|
|
555
|
+
return entry;
|
|
508
556
|
}
|
|
509
557
|
/**
|
|
510
558
|
* Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
|
|
511
|
-
* @param {string} event
|
|
512
|
-
* @param {
|
|
559
|
+
* @param {string} event - the event name
|
|
560
|
+
* @param {HookFn} handler - the handler function
|
|
513
561
|
* @returns {void}
|
|
514
562
|
*/
|
|
515
563
|
addHook(event, handler) {
|
|
516
|
-
this.
|
|
564
|
+
this.onHook({ event, handler });
|
|
517
565
|
}
|
|
518
566
|
/**
|
|
519
|
-
* Adds
|
|
520
|
-
* @param {Array<
|
|
567
|
+
* Adds handler functions for specific events
|
|
568
|
+
* @param {Array<IHook>} hooks
|
|
569
|
+
* @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
|
|
521
570
|
* @returns {void}
|
|
522
571
|
*/
|
|
523
|
-
onHooks(hooks) {
|
|
572
|
+
onHooks(hooks, options) {
|
|
524
573
|
for (const hook of hooks) {
|
|
525
|
-
this.onHook(hook
|
|
574
|
+
this.onHook(hook, options);
|
|
526
575
|
}
|
|
527
576
|
}
|
|
528
577
|
/**
|
|
529
|
-
* Adds a handler function for a specific event that runs before all other handlers
|
|
530
|
-
*
|
|
531
|
-
* @param {
|
|
532
|
-
* @
|
|
578
|
+
* Adds a handler function for a specific event that runs before all other handlers.
|
|
579
|
+
* Equivalent to calling `onHook(hook, { position: "Top" })`.
|
|
580
|
+
* @param {IHook} hook - the hook containing event name and handler
|
|
581
|
+
* @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)
|
|
582
|
+
* @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
|
|
533
583
|
*/
|
|
534
|
-
prependHook(
|
|
535
|
-
this.
|
|
536
|
-
if (!this.checkDeprecatedHook(event)) {
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
const eventHandlers = this._hooks.get(event);
|
|
540
|
-
if (eventHandlers) {
|
|
541
|
-
eventHandlers.unshift(handler);
|
|
542
|
-
} else {
|
|
543
|
-
this._hooks.set(event, [handler]);
|
|
544
|
-
}
|
|
584
|
+
prependHook(hook, options) {
|
|
585
|
+
return this.onHook(hook, { ...options, position: "Top" });
|
|
545
586
|
}
|
|
546
587
|
/**
|
|
547
|
-
* Adds a handler that only executes once for a specific event before all other handlers
|
|
548
|
-
*
|
|
549
|
-
* @param handler
|
|
588
|
+
* Adds a handler that only executes once for a specific event before all other handlers.
|
|
589
|
+
* Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`.
|
|
590
|
+
* @param {IHook} hook - the hook containing event name and handler
|
|
591
|
+
* @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)
|
|
592
|
+
* @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
|
|
550
593
|
*/
|
|
551
|
-
prependOnceHook(
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
const hook = async (...arguments_) => {
|
|
557
|
-
this.removeHook(event, hook);
|
|
558
|
-
return handler(...arguments_);
|
|
594
|
+
prependOnceHook(hook, options) {
|
|
595
|
+
const wrappedHandler = async (...arguments_) => {
|
|
596
|
+
this.removeHook({ event: hook.event, handler: wrappedHandler });
|
|
597
|
+
return hook.handler(...arguments_);
|
|
559
598
|
};
|
|
560
|
-
this.
|
|
599
|
+
return this.onHook(
|
|
600
|
+
{ id: hook.id, event: hook.event, handler: wrappedHandler },
|
|
601
|
+
{ ...options, position: "Top" }
|
|
602
|
+
);
|
|
561
603
|
}
|
|
562
604
|
/**
|
|
563
605
|
* Adds a handler that only executes once for a specific event
|
|
564
|
-
* @param event
|
|
565
|
-
* @param handler
|
|
606
|
+
* @param {IHook} hook - the hook containing event name and handler
|
|
566
607
|
*/
|
|
567
|
-
onceHook(
|
|
568
|
-
this.validateHookName(event);
|
|
569
|
-
if (!this.checkDeprecatedHook(event)) {
|
|
608
|
+
onceHook(hook) {
|
|
609
|
+
this.validateHookName(hook.event);
|
|
610
|
+
if (!this.checkDeprecatedHook(hook.event)) {
|
|
570
611
|
return;
|
|
571
612
|
}
|
|
572
|
-
const
|
|
573
|
-
this.removeHook(event,
|
|
574
|
-
return handler(...arguments_);
|
|
613
|
+
const wrappedHandler = async (...arguments_) => {
|
|
614
|
+
this.removeHook({ event: hook.event, handler: wrappedHandler });
|
|
615
|
+
return hook.handler(...arguments_);
|
|
575
616
|
};
|
|
576
|
-
this.onHook(event,
|
|
617
|
+
this.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler });
|
|
577
618
|
}
|
|
578
619
|
/**
|
|
579
620
|
* Removes a handler function for a specific event
|
|
580
|
-
* @param {
|
|
581
|
-
* @
|
|
582
|
-
* @returns {void}
|
|
621
|
+
* @param {IHook} hook - the hook containing event name and handler to remove
|
|
622
|
+
* @returns {IHook | undefined} the removed hook, or undefined if not found
|
|
583
623
|
*/
|
|
584
|
-
removeHook(
|
|
585
|
-
this.validateHookName(event);
|
|
586
|
-
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
const eventHandlers = this._hooks.get(event);
|
|
624
|
+
removeHook(hook) {
|
|
625
|
+
this.validateHookName(hook.event);
|
|
626
|
+
const eventHandlers = this._hooks.get(hook.event);
|
|
590
627
|
if (eventHandlers) {
|
|
591
|
-
const index = eventHandlers.
|
|
628
|
+
const index = eventHandlers.findIndex((h) => h.handler === hook.handler);
|
|
592
629
|
if (index !== -1) {
|
|
593
630
|
eventHandlers.splice(index, 1);
|
|
631
|
+
if (eventHandlers.length === 0) {
|
|
632
|
+
this._hooks.delete(hook.event);
|
|
633
|
+
}
|
|
634
|
+
return { event: hook.event, handler: hook.handler };
|
|
594
635
|
}
|
|
595
636
|
}
|
|
637
|
+
return void 0;
|
|
596
638
|
}
|
|
597
639
|
/**
|
|
598
|
-
* Removes
|
|
599
|
-
* @param {Array<
|
|
600
|
-
* @returns {
|
|
640
|
+
* Removes multiple hook handlers
|
|
641
|
+
* @param {Array<IHook>} hooks
|
|
642
|
+
* @returns {IHook[]} the hooks that were successfully removed
|
|
601
643
|
*/
|
|
602
644
|
removeHooks(hooks) {
|
|
645
|
+
const removed = [];
|
|
603
646
|
for (const hook of hooks) {
|
|
604
|
-
this.removeHook(hook
|
|
647
|
+
const result = this.removeHook(hook);
|
|
648
|
+
if (result) {
|
|
649
|
+
removed.push(result);
|
|
650
|
+
}
|
|
605
651
|
}
|
|
652
|
+
return removed;
|
|
606
653
|
}
|
|
607
654
|
/**
|
|
608
655
|
* Calls all handlers for a specific event
|
|
@@ -617,9 +664,9 @@ var Hookified = class extends Eventified {
|
|
|
617
664
|
}
|
|
618
665
|
const eventHandlers = this._hooks.get(event);
|
|
619
666
|
if (eventHandlers) {
|
|
620
|
-
for (const
|
|
667
|
+
for (const hook of [...eventHandlers]) {
|
|
621
668
|
try {
|
|
622
|
-
await handler(...arguments_);
|
|
669
|
+
await hook.handler(...arguments_);
|
|
623
670
|
} catch (error) {
|
|
624
671
|
const message = `${event}: ${error.message}`;
|
|
625
672
|
this.emit("error", new Error(message));
|
|
@@ -647,12 +694,12 @@ var Hookified = class extends Eventified {
|
|
|
647
694
|
}
|
|
648
695
|
const eventHandlers = this._hooks.get(event);
|
|
649
696
|
if (eventHandlers) {
|
|
650
|
-
for (const
|
|
651
|
-
if (handler.constructor.name === "AsyncFunction") {
|
|
697
|
+
for (const hook of [...eventHandlers]) {
|
|
698
|
+
if (hook.handler.constructor.name === "AsyncFunction") {
|
|
652
699
|
continue;
|
|
653
700
|
}
|
|
654
701
|
try {
|
|
655
|
-
handler(...arguments_);
|
|
702
|
+
hook.handler(...arguments_);
|
|
656
703
|
} catch (error) {
|
|
657
704
|
const message = `${event}: ${error.message}`;
|
|
658
705
|
this.emit("error", new Error(message));
|
|
@@ -692,15 +739,54 @@ var Hookified = class extends Eventified {
|
|
|
692
739
|
/**
|
|
693
740
|
* Gets all hooks for a specific event
|
|
694
741
|
* @param {string} event
|
|
695
|
-
* @returns {
|
|
742
|
+
* @returns {IHook[]}
|
|
696
743
|
*/
|
|
697
744
|
getHooks(event) {
|
|
698
745
|
this.validateHookName(event);
|
|
699
|
-
if (!this.checkDeprecatedHook(event)) {
|
|
700
|
-
return void 0;
|
|
701
|
-
}
|
|
702
746
|
return this._hooks.get(event);
|
|
703
747
|
}
|
|
748
|
+
/**
|
|
749
|
+
* Gets a specific hook by id, searching across all events
|
|
750
|
+
* @param {string} id - the hook id
|
|
751
|
+
* @returns {IHook | undefined} the hook if found, or undefined
|
|
752
|
+
*/
|
|
753
|
+
getHook(id) {
|
|
754
|
+
for (const eventHandlers of this._hooks.values()) {
|
|
755
|
+
const found = eventHandlers.find((h) => h.id === id);
|
|
756
|
+
if (found) {
|
|
757
|
+
return found;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return void 0;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Removes one or more hooks by id, searching across all events
|
|
764
|
+
* @param {string | string[]} id - the hook id or array of hook ids to remove
|
|
765
|
+
* @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found
|
|
766
|
+
*/
|
|
767
|
+
removeHookById(id) {
|
|
768
|
+
if (Array.isArray(id)) {
|
|
769
|
+
const removed = [];
|
|
770
|
+
for (const singleId of id) {
|
|
771
|
+
const result = this.removeHookById(singleId);
|
|
772
|
+
if (result && !Array.isArray(result)) {
|
|
773
|
+
removed.push(result);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return removed;
|
|
777
|
+
}
|
|
778
|
+
for (const [event, eventHandlers] of this._hooks.entries()) {
|
|
779
|
+
const index = eventHandlers.findIndex((h) => h.id === id);
|
|
780
|
+
if (index !== -1) {
|
|
781
|
+
const [removed] = eventHandlers.splice(index, 1);
|
|
782
|
+
if (eventHandlers.length === 0) {
|
|
783
|
+
this._hooks.delete(event);
|
|
784
|
+
}
|
|
785
|
+
return removed;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
return void 0;
|
|
789
|
+
}
|
|
704
790
|
/**
|
|
705
791
|
* Removes all hooks
|
|
706
792
|
* @returns {void}
|
|
@@ -708,10 +794,56 @@ var Hookified = class extends Eventified {
|
|
|
708
794
|
clearHooks() {
|
|
709
795
|
this._hooks.clear();
|
|
710
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* Removes all hooks for a specific event and returns the removed hooks.
|
|
799
|
+
* @param {string} event - The event name to remove hooks for.
|
|
800
|
+
* @returns {IHook[]} the hooks that were removed
|
|
801
|
+
*/
|
|
802
|
+
removeEventHooks(event) {
|
|
803
|
+
this.validateHookName(event);
|
|
804
|
+
const eventHandlers = this._hooks.get(event);
|
|
805
|
+
if (eventHandlers) {
|
|
806
|
+
const removed = [...eventHandlers];
|
|
807
|
+
this._hooks.delete(event);
|
|
808
|
+
return removed;
|
|
809
|
+
}
|
|
810
|
+
return [];
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Validates hook event name if enforceBeforeAfter is enabled
|
|
814
|
+
* @param {string} event - The event name to validate
|
|
815
|
+
* @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
|
|
816
|
+
*/
|
|
817
|
+
validateHookName(event) {
|
|
818
|
+
if (this._enforceBeforeAfter) {
|
|
819
|
+
const eventValue = event.trim().toLocaleLowerCase();
|
|
820
|
+
if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
|
|
821
|
+
throw new Error(
|
|
822
|
+
`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Checks if a hook is deprecated and emits a warning if it is
|
|
829
|
+
* @param {string} event - The event name to check
|
|
830
|
+
* @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
|
|
831
|
+
*/
|
|
832
|
+
checkDeprecatedHook(event) {
|
|
833
|
+
if (this._deprecatedHooks.has(event)) {
|
|
834
|
+
const message = this._deprecatedHooks.get(event);
|
|
835
|
+
const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
|
|
836
|
+
this.emit("warn", { hook: event, message: warningMessage });
|
|
837
|
+
return this._allowDeprecated;
|
|
838
|
+
}
|
|
839
|
+
return true;
|
|
840
|
+
}
|
|
711
841
|
};
|
|
712
842
|
// Annotate the CommonJS export names for ESM import in node:
|
|
713
843
|
0 && (module.exports = {
|
|
714
844
|
Eventified,
|
|
715
|
-
|
|
845
|
+
Hook,
|
|
846
|
+
Hookified,
|
|
847
|
+
WaterfallHook
|
|
716
848
|
});
|
|
717
849
|
/* v8 ignore next -- @preserve */
|