Skip to content

Commit ab66635

Browse files
committed
Deprecate Events for Hooks
1 parent c61aa16 commit ab66635

File tree

4 files changed

+23
-110
lines changed

4 files changed

+23
-110
lines changed

src/core/events/constants.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 7 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,21 @@
1-
import { Injectable, type OnApplicationBootstrap } from '@nestjs/common';
2-
import {
3-
type FnLike,
4-
groupToMapBy,
5-
mapValues,
6-
sortBy,
7-
} from '@seedcompany/common';
8-
import { stripIndent } from 'common-tags';
9-
import { type ID, ServerException } from '~/common';
10-
import { MetadataDiscovery } from '~/core/discovery';
11-
import { ILogger, Logger } from '../logger';
12-
import { EVENT_METADATA } from './constants';
13-
import { EventsHandler, type IEventHandler } from './event-handler.decorator';
1+
import { Injectable } from '@nestjs/common';
2+
import { Hooks } from '../hooks';
143

154
/**
165
* An event bus for internal use.
176
* This should be used to de-couple logic between different modules.
7+
*
8+
* @deprecated use {@link Hooks} instead.
189
*/
19-
export abstract class IEventBus {
20-
publish: (event: object) => Promise<void>;
21-
publishAll: (events: object[]) => Promise<void>;
22-
}
23-
2410
@Injectable()
25-
export class SyncEventBus implements IEventBus, OnApplicationBootstrap {
26-
private listenerMap: Record<ID, FnLike[]> = {};
27-
28-
constructor(
29-
private readonly discovery: MetadataDiscovery,
30-
@Logger('event-bus') private readonly logger: ILogger,
31-
) {}
11+
export class IEventBus {
12+
constructor(private readonly hooks: Hooks) {}
3213

3314
async publish(event: object): Promise<void> {
34-
let id;
35-
try {
36-
id = Reflect.getMetadata(EVENT_METADATA, event.constructor).id;
37-
} catch (e) {
38-
// Fails when event doesn't have an ID in its metadata,
39-
// which is created upon first registration with a handler.
40-
if (process.env.NODE_ENV === 'production') {
41-
return;
42-
}
43-
this.logger.warning(
44-
`It appears that ${event.constructor.name} does not have any registered handlers. Are you sure this is right?`,
45-
);
46-
return;
47-
}
48-
for (const handler of this.listenerMap[id] || []) {
49-
await handler(event);
50-
}
15+
await this.hooks.run(event);
5116
}
5217

5318
async publishAll(events: any[]): Promise<void> {
5419
await Promise.all(events.map((e) => this.publish(e)));
5520
}
56-
57-
async onApplicationBootstrap() {
58-
const discovered = this.discovery
59-
.discover(EventsHandler)
60-
.classes<IEventHandler<any>>();
61-
62-
if (process.env.NODE_ENV !== 'production') {
63-
const defined = new Set<string>();
64-
for (const entry of discovered) {
65-
const name = entry.instance.constructor.name;
66-
if (defined.has(name)) {
67-
throw new ServerException(stripIndent`
68-
Event handler "${name}" has already been defined.
69-
Event handlers have to have unique class names for DX sanity.
70-
`);
71-
}
72-
defined.add(name);
73-
}
74-
}
75-
76-
const flat = discovered.flatMap(({ instance, meta }) => {
77-
const handler = instance.handle.bind(instance);
78-
return [...meta].map(([id, priority]) => ({
79-
id,
80-
priority,
81-
handler,
82-
}));
83-
});
84-
const grouped = groupToMapBy(flat, (entry) => entry.id);
85-
this.listenerMap = mapValues(grouped, (_, entries) =>
86-
sortBy(entries, [(entry) => entry.priority, 'desc']).map(
87-
(e) => e.handler,
88-
),
89-
).asRecord;
90-
}
9121
}
Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import { type Type } from '@nestjs/common';
2-
import { createMetadataDecorator } from '@seedcompany/nest';
3-
import { nanoid } from 'nanoid';
4-
import { type ID } from '~/common';
5-
import { EVENT_METADATA, type Priority } from './constants';
2+
import { OnHook } from '../hooks';
63

74
/**
85
* Subscribe to these given events.
@@ -20,41 +17,33 @@ import { EVENT_METADATA, type Priority } from './constants';
2017
* Event2
2118
* )
2219
* ```
20+
*
21+
* @deprecated use {@link OnHook} instead.
2322
*/
24-
export const EventsHandler = createMetadataDecorator({
25-
types: ['class'],
26-
setter: (...events: Array<Type | [...Type[], Priority]>) => {
27-
const metadata = new Map<ID, Priority>();
28-
23+
export const EventsHandler =
24+
(...events: Array<Type | [...Type[], priority: number]>): ClassDecorator =>
25+
(target) => {
2926
for (const arg of events) {
30-
let priority: Priority = 0;
27+
let priority = 0;
3128
let eventTypes: Type[] = [];
3229
if (!Array.isArray(arg)) {
3330
eventTypes = [arg];
3431
} else {
35-
priority = arg.pop() as Priority;
32+
priority = arg.pop() as number;
3633
eventTypes = arg as Type[];
3734
}
3835

3936
for (const event of eventTypes) {
40-
metadata.set(getOrDefineEventId(event), priority);
37+
OnHook(event, priority * -1)(target);
4138
}
4239
}
40+
};
4341

44-
return metadata;
45-
},
46-
});
47-
42+
/**
43+
* @deprecated IMO this adds little value. It is just a small hint
44+
* that "hey this class's handle() method is called indirectly".
45+
*/
4846
// eslint-disable-next-line @typescript-eslint/naming-convention
4947
export interface IEventHandler<T> {
5048
handle: (event: T) => Promise<void>;
5149
}
52-
53-
const getOrDefineEventId = (event: Type): ID => {
54-
if (!Reflect.hasMetadata(EVENT_METADATA, event)) {
55-
const id: ID = nanoid();
56-
Reflect.defineMetadata(EVENT_METADATA, { id }, event);
57-
return id;
58-
}
59-
return Reflect.getMetadata(EVENT_METADATA, event).id;
60-
};

src/core/events/events.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Module } from '@nestjs/common';
2-
import { IEventBus, SyncEventBus } from './event-bus.service';
2+
import { IEventBus } from './event-bus.service';
33

44
@Module({
5-
providers: [SyncEventBus, { provide: IEventBus, useExisting: SyncEventBus }],
5+
providers: [IEventBus],
66
exports: [IEventBus],
77
})
88
export class EventsModule {}

0 commit comments

Comments
 (0)