|
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'; |
14 | 3 |
|
15 | 4 | /**
|
16 | 5 | * An event bus for internal use.
|
17 | 6 | * This should be used to de-couple logic between different modules.
|
| 7 | + * |
| 8 | + * @deprecated use {@link Hooks} instead. |
18 | 9 | */
|
19 |
| -export abstract class IEventBus { |
20 |
| - publish: (event: object) => Promise<void>; |
21 |
| - publishAll: (events: object[]) => Promise<void>; |
22 |
| -} |
23 |
| - |
24 | 10 | @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) {} |
32 | 13 |
|
33 | 14 | 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); |
51 | 16 | }
|
52 | 17 |
|
53 | 18 | async publishAll(events: any[]): Promise<void> {
|
54 | 19 | await Promise.all(events.map((e) => this.publish(e)));
|
55 | 20 | }
|
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 |
| - } |
91 | 21 | }
|
0 commit comments