hookified 2.0.0 → 2.1.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.
@@ -28,13 +28,13 @@ __export(index_exports, {
28
28
  module.exports = __toCommonJS(index_exports);
29
29
 
30
30
  // src/eventified.ts
31
+ var ERROR_EVENT = "error";
31
32
  var Eventified = class {
32
33
  _eventListeners;
33
34
  _maxListeners;
34
35
  _eventLogger;
35
36
  _throwOnEmitError = false;
36
37
  _throwOnEmptyListeners = true;
37
- _errorEvent = "error";
38
38
  constructor(options) {
39
39
  this._eventListeners = /* @__PURE__ */ new Map();
40
40
  this._maxListeners = 0;
@@ -109,10 +109,17 @@ var Eventified = class {
109
109
  */
110
110
  listenerCount(eventName) {
111
111
  if (eventName === void 0) {
112
- return this.getAllListeners().length;
112
+ let count = 0;
113
+ for (const entry2 of this._eventListeners.values()) {
114
+ count += typeof entry2 === "function" ? 1 : entry2.length;
115
+ }
116
+ return count;
117
+ }
118
+ const entry = this._eventListeners.get(eventName);
119
+ if (entry === void 0) {
120
+ return 0;
113
121
  }
114
- const listeners = this._eventListeners.get(eventName);
115
- return listeners ? listeners.length : 0;
122
+ return typeof entry === "function" ? 1 : entry.length;
116
123
  }
117
124
  /**
118
125
  * Gets an array of event names
@@ -130,7 +137,11 @@ var Eventified = class {
130
137
  if (event === void 0) {
131
138
  return this.getAllListeners();
132
139
  }
133
- return this._eventListeners.get(event) ?? [];
140
+ const entry = this._eventListeners.get(event);
141
+ if (entry === void 0) {
142
+ return [];
143
+ }
144
+ return typeof entry === "function" ? [entry] : entry;
134
145
  }
135
146
  /**
136
147
  * Prepends a listener to the beginning of the listeners array for the specified event
@@ -139,9 +150,14 @@ var Eventified = class {
139
150
  * @returns {IEventEmitter} returns the instance of the class for chaining
140
151
  */
141
152
  prependListener(eventName, listener) {
142
- const listeners = this._eventListeners.get(eventName) ?? [];
143
- listeners.unshift(listener);
144
- this._eventListeners.set(eventName, listeners);
153
+ const existing = this._eventListeners.get(eventName);
154
+ if (existing === void 0) {
155
+ this._eventListeners.set(eventName, listener);
156
+ } else if (typeof existing === "function") {
157
+ this._eventListeners.set(eventName, [listener, existing]);
158
+ } else {
159
+ existing.unshift(listener);
160
+ }
145
161
  return this;
146
162
  }
147
163
  /**
@@ -182,17 +198,26 @@ var Eventified = class {
182
198
  * @returns {IEventEmitter} returns the instance of the class for chaining
183
199
  */
184
200
  on(event, listener) {
185
- if (!this._eventListeners.has(event)) {
186
- this._eventListeners.set(event, []);
187
- }
188
- const listeners = this._eventListeners.get(event);
189
- if (listeners) {
190
- if (this._maxListeners > 0 && listeners.length >= this._maxListeners) {
201
+ const existing = this._eventListeners.get(event);
202
+ if (existing === void 0) {
203
+ this._eventListeners.set(event, listener);
204
+ return this;
205
+ }
206
+ if (typeof existing === "function") {
207
+ const arr = [existing, listener];
208
+ this._eventListeners.set(event, arr);
209
+ if (this._maxListeners > 0 && arr.length > this._maxListeners) {
210
+ console.warn(
211
+ `MaxListenersExceededWarning: Possible event memory leak detected. ${arr.length} ${event} listeners added. Use setMaxListeners() to increase limit.`
212
+ );
213
+ }
214
+ } else {
215
+ existing.push(listener);
216
+ if (this._maxListeners > 0 && existing.length > this._maxListeners) {
191
217
  console.warn(
192
- `MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
218
+ `MaxListenersExceededWarning: Possible event memory leak detected. ${existing.length} ${event} listeners added. Use setMaxListeners() to increase limit.`
193
219
  );
194
220
  }
195
- listeners.push(listener);
196
221
  }
197
222
  return this;
198
223
  }
@@ -213,13 +238,25 @@ var Eventified = class {
213
238
  * @returns {IEventEmitter} returns the instance of the class for chaining
214
239
  */
215
240
  off(event, listener) {
216
- const listeners = this._eventListeners.get(event) ?? [];
217
- const index = listeners.indexOf(listener);
218
- if (index !== -1) {
219
- listeners.splice(index, 1);
241
+ const entry = this._eventListeners.get(event);
242
+ if (entry === void 0) {
243
+ return this;
220
244
  }
221
- if (listeners.length === 0) {
222
- this._eventListeners.delete(event);
245
+ if (typeof entry === "function") {
246
+ if (entry === listener) {
247
+ this._eventListeners.delete(event);
248
+ }
249
+ return this;
250
+ }
251
+ const index = entry.indexOf(listener);
252
+ if (index !== -1) {
253
+ if (entry.length === 2) {
254
+ this._eventListeners.set(event, entry[1 - index]);
255
+ } else if (entry.length === 1) {
256
+ this._eventListeners.delete(event);
257
+ } else {
258
+ entry.splice(index, 1);
259
+ }
223
260
  }
224
261
  return this;
225
262
  }
@@ -231,22 +268,38 @@ var Eventified = class {
231
268
  */
232
269
  emit(event, ...arguments_) {
233
270
  let result = false;
234
- const listeners = this._eventListeners.get(event);
235
- if (listeners && listeners.length > 0) {
236
- for (const listener of listeners) {
237
- listener(...arguments_);
238
- result = true;
271
+ const entry = this._eventListeners.get(event);
272
+ const argumentLength = arguments_.length;
273
+ if (entry !== void 0) {
274
+ if (typeof entry === "function") {
275
+ if (argumentLength === 1) {
276
+ entry(arguments_[0]);
277
+ } else if (argumentLength === 2) {
278
+ entry(arguments_[0], arguments_[1]);
279
+ } else {
280
+ entry(...arguments_);
281
+ }
282
+ } else {
283
+ const snapshot = [...entry];
284
+ for (let i = 0; i < snapshot.length; i++) {
285
+ if (argumentLength === 1) {
286
+ snapshot[i](arguments_[0]);
287
+ } else if (argumentLength === 2) {
288
+ snapshot[i](arguments_[0], arguments_[1]);
289
+ } else {
290
+ snapshot[i](...arguments_);
291
+ }
292
+ }
239
293
  }
294
+ result = true;
240
295
  }
241
- this.sendToEventLogger(event, arguments_);
242
- if (event === this._errorEvent) {
296
+ if (this._eventLogger) {
297
+ this.sendToEventLogger(event, arguments_);
298
+ }
299
+ if (event === ERROR_EVENT && !result) {
243
300
  const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
244
- if (this._throwOnEmitError && !result) {
301
+ if (this._throwOnEmitError || this._throwOnEmptyListeners) {
245
302
  throw error;
246
- } else {
247
- if (this.listeners(this._errorEvent).length === 0 && this._throwOnEmptyListeners === true) {
248
- throw error;
249
- }
250
303
  }
251
304
  }
252
305
  return result;
@@ -257,7 +310,11 @@ var Eventified = class {
257
310
  * @returns {EventListener[]} An array of listeners
258
311
  */
259
312
  listeners(event) {
260
- return this._eventListeners.get(event) ?? [];
313
+ const entry = this._eventListeners.get(event);
314
+ if (entry === void 0) {
315
+ return [];
316
+ }
317
+ return typeof entry === "function" ? [entry] : entry;
261
318
  }
262
319
  /**
263
320
  * Removes all listeners for a specific event. If no event is provided, it removes all listeners
@@ -285,9 +342,15 @@ var Eventified = class {
285
342
  * @returns {EventListener[]} An array of listeners
286
343
  */
287
344
  getAllListeners() {
288
- let result = [];
289
- for (const listeners of this._eventListeners.values()) {
290
- result = [...result, ...listeners];
345
+ const result = [];
346
+ for (const entry of this._eventListeners.values()) {
347
+ if (typeof entry === "function") {
348
+ result.push(entry);
349
+ } else {
350
+ for (let i = 0; i < entry.length; i++) {
351
+ result.push(entry[i]);
352
+ }
353
+ }
291
354
  }
292
355
  return result;
293
356
  }
@@ -517,15 +580,21 @@ var Hookified = class extends Eventified {
517
580
  set useHookClone(value) {
518
581
  this._useHookClone = value;
519
582
  }
520
- /**
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
527
- */
528
- onHook(hook, options) {
583
+ onHook(hookOrEvent, optionsOrHandler) {
584
+ let hook;
585
+ let options;
586
+ if (typeof hookOrEvent === "string") {
587
+ if (typeof optionsOrHandler !== "function") {
588
+ throw new TypeError(
589
+ "When calling onHook(event, handler), the second argument must be a function"
590
+ );
591
+ }
592
+ hook = { event: hookOrEvent, handler: optionsOrHandler };
593
+ options = void 0;
594
+ } else {
595
+ hook = hookOrEvent;
596
+ options = optionsOrHandler;
597
+ }
529
598
  this.validateHookName(hook.event);
530
599
  if (!this.checkDeprecatedHook(hook.event)) {
531
600
  return void 0;
@@ -846,4 +915,6 @@ var Hookified = class extends Eventified {
846
915
  Hookified,
847
916
  WaterfallHook
848
917
  });
918
+ /* v8 ignore start -- @preserve: single-element arrays are stored as functions */
919
+ /* v8 ignore next 3 -- @preserve: guarded by caller */
849
920
  /* v8 ignore next -- @preserve */
@@ -274,7 +274,6 @@ declare class Eventified implements IEventEmitter {
274
274
  private _eventLogger?;
275
275
  private _throwOnEmitError;
276
276
  private _throwOnEmptyListeners;
277
- private _errorEvent;
278
277
  constructor(options?: EventEmitterOptions);
279
278
  /**
280
279
  * Gets the event logger
@@ -532,13 +531,16 @@ declare class Hookified extends Eventified {
532
531
  set useHookClone(value: boolean);
533
532
  /**
534
533
  * Adds a handler function for a specific event.
535
- * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead.
534
+ * Supports both object and legacy signatures:
535
+ * onHook({ event, handler }, options?)
536
+ * onHook(event, handler)
536
537
  * To register multiple hooks at once, use {@link onHooks}.
537
- * @param {IHook} hook - the hook containing event name and handler
538
- * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
538
+ * @param {IHook | string} hookOrEvent - the hook object or event name
539
+ * @param {OnHookOptions | HookFn} [optionsOrHandler] - options (when using object form) or handler function (when using string form)
539
540
  * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
540
541
  */
541
542
  onHook(hook: IHook, options?: OnHookOptions): IHook | undefined;
543
+ onHook(event: string, handler: HookFn): IHook | undefined;
542
544
  /**
543
545
  * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
544
546
  * @param {string} event - the event name
@@ -274,7 +274,6 @@ declare class Eventified implements IEventEmitter {
274
274
  private _eventLogger?;
275
275
  private _throwOnEmitError;
276
276
  private _throwOnEmptyListeners;
277
- private _errorEvent;
278
277
  constructor(options?: EventEmitterOptions);
279
278
  /**
280
279
  * Gets the event logger
@@ -532,13 +531,16 @@ declare class Hookified extends Eventified {
532
531
  set useHookClone(value: boolean);
533
532
  /**
534
533
  * Adds a handler function for a specific event.
535
- * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead.
534
+ * Supports both object and legacy signatures:
535
+ * onHook({ event, handler }, options?)
536
+ * onHook(event, handler)
536
537
  * To register multiple hooks at once, use {@link onHooks}.
537
- * @param {IHook} hook - the hook containing event name and handler
538
- * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
538
+ * @param {IHook | string} hookOrEvent - the hook object or event name
539
+ * @param {OnHookOptions | HookFn} [optionsOrHandler] - options (when using object form) or handler function (when using string form)
539
540
  * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
540
541
  */
541
542
  onHook(hook: IHook, options?: OnHookOptions): IHook | undefined;
543
+ onHook(event: string, handler: HookFn): IHook | undefined;
542
544
  /**
543
545
  * Alias for onHook. This is provided for compatibility with other libraries that use the `addHook` method.
544
546
  * @param {string} event - the event name
@@ -1,11 +1,11 @@
1
1
  // src/eventified.ts
2
+ var ERROR_EVENT = "error";
2
3
  var Eventified = class {
3
4
  _eventListeners;
4
5
  _maxListeners;
5
6
  _eventLogger;
6
7
  _throwOnEmitError = false;
7
8
  _throwOnEmptyListeners = true;
8
- _errorEvent = "error";
9
9
  constructor(options) {
10
10
  this._eventListeners = /* @__PURE__ */ new Map();
11
11
  this._maxListeners = 0;
@@ -80,10 +80,17 @@ var Eventified = class {
80
80
  */
81
81
  listenerCount(eventName) {
82
82
  if (eventName === void 0) {
83
- return this.getAllListeners().length;
83
+ let count = 0;
84
+ for (const entry2 of this._eventListeners.values()) {
85
+ count += typeof entry2 === "function" ? 1 : entry2.length;
86
+ }
87
+ return count;
88
+ }
89
+ const entry = this._eventListeners.get(eventName);
90
+ if (entry === void 0) {
91
+ return 0;
84
92
  }
85
- const listeners = this._eventListeners.get(eventName);
86
- return listeners ? listeners.length : 0;
93
+ return typeof entry === "function" ? 1 : entry.length;
87
94
  }
88
95
  /**
89
96
  * Gets an array of event names
@@ -101,7 +108,11 @@ var Eventified = class {
101
108
  if (event === void 0) {
102
109
  return this.getAllListeners();
103
110
  }
104
- return this._eventListeners.get(event) ?? [];
111
+ const entry = this._eventListeners.get(event);
112
+ if (entry === void 0) {
113
+ return [];
114
+ }
115
+ return typeof entry === "function" ? [entry] : entry;
105
116
  }
106
117
  /**
107
118
  * Prepends a listener to the beginning of the listeners array for the specified event
@@ -110,9 +121,14 @@ var Eventified = class {
110
121
  * @returns {IEventEmitter} returns the instance of the class for chaining
111
122
  */
112
123
  prependListener(eventName, listener) {
113
- const listeners = this._eventListeners.get(eventName) ?? [];
114
- listeners.unshift(listener);
115
- this._eventListeners.set(eventName, listeners);
124
+ const existing = this._eventListeners.get(eventName);
125
+ if (existing === void 0) {
126
+ this._eventListeners.set(eventName, listener);
127
+ } else if (typeof existing === "function") {
128
+ this._eventListeners.set(eventName, [listener, existing]);
129
+ } else {
130
+ existing.unshift(listener);
131
+ }
116
132
  return this;
117
133
  }
118
134
  /**
@@ -153,17 +169,26 @@ var Eventified = class {
153
169
  * @returns {IEventEmitter} returns the instance of the class for chaining
154
170
  */
155
171
  on(event, listener) {
156
- if (!this._eventListeners.has(event)) {
157
- this._eventListeners.set(event, []);
158
- }
159
- const listeners = this._eventListeners.get(event);
160
- if (listeners) {
161
- if (this._maxListeners > 0 && listeners.length >= this._maxListeners) {
172
+ const existing = this._eventListeners.get(event);
173
+ if (existing === void 0) {
174
+ this._eventListeners.set(event, listener);
175
+ return this;
176
+ }
177
+ if (typeof existing === "function") {
178
+ const arr = [existing, listener];
179
+ this._eventListeners.set(event, arr);
180
+ if (this._maxListeners > 0 && arr.length > this._maxListeners) {
181
+ console.warn(
182
+ `MaxListenersExceededWarning: Possible event memory leak detected. ${arr.length} ${event} listeners added. Use setMaxListeners() to increase limit.`
183
+ );
184
+ }
185
+ } else {
186
+ existing.push(listener);
187
+ if (this._maxListeners > 0 && existing.length > this._maxListeners) {
162
188
  console.warn(
163
- `MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`
189
+ `MaxListenersExceededWarning: Possible event memory leak detected. ${existing.length} ${event} listeners added. Use setMaxListeners() to increase limit.`
164
190
  );
165
191
  }
166
- listeners.push(listener);
167
192
  }
168
193
  return this;
169
194
  }
@@ -184,13 +209,25 @@ var Eventified = class {
184
209
  * @returns {IEventEmitter} returns the instance of the class for chaining
185
210
  */
186
211
  off(event, listener) {
187
- const listeners = this._eventListeners.get(event) ?? [];
188
- const index = listeners.indexOf(listener);
189
- if (index !== -1) {
190
- listeners.splice(index, 1);
212
+ const entry = this._eventListeners.get(event);
213
+ if (entry === void 0) {
214
+ return this;
191
215
  }
192
- if (listeners.length === 0) {
193
- this._eventListeners.delete(event);
216
+ if (typeof entry === "function") {
217
+ if (entry === listener) {
218
+ this._eventListeners.delete(event);
219
+ }
220
+ return this;
221
+ }
222
+ const index = entry.indexOf(listener);
223
+ if (index !== -1) {
224
+ if (entry.length === 2) {
225
+ this._eventListeners.set(event, entry[1 - index]);
226
+ } else if (entry.length === 1) {
227
+ this._eventListeners.delete(event);
228
+ } else {
229
+ entry.splice(index, 1);
230
+ }
194
231
  }
195
232
  return this;
196
233
  }
@@ -202,22 +239,38 @@ var Eventified = class {
202
239
  */
203
240
  emit(event, ...arguments_) {
204
241
  let result = false;
205
- const listeners = this._eventListeners.get(event);
206
- if (listeners && listeners.length > 0) {
207
- for (const listener of listeners) {
208
- listener(...arguments_);
209
- result = true;
242
+ const entry = this._eventListeners.get(event);
243
+ const argumentLength = arguments_.length;
244
+ if (entry !== void 0) {
245
+ if (typeof entry === "function") {
246
+ if (argumentLength === 1) {
247
+ entry(arguments_[0]);
248
+ } else if (argumentLength === 2) {
249
+ entry(arguments_[0], arguments_[1]);
250
+ } else {
251
+ entry(...arguments_);
252
+ }
253
+ } else {
254
+ const snapshot = [...entry];
255
+ for (let i = 0; i < snapshot.length; i++) {
256
+ if (argumentLength === 1) {
257
+ snapshot[i](arguments_[0]);
258
+ } else if (argumentLength === 2) {
259
+ snapshot[i](arguments_[0], arguments_[1]);
260
+ } else {
261
+ snapshot[i](...arguments_);
262
+ }
263
+ }
210
264
  }
265
+ result = true;
211
266
  }
212
- this.sendToEventLogger(event, arguments_);
213
- if (event === this._errorEvent) {
267
+ if (this._eventLogger) {
268
+ this.sendToEventLogger(event, arguments_);
269
+ }
270
+ if (event === ERROR_EVENT && !result) {
214
271
  const error = arguments_[0] instanceof Error ? arguments_[0] : new Error(`${arguments_[0]}`);
215
- if (this._throwOnEmitError && !result) {
272
+ if (this._throwOnEmitError || this._throwOnEmptyListeners) {
216
273
  throw error;
217
- } else {
218
- if (this.listeners(this._errorEvent).length === 0 && this._throwOnEmptyListeners === true) {
219
- throw error;
220
- }
221
274
  }
222
275
  }
223
276
  return result;
@@ -228,7 +281,11 @@ var Eventified = class {
228
281
  * @returns {EventListener[]} An array of listeners
229
282
  */
230
283
  listeners(event) {
231
- return this._eventListeners.get(event) ?? [];
284
+ const entry = this._eventListeners.get(event);
285
+ if (entry === void 0) {
286
+ return [];
287
+ }
288
+ return typeof entry === "function" ? [entry] : entry;
232
289
  }
233
290
  /**
234
291
  * Removes all listeners for a specific event. If no event is provided, it removes all listeners
@@ -256,9 +313,15 @@ var Eventified = class {
256
313
  * @returns {EventListener[]} An array of listeners
257
314
  */
258
315
  getAllListeners() {
259
- let result = [];
260
- for (const listeners of this._eventListeners.values()) {
261
- result = [...result, ...listeners];
316
+ const result = [];
317
+ for (const entry of this._eventListeners.values()) {
318
+ if (typeof entry === "function") {
319
+ result.push(entry);
320
+ } else {
321
+ for (let i = 0; i < entry.length; i++) {
322
+ result.push(entry[i]);
323
+ }
324
+ }
262
325
  }
263
326
  return result;
264
327
  }
@@ -488,15 +551,21 @@ var Hookified = class extends Eventified {
488
551
  set useHookClone(value) {
489
552
  this._useHookClone = value;
490
553
  }
491
- /**
492
- * Adds a handler function for a specific event.
493
- * If you prefer the legacy `(event, handler)` signature, use {@link addHook} instead.
494
- * To register multiple hooks at once, use {@link onHooks}.
495
- * @param {IHook} hook - the hook containing event name and handler
496
- * @param {OnHookOptions} [options] - optional per-call options (e.g., useHookClone override, position)
497
- * @returns {IHook | undefined} the stored hook, or undefined if blocked by deprecation
498
- */
499
- onHook(hook, options) {
554
+ onHook(hookOrEvent, optionsOrHandler) {
555
+ let hook;
556
+ let options;
557
+ if (typeof hookOrEvent === "string") {
558
+ if (typeof optionsOrHandler !== "function") {
559
+ throw new TypeError(
560
+ "When calling onHook(event, handler), the second argument must be a function"
561
+ );
562
+ }
563
+ hook = { event: hookOrEvent, handler: optionsOrHandler };
564
+ options = void 0;
565
+ } else {
566
+ hook = hookOrEvent;
567
+ options = optionsOrHandler;
568
+ }
500
569
  this.validateHookName(hook.event);
501
570
  if (!this.checkDeprecatedHook(hook.event)) {
502
571
  return void 0;
@@ -816,4 +885,6 @@ export {
816
885
  Hookified,
817
886
  WaterfallHook
818
887
  };
888
+ /* v8 ignore start -- @preserve: single-element arrays are stored as functions */
889
+ /* v8 ignore next 3 -- @preserve: guarded by caller */
819
890
  /* v8 ignore next -- @preserve */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hookified",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Event Emitting and Middleware Hooks",
5
5
  "type": "module",
6
6
  "main": "./dist/node/index.js",