@hazeljs/event-emitter 0.2.0-alpha.1
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/LICENSE +192 -0
- package/README.md +117 -0
- package/dist/event-emitter.module.d.ts +68 -0
- package/dist/event-emitter.module.d.ts.map +1 -0
- package/dist/event-emitter.module.js +144 -0
- package/dist/event-emitter.module.test.d.ts +2 -0
- package/dist/event-emitter.module.test.d.ts.map +1 -0
- package/dist/event-emitter.module.test.js +280 -0
- package/dist/event-emitter.service.d.ts +33 -0
- package/dist/event-emitter.service.d.ts.map +1 -0
- package/dist/event-emitter.service.js +58 -0
- package/dist/event-emitter.service.test.d.ts +2 -0
- package/dist/event-emitter.service.test.d.ts.map +1 -0
- package/dist/event-emitter.service.test.js +61 -0
- package/dist/event-emitter.types.d.ts +61 -0
- package/dist/event-emitter.types.d.ts.map +1 -0
- package/dist/event-emitter.types.js +2 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/on-event.decorator.d.ts +36 -0
- package/dist/on-event.decorator.d.ts.map +1 -0
- package/dist/on-event.decorator.js +49 -0
- package/dist/on-event.decorator.test.d.ts +2 -0
- package/dist/on-event.decorator.test.d.ts.map +1 -0
- package/dist/on-event.decorator.test.js +136 -0
- package/package.json +54 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const core_1 = require("@hazeljs/core");
|
|
13
|
+
const event_emitter_module_1 = require("./event-emitter.module");
|
|
14
|
+
const event_emitter_service_1 = require("./event-emitter.service");
|
|
15
|
+
const on_event_decorator_1 = require("./on-event.decorator");
|
|
16
|
+
describe('EventEmitterModule', () => {
|
|
17
|
+
let container;
|
|
18
|
+
const originalGetInstance = core_1.Container.getInstance;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
container = core_1.Container.createTestInstance();
|
|
21
|
+
core_1.Container.getInstance = jest.fn(() => container);
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
core_1.Container.getInstance = originalGetInstance;
|
|
25
|
+
});
|
|
26
|
+
describe('forRoot', () => {
|
|
27
|
+
it('should return module config with providers and exports', () => {
|
|
28
|
+
const config = event_emitter_module_1.EventEmitterModule.forRoot();
|
|
29
|
+
expect(config.module).toBe(event_emitter_module_1.EventEmitterModule);
|
|
30
|
+
expect(config.providers).toHaveLength(1);
|
|
31
|
+
expect(config.providers[0].provide).toBe(event_emitter_service_1.EventEmitterService);
|
|
32
|
+
expect(config.providers[0].useFactory).toBeDefined();
|
|
33
|
+
expect(config.exports).toContain(event_emitter_service_1.EventEmitterService);
|
|
34
|
+
expect(config.global).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
it('should use isGlobal option', () => {
|
|
37
|
+
const config = event_emitter_module_1.EventEmitterModule.forRoot({ isGlobal: false });
|
|
38
|
+
expect(config.global).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it('should default isGlobal to true when not provided', () => {
|
|
41
|
+
const config = event_emitter_module_1.EventEmitterModule.forRoot({});
|
|
42
|
+
expect(config.global).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
it('should pass options to EventEmitterService factory', () => {
|
|
45
|
+
const config = event_emitter_module_1.EventEmitterModule.forRoot({
|
|
46
|
+
wildcard: true,
|
|
47
|
+
delimiter: ':',
|
|
48
|
+
});
|
|
49
|
+
const factory = config.providers[0].useFactory;
|
|
50
|
+
const service = factory();
|
|
51
|
+
expect(service).toBeInstanceOf(event_emitter_service_1.EventEmitterService);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('registerListenersFromProvider', () => {
|
|
55
|
+
it('should register event listeners from provider with @OnEvent', () => {
|
|
56
|
+
const mockEmit = jest.fn();
|
|
57
|
+
const mockOn = jest.fn();
|
|
58
|
+
const eventEmitter = {
|
|
59
|
+
on: mockOn,
|
|
60
|
+
emit: mockEmit,
|
|
61
|
+
};
|
|
62
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
63
|
+
class TestHandler {
|
|
64
|
+
handleOrderCreated(payload) {
|
|
65
|
+
return payload;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
__decorate([
|
|
69
|
+
(0, on_event_decorator_1.OnEvent)('order.created'),
|
|
70
|
+
__metadata("design:type", Function),
|
|
71
|
+
__metadata("design:paramtypes", [Object]),
|
|
72
|
+
__metadata("design:returntype", void 0)
|
|
73
|
+
], TestHandler.prototype, "handleOrderCreated", null);
|
|
74
|
+
const handler = new TestHandler();
|
|
75
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(handler);
|
|
76
|
+
expect(mockOn).toHaveBeenCalledWith('order.created', expect.any(Function), expect.objectContaining({ suppressErrors: true }));
|
|
77
|
+
});
|
|
78
|
+
it('should handle async listeners', () => {
|
|
79
|
+
const mockOn = jest.fn();
|
|
80
|
+
const eventEmitter = {
|
|
81
|
+
on: mockOn,
|
|
82
|
+
};
|
|
83
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
84
|
+
class TestHandler {
|
|
85
|
+
async handleAsync(_payload) { }
|
|
86
|
+
}
|
|
87
|
+
__decorate([
|
|
88
|
+
(0, on_event_decorator_1.OnEvent)('async.event', { async: true }),
|
|
89
|
+
__metadata("design:type", Function),
|
|
90
|
+
__metadata("design:paramtypes", [Object]),
|
|
91
|
+
__metadata("design:returntype", Promise)
|
|
92
|
+
], TestHandler.prototype, "handleAsync", null);
|
|
93
|
+
const handler = new TestHandler();
|
|
94
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(handler);
|
|
95
|
+
expect(mockOn).toHaveBeenCalledWith('async.event', expect.any(Function), expect.objectContaining({ async: true }));
|
|
96
|
+
});
|
|
97
|
+
it('should call listener when event is emitted', () => {
|
|
98
|
+
const eventEmitter = new event_emitter_service_1.EventEmitterService();
|
|
99
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
100
|
+
const handlerFn = jest.fn();
|
|
101
|
+
class TestHandler {
|
|
102
|
+
handleTest(payload) {
|
|
103
|
+
handlerFn(payload);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
__decorate([
|
|
107
|
+
(0, on_event_decorator_1.OnEvent)('test.event'),
|
|
108
|
+
__metadata("design:type", Function),
|
|
109
|
+
__metadata("design:paramtypes", [Object]),
|
|
110
|
+
__metadata("design:returntype", void 0)
|
|
111
|
+
], TestHandler.prototype, "handleTest", null);
|
|
112
|
+
const handler = new TestHandler();
|
|
113
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(handler);
|
|
114
|
+
eventEmitter.emit('test.event', { data: 'test' });
|
|
115
|
+
expect(handlerFn).toHaveBeenCalledWith({ data: 'test' });
|
|
116
|
+
});
|
|
117
|
+
it('should suppress errors when suppressErrors is true', () => {
|
|
118
|
+
const eventEmitter = new event_emitter_service_1.EventEmitterService();
|
|
119
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
120
|
+
class TestHandler {
|
|
121
|
+
handleTest() {
|
|
122
|
+
throw new Error('Handler error');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
__decorate([
|
|
126
|
+
(0, on_event_decorator_1.OnEvent)('test.event'),
|
|
127
|
+
__metadata("design:type", Function),
|
|
128
|
+
__metadata("design:paramtypes", []),
|
|
129
|
+
__metadata("design:returntype", void 0)
|
|
130
|
+
], TestHandler.prototype, "handleTest", null);
|
|
131
|
+
const handler = new TestHandler();
|
|
132
|
+
expect(() => {
|
|
133
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(handler);
|
|
134
|
+
eventEmitter.emit('test.event');
|
|
135
|
+
}).not.toThrow();
|
|
136
|
+
});
|
|
137
|
+
it('should rethrow errors when suppressErrors is false', () => {
|
|
138
|
+
const eventEmitter = new event_emitter_service_1.EventEmitterService();
|
|
139
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
140
|
+
class TestHandler {
|
|
141
|
+
handleTest() {
|
|
142
|
+
throw new Error('Handler error');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
__decorate([
|
|
146
|
+
(0, on_event_decorator_1.OnEvent)('test.event', { suppressErrors: false }),
|
|
147
|
+
__metadata("design:type", Function),
|
|
148
|
+
__metadata("design:paramtypes", []),
|
|
149
|
+
__metadata("design:returntype", void 0)
|
|
150
|
+
], TestHandler.prototype, "handleTest", null);
|
|
151
|
+
const handler = new TestHandler();
|
|
152
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(handler);
|
|
153
|
+
expect(() => eventEmitter.emit('test.event')).toThrow('Handler error');
|
|
154
|
+
});
|
|
155
|
+
it('should skip when method is not a function', () => {
|
|
156
|
+
const mockOn = jest.fn();
|
|
157
|
+
const eventEmitter = { on: mockOn };
|
|
158
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
159
|
+
class TestHandler {
|
|
160
|
+
handleTest(_payload) {
|
|
161
|
+
return _payload;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
__decorate([
|
|
165
|
+
(0, on_event_decorator_1.OnEvent)('test.event'),
|
|
166
|
+
__metadata("design:type", Function),
|
|
167
|
+
__metadata("design:paramtypes", [Object]),
|
|
168
|
+
__metadata("design:returntype", void 0)
|
|
169
|
+
], TestHandler.prototype, "handleTest", null);
|
|
170
|
+
const handler = new TestHandler();
|
|
171
|
+
// Overwrite method with non-function to simulate missing/invalid method
|
|
172
|
+
handler.handleTest = 'not a function';
|
|
173
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(handler);
|
|
174
|
+
expect(mockOn).not.toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
it('should handle provider with no @OnEvent decorators', () => {
|
|
177
|
+
const mockOn = jest.fn();
|
|
178
|
+
const eventEmitter = { on: mockOn };
|
|
179
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
180
|
+
class PlainHandler {
|
|
181
|
+
}
|
|
182
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(new PlainHandler());
|
|
183
|
+
expect(mockOn).not.toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
it('should handle EventEmitterService not in container', () => {
|
|
186
|
+
const emptyContainer = core_1.Container.createTestInstance();
|
|
187
|
+
// Register EventEmitterService as undefined to simulate "not found"
|
|
188
|
+
emptyContainer.register(event_emitter_service_1.EventEmitterService, undefined);
|
|
189
|
+
core_1.Container.getInstance = jest.fn(() => emptyContainer);
|
|
190
|
+
class TestHandler {
|
|
191
|
+
handle() { }
|
|
192
|
+
}
|
|
193
|
+
__decorate([
|
|
194
|
+
(0, on_event_decorator_1.OnEvent)('test'),
|
|
195
|
+
__metadata("design:type", Function),
|
|
196
|
+
__metadata("design:paramtypes", []),
|
|
197
|
+
__metadata("design:returntype", void 0)
|
|
198
|
+
], TestHandler.prototype, "handle", null);
|
|
199
|
+
expect(() => {
|
|
200
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(new TestHandler());
|
|
201
|
+
}).not.toThrow();
|
|
202
|
+
});
|
|
203
|
+
it('should handle errors during registration gracefully', () => {
|
|
204
|
+
core_1.Container.getInstance = jest.fn(() => {
|
|
205
|
+
throw new Error('Container error');
|
|
206
|
+
});
|
|
207
|
+
class TestHandler {
|
|
208
|
+
handle() { }
|
|
209
|
+
}
|
|
210
|
+
__decorate([
|
|
211
|
+
(0, on_event_decorator_1.OnEvent)('test'),
|
|
212
|
+
__metadata("design:type", Function),
|
|
213
|
+
__metadata("design:paramtypes", []),
|
|
214
|
+
__metadata("design:returntype", void 0)
|
|
215
|
+
], TestHandler.prototype, "handle", null);
|
|
216
|
+
expect(() => {
|
|
217
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProvider(new TestHandler());
|
|
218
|
+
}).not.toThrow();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe('registerListenersFromProviders', () => {
|
|
222
|
+
it('should register listeners from multiple provider classes', () => {
|
|
223
|
+
const eventEmitter = new event_emitter_service_1.EventEmitterService();
|
|
224
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
225
|
+
const handler1Fn = jest.fn();
|
|
226
|
+
const handler2Fn = jest.fn();
|
|
227
|
+
class Handler1 {
|
|
228
|
+
handle() {
|
|
229
|
+
handler1Fn();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
__decorate([
|
|
233
|
+
(0, on_event_decorator_1.OnEvent)('event.1'),
|
|
234
|
+
__metadata("design:type", Function),
|
|
235
|
+
__metadata("design:paramtypes", []),
|
|
236
|
+
__metadata("design:returntype", void 0)
|
|
237
|
+
], Handler1.prototype, "handle", null);
|
|
238
|
+
class Handler2 {
|
|
239
|
+
handle() {
|
|
240
|
+
handler2Fn();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
__decorate([
|
|
244
|
+
(0, on_event_decorator_1.OnEvent)('event.2'),
|
|
245
|
+
__metadata("design:type", Function),
|
|
246
|
+
__metadata("design:paramtypes", []),
|
|
247
|
+
__metadata("design:returntype", void 0)
|
|
248
|
+
], Handler2.prototype, "handle", null);
|
|
249
|
+
container.register(Handler1, new Handler1());
|
|
250
|
+
container.register(Handler2, new Handler2());
|
|
251
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProviders([Handler1, Handler2]);
|
|
252
|
+
eventEmitter.emit('event.1');
|
|
253
|
+
eventEmitter.emit('event.2');
|
|
254
|
+
expect(handler1Fn).toHaveBeenCalled();
|
|
255
|
+
expect(handler2Fn).toHaveBeenCalled();
|
|
256
|
+
});
|
|
257
|
+
it('should skip provider when resolve returns undefined', () => {
|
|
258
|
+
const eventEmitter = new event_emitter_service_1.EventEmitterService();
|
|
259
|
+
container.register(event_emitter_service_1.EventEmitterService, eventEmitter);
|
|
260
|
+
class Handler1 {
|
|
261
|
+
handle() { }
|
|
262
|
+
}
|
|
263
|
+
__decorate([
|
|
264
|
+
(0, on_event_decorator_1.OnEvent)('event.1'),
|
|
265
|
+
__metadata("design:type", Function),
|
|
266
|
+
__metadata("design:paramtypes", []),
|
|
267
|
+
__metadata("design:returntype", void 0)
|
|
268
|
+
], Handler1.prototype, "handle", null);
|
|
269
|
+
const originalResolve = container.resolve.bind(container);
|
|
270
|
+
jest.spyOn(container, 'resolve').mockImplementation((token) => {
|
|
271
|
+
if (token === Handler1)
|
|
272
|
+
return undefined;
|
|
273
|
+
return originalResolve(token);
|
|
274
|
+
});
|
|
275
|
+
expect(() => {
|
|
276
|
+
event_emitter_module_1.EventEmitterModule.registerListenersFromProviders([Handler1]);
|
|
277
|
+
}).not.toThrow();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import EventEmitter2 from 'eventemitter2';
|
|
2
|
+
import type { EventEmitterModuleOptions } from './event-emitter.types';
|
|
3
|
+
/**
|
|
4
|
+
* Event emitter service - wraps EventEmitter2 for DI injection
|
|
5
|
+
* Use this service to emit events throughout your application
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* @Injectable()
|
|
10
|
+
* class OrderService {
|
|
11
|
+
* constructor(private eventEmitter: EventEmitterService) {}
|
|
12
|
+
*
|
|
13
|
+
* createOrder(order: Order) {
|
|
14
|
+
* // ... create order
|
|
15
|
+
* this.eventEmitter.emit('order.created', new OrderCreatedEvent(order));
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare class EventEmitterService extends EventEmitter2 {
|
|
21
|
+
constructor(options?: EventEmitterModuleOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Emit an event
|
|
24
|
+
* @param event - Event name
|
|
25
|
+
* @param values - Payload values (spread as arguments to listeners)
|
|
26
|
+
*/
|
|
27
|
+
emit(event: string | symbol, ...values: unknown[]): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Emit an event asynchronously (listeners receive a promise)
|
|
30
|
+
*/
|
|
31
|
+
emitAsync(event: string | symbol, ...values: unknown[]): Promise<unknown[]>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=event-emitter.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-emitter.service.d.ts","sourceRoot":"","sources":["../src/event-emitter.service.ts"],"names":[],"mappings":"AACA,OAAO,aAAa,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAEvE;;;;;;;;;;;;;;;;GAgBG;AACH,qBACa,mBAAoB,SAAQ,aAAa;gBACxC,OAAO,CAAC,EAAE,yBAAyB;IAI/C;;;;OAIG;IACM,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO;IAIpE;;OAEG;IACM,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAGrF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.EventEmitterService = void 0;
|
|
16
|
+
const core_1 = require("@hazeljs/core");
|
|
17
|
+
const eventemitter2_1 = __importDefault(require("eventemitter2"));
|
|
18
|
+
/**
|
|
19
|
+
* Event emitter service - wraps EventEmitter2 for DI injection
|
|
20
|
+
* Use this service to emit events throughout your application
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* @Injectable()
|
|
25
|
+
* class OrderService {
|
|
26
|
+
* constructor(private eventEmitter: EventEmitterService) {}
|
|
27
|
+
*
|
|
28
|
+
* createOrder(order: Order) {
|
|
29
|
+
* // ... create order
|
|
30
|
+
* this.eventEmitter.emit('order.created', new OrderCreatedEvent(order));
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
let EventEmitterService = class EventEmitterService extends eventemitter2_1.default {
|
|
36
|
+
constructor(options) {
|
|
37
|
+
super(options ?? {});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Emit an event
|
|
41
|
+
* @param event - Event name
|
|
42
|
+
* @param values - Payload values (spread as arguments to listeners)
|
|
43
|
+
*/
|
|
44
|
+
emit(event, ...values) {
|
|
45
|
+
return super.emit(event, ...values);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Emit an event asynchronously (listeners receive a promise)
|
|
49
|
+
*/
|
|
50
|
+
emitAsync(event, ...values) {
|
|
51
|
+
return super.emitAsync(event, ...values);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
exports.EventEmitterService = EventEmitterService;
|
|
55
|
+
exports.EventEmitterService = EventEmitterService = __decorate([
|
|
56
|
+
(0, core_1.Service)(),
|
|
57
|
+
__metadata("design:paramtypes", [Object])
|
|
58
|
+
], EventEmitterService);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-emitter.service.test.d.ts","sourceRoot":"","sources":["../src/event-emitter.service.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const event_emitter_service_1 = require("./event-emitter.service");
|
|
4
|
+
describe('EventEmitterService', () => {
|
|
5
|
+
describe('constructor', () => {
|
|
6
|
+
it('should create instance with default options when no options provided', () => {
|
|
7
|
+
const service = new event_emitter_service_1.EventEmitterService();
|
|
8
|
+
expect(service).toBeInstanceOf(event_emitter_service_1.EventEmitterService);
|
|
9
|
+
});
|
|
10
|
+
it('should create instance with custom options', () => {
|
|
11
|
+
const service = new event_emitter_service_1.EventEmitterService({
|
|
12
|
+
wildcard: true,
|
|
13
|
+
delimiter: ':',
|
|
14
|
+
maxListeners: 20,
|
|
15
|
+
});
|
|
16
|
+
expect(service).toBeInstanceOf(event_emitter_service_1.EventEmitterService);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('emit', () => {
|
|
20
|
+
it('should emit event and invoke listeners', () => {
|
|
21
|
+
const service = new event_emitter_service_1.EventEmitterService();
|
|
22
|
+
const listener = jest.fn();
|
|
23
|
+
service.on('test.event', listener);
|
|
24
|
+
const result = service.emit('test.event', { data: 'payload' });
|
|
25
|
+
expect(result).toBe(true);
|
|
26
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(listener).toHaveBeenCalledWith({ data: 'payload' });
|
|
28
|
+
});
|
|
29
|
+
it('should emit event with multiple arguments', () => {
|
|
30
|
+
const service = new event_emitter_service_1.EventEmitterService();
|
|
31
|
+
const listener = jest.fn();
|
|
32
|
+
service.on('multi', listener);
|
|
33
|
+
service.emit('multi', 'arg1', 'arg2', 123);
|
|
34
|
+
expect(listener).toHaveBeenCalledWith('arg1', 'arg2', 123);
|
|
35
|
+
});
|
|
36
|
+
it('should return false when no listeners', () => {
|
|
37
|
+
const service = new event_emitter_service_1.EventEmitterService();
|
|
38
|
+
const result = service.emit('nonexistent.event');
|
|
39
|
+
expect(result).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe('emitAsync', () => {
|
|
43
|
+
it('should emit event asynchronously and return promise', async () => {
|
|
44
|
+
const service = new event_emitter_service_1.EventEmitterService();
|
|
45
|
+
const listener = jest.fn().mockResolvedValue(undefined);
|
|
46
|
+
service.on('async.event', listener);
|
|
47
|
+
const promise = service.emitAsync('async.event', { id: 1 });
|
|
48
|
+
expect(promise).toBeInstanceOf(Promise);
|
|
49
|
+
const results = await promise;
|
|
50
|
+
expect(listener).toHaveBeenCalledWith({ id: 1 });
|
|
51
|
+
expect(results).toEqual([undefined]);
|
|
52
|
+
});
|
|
53
|
+
it('should handle async listeners that return values', async () => {
|
|
54
|
+
const service = new event_emitter_service_1.EventEmitterService();
|
|
55
|
+
const listener = jest.fn().mockResolvedValue('result');
|
|
56
|
+
service.on('async.event', listener);
|
|
57
|
+
const results = await service.emitAsync('async.event');
|
|
58
|
+
expect(results).toEqual(['result']);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event emitter configuration options (passed to eventemitter2)
|
|
3
|
+
*/
|
|
4
|
+
export interface EventEmitterModuleOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Use wildcards for event names (e.g. 'order.*')
|
|
7
|
+
* @default false
|
|
8
|
+
*/
|
|
9
|
+
wildcard?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Delimiter used to segment namespaces
|
|
12
|
+
* @default '.'
|
|
13
|
+
*/
|
|
14
|
+
delimiter?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Emit newListener event when adding listeners
|
|
17
|
+
* @default false
|
|
18
|
+
*/
|
|
19
|
+
newListener?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Emit removeListener event when removing listeners
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
removeListener?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Maximum number of listeners per event
|
|
27
|
+
* @default 10
|
|
28
|
+
*/
|
|
29
|
+
maxListeners?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Show event name in memory leak message
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
verboseMemoryLeak?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Disable throwing uncaughtException if error event has no listeners
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
ignoreErrors?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Options for @OnEvent decorator
|
|
43
|
+
*/
|
|
44
|
+
export interface OnEventOptions {
|
|
45
|
+
/**
|
|
46
|
+
* If true, listener runs asynchronously
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
async?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* If true, prepends listener instead of appending
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
prependListener?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* If true, errors in the handler are suppressed (not rethrown)
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
suppressErrors?: boolean;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=event-emitter.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-emitter.types.d.ts","sourceRoot":"","sources":["../src/event-emitter.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hazeljs/event-emitter - Event emitter module for HazelJS
|
|
3
|
+
*
|
|
4
|
+
* Event-driven architecture with decorators, similar to @nestjs/event-emitter.
|
|
5
|
+
* Built on eventemitter2 - supports wildcards, namespaces, and async listeners.
|
|
6
|
+
*/
|
|
7
|
+
export { EventEmitterModule, type EventEmitterModuleConfig } from './event-emitter.module';
|
|
8
|
+
export { EventEmitterService } from './event-emitter.service';
|
|
9
|
+
export { OnEvent, getOnEventMetadata } from './on-event.decorator';
|
|
10
|
+
export type { OnEventMetadata } from './on-event.decorator';
|
|
11
|
+
export type { EventEmitterModuleOptions, OnEventOptions } from './event-emitter.types';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAE,KAAK,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @hazeljs/event-emitter - Event emitter module for HazelJS
|
|
4
|
+
*
|
|
5
|
+
* Event-driven architecture with decorators, similar to @nestjs/event-emitter.
|
|
6
|
+
* Built on eventemitter2 - supports wildcards, namespaces, and async listeners.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getOnEventMetadata = exports.OnEvent = exports.EventEmitterService = exports.EventEmitterModule = void 0;
|
|
10
|
+
var event_emitter_module_1 = require("./event-emitter.module");
|
|
11
|
+
Object.defineProperty(exports, "EventEmitterModule", { enumerable: true, get: function () { return event_emitter_module_1.EventEmitterModule; } });
|
|
12
|
+
var event_emitter_service_1 = require("./event-emitter.service");
|
|
13
|
+
Object.defineProperty(exports, "EventEmitterService", { enumerable: true, get: function () { return event_emitter_service_1.EventEmitterService; } });
|
|
14
|
+
var on_event_decorator_1 = require("./on-event.decorator");
|
|
15
|
+
Object.defineProperty(exports, "OnEvent", { enumerable: true, get: function () { return on_event_decorator_1.OnEvent; } });
|
|
16
|
+
Object.defineProperty(exports, "getOnEventMetadata", { enumerable: true, get: function () { return on_event_decorator_1.getOnEventMetadata; } });
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { OnEventOptions } from './event-emitter.types';
|
|
3
|
+
/**
|
|
4
|
+
* Metadata key for event listeners
|
|
5
|
+
*/
|
|
6
|
+
export declare const ON_EVENT_METADATA_KEY: unique symbol;
|
|
7
|
+
export interface OnEventMetadata {
|
|
8
|
+
event: string | symbol | string[];
|
|
9
|
+
methodName: string;
|
|
10
|
+
options?: OnEventOptions;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Decorator to mark a method as an event listener
|
|
14
|
+
* @param event - Event name(s) to listen for. With wildcards enabled, supports patterns like 'order.*'
|
|
15
|
+
* @param options - Listener options
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* @OnEvent('order.created')
|
|
20
|
+
* handleOrderCreated(payload: OrderCreatedEvent) {
|
|
21
|
+
* // handle event
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* // With wildcards (when EventEmitterModule.forRoot({ wildcard: true }))
|
|
25
|
+
* @OnEvent('order.*')
|
|
26
|
+
* handleOrderEvents(payload: OrderCreatedEvent | OrderUpdatedEvent) {
|
|
27
|
+
* // handle any order event
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function OnEvent(event: string | symbol | string[], options?: OnEventOptions): MethodDecorator;
|
|
32
|
+
/**
|
|
33
|
+
* Get @OnEvent metadata from a class
|
|
34
|
+
*/
|
|
35
|
+
export declare function getOnEventMetadata(target: object): OnEventMetadata[];
|
|
36
|
+
//# sourceMappingURL=on-event.decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on-event.decorator.d.ts","sourceRoot":"","sources":["../src/on-event.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;GAEG;AACH,eAAO,MAAM,qBAAqB,eAAmC,CAAC;AAEtE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CACrB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,EACjC,OAAO,CAAC,EAAE,cAAc,GACvB,eAAe,CAgBjB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,EAAE,CAEpE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ON_EVENT_METADATA_KEY = void 0;
|
|
4
|
+
exports.OnEvent = OnEvent;
|
|
5
|
+
exports.getOnEventMetadata = getOnEventMetadata;
|
|
6
|
+
require("reflect-metadata");
|
|
7
|
+
/**
|
|
8
|
+
* Metadata key for event listeners
|
|
9
|
+
*/
|
|
10
|
+
exports.ON_EVENT_METADATA_KEY = Symbol('event-emitter:on-event');
|
|
11
|
+
/**
|
|
12
|
+
* Decorator to mark a method as an event listener
|
|
13
|
+
* @param event - Event name(s) to listen for. With wildcards enabled, supports patterns like 'order.*'
|
|
14
|
+
* @param options - Listener options
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* @OnEvent('order.created')
|
|
19
|
+
* handleOrderCreated(payload: OrderCreatedEvent) {
|
|
20
|
+
* // handle event
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // With wildcards (when EventEmitterModule.forRoot({ wildcard: true }))
|
|
24
|
+
* @OnEvent('order.*')
|
|
25
|
+
* handleOrderEvents(payload: OrderCreatedEvent | OrderUpdatedEvent) {
|
|
26
|
+
* // handle any order event
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function OnEvent(event, options) {
|
|
31
|
+
return (target, propertyKey, _descriptor) => {
|
|
32
|
+
const existing = Reflect.getMetadata(exports.ON_EVENT_METADATA_KEY, target.constructor) || [];
|
|
33
|
+
existing.push({
|
|
34
|
+
event,
|
|
35
|
+
methodName: propertyKey.toString(),
|
|
36
|
+
options: {
|
|
37
|
+
suppressErrors: true,
|
|
38
|
+
...options,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
Reflect.defineMetadata(exports.ON_EVENT_METADATA_KEY, existing, target.constructor);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get @OnEvent metadata from a class
|
|
46
|
+
*/
|
|
47
|
+
function getOnEventMetadata(target) {
|
|
48
|
+
return Reflect.getMetadata(exports.ON_EVENT_METADATA_KEY, target.constructor) || [];
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on-event.decorator.test.d.ts","sourceRoot":"","sources":["../src/on-event.decorator.test.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC"}
|