hookified 0.7.1 → 1.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.
package/README.md CHANGED
@@ -9,20 +9,18 @@
9
9
  [![npm](https://img.shields.io/npm/v/hookified)](https://npmjs.com/package/hookified)
10
10
 
11
11
  ## Features
12
- - Emit Events via [Emittery](https://npmjs.com/package/emittery)
12
+ - Simple replacement for EventEmitter
13
13
  - Async Middleware Hooks for Your Methods
14
- - ESM and Nodejs 20+
14
+ - ESM / CJS and Nodejs 20+
15
15
  - Maintained on a regular basis!
16
16
 
17
- Special thanks to [@sindresorhus](https://github.com/sindresorhus) for the [Emittery](https://npmjs.com/package/emittery) library. 🍻
18
-
19
17
  ## Installation
20
18
  ```bash
21
19
  npm install hookified --save
22
20
  ```
23
21
 
24
22
  ## Usage
25
- This was built because we constantly wanted hooks and events extended on libraires we are building such as [Keyv](https://keyv.org). This is a simple way to add hooks and events (via [emittery](https://npmjs.com/package/emittery)) to your libraries.
23
+ This was built because we constantly wanted hooks and events extended on libraires we are building such as [Keyv](https://keyv.org) and [Cacheable](https://cacheable.org). This is a simple way to add hooks and events (via [emittery](https://npmjs.com/package/emittery)) to your libraries.
26
24
 
27
25
  ```javascript
28
26
  import { Hookified } from 'hookified';
@@ -70,8 +68,6 @@ class MyClass extends Hookified {
70
68
 
71
69
  ## API
72
70
 
73
- Please see the [Emittery](https://npmjs.com/package/emittery) documentation for more information on the event emitter.
74
-
75
71
  ### .onHook(eventName, handler)
76
72
 
77
73
  Subscribe to a hook event.
@@ -94,6 +90,30 @@ Get all hooks for an event.
94
90
 
95
91
  ### .clearHooks(eventName)
96
92
 
93
+ ### .on(eventName, handler)
94
+
95
+ Subscribe to an event.
96
+
97
+ ### .off(eventName, handler)
98
+
99
+ Unsubscribe from an event.
100
+
101
+ ### .emit(eventName, ...args)
102
+
103
+ Emit an event.
104
+
105
+ ## .listeners(eventName)
106
+
107
+ Get all listeners for an event.
108
+
109
+ ## .removeAllListeners(eventName)
110
+
111
+ Remove all listeners for an event.
112
+
113
+ ## .setMaxListeners(maxListeners: number)
114
+
115
+ Set the maximum number of listeners and will truncate if there are already too many.
116
+
97
117
  ## Development and Testing
98
118
 
99
119
  Hookified is written in TypeScript and tests are written in `vitest`. To run the tests, use the following command:
package/dist/index.cjs ADDED
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ Eventified: () => Eventified,
24
+ Hookified: () => Hookified
25
+ });
26
+ module.exports = __toCommonJS(src_exports);
27
+
28
+ // src/eventified.ts
29
+ var Eventified = class {
30
+ _eventListeners;
31
+ _maxListeners;
32
+ constructor() {
33
+ this._eventListeners = /* @__PURE__ */ new Map();
34
+ this._maxListeners = 100;
35
+ }
36
+ maxListeners() {
37
+ return this._maxListeners;
38
+ }
39
+ // Add an event listener
40
+ addListener(event, listener) {
41
+ this.on(event, listener);
42
+ }
43
+ on(event, listener) {
44
+ if (!this._eventListeners.has(event)) {
45
+ this._eventListeners.set(event, []);
46
+ }
47
+ const listeners = this._eventListeners.get(event);
48
+ if (listeners) {
49
+ if (listeners.length >= this._maxListeners) {
50
+ console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`);
51
+ }
52
+ listeners.push(listener);
53
+ }
54
+ }
55
+ // Remove an event listener
56
+ removeListener(event, listener) {
57
+ this.off(event, listener);
58
+ }
59
+ off(event, listener) {
60
+ const listeners = this._eventListeners.get(event) ?? [];
61
+ const index = listeners.indexOf(listener);
62
+ if (index > -1) {
63
+ listeners.splice(index, 1);
64
+ }
65
+ if (listeners.length === 0) {
66
+ this._eventListeners.delete(event);
67
+ }
68
+ }
69
+ // Emit an event
70
+ emit(event, ...arguments_) {
71
+ const listeners = this._eventListeners.get(event);
72
+ if (listeners && listeners.length > 0) {
73
+ for (const listener of listeners) {
74
+ listener(...arguments_);
75
+ }
76
+ } else if (event === "error") {
77
+ if (arguments_[0] instanceof Error) {
78
+ throw arguments_[0];
79
+ } else {
80
+ const error = new CustomEventError(arguments_[0]);
81
+ error.context = arguments_[0];
82
+ throw error;
83
+ }
84
+ }
85
+ }
86
+ // Get all listeners for a specific event
87
+ listeners(event) {
88
+ return this._eventListeners.get(event) ?? [];
89
+ }
90
+ // Remove all listeners for a specific event
91
+ removeAllListeners(event) {
92
+ if (event) {
93
+ this._eventListeners.delete(event);
94
+ } else {
95
+ this._eventListeners.clear();
96
+ }
97
+ }
98
+ // Set the maximum number of listeners for a single event
99
+ setMaxListeners(n) {
100
+ this._maxListeners = n;
101
+ for (const listeners of this._eventListeners.values()) {
102
+ if (listeners.length > n) {
103
+ listeners.splice(n);
104
+ }
105
+ }
106
+ }
107
+ };
108
+ var CustomEventError = class _CustomEventError extends Error {
109
+ context;
110
+ constructor(message, context) {
111
+ super(message);
112
+ this.context = context;
113
+ if (Error.captureStackTrace) {
114
+ Error.captureStackTrace(this, _CustomEventError);
115
+ }
116
+ this.name = this.constructor.name;
117
+ }
118
+ };
119
+
120
+ // src/index.ts
121
+ var Hookified = class extends Eventified {
122
+ _hooks;
123
+ constructor() {
124
+ super();
125
+ this._hooks = /* @__PURE__ */ new Map();
126
+ }
127
+ // Adds a handler function for a specific event
128
+ onHook(event, handler) {
129
+ const eventHandlers = this._hooks.get(event);
130
+ if (eventHandlers) {
131
+ eventHandlers.push(handler);
132
+ } else {
133
+ this._hooks.set(event, [handler]);
134
+ }
135
+ }
136
+ // Removes a specific handler function for a specific event
137
+ removeHook(event, handler) {
138
+ const eventHandlers = this._hooks.get(event);
139
+ if (eventHandlers) {
140
+ const index = eventHandlers.indexOf(handler);
141
+ if (index !== -1) {
142
+ eventHandlers.splice(index, 1);
143
+ }
144
+ }
145
+ }
146
+ // Triggers all handlers for a specific event with provided data
147
+ async hook(event, ...arguments_) {
148
+ const eventHandlers = this._hooks.get(event);
149
+ if (eventHandlers) {
150
+ for (const handler of eventHandlers) {
151
+ try {
152
+ await handler(...arguments_);
153
+ } catch (error) {
154
+ this.emit("error", new Error(`Error in hook handler for event "${event}": ${error.message}`));
155
+ }
156
+ }
157
+ }
158
+ }
159
+ // Provides read-only access to the current handlers
160
+ get hooks() {
161
+ return this._hooks;
162
+ }
163
+ getHooks(event) {
164
+ return this._hooks.get(event);
165
+ }
166
+ clearHooks() {
167
+ this._hooks.clear();
168
+ }
169
+ };
170
+ // Annotate the CommonJS export names for ESM import in node:
171
+ 0 && (module.exports = {
172
+ Eventified,
173
+ Hookified
174
+ });
@@ -0,0 +1,29 @@
1
+ type EventListener = (...arguments_: any[]) => void;
2
+ declare class Eventified {
3
+ _eventListeners: Map<string, EventListener[]>;
4
+ _maxListeners: number;
5
+ constructor();
6
+ maxListeners(): number;
7
+ addListener(event: string, listener: EventListener): void;
8
+ on(event: string, listener: EventListener): void;
9
+ removeListener(event: string, listener: EventListener): void;
10
+ off(event: string, listener: EventListener): void;
11
+ emit(event: string, ...arguments_: any[]): void;
12
+ listeners(event: string): EventListener[];
13
+ removeAllListeners(event?: string): void;
14
+ setMaxListeners(n: number): void;
15
+ }
16
+
17
+ type Hook = (...arguments_: any[]) => Promise<void> | void;
18
+ declare class Hookified extends Eventified {
19
+ _hooks: Map<string, Hook[]>;
20
+ constructor();
21
+ onHook(event: string, handler: Hook): void;
22
+ removeHook(event: string, handler: Hook): void;
23
+ hook<T>(event: string, ...arguments_: T[]): Promise<void>;
24
+ get hooks(): Map<string, Hook[]>;
25
+ getHooks(event: string): Hook[] | undefined;
26
+ clearHooks(): void;
27
+ }
28
+
29
+ export { type EventListener, Eventified, type Hook, Hookified };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,21 @@
1
- import Emittery from 'emittery';
2
- export type Hook = (...arguments_: any[]) => Promise<void> | void;
3
- export declare class Hookified extends Emittery {
1
+ type EventListener = (...arguments_: any[]) => void;
2
+ declare class Eventified {
3
+ _eventListeners: Map<string, EventListener[]>;
4
+ _maxListeners: number;
5
+ constructor();
6
+ maxListeners(): number;
7
+ addListener(event: string, listener: EventListener): void;
8
+ on(event: string, listener: EventListener): void;
9
+ removeListener(event: string, listener: EventListener): void;
10
+ off(event: string, listener: EventListener): void;
11
+ emit(event: string, ...arguments_: any[]): void;
12
+ listeners(event: string): EventListener[];
13
+ removeAllListeners(event?: string): void;
14
+ setMaxListeners(n: number): void;
15
+ }
16
+
17
+ type Hook = (...arguments_: any[]) => Promise<void> | void;
18
+ declare class Hookified extends Eventified {
4
19
  _hooks: Map<string, Hook[]>;
5
20
  constructor();
6
21
  onHook(event: string, handler: Hook): void;
@@ -10,3 +25,5 @@ export declare class Hookified extends Emittery {
10
25
  getHooks(event: string): Hook[] | undefined;
11
26
  clearHooks(): void;
12
27
  }
28
+
29
+ export { type EventListener, Eventified, type Hook, Hookified };
package/dist/index.js CHANGED
@@ -1,56 +1,146 @@
1
- import Emittery from 'emittery';
2
- export class Hookified extends Emittery {
3
- _hooks;
4
- constructor() {
5
- super();
6
- this._hooks = new Map();
7
- }
8
- // Adds a handler function for a specific event
9
- onHook(event, handler) {
10
- const eventHandlers = this._hooks.get(event);
11
- if (eventHandlers) {
12
- eventHandlers.push(handler);
13
- }
14
- else {
15
- this._hooks.set(event, [handler]);
16
- }
1
+ // src/eventified.ts
2
+ var Eventified = class {
3
+ _eventListeners;
4
+ _maxListeners;
5
+ constructor() {
6
+ this._eventListeners = /* @__PURE__ */ new Map();
7
+ this._maxListeners = 100;
8
+ }
9
+ maxListeners() {
10
+ return this._maxListeners;
11
+ }
12
+ // Add an event listener
13
+ addListener(event, listener) {
14
+ this.on(event, listener);
15
+ }
16
+ on(event, listener) {
17
+ if (!this._eventListeners.has(event)) {
18
+ this._eventListeners.set(event, []);
17
19
  }
18
- // Removes a specific handler function for a specific event
19
- removeHook(event, handler) {
20
- const eventHandlers = this._hooks.get(event);
21
- if (eventHandlers) {
22
- const index = eventHandlers.indexOf(handler);
23
- if (index !== -1) {
24
- eventHandlers.splice(index, 1);
25
- }
26
- }
20
+ const listeners = this._eventListeners.get(event);
21
+ if (listeners) {
22
+ if (listeners.length >= this._maxListeners) {
23
+ console.warn(`MaxListenersExceededWarning: Possible event memory leak detected. ${listeners.length + 1} ${event} listeners added. Use setMaxListeners() to increase limit.`);
24
+ }
25
+ listeners.push(listener);
27
26
  }
28
- // Triggers all handlers for a specific event with provided data
29
- async hook(event, ...arguments_) {
30
- const eventHandlers = this._hooks.get(event);
31
- if (eventHandlers) {
32
- for (const handler of eventHandlers) {
33
- try {
34
- // eslint-disable-next-line no-await-in-loop
35
- await handler(...arguments_);
36
- }
37
- catch (error) {
38
- // eslint-disable-next-line no-await-in-loop
39
- await this.emit('error', new Error(`Error in hook handler for event "${event}": ${error.message}`));
40
- }
41
- }
42
- }
27
+ }
28
+ // Remove an event listener
29
+ removeListener(event, listener) {
30
+ this.off(event, listener);
31
+ }
32
+ off(event, listener) {
33
+ const listeners = this._eventListeners.get(event) ?? [];
34
+ const index = listeners.indexOf(listener);
35
+ if (index > -1) {
36
+ listeners.splice(index, 1);
37
+ }
38
+ if (listeners.length === 0) {
39
+ this._eventListeners.delete(event);
43
40
  }
44
- // Provides read-only access to the current handlers
45
- get hooks() {
46
- // Creating a new map to prevent external modifications to the original map
47
- return this._hooks;
41
+ }
42
+ // Emit an event
43
+ emit(event, ...arguments_) {
44
+ const listeners = this._eventListeners.get(event);
45
+ if (listeners && listeners.length > 0) {
46
+ for (const listener of listeners) {
47
+ listener(...arguments_);
48
+ }
49
+ } else if (event === "error") {
50
+ if (arguments_[0] instanceof Error) {
51
+ throw arguments_[0];
52
+ } else {
53
+ const error = new CustomEventError(arguments_[0]);
54
+ error.context = arguments_[0];
55
+ throw error;
56
+ }
48
57
  }
49
- getHooks(event) {
50
- return this._hooks.get(event);
58
+ }
59
+ // Get all listeners for a specific event
60
+ listeners(event) {
61
+ return this._eventListeners.get(event) ?? [];
62
+ }
63
+ // Remove all listeners for a specific event
64
+ removeAllListeners(event) {
65
+ if (event) {
66
+ this._eventListeners.delete(event);
67
+ } else {
68
+ this._eventListeners.clear();
51
69
  }
52
- clearHooks() {
53
- this._hooks.clear();
70
+ }
71
+ // Set the maximum number of listeners for a single event
72
+ setMaxListeners(n) {
73
+ this._maxListeners = n;
74
+ for (const listeners of this._eventListeners.values()) {
75
+ if (listeners.length > n) {
76
+ listeners.splice(n);
77
+ }
78
+ }
79
+ }
80
+ };
81
+ var CustomEventError = class _CustomEventError extends Error {
82
+ context;
83
+ constructor(message, context) {
84
+ super(message);
85
+ this.context = context;
86
+ if (Error.captureStackTrace) {
87
+ Error.captureStackTrace(this, _CustomEventError);
88
+ }
89
+ this.name = this.constructor.name;
90
+ }
91
+ };
92
+
93
+ // src/index.ts
94
+ var Hookified = class extends Eventified {
95
+ _hooks;
96
+ constructor() {
97
+ super();
98
+ this._hooks = /* @__PURE__ */ new Map();
99
+ }
100
+ // Adds a handler function for a specific event
101
+ onHook(event, handler) {
102
+ const eventHandlers = this._hooks.get(event);
103
+ if (eventHandlers) {
104
+ eventHandlers.push(handler);
105
+ } else {
106
+ this._hooks.set(event, [handler]);
107
+ }
108
+ }
109
+ // Removes a specific handler function for a specific event
110
+ removeHook(event, handler) {
111
+ const eventHandlers = this._hooks.get(event);
112
+ if (eventHandlers) {
113
+ const index = eventHandlers.indexOf(handler);
114
+ if (index !== -1) {
115
+ eventHandlers.splice(index, 1);
116
+ }
117
+ }
118
+ }
119
+ // Triggers all handlers for a specific event with provided data
120
+ async hook(event, ...arguments_) {
121
+ const eventHandlers = this._hooks.get(event);
122
+ if (eventHandlers) {
123
+ for (const handler of eventHandlers) {
124
+ try {
125
+ await handler(...arguments_);
126
+ } catch (error) {
127
+ this.emit("error", new Error(`Error in hook handler for event "${event}": ${error.message}`));
128
+ }
129
+ }
54
130
  }
55
- }
56
- //# sourceMappingURL=index.js.map
131
+ }
132
+ // Provides read-only access to the current handlers
133
+ get hooks() {
134
+ return this._hooks;
135
+ }
136
+ getHooks(event) {
137
+ return this._hooks.get(event);
138
+ }
139
+ clearHooks() {
140
+ this._hooks.clear();
141
+ }
142
+ };
143
+ export {
144
+ Eventified,
145
+ Hookified
146
+ };
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "hookified",
3
- "version": "0.7.1",
3
+ "version": "1.1.0",
4
4
  "description": "Event and Middleware Hooks",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
7
- "exports": "./dist/index.js",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "require": "./dist/index.cjs",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
9
15
  "engines": {
10
16
  "node": ">=20"
11
17
  },
@@ -13,7 +19,7 @@
13
19
  "test": "xo --fix && vitest run --coverage",
14
20
  "test:ci": "xo && vitest run --coverage",
15
21
  "clean": "rimraf ./dist ./coverage ./site/dist",
16
- "build": "rimraf ./dist && tsc",
22
+ "build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
17
23
  "website:build": "docula build",
18
24
  "website:serve": "docula serve",
19
25
  "prepare": "npm run build"
@@ -51,15 +57,13 @@
51
57
  "url": "https://github.com/jaredwray/hookified/issues"
52
58
  },
53
59
  "homepage": "https://github.com/jaredwray/hookified#readme",
54
- "dependencies": {
55
- "emittery": "^1.0.3"
56
- },
57
60
  "devDependencies": {
58
- "@vitest/coverage-v8": "^2.0.5",
61
+ "@vitest/coverage-v8": "^2.1.1",
59
62
  "docula": "^0.9.0",
60
63
  "rimraf": "^6.0.1",
61
- "typescript": "^5.5.4",
62
- "vitest": "^2.0.5",
64
+ "tsup": "^8.3.0",
65
+ "typescript": "^5.6.2",
66
+ "vitest": "^2.1.1",
63
67
  "xo": "^0.59.3"
64
68
  },
65
69
  "files": [
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAIhC,MAAM,OAAO,SAAU,SAAQ,QAAQ;IACtC,MAAM,CAAsB;IAE5B;QACC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,+CAA+C;IAC/C,MAAM,CAAC,KAAa,EAAE,OAAa;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,aAAa,EAAE,CAAC;YACnB,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QACnC,CAAC;IACF,CAAC;IAED,2DAA2D;IAC3D,UAAU,CAAC,KAAa,EAAE,OAAa;QACtC,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,aAAa,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC7C,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBAClB,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAChC,CAAC;QACF,CAAC;IACF,CAAC;IAED,gEAAgE;IAChE,KAAK,CAAC,IAAI,CAAI,KAAa,EAAE,GAAG,UAAe;QAC9C,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,aAAa,EAAE,CAAC;YACnB,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;gBACrC,IAAI,CAAC;oBACJ,4CAA4C;oBAC5C,MAAM,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC;gBAC9B,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,4CAA4C;oBAC5C,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,oCAAoC,KAAK,MAAO,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAChH,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IAED,oDAAoD;IACpD,IAAI,KAAK;QACR,2EAA2E;QAC3E,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,KAAa;QACrB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,UAAU;QACT,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACD"}