hookified 2.1.1 → 2.2.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,895 +1,943 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
-
5
- // src/eventified.ts
6
- var ERROR_EVENT = "error";
1
+ //#region \0@oxc-project+runtime@0.127.0/helpers/typeof.js
2
+ function _typeof(o) {
3
+ "@babel/helpers - typeof";
4
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
5
+ return typeof o;
6
+ } : function(o) {
7
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
8
+ }, _typeof(o);
9
+ }
10
+ //#endregion
11
+ //#region \0@oxc-project+runtime@0.127.0/helpers/toPrimitive.js
12
+ function toPrimitive(t, r) {
13
+ if ("object" != _typeof(t) || !t) return t;
14
+ var e = t[Symbol.toPrimitive];
15
+ if (void 0 !== e) {
16
+ var i = e.call(t, r || "default");
17
+ if ("object" != _typeof(i)) return i;
18
+ throw new TypeError("@@toPrimitive must return a primitive value.");
19
+ }
20
+ return ("string" === r ? String : Number)(t);
21
+ }
22
+ //#endregion
23
+ //#region \0@oxc-project+runtime@0.127.0/helpers/toPropertyKey.js
24
+ function toPropertyKey(t) {
25
+ var i = toPrimitive(t, "string");
26
+ return "symbol" == _typeof(i) ? i : i + "";
27
+ }
28
+ //#endregion
29
+ //#region \0@oxc-project+runtime@0.127.0/helpers/defineProperty.js
30
+ function _defineProperty(e, r, t) {
31
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
32
+ value: t,
33
+ enumerable: !0,
34
+ configurable: !0,
35
+ writable: !0
36
+ }) : e[r] = t, e;
37
+ }
38
+ //#endregion
39
+ //#region src/eventified.ts
40
+ const ERROR_EVENT = "error";
7
41
  var Eventified = class {
8
- constructor(options) {
9
- __publicField(this, "_eventListeners");
10
- __publicField(this, "_maxListeners");
11
- __publicField(this, "_eventLogger");
12
- __publicField(this, "_throwOnEmitError", false);
13
- __publicField(this, "_throwOnEmptyListeners", true);
14
- this._eventListeners = /* @__PURE__ */ new Map();
15
- this._maxListeners = 0;
16
- this._eventLogger = options?.eventLogger;
17
- if (options?.throwOnEmitError !== void 0) {
18
- this._throwOnEmitError = options.throwOnEmitError;
19
- }
20
- if (options?.throwOnEmptyListeners !== void 0) {
21
- this._throwOnEmptyListeners = options.throwOnEmptyListeners;
22
- }
23
- }
24
- /**
25
- * Gets the event logger
26
- * @returns {Logger}
27
- */
28
- get eventLogger() {
29
- return this._eventLogger;
30
- }
31
- /**
32
- * Sets the event logger
33
- * @param {Logger} eventLogger
34
- */
35
- set eventLogger(eventLogger) {
36
- this._eventLogger = eventLogger;
37
- }
38
- /**
39
- * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
40
- * @returns {boolean}
41
- */
42
- get throwOnEmitError() {
43
- return this._throwOnEmitError;
44
- }
45
- /**
46
- * Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
47
- * @param {boolean} value
48
- */
49
- set throwOnEmitError(value) {
50
- this._throwOnEmitError = value;
51
- }
52
- /**
53
- * Gets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.
54
- * @returns {boolean}
55
- */
56
- get throwOnEmptyListeners() {
57
- return this._throwOnEmptyListeners;
58
- }
59
- /**
60
- * Sets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.
61
- * @param {boolean} value
62
- */
63
- set throwOnEmptyListeners(value) {
64
- this._throwOnEmptyListeners = value;
65
- }
66
- /**
67
- * Adds a handler function for a specific event that will run only once
68
- * @param {string | symbol} eventName
69
- * @param {EventListener} listener
70
- * @returns {IEventEmitter} returns the instance of the class for chaining
71
- */
72
- once(eventName, listener) {
73
- const onceListener = (...arguments_) => {
74
- this.off(eventName, onceListener);
75
- listener(...arguments_);
76
- };
77
- this.on(eventName, onceListener);
78
- return this;
79
- }
80
- /**
81
- * Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners
82
- * @param {string} eventName The event name. Not required
83
- * @returns {number} The number of listeners
84
- */
85
- listenerCount(eventName) {
86
- if (eventName === void 0) {
87
- let count = 0;
88
- for (const entry2 of this._eventListeners.values()) {
89
- count += typeof entry2 === "function" ? 1 : entry2.length;
90
- }
91
- return count;
92
- }
93
- const entry = this._eventListeners.get(eventName);
94
- if (entry === void 0) {
95
- return 0;
96
- }
97
- return typeof entry === "function" ? 1 : entry.length;
98
- }
99
- /**
100
- * Gets an array of event names
101
- * @returns {Array<string | symbol>} An array of event names
102
- */
103
- eventNames() {
104
- return [...this._eventListeners.keys()];
105
- }
106
- /**
107
- * Gets an array of listeners for a specific event. If no event is provided, it returns all listeners
108
- * @param {string} [event] (Optional) The event name
109
- * @returns {EventListener[]} An array of listeners
110
- */
111
- rawListeners(event) {
112
- if (event === void 0) {
113
- return this.getAllListeners();
114
- }
115
- const entry = this._eventListeners.get(event);
116
- if (entry === void 0) {
117
- return [];
118
- }
119
- return typeof entry === "function" ? [entry] : entry;
120
- }
121
- /**
122
- * Prepends a listener to the beginning of the listeners array for the specified event
123
- * @param {string | symbol} eventName
124
- * @param {EventListener} listener
125
- * @returns {IEventEmitter} returns the instance of the class for chaining
126
- */
127
- prependListener(eventName, listener) {
128
- const existing = this._eventListeners.get(eventName);
129
- if (existing === void 0) {
130
- this._eventListeners.set(eventName, listener);
131
- } else if (typeof existing === "function") {
132
- this._eventListeners.set(eventName, [listener, existing]);
133
- } else {
134
- existing.unshift(listener);
135
- }
136
- return this;
137
- }
138
- /**
139
- * Prepends a one-time listener to the beginning of the listeners array for the specified event
140
- * @param {string | symbol} eventName
141
- * @param {EventListener} listener
142
- * @returns {IEventEmitter} returns the instance of the class for chaining
143
- */
144
- prependOnceListener(eventName, listener) {
145
- const onceListener = (...arguments_) => {
146
- this.off(eventName, onceListener);
147
- listener(...arguments_);
148
- };
149
- this.prependListener(eventName, onceListener);
150
- return this;
151
- }
152
- /**
153
- * Gets the maximum number of listeners that can be added for a single event
154
- * @returns {number} The maximum number of listeners
155
- */
156
- maxListeners() {
157
- return this._maxListeners;
158
- }
159
- /**
160
- * Adds a listener for a specific event. It is an alias for the on() method
161
- * @param {string | symbol} event
162
- * @param {EventListener} listener
163
- * @returns {IEventEmitter} returns the instance of the class for chaining
164
- */
165
- addListener(event, listener) {
166
- this.on(event, listener);
167
- return this;
168
- }
169
- /**
170
- * Adds a listener for a specific event
171
- * @param {string | symbol} event
172
- * @param {EventListener} listener
173
- * @returns {IEventEmitter} returns the instance of the class for chaining
174
- */
175
- on(event, listener) {
176
- const existing = this._eventListeners.get(event);
177
- if (existing === void 0) {
178
- this._eventListeners.set(event, listener);
179
- return this;
180
- }
181
- if (typeof existing === "function") {
182
- const arr = [existing, listener];
183
- this._eventListeners.set(event, arr);
184
- if (this._maxListeners > 0 && arr.length > this._maxListeners) {
185
- console.warn(
186
- `MaxListenersExceededWarning: Possible event memory leak detected. ${arr.length} ${event} listeners added. Use setMaxListeners() to increase limit.`
187
- );
188
- }
189
- } else {
190
- existing.push(listener);
191
- if (this._maxListeners > 0 && existing.length > this._maxListeners) {
192
- console.warn(
193
- `MaxListenersExceededWarning: Possible event memory leak detected. ${existing.length} ${event} listeners added. Use setMaxListeners() to increase limit.`
194
- );
195
- }
196
- }
197
- return this;
198
- }
199
- /**
200
- * Removes a listener for a specific event. It is an alias for the off() method
201
- * @param {string | symbol} event
202
- * @param {EventListener} listener
203
- * @returns {IEventEmitter} returns the instance of the class for chaining
204
- */
205
- removeListener(event, listener) {
206
- this.off(event, listener);
207
- return this;
208
- }
209
- /**
210
- * Removes a listener for a specific event
211
- * @param {string | symbol} event
212
- * @param {EventListener} listener
213
- * @returns {IEventEmitter} returns the instance of the class for chaining
214
- */
215
- off(event, listener) {
216
- const entry = this._eventListeners.get(event);
217
- if (entry === void 0) {
218
- return this;
219
- }
220
- if (typeof entry === "function") {
221
- if (entry === listener) {
222
- this._eventListeners.delete(event);
223
- }
224
- return this;
225
- }
226
- const index = entry.indexOf(listener);
227
- if (index !== -1) {
228
- if (entry.length === 2) {
229
- this._eventListeners.set(event, entry[1 - index]);
230
- } else if (entry.length === 1) {
231
- this._eventListeners.delete(event);
232
- } else {
233
- entry.splice(index, 1);
234
- }
235
- }
236
- return this;
237
- }
238
- /**
239
- * Calls all listeners for a specific event
240
- * @param {string | symbol} event
241
- * @param arguments_ The arguments to pass to the listeners
242
- * @returns {boolean} Returns true if the event had listeners, false otherwise
243
- */
244
- emit(event, ...arguments_) {
245
- let result = false;
246
- const entry = this._eventListeners.get(event);
247
- const argumentLength = arguments_.length;
248
- if (entry !== void 0) {
249
- if (typeof entry === "function") {
250
- if (argumentLength === 1) {
251
- entry(arguments_[0]);
252
- } else if (argumentLength === 2) {
253
- entry(arguments_[0], arguments_[1]);
254
- } else {
255
- entry(...arguments_);
256
- }
257
- } else {
258
- const len = entry.length;
259
- for (let i = 0; i < len; i++) {
260
- if (argumentLength === 1) {
261
- entry[i](arguments_[0]);
262
- } else if (argumentLength === 2) {
263
- entry[i](arguments_[0], arguments_[1]);
264
- } else {
265
- entry[i](...arguments_);
266
- }
267
- }
268
- }
269
- result = true;
270
- }
271
- if (this._eventLogger) {
272
- this.sendToEventLogger(event, arguments_);
273
- }
274
- if (!result && event === ERROR_EVENT) {
275
- const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
276
- if (this._throwOnEmitError || this._throwOnEmptyListeners) {
277
- throw error;
278
- }
279
- }
280
- return result;
281
- }
282
- /**
283
- * Gets all listeners for a specific event. If no event is provided, it returns all listeners
284
- * @param {string} [event] (Optional) The event name
285
- * @returns {EventListener[]} An array of listeners
286
- */
287
- listeners(event) {
288
- const entry = this._eventListeners.get(event);
289
- if (entry === void 0) {
290
- return [];
291
- }
292
- return typeof entry === "function" ? [entry] : entry;
293
- }
294
- /**
295
- * Removes all listeners for a specific event. If no event is provided, it removes all listeners
296
- * @param {string} [event] (Optional) The event name
297
- * @returns {IEventEmitter} returns the instance of the class for chaining
298
- */
299
- removeAllListeners(event) {
300
- if (event !== void 0) {
301
- this._eventListeners.delete(event);
302
- } else {
303
- this._eventListeners.clear();
304
- }
305
- return this;
306
- }
307
- /**
308
- * Sets the maximum number of listeners that can be added for a single event
309
- * @param {number} n The maximum number of listeners
310
- * @returns {void}
311
- */
312
- setMaxListeners(n) {
313
- this._maxListeners = n < 0 ? 0 : n;
314
- }
315
- /**
316
- * Gets all listeners
317
- * @returns {EventListener[]} An array of listeners
318
- */
319
- getAllListeners() {
320
- const result = [];
321
- for (const entry of this._eventListeners.values()) {
322
- if (typeof entry === "function") {
323
- result.push(entry);
324
- } else {
325
- for (let i = 0; i < entry.length; i++) {
326
- result.push(entry[i]);
327
- }
328
- }
329
- }
330
- return result;
331
- }
332
- /**
333
- * Sends a log message using the configured logger based on the event name
334
- * @param {string | symbol} eventName - The event name that determines the log level
335
- * @param {unknown} data - The data to log
336
- */
337
- sendToEventLogger(eventName, data) {
338
- if (!this._eventLogger) {
339
- return;
340
- }
341
- let message;
342
- if (typeof data === "string") {
343
- message = data;
344
- } else if (Array.isArray(data) && data.length > 0 && data[0] instanceof Error) {
345
- message = data[0].message;
346
- } else if (data instanceof Error) {
347
- message = data.message;
348
- } else if (Array.isArray(data) && data.length > 0 && typeof data[0]?.message === "string") {
349
- message = data[0].message;
350
- } else {
351
- message = JSON.stringify(data);
352
- }
353
- switch (eventName) {
354
- case "error": {
355
- this._eventLogger.error?.(message, { event: eventName, data });
356
- break;
357
- }
358
- case "warn": {
359
- this._eventLogger.warn?.(message, { event: eventName, data });
360
- break;
361
- }
362
- case "trace": {
363
- this._eventLogger.trace?.(message, { event: eventName, data });
364
- break;
365
- }
366
- case "debug": {
367
- this._eventLogger.debug?.(message, { event: eventName, data });
368
- break;
369
- }
370
- case "fatal": {
371
- this._eventLogger.fatal?.(message, { event: eventName, data });
372
- break;
373
- }
374
- default: {
375
- this._eventLogger.info?.(message, { event: eventName, data });
376
- break;
377
- }
378
- }
379
- }
42
+ constructor(options) {
43
+ _defineProperty(this, "_eventListeners", void 0);
44
+ _defineProperty(this, "_maxListeners", void 0);
45
+ _defineProperty(this, "_eventLogger", void 0);
46
+ _defineProperty(this, "_throwOnEmitError", false);
47
+ _defineProperty(this, "_throwOnEmptyListeners", true);
48
+ this._eventListeners = /* @__PURE__ */ new Map();
49
+ this._maxListeners = 0;
50
+ this._eventLogger = options?.eventLogger;
51
+ if (options?.throwOnEmitError !== void 0) this._throwOnEmitError = options.throwOnEmitError;
52
+ if (options?.throwOnEmptyListeners !== void 0) this._throwOnEmptyListeners = options.throwOnEmptyListeners;
53
+ }
54
+ /**
55
+ * Gets the event logger
56
+ * @returns {Logger}
57
+ */
58
+ get eventLogger() {
59
+ return this._eventLogger;
60
+ }
61
+ /**
62
+ * Sets the event logger
63
+ * @param {Logger} eventLogger
64
+ */
65
+ set eventLogger(eventLogger) {
66
+ this._eventLogger = eventLogger;
67
+ }
68
+ /**
69
+ * Gets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
70
+ * @returns {boolean}
71
+ */
72
+ get throwOnEmitError() {
73
+ return this._throwOnEmitError;
74
+ }
75
+ /**
76
+ * Sets whether an error should be thrown when an emit throws an error. Default is false and only emits an error event.
77
+ * @param {boolean} value
78
+ */
79
+ set throwOnEmitError(value) {
80
+ this._throwOnEmitError = value;
81
+ }
82
+ /**
83
+ * Gets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.
84
+ * @returns {boolean}
85
+ */
86
+ get throwOnEmptyListeners() {
87
+ return this._throwOnEmptyListeners;
88
+ }
89
+ /**
90
+ * Sets whether an error should be thrown when emitting 'error' event with no listeners. Default is false.
91
+ * @param {boolean} value
92
+ */
93
+ set throwOnEmptyListeners(value) {
94
+ this._throwOnEmptyListeners = value;
95
+ }
96
+ /**
97
+ * Adds a handler function for a specific event that will run only once
98
+ * @param {string | symbol} eventName
99
+ * @param {EventListener} listener
100
+ * @returns {IEventEmitter} returns the instance of the class for chaining
101
+ */
102
+ once(eventName, listener) {
103
+ const onceListener = (...arguments_) => {
104
+ this.off(eventName, onceListener);
105
+ listener(...arguments_);
106
+ };
107
+ this.on(eventName, onceListener);
108
+ return this;
109
+ }
110
+ /**
111
+ * Gets the number of listeners for a specific event. If no event is provided, it returns the total number of listeners
112
+ * @param {string} eventName The event name. Not required
113
+ * @returns {number} The number of listeners
114
+ */
115
+ listenerCount(eventName) {
116
+ if (eventName === void 0) {
117
+ let count = 0;
118
+ for (const entry of this._eventListeners.values()) count += typeof entry === "function" ? 1 : entry.length;
119
+ return count;
120
+ }
121
+ const entry = this._eventListeners.get(eventName);
122
+ if (entry === void 0) return 0;
123
+ return typeof entry === "function" ? 1 : entry.length;
124
+ }
125
+ /**
126
+ * Gets an array of event names
127
+ * @returns {Array<string | symbol>} An array of event names
128
+ */
129
+ eventNames() {
130
+ return [...this._eventListeners.keys()];
131
+ }
132
+ /**
133
+ * Gets an array of listeners for a specific event. If no event is provided, it returns all listeners
134
+ * @param {string} [event] (Optional) The event name
135
+ * @returns {EventListener[]} An array of listeners
136
+ */
137
+ rawListeners(event) {
138
+ if (event === void 0) return this.getAllListeners();
139
+ const entry = this._eventListeners.get(event);
140
+ if (entry === void 0) return [];
141
+ return typeof entry === "function" ? [entry] : entry;
142
+ }
143
+ /**
144
+ * Prepends a listener to the beginning of the listeners array for the specified event
145
+ * @param {string | symbol} eventName
146
+ * @param {EventListener} listener
147
+ * @returns {IEventEmitter} returns the instance of the class for chaining
148
+ */
149
+ prependListener(eventName, listener) {
150
+ const existing = this._eventListeners.get(eventName);
151
+ if (existing === void 0) this._eventListeners.set(eventName, listener);
152
+ else if (typeof existing === "function") this._eventListeners.set(eventName, [listener, existing]);
153
+ else existing.unshift(listener);
154
+ return this;
155
+ }
156
+ /**
157
+ * Prepends a one-time listener to the beginning of the listeners array for the specified event
158
+ * @param {string | symbol} eventName
159
+ * @param {EventListener} listener
160
+ * @returns {IEventEmitter} returns the instance of the class for chaining
161
+ */
162
+ prependOnceListener(eventName, listener) {
163
+ const onceListener = (...arguments_) => {
164
+ this.off(eventName, onceListener);
165
+ listener(...arguments_);
166
+ };
167
+ this.prependListener(eventName, onceListener);
168
+ return this;
169
+ }
170
+ /**
171
+ * Gets the maximum number of listeners that can be added for a single event
172
+ * @returns {number} The maximum number of listeners
173
+ */
174
+ maxListeners() {
175
+ return this._maxListeners;
176
+ }
177
+ /**
178
+ * Adds a listener for a specific event. It is an alias for the on() method
179
+ * @param {string | symbol} event
180
+ * @param {EventListener} listener
181
+ * @returns {IEventEmitter} returns the instance of the class for chaining
182
+ */
183
+ addListener(event, listener) {
184
+ this.on(event, listener);
185
+ return this;
186
+ }
187
+ /**
188
+ * Adds a listener for a specific event
189
+ * @param {string | symbol} event
190
+ * @param {EventListener} listener
191
+ * @returns {IEventEmitter} returns the instance of the class for chaining
192
+ */
193
+ on(event, listener) {
194
+ const existing = this._eventListeners.get(event);
195
+ if (existing === void 0) {
196
+ this._eventListeners.set(event, listener);
197
+ return this;
198
+ }
199
+ if (typeof existing === "function") {
200
+ const arr = [existing, listener];
201
+ this._eventListeners.set(event, arr);
202
+ if (this._maxListeners > 0 && arr.length > this._maxListeners) console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${arr.length} ${event} listeners added. Use setMaxListeners() to increase limit.`);
203
+ } else {
204
+ existing.push(listener);
205
+ if (this._maxListeners > 0 && existing.length > this._maxListeners) console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${existing.length} ${event} listeners added. Use setMaxListeners() to increase limit.`);
206
+ }
207
+ return this;
208
+ }
209
+ /**
210
+ * Removes a listener for a specific event. It is an alias for the off() method
211
+ * @param {string | symbol} event
212
+ * @param {EventListener} listener
213
+ * @returns {IEventEmitter} returns the instance of the class for chaining
214
+ */
215
+ removeListener(event, listener) {
216
+ this.off(event, listener);
217
+ return this;
218
+ }
219
+ /**
220
+ * Removes a listener for a specific event
221
+ * @param {string | symbol} event
222
+ * @param {EventListener} listener
223
+ * @returns {IEventEmitter} returns the instance of the class for chaining
224
+ */
225
+ off(event, listener) {
226
+ const entry = this._eventListeners.get(event);
227
+ if (entry === void 0) return this;
228
+ if (typeof entry === "function") {
229
+ if (entry === listener) this._eventListeners.delete(event);
230
+ return this;
231
+ }
232
+ const index = entry.indexOf(listener);
233
+ if (index !== -1) if (entry.length === 2) this._eventListeners.set(event, entry[1 - index]);
234
+ else if (entry.length === 1) this._eventListeners.delete(event);
235
+ else entry.splice(index, 1);
236
+ return this;
237
+ }
238
+ /**
239
+ * Calls all listeners for a specific event
240
+ * @param {string | symbol} event
241
+ * @param arguments_ The arguments to pass to the listeners
242
+ * @returns {boolean} Returns true if the event had listeners, false otherwise
243
+ */
244
+ emit(event, ...arguments_) {
245
+ let result = false;
246
+ const entry = this._eventListeners.get(event);
247
+ const argumentLength = arguments_.length;
248
+ if (entry !== void 0) {
249
+ if (typeof entry === "function") if (argumentLength === 1) entry(arguments_[0]);
250
+ else if (argumentLength === 2) entry(arguments_[0], arguments_[1]);
251
+ else entry(...arguments_);
252
+ else {
253
+ const len = entry.length;
254
+ for (let i = 0; i < len; i++) if (argumentLength === 1) entry[i](arguments_[0]);
255
+ else if (argumentLength === 2) entry[i](arguments_[0], arguments_[1]);
256
+ else entry[i](...arguments_);
257
+ }
258
+ result = true;
259
+ }
260
+ if (this._eventLogger) this.sendToEventLogger(event, arguments_);
261
+ if (!result && event === ERROR_EVENT) {
262
+ const error = arguments_[0] instanceof Error ? arguments_[0] : /* @__PURE__ */ new Error(`${arguments_[0]}`);
263
+ if (this._throwOnEmitError || this._throwOnEmptyListeners) throw error;
264
+ }
265
+ return result;
266
+ }
267
+ /**
268
+ * Gets all listeners for a specific event. If no event is provided, it returns all listeners
269
+ * @param {string} [event] (Optional) The event name
270
+ * @returns {EventListener[]} An array of listeners
271
+ */
272
+ listeners(event) {
273
+ const entry = this._eventListeners.get(event);
274
+ if (entry === void 0) return [];
275
+ return typeof entry === "function" ? [entry] : entry;
276
+ }
277
+ /**
278
+ * Removes all listeners for a specific event. If no event is provided, it removes all listeners
279
+ * @param {string} [event] (Optional) The event name
280
+ * @returns {IEventEmitter} returns the instance of the class for chaining
281
+ */
282
+ removeAllListeners(event) {
283
+ if (event !== void 0) this._eventListeners.delete(event);
284
+ else this._eventListeners.clear();
285
+ return this;
286
+ }
287
+ /**
288
+ * Sets the maximum number of listeners that can be added for a single event
289
+ * @param {number} n The maximum number of listeners
290
+ * @returns {void}
291
+ */
292
+ setMaxListeners(n) {
293
+ this._maxListeners = n < 0 ? 0 : n;
294
+ }
295
+ /**
296
+ * Gets all listeners
297
+ * @returns {EventListener[]} An array of listeners
298
+ */
299
+ getAllListeners() {
300
+ const result = [];
301
+ for (const entry of this._eventListeners.values()) if (typeof entry === "function") result.push(entry);
302
+ else for (let i = 0; i < entry.length; i++) result.push(entry[i]);
303
+ return result;
304
+ }
305
+ /**
306
+ * Sends a log message using the configured logger based on the event name
307
+ * @param {string | symbol} eventName - The event name that determines the log level
308
+ * @param {unknown} data - The data to log
309
+ */
310
+ sendToEventLogger(eventName, data) {
311
+ /* v8 ignore next 3 -- @preserve: guarded by caller */
312
+ if (!this._eventLogger) return;
313
+ let message;
314
+ /* v8 ignore next -- @preserve */
315
+ if (typeof data === "string") message = data;
316
+ else if (Array.isArray(data) && data.length > 0 && data[0] instanceof Error) message = data[0].message;
317
+ else if (data instanceof Error) message = data.message;
318
+ else if (Array.isArray(data) && data.length > 0 && typeof data[0]?.message === "string") message = data[0].message;
319
+ else message = JSON.stringify(data);
320
+ switch (eventName) {
321
+ case "error":
322
+ this._eventLogger.error?.(message, {
323
+ event: eventName,
324
+ data
325
+ });
326
+ break;
327
+ case "warn":
328
+ this._eventLogger.warn?.(message, {
329
+ event: eventName,
330
+ data
331
+ });
332
+ break;
333
+ case "trace":
334
+ this._eventLogger.trace?.(message, {
335
+ event: eventName,
336
+ data
337
+ });
338
+ break;
339
+ case "debug":
340
+ this._eventLogger.debug?.(message, {
341
+ event: eventName,
342
+ data
343
+ });
344
+ break;
345
+ case "fatal":
346
+ this._eventLogger.fatal?.(message, {
347
+ event: eventName,
348
+ data
349
+ });
350
+ break;
351
+ default:
352
+ this._eventLogger.info?.(message, {
353
+ event: eventName,
354
+ data
355
+ });
356
+ break;
357
+ }
358
+ }
380
359
  };
381
-
382
- // src/hooks/hook.ts
360
+ //#endregion
361
+ //#region src/hooks/hook.ts
362
+ /**
363
+ * Concrete implementation of the IHook interface.
364
+ * Provides a convenient class-based way to create hook entries.
365
+ */
383
366
  var Hook = class {
384
- /**
385
- * Creates a new Hook instance
386
- * @param {string} event - The event name for the hook
387
- * @param {HookFn} handler - The handler function for the hook
388
- * @param {string} [id] - Optional unique identifier for the hook
389
- */
390
- constructor(event, handler, id) {
391
- __publicField(this, "id");
392
- __publicField(this, "event");
393
- __publicField(this, "handler");
394
- this.id = id;
395
- this.event = event;
396
- this.handler = handler;
397
- }
367
+ /**
368
+ * Creates a new Hook instance
369
+ * @param {string} event - The event name for the hook
370
+ * @param {HookFn} handler - The handler function for the hook
371
+ * @param {string} [id] - Optional unique identifier for the hook
372
+ */
373
+ constructor(event, handler, id) {
374
+ _defineProperty(this, "id", void 0);
375
+ _defineProperty(this, "event", void 0);
376
+ _defineProperty(this, "handler", void 0);
377
+ this.id = id;
378
+ this.event = event;
379
+ this.handler = handler;
380
+ }
398
381
  };
399
-
400
- // src/hooks/waterfall-hook.ts
382
+ //#endregion
383
+ //#region src/hooks/parallel-hook.ts
384
+ /**
385
+ * A ParallelHook fans a single invocation out to many registered hook
386
+ * functions concurrently via Promise.allSettled, then calls a final
387
+ * handler with the aggregated results — including failures — keyed by
388
+ * the hook function reference for direct lookup. Each hook receives only
389
+ * the original arguments; hooks do not see each other's results.
390
+ *
391
+ * Implements IHook for compatibility with Hookified.onHook(); the final
392
+ * handler is called regardless of whether the parallel hook is invoked
393
+ * directly or through Hookified.hook().
394
+ *
395
+ * Generics tighten the per-hook signature when every hook in the set
396
+ * accepts the same `initialArgs` and returns the same result type.
397
+ * Both default to `any` for backwards compatibility.
398
+ *
399
+ * @template TArgs - The shape of `initialArgs` passed to every hook
400
+ * @template TResult - The result type each hook returns
401
+ */
402
+ var ParallelHook = class {
403
+ /**
404
+ * Creates a new ParallelHook instance
405
+ * @param {string} event - The event name for the hook
406
+ * @param {ParallelHookFinalFn<TArgs, TResult>} finalHandler - Called once with `{ initialArgs, results }` after all parallel hooks settle
407
+ * @param {string} [id] - Optional unique identifier for the hook
408
+ */
409
+ constructor(event, finalHandler, id) {
410
+ _defineProperty(this, "id", void 0);
411
+ _defineProperty(this, "event", void 0);
412
+ _defineProperty(this, "handler", void 0);
413
+ _defineProperty(this, "hooks", void 0);
414
+ _defineProperty(this, "_finalHandler", void 0);
415
+ this.id = id;
416
+ this.event = event;
417
+ this.hooks = [];
418
+ this._finalHandler = finalHandler;
419
+ this.handler = async (...arguments_) => {
420
+ const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_;
421
+ const snapshot = [...this.hooks];
422
+ const settled = await Promise.allSettled(snapshot.map((hook) => Promise.resolve().then(() => hook({ initialArgs }))));
423
+ const results = /* @__PURE__ */ new Map();
424
+ snapshot.forEach((hook, index) => {
425
+ const outcome = settled[index];
426
+ results.set(hook, outcome.status === "fulfilled" ? {
427
+ status: "fulfilled",
428
+ result: outcome.value
429
+ } : {
430
+ status: "rejected",
431
+ reason: outcome.reason
432
+ });
433
+ });
434
+ await this._finalHandler({
435
+ initialArgs,
436
+ results
437
+ });
438
+ };
439
+ }
440
+ /**
441
+ * Adds a hook function to the parallel set
442
+ * @param {ParallelHookFn<TArgs, TResult>} hook - The hook function to add
443
+ */
444
+ addHook(hook) {
445
+ this.hooks.push(hook);
446
+ }
447
+ /**
448
+ * Removes a specific hook function from the parallel set
449
+ * @param {ParallelHookFn<TArgs, TResult>} hook - The hook function to remove
450
+ * @returns {boolean} true if the hook was found and removed
451
+ */
452
+ removeHook(hook) {
453
+ const index = this.hooks.indexOf(hook);
454
+ if (index !== -1) {
455
+ this.hooks.splice(index, 1);
456
+ return true;
457
+ }
458
+ return false;
459
+ }
460
+ };
461
+ //#endregion
462
+ //#region src/hooks/waterfall-hook.ts
463
+ /**
464
+ * A WaterfallHook chains multiple hook functions sequentially,
465
+ * where each hook receives a context with the previous result,
466
+ * initial arguments, and accumulated results. After all hooks
467
+ * have executed, the final handler receives the transformed result.
468
+ * Implements IHook for compatibility with Hookified.onHook().
469
+ */
401
470
  var WaterfallHook = class {
402
- /**
403
- * Creates a new WaterfallHook instance
404
- * @param {string} event - The event name for the hook
405
- * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result
406
- * @param {string} [id] - Optional unique identifier for the hook
407
- */
408
- constructor(event, finalHandler, id) {
409
- __publicField(this, "id");
410
- __publicField(this, "event");
411
- __publicField(this, "handler");
412
- __publicField(this, "hooks");
413
- __publicField(this, "_finalHandler");
414
- this.id = id;
415
- this.event = event;
416
- this.hooks = [];
417
- this._finalHandler = finalHandler;
418
- this.handler = async (...arguments_) => {
419
- const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_;
420
- const results = [];
421
- for (const hook of this.hooks) {
422
- const result = await hook({ initialArgs, results: [...results] });
423
- results.push({ hook, result });
424
- }
425
- await this._finalHandler({ initialArgs, results: [...results] });
426
- };
427
- }
428
- /**
429
- * Adds a hook function to the end of the waterfall chain
430
- * @param {WaterfallHookFn} hook - The hook function to add
431
- */
432
- addHook(hook) {
433
- this.hooks.push(hook);
434
- }
435
- /**
436
- * Removes a specific hook function from the waterfall chain
437
- * @param {WaterfallHookFn} hook - The hook function to remove
438
- * @returns {boolean} true if the hook was found and removed
439
- */
440
- removeHook(hook) {
441
- const index = this.hooks.indexOf(hook);
442
- if (index !== -1) {
443
- this.hooks.splice(index, 1);
444
- return true;
445
- }
446
- return false;
447
- }
471
+ /**
472
+ * Creates a new WaterfallHook instance
473
+ * @param {string} event - The event name for the hook
474
+ * @param {WaterfallHookFn} finalHandler - The final handler function that receives the transformed result
475
+ * @param {string} [id] - Optional unique identifier for the hook
476
+ */
477
+ constructor(event, finalHandler, id) {
478
+ _defineProperty(this, "id", void 0);
479
+ _defineProperty(this, "event", void 0);
480
+ _defineProperty(this, "handler", void 0);
481
+ _defineProperty(this, "hooks", void 0);
482
+ _defineProperty(this, "_finalHandler", void 0);
483
+ this.id = id;
484
+ this.event = event;
485
+ this.hooks = [];
486
+ this._finalHandler = finalHandler;
487
+ this.handler = async (...arguments_) => {
488
+ const initialArgs = arguments_.length === 1 ? arguments_[0] : arguments_;
489
+ const results = [];
490
+ for (const hook of this.hooks) {
491
+ const result = await hook({
492
+ initialArgs,
493
+ results: [...results]
494
+ });
495
+ results.push({
496
+ hook,
497
+ result
498
+ });
499
+ }
500
+ await this._finalHandler({
501
+ initialArgs,
502
+ results: [...results]
503
+ });
504
+ };
505
+ }
506
+ /**
507
+ * Adds a hook function to the end of the waterfall chain
508
+ * @param {WaterfallHookFn} hook - The hook function to add
509
+ */
510
+ addHook(hook) {
511
+ this.hooks.push(hook);
512
+ }
513
+ /**
514
+ * Removes a specific hook function from the waterfall chain
515
+ * @param {WaterfallHookFn} hook - The hook function to remove
516
+ * @returns {boolean} true if the hook was found and removed
517
+ */
518
+ removeHook(hook) {
519
+ const index = this.hooks.indexOf(hook);
520
+ if (index !== -1) {
521
+ this.hooks.splice(index, 1);
522
+ return true;
523
+ }
524
+ return false;
525
+ }
448
526
  };
449
-
450
- // src/index.ts
527
+ //#endregion
528
+ //#region src/index.ts
451
529
  var Hookified = class extends Eventified {
452
- constructor(options) {
453
- super({
454
- eventLogger: options?.eventLogger,
455
- throwOnEmitError: options?.throwOnEmitError,
456
- throwOnEmptyListeners: options?.throwOnEmptyListeners
457
- });
458
- __publicField(this, "_hooks");
459
- __publicField(this, "_throwOnHookError", false);
460
- __publicField(this, "_enforceBeforeAfter", false);
461
- __publicField(this, "_deprecatedHooks");
462
- __publicField(this, "_allowDeprecated", true);
463
- __publicField(this, "_useHookClone", true);
464
- this._hooks = /* @__PURE__ */ new Map();
465
- this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
466
- if (options?.throwOnHookError !== void 0) {
467
- this._throwOnHookError = options.throwOnHookError;
468
- }
469
- if (options?.enforceBeforeAfter !== void 0) {
470
- this._enforceBeforeAfter = options.enforceBeforeAfter;
471
- }
472
- if (options?.allowDeprecated !== void 0) {
473
- this._allowDeprecated = options.allowDeprecated;
474
- }
475
- if (options?.useHookClone !== void 0) {
476
- this._useHookClone = options.useHookClone;
477
- }
478
- }
479
- /**
480
- * Gets all hooks
481
- * @returns {Map<string, IHook[]>}
482
- */
483
- get hooks() {
484
- return this._hooks;
485
- }
486
- /**
487
- * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
488
- * @returns {boolean}
489
- */
490
- get throwOnHookError() {
491
- return this._throwOnHookError;
492
- }
493
- /**
494
- * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
495
- * @param {boolean} value
496
- */
497
- set throwOnHookError(value) {
498
- this._throwOnHookError = value;
499
- }
500
- /**
501
- * Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
502
- * @returns {boolean}
503
- * @default false
504
- */
505
- get enforceBeforeAfter() {
506
- return this._enforceBeforeAfter;
507
- }
508
- /**
509
- * Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
510
- * @param {boolean} value
511
- */
512
- set enforceBeforeAfter(value) {
513
- this._enforceBeforeAfter = value;
514
- }
515
- /**
516
- * Gets the map of deprecated hook names to deprecation messages.
517
- * @returns {Map<string, string>}
518
- */
519
- get deprecatedHooks() {
520
- return this._deprecatedHooks;
521
- }
522
- /**
523
- * Sets the map of deprecated hook names to deprecation messages.
524
- * @param {Map<string, string>} value
525
- */
526
- set deprecatedHooks(value) {
527
- this._deprecatedHooks = value;
528
- }
529
- /**
530
- * Gets whether deprecated hooks are allowed to be registered and executed. Default is true.
531
- * @returns {boolean}
532
- */
533
- get allowDeprecated() {
534
- return this._allowDeprecated;
535
- }
536
- /**
537
- * Sets whether deprecated hooks are allowed to be registered and executed. Default is true.
538
- * @param {boolean} value
539
- */
540
- set allowDeprecated(value) {
541
- this._allowDeprecated = value;
542
- }
543
- /**
544
- * Gets whether hook objects are cloned before storing. Default is true.
545
- * @returns {boolean}
546
- */
547
- get useHookClone() {
548
- return this._useHookClone;
549
- }
550
- /**
551
- * Sets whether hook objects are cloned before storing. Default is true.
552
- * When false, the original IHook reference is stored directly.
553
- * @param {boolean} value
554
- */
555
- set useHookClone(value) {
556
- this._useHookClone = value;
557
- }
558
- onHook(hookOrEvent, optionsOrHandler) {
559
- let hook;
560
- let options;
561
- if (typeof hookOrEvent === "string") {
562
- if (typeof optionsOrHandler !== "function") {
563
- throw new TypeError(
564
- "When calling onHook(event, handler), the second argument must be a function"
565
- );
566
- }
567
- hook = { event: hookOrEvent, handler: optionsOrHandler };
568
- options = void 0;
569
- } else {
570
- hook = hookOrEvent;
571
- options = optionsOrHandler;
572
- }
573
- this.validateHookName(hook.event);
574
- if (!this.checkDeprecatedHook(hook.event)) {
575
- return void 0;
576
- }
577
- const shouldClone = options?.useHookClone ?? this._useHookClone;
578
- const entry = shouldClone ? { id: hook.id, event: hook.event, handler: hook.handler } : hook;
579
- entry.id = entry.id ?? crypto.randomUUID();
580
- const eventHandlers = this._hooks.get(hook.event);
581
- if (eventHandlers) {
582
- const existingIndex = eventHandlers.findIndex((h) => h.id === entry.id);
583
- if (existingIndex !== -1) {
584
- eventHandlers[existingIndex] = entry;
585
- } else {
586
- const position = options?.position ?? "Bottom";
587
- if (position === "Top") {
588
- eventHandlers.unshift(entry);
589
- } else if (position === "Bottom") {
590
- eventHandlers.push(entry);
591
- } else {
592
- const index = Math.max(0, Math.min(position, eventHandlers.length));
593
- eventHandlers.splice(index, 0, entry);
594
- }
595
- }
596
- } else {
597
- this._hooks.set(hook.event, [entry]);
598
- }
599
- return entry;
600
- }
601
- /**
602
- * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
603
- * @param {string} event - the event name
604
- * @param {HookFn} handler - the handler function
605
- * @returns {void}
606
- */
607
- addHook(event, handler) {
608
- this.onHook({ event, handler });
609
- }
610
- /**
611
- * Adds handler functions for specific events
612
- * @param {Array<IHook>} hooks
613
- * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
614
- * @returns {void}
615
- */
616
- onHooks(hooks, options) {
617
- for (const hook of hooks) {
618
- this.onHook(hook, options);
619
- }
620
- }
621
- /**
622
- * Adds a handler function for a specific event that runs before all other handlers.
623
- * Equivalent to calling `onHook(hook, { position: "Top" })`.
624
- * @param {IHook} hook - the hook containing event name and handler
625
- * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)
626
- * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
627
- */
628
- prependHook(hook, options) {
629
- return this.onHook(hook, { ...options, position: "Top" });
630
- }
631
- /**
632
- * Adds a handler that only executes once for a specific event before all other handlers.
633
- * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`.
634
- * @param {IHook} hook - the hook containing event name and handler
635
- * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)
636
- * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
637
- */
638
- prependOnceHook(hook, options) {
639
- const wrappedHandler = async (...arguments_) => {
640
- this.removeHook({ event: hook.event, handler: wrappedHandler });
641
- return hook.handler(...arguments_);
642
- };
643
- return this.onHook(
644
- { id: hook.id, event: hook.event, handler: wrappedHandler },
645
- { ...options, position: "Top" }
646
- );
647
- }
648
- /**
649
- * Adds a handler that only executes once for a specific event
650
- * @param {IHook} hook - the hook containing event name and handler
651
- */
652
- onceHook(hook) {
653
- this.validateHookName(hook.event);
654
- if (!this.checkDeprecatedHook(hook.event)) {
655
- return;
656
- }
657
- const wrappedHandler = async (...arguments_) => {
658
- this.removeHook({ event: hook.event, handler: wrappedHandler });
659
- return hook.handler(...arguments_);
660
- };
661
- this.onHook({ id: hook.id, event: hook.event, handler: wrappedHandler });
662
- }
663
- /**
664
- * Removes a handler function for a specific event
665
- * @param {IHook} hook - the hook containing event name and handler to remove
666
- * @returns {IHook | undefined} the removed hook, or undefined if not found
667
- */
668
- removeHook(hook) {
669
- this.validateHookName(hook.event);
670
- const eventHandlers = this._hooks.get(hook.event);
671
- if (eventHandlers) {
672
- const index = eventHandlers.findIndex((h) => h.handler === hook.handler);
673
- if (index !== -1) {
674
- eventHandlers.splice(index, 1);
675
- if (eventHandlers.length === 0) {
676
- this._hooks.delete(hook.event);
677
- }
678
- return { event: hook.event, handler: hook.handler };
679
- }
680
- }
681
- return void 0;
682
- }
683
- /**
684
- * Removes multiple hook handlers
685
- * @param {Array<IHook>} hooks
686
- * @returns {IHook[]} the hooks that were successfully removed
687
- */
688
- removeHooks(hooks) {
689
- const removed = [];
690
- for (const hook of hooks) {
691
- const result = this.removeHook(hook);
692
- if (result) {
693
- removed.push(result);
694
- }
695
- }
696
- return removed;
697
- }
698
- /**
699
- * Calls all handlers for a specific event
700
- * @param {string} event
701
- * @param {T[]} arguments_
702
- * @returns {Promise<void>}
703
- */
704
- async hook(event, ...arguments_) {
705
- this.validateHookName(event);
706
- if (!this.checkDeprecatedHook(event)) {
707
- return;
708
- }
709
- const eventHandlers = this._hooks.get(event);
710
- if (eventHandlers) {
711
- for (const hook of [...eventHandlers]) {
712
- try {
713
- await hook.handler(...arguments_);
714
- } catch (error) {
715
- const message = `${event}: ${error.message}`;
716
- this.emit("error", new Error(message));
717
- if (this._throwOnHookError) {
718
- throw new Error(message);
719
- }
720
- }
721
- }
722
- }
723
- }
724
- /**
725
- * Calls all synchronous handlers for a specific event.
726
- * Async handlers (declared with `async` keyword) are silently skipped.
727
- *
728
- * Note: The `hook` method is preferred as it executes both sync and async functions.
729
- * Use `hookSync` only when you specifically need synchronous execution.
730
- * @param {string} event
731
- * @param {T[]} arguments_
732
- * @returns {void}
733
- */
734
- hookSync(event, ...arguments_) {
735
- this.validateHookName(event);
736
- if (!this.checkDeprecatedHook(event)) {
737
- return;
738
- }
739
- const eventHandlers = this._hooks.get(event);
740
- if (eventHandlers) {
741
- for (const hook of [...eventHandlers]) {
742
- if (hook.handler.constructor.name === "AsyncFunction") {
743
- continue;
744
- }
745
- try {
746
- hook.handler(...arguments_);
747
- } catch (error) {
748
- const message = `${event}: ${error.message}`;
749
- this.emit("error", new Error(message));
750
- if (this._throwOnHookError) {
751
- throw new Error(message);
752
- }
753
- }
754
- }
755
- }
756
- }
757
- /**
758
- * Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.
759
- * @param {string} event - The event name
760
- * @param {T[]} arguments_ - The arguments to pass to the hook
761
- */
762
- async beforeHook(event, ...arguments_) {
763
- await this.hook(`before:${event}`, ...arguments_);
764
- }
765
- /**
766
- * Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.
767
- * @param {string} event - The event name
768
- * @param {T[]} arguments_ - The arguments to pass to the hook
769
- */
770
- async afterHook(event, ...arguments_) {
771
- await this.hook(`after:${event}`, ...arguments_);
772
- }
773
- /**
774
- * Calls all handlers for a specific event. This is an alias for `hook` and is provided for
775
- * compatibility with other libraries that use the `callHook` method.
776
- * @param {string} event
777
- * @param {T[]} arguments_
778
- * @returns {Promise<void>}
779
- */
780
- async callHook(event, ...arguments_) {
781
- await this.hook(event, ...arguments_);
782
- }
783
- /**
784
- * Gets all hooks for a specific event
785
- * @param {string} event
786
- * @returns {IHook[]}
787
- */
788
- getHooks(event) {
789
- this.validateHookName(event);
790
- return this._hooks.get(event);
791
- }
792
- /**
793
- * Gets a specific hook by id, searching across all events
794
- * @param {string} id - the hook id
795
- * @returns {IHook | undefined} the hook if found, or undefined
796
- */
797
- getHook(id) {
798
- for (const eventHandlers of this._hooks.values()) {
799
- const found = eventHandlers.find((h) => h.id === id);
800
- if (found) {
801
- return found;
802
- }
803
- }
804
- return void 0;
805
- }
806
- /**
807
- * Removes one or more hooks by id, searching across all events
808
- * @param {string | string[]} id - the hook id or array of hook ids to remove
809
- * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found
810
- */
811
- removeHookById(id) {
812
- if (Array.isArray(id)) {
813
- const removed = [];
814
- for (const singleId of id) {
815
- const result = this.removeHookById(singleId);
816
- if (result && !Array.isArray(result)) {
817
- removed.push(result);
818
- }
819
- }
820
- return removed;
821
- }
822
- for (const [event, eventHandlers] of this._hooks.entries()) {
823
- const index = eventHandlers.findIndex((h) => h.id === id);
824
- if (index !== -1) {
825
- const [removed] = eventHandlers.splice(index, 1);
826
- if (eventHandlers.length === 0) {
827
- this._hooks.delete(event);
828
- }
829
- return removed;
830
- }
831
- }
832
- return void 0;
833
- }
834
- /**
835
- * Removes all hooks
836
- * @returns {void}
837
- */
838
- clearHooks() {
839
- this._hooks.clear();
840
- }
841
- /**
842
- * Removes all hooks for a specific event and returns the removed hooks.
843
- * @param {string} event - The event name to remove hooks for.
844
- * @returns {IHook[]} the hooks that were removed
845
- */
846
- removeEventHooks(event) {
847
- this.validateHookName(event);
848
- const eventHandlers = this._hooks.get(event);
849
- if (eventHandlers) {
850
- const removed = [...eventHandlers];
851
- this._hooks.delete(event);
852
- return removed;
853
- }
854
- return [];
855
- }
856
- /**
857
- * Validates hook event name if enforceBeforeAfter is enabled
858
- * @param {string} event - The event name to validate
859
- * @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
860
- */
861
- validateHookName(event) {
862
- if (this._enforceBeforeAfter) {
863
- const eventValue = event.trim().toLocaleLowerCase();
864
- if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) {
865
- throw new Error(
866
- `Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`
867
- );
868
- }
869
- }
870
- }
871
- /**
872
- * Checks if a hook is deprecated and emits a warning if it is
873
- * @param {string} event - The event name to check
874
- * @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
875
- */
876
- checkDeprecatedHook(event) {
877
- if (this._deprecatedHooks.has(event)) {
878
- const message = this._deprecatedHooks.get(event);
879
- const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
880
- this.emit("warn", { hook: event, message: warningMessage });
881
- return this._allowDeprecated;
882
- }
883
- return true;
884
- }
885
- };
886
- export {
887
- Eventified,
888
- Hook,
889
- Hookified,
890
- WaterfallHook
530
+ constructor(options) {
531
+ super({
532
+ eventLogger: options?.eventLogger,
533
+ throwOnEmitError: options?.throwOnEmitError,
534
+ throwOnEmptyListeners: options?.throwOnEmptyListeners
535
+ });
536
+ _defineProperty(this, "_hooks", void 0);
537
+ _defineProperty(this, "_throwOnHookError", false);
538
+ _defineProperty(this, "_enforceBeforeAfter", false);
539
+ _defineProperty(this, "_deprecatedHooks", void 0);
540
+ _defineProperty(this, "_allowDeprecated", true);
541
+ _defineProperty(this, "_useHookClone", true);
542
+ this._hooks = /* @__PURE__ */ new Map();
543
+ this._deprecatedHooks = options?.deprecatedHooks ? new Map(options.deprecatedHooks) : /* @__PURE__ */ new Map();
544
+ if (options?.throwOnHookError !== void 0) this._throwOnHookError = options.throwOnHookError;
545
+ if (options?.enforceBeforeAfter !== void 0) this._enforceBeforeAfter = options.enforceBeforeAfter;
546
+ if (options?.allowDeprecated !== void 0) this._allowDeprecated = options.allowDeprecated;
547
+ if (options?.useHookClone !== void 0) this._useHookClone = options.useHookClone;
548
+ }
549
+ /**
550
+ * Gets all hooks
551
+ * @returns {Map<string, IHook[]>}
552
+ */
553
+ get hooks() {
554
+ return this._hooks;
555
+ }
556
+ /**
557
+ * Gets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
558
+ * @returns {boolean}
559
+ */
560
+ get throwOnHookError() {
561
+ return this._throwOnHookError;
562
+ }
563
+ /**
564
+ * Sets whether an error should be thrown when a hook throws an error. Default is false and only emits an error event.
565
+ * @param {boolean} value
566
+ */
567
+ set throwOnHookError(value) {
568
+ this._throwOnHookError = value;
569
+ }
570
+ /**
571
+ * Gets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
572
+ * @returns {boolean}
573
+ * @default false
574
+ */
575
+ get enforceBeforeAfter() {
576
+ return this._enforceBeforeAfter;
577
+ }
578
+ /**
579
+ * Sets whether to enforce that all hook names start with 'before' or 'after'. Default is false.
580
+ * @param {boolean} value
581
+ */
582
+ set enforceBeforeAfter(value) {
583
+ this._enforceBeforeAfter = value;
584
+ }
585
+ /**
586
+ * Gets the map of deprecated hook names to deprecation messages.
587
+ * @returns {Map<string, string>}
588
+ */
589
+ get deprecatedHooks() {
590
+ return this._deprecatedHooks;
591
+ }
592
+ /**
593
+ * Sets the map of deprecated hook names to deprecation messages.
594
+ * @param {Map<string, string>} value
595
+ */
596
+ set deprecatedHooks(value) {
597
+ this._deprecatedHooks = value;
598
+ }
599
+ /**
600
+ * Gets whether deprecated hooks are allowed to be registered and executed. Default is true.
601
+ * @returns {boolean}
602
+ */
603
+ get allowDeprecated() {
604
+ return this._allowDeprecated;
605
+ }
606
+ /**
607
+ * Sets whether deprecated hooks are allowed to be registered and executed. Default is true.
608
+ * @param {boolean} value
609
+ */
610
+ set allowDeprecated(value) {
611
+ this._allowDeprecated = value;
612
+ }
613
+ /**
614
+ * Gets whether hook objects are cloned before storing. Default is true.
615
+ * @returns {boolean}
616
+ */
617
+ get useHookClone() {
618
+ return this._useHookClone;
619
+ }
620
+ /**
621
+ * Sets whether hook objects are cloned before storing. Default is true.
622
+ * When false, the original IHook reference is stored directly.
623
+ * @param {boolean} value
624
+ */
625
+ set useHookClone(value) {
626
+ this._useHookClone = value;
627
+ }
628
+ onHook(hookOrEvent, optionsOrHandler) {
629
+ let hook;
630
+ let options;
631
+ if (typeof hookOrEvent === "string") {
632
+ if (typeof optionsOrHandler !== "function") throw new TypeError("When calling onHook(event, handler), the second argument must be a function");
633
+ hook = {
634
+ event: hookOrEvent,
635
+ handler: optionsOrHandler
636
+ };
637
+ options = void 0;
638
+ } else {
639
+ hook = hookOrEvent;
640
+ options = optionsOrHandler;
641
+ }
642
+ this.validateHookName(hook.event);
643
+ if (!this.checkDeprecatedHook(hook.event)) return;
644
+ const entry = options?.useHookClone ?? this._useHookClone ? {
645
+ id: hook.id,
646
+ event: hook.event,
647
+ handler: hook.handler
648
+ } : hook;
649
+ entry.id = entry.id ?? crypto.randomUUID();
650
+ const eventHandlers = this._hooks.get(hook.event);
651
+ if (eventHandlers) {
652
+ const existingIndex = eventHandlers.findIndex((h) => h.id === entry.id);
653
+ if (existingIndex !== -1) eventHandlers[existingIndex] = entry;
654
+ else {
655
+ const position = options?.position ?? "Bottom";
656
+ if (position === "Top") eventHandlers.unshift(entry);
657
+ else if (position === "Bottom") eventHandlers.push(entry);
658
+ else {
659
+ const index = Math.max(0, Math.min(position, eventHandlers.length));
660
+ eventHandlers.splice(index, 0, entry);
661
+ }
662
+ }
663
+ } else this._hooks.set(hook.event, [entry]);
664
+ return entry;
665
+ }
666
+ /**
667
+ * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
668
+ * @param {string} event - the event name
669
+ * @param {HookFn} handler - the handler function
670
+ * @returns {void}
671
+ */
672
+ addHook(event, handler) {
673
+ this.onHook({
674
+ event,
675
+ handler
676
+ });
677
+ }
678
+ /**
679
+ * Adds handler functions for specific events
680
+ * @param {Array<IHook>} hooks
681
+ * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
682
+ * @returns {void}
683
+ */
684
+ onHooks(hooks, options) {
685
+ for (const hook of hooks) this.onHook(hook, options);
686
+ }
687
+ /**
688
+ * Adds a handler function for a specific event that runs before all other handlers.
689
+ * Equivalent to calling `onHook(hook, { position: "Top" })`.
690
+ * @param {IHook} hook - the hook containing event name and handler
691
+ * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)
692
+ * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
693
+ */
694
+ prependHook(hook, options) {
695
+ return this.onHook(hook, {
696
+ ...options,
697
+ position: "Top"
698
+ });
699
+ }
700
+ /**
701
+ * Adds a handler that only executes once for a specific event before all other handlers.
702
+ * Equivalent to calling `onHook` with a self-removing wrapper and `{ position: "Top" }`.
703
+ * @param {IHook} hook - the hook containing event name and handler
704
+ * @param {PrependHookOptions} [options] - optional per-call options (e.g., useHookClone override)
705
+ * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
706
+ */
707
+ prependOnceHook(hook, options) {
708
+ const wrappedHandler = async (...arguments_) => {
709
+ this.removeHook({
710
+ event: hook.event,
711
+ handler: wrappedHandler
712
+ });
713
+ return hook.handler(...arguments_);
714
+ };
715
+ return this.onHook({
716
+ id: hook.id,
717
+ event: hook.event,
718
+ handler: wrappedHandler
719
+ }, {
720
+ ...options,
721
+ position: "Top"
722
+ });
723
+ }
724
+ /**
725
+ * Adds a handler that only executes once for a specific event
726
+ * @param {IHook} hook - the hook containing event name and handler
727
+ */
728
+ onceHook(hook) {
729
+ this.validateHookName(hook.event);
730
+ if (!this.checkDeprecatedHook(hook.event)) return;
731
+ const wrappedHandler = async (...arguments_) => {
732
+ this.removeHook({
733
+ event: hook.event,
734
+ handler: wrappedHandler
735
+ });
736
+ return hook.handler(...arguments_);
737
+ };
738
+ this.onHook({
739
+ id: hook.id,
740
+ event: hook.event,
741
+ handler: wrappedHandler
742
+ });
743
+ }
744
+ /**
745
+ * Removes a handler function for a specific event
746
+ * @param {IHook} hook - the hook containing event name and handler to remove
747
+ * @returns {IHook | undefined} the removed hook, or undefined if not found
748
+ */
749
+ removeHook(hook) {
750
+ this.validateHookName(hook.event);
751
+ const eventHandlers = this._hooks.get(hook.event);
752
+ if (eventHandlers) {
753
+ const index = eventHandlers.findIndex((h) => h.handler === hook.handler);
754
+ if (index !== -1) {
755
+ eventHandlers.splice(index, 1);
756
+ if (eventHandlers.length === 0) this._hooks.delete(hook.event);
757
+ return {
758
+ event: hook.event,
759
+ handler: hook.handler
760
+ };
761
+ }
762
+ }
763
+ }
764
+ /**
765
+ * Removes multiple hook handlers
766
+ * @param {Array<IHook>} hooks
767
+ * @returns {IHook[]} the hooks that were successfully removed
768
+ */
769
+ removeHooks(hooks) {
770
+ const removed = [];
771
+ for (const hook of hooks) {
772
+ const result = this.removeHook(hook);
773
+ if (result) removed.push(result);
774
+ }
775
+ return removed;
776
+ }
777
+ /**
778
+ * Calls all handlers for a specific event
779
+ * @param {string} event
780
+ * @param {T[]} arguments_
781
+ * @returns {Promise<void>}
782
+ */
783
+ async hook(event, ...arguments_) {
784
+ this.validateHookName(event);
785
+ if (!this.checkDeprecatedHook(event)) return;
786
+ const eventHandlers = this._hooks.get(event);
787
+ if (eventHandlers) for (const hook of [...eventHandlers]) try {
788
+ await hook.handler(...arguments_);
789
+ } catch (error) {
790
+ const message = `${event}: ${error.message}`;
791
+ this.emit("error", new Error(message));
792
+ if (this._throwOnHookError) throw new Error(message);
793
+ }
794
+ }
795
+ /**
796
+ * Calls all synchronous handlers for a specific event.
797
+ * Async handlers (declared with `async` keyword) are silently skipped.
798
+ *
799
+ * Note: The `hook` method is preferred as it executes both sync and async functions.
800
+ * Use `hookSync` only when you specifically need synchronous execution.
801
+ * @param {string} event
802
+ * @param {T[]} arguments_
803
+ * @returns {void}
804
+ */
805
+ hookSync(event, ...arguments_) {
806
+ this.validateHookName(event);
807
+ if (!this.checkDeprecatedHook(event)) return;
808
+ const eventHandlers = this._hooks.get(event);
809
+ if (eventHandlers) for (const hook of [...eventHandlers]) {
810
+ if (hook.handler.constructor.name === "AsyncFunction") continue;
811
+ try {
812
+ hook.handler(...arguments_);
813
+ } catch (error) {
814
+ const message = `${event}: ${error.message}`;
815
+ this.emit("error", new Error(message));
816
+ if (this._throwOnHookError) throw new Error(message);
817
+ }
818
+ }
819
+ }
820
+ /**
821
+ * Prepends the word `before` to your hook. Example is event is `test`, the before hook is `before:test`.
822
+ * @param {string} event - The event name
823
+ * @param {T[]} arguments_ - The arguments to pass to the hook
824
+ */
825
+ async beforeHook(event, ...arguments_) {
826
+ await this.hook(`before:${event}`, ...arguments_);
827
+ }
828
+ /**
829
+ * Prepends the word `after` to your hook. Example is event is `test`, the after hook is `after:test`.
830
+ * @param {string} event - The event name
831
+ * @param {T[]} arguments_ - The arguments to pass to the hook
832
+ */
833
+ async afterHook(event, ...arguments_) {
834
+ await this.hook(`after:${event}`, ...arguments_);
835
+ }
836
+ /**
837
+ * Calls all handlers for a specific event. This is an alias for `hook` and is provided for
838
+ * compatibility with other libraries that use the `callHook` method.
839
+ * @param {string} event
840
+ * @param {T[]} arguments_
841
+ * @returns {Promise<void>}
842
+ */
843
+ async callHook(event, ...arguments_) {
844
+ await this.hook(event, ...arguments_);
845
+ }
846
+ /**
847
+ * Gets all hooks for a specific event
848
+ * @param {string} event
849
+ * @returns {IHook[]}
850
+ */
851
+ getHooks(event) {
852
+ this.validateHookName(event);
853
+ return this._hooks.get(event);
854
+ }
855
+ /**
856
+ * Gets a specific hook by id, searching across all events
857
+ * @param {string} id - the hook id
858
+ * @returns {IHook | undefined} the hook if found, or undefined
859
+ */
860
+ getHook(id) {
861
+ for (const eventHandlers of this._hooks.values()) {
862
+ const found = eventHandlers.find((h) => h.id === id);
863
+ if (found) return found;
864
+ }
865
+ }
866
+ /**
867
+ * Removes one or more hooks by id, searching across all events
868
+ * @param {string | string[]} id - the hook id or array of hook ids to remove
869
+ * @returns {IHook | IHook[] | undefined} the removed hook(s), or undefined/empty array if not found
870
+ */
871
+ removeHookById(id) {
872
+ if (Array.isArray(id)) {
873
+ const removed = [];
874
+ for (const singleId of id) {
875
+ const result = this.removeHookById(singleId);
876
+ if (result && !Array.isArray(result)) removed.push(result);
877
+ }
878
+ return removed;
879
+ }
880
+ for (const [event, eventHandlers] of this._hooks.entries()) {
881
+ const index = eventHandlers.findIndex((h) => h.id === id);
882
+ if (index !== -1) {
883
+ const [removed] = eventHandlers.splice(index, 1);
884
+ if (eventHandlers.length === 0) this._hooks.delete(event);
885
+ return removed;
886
+ }
887
+ }
888
+ }
889
+ /**
890
+ * Removes all hooks
891
+ * @returns {void}
892
+ */
893
+ clearHooks() {
894
+ this._hooks.clear();
895
+ }
896
+ /**
897
+ * Removes all hooks for a specific event and returns the removed hooks.
898
+ * @param {string} event - The event name to remove hooks for.
899
+ * @returns {IHook[]} the hooks that were removed
900
+ */
901
+ removeEventHooks(event) {
902
+ this.validateHookName(event);
903
+ const eventHandlers = this._hooks.get(event);
904
+ if (eventHandlers) {
905
+ const removed = [...eventHandlers];
906
+ this._hooks.delete(event);
907
+ return removed;
908
+ }
909
+ return [];
910
+ }
911
+ /**
912
+ * Validates hook event name if enforceBeforeAfter is enabled
913
+ * @param {string} event - The event name to validate
914
+ * @throws {Error} If enforceBeforeAfter is true and event doesn't start with 'before' or 'after'
915
+ */
916
+ validateHookName(event) {
917
+ if (this._enforceBeforeAfter) {
918
+ const eventValue = event.trim().toLocaleLowerCase();
919
+ if (!eventValue.startsWith("before") && !eventValue.startsWith("after")) throw new Error(`Hook event "${event}" must start with "before" or "after" when enforceBeforeAfter is enabled`);
920
+ }
921
+ }
922
+ /**
923
+ * Checks if a hook is deprecated and emits a warning if it is
924
+ * @param {string} event - The event name to check
925
+ * @returns {boolean} - Returns true if the hook should proceed, false if it should be blocked
926
+ */
927
+ checkDeprecatedHook(event) {
928
+ if (this._deprecatedHooks.has(event)) {
929
+ const message = this._deprecatedHooks.get(event);
930
+ const warningMessage = `Hook "${event}" is deprecated${message ? `: ${message}` : ""}`;
931
+ this.emit("warn", {
932
+ hook: event,
933
+ message: warningMessage
934
+ });
935
+ return this._allowDeprecated;
936
+ }
937
+ return true;
938
+ }
891
939
  };
892
- /* v8 ignore start -- @preserve: single-element arrays are stored as functions */
893
- /* v8 ignore next 3 -- @preserve: guarded by caller */
894
- /* v8 ignore next -- @preserve */
940
+ //#endregion
941
+ export { Eventified, Hook, Hookified, ParallelHook, WaterfallHook };
942
+
895
943
  //# sourceMappingURL=index.js.map