Files
AFFiNE-Mirror/packages/backend/server/src/base/event/scanner.ts
2025-03-27 12:32:28 +00:00

72 lines
1.9 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { once } from 'lodash-es';
import { ModuleScanner } from '../nestjs/scanner';
import {
type EventName,
type EventOptions,
getEventHandlerMetadata,
} from './def';
@Injectable()
export class EventHandlerScanner {
constructor(private readonly scanner: ModuleScanner) {}
scan = once(() => {
const handlers: Array<{
event: EventName;
handler: (payload: any) => any;
opts?: EventOptions;
}> = [];
const providers = this.scanner.getAtInjectables();
providers.forEach(wrapper => {
const { instance, name } = wrapper;
if (!instance || wrapper.isAlias) {
return;
}
const methods = this.scanner.getAllMethodNames(instance);
methods.forEach(method => {
const fn = instance[method];
let defs = getEventHandlerMetadata(instance[method]);
if (defs.length === 0) {
return;
}
const signature = `${name}.${method}`;
if (typeof fn !== 'function') {
throw new Error(`Event handler [${signature}] is not a function.`);
}
if (!wrapper.isDependencyTreeStatic()) {
throw new Error(
`Provider [${name}] could not be RequestScoped or TransientScoped injectable if it contains event handlers.`
);
}
defs.forEach(({ event, opts }) => {
handlers.push({
event,
handler: (payload: any) => {
// NOTE(@forehalo):
// we might create spies on the event handlers when testing,
// avoid reusing `fn` variable to fail the spies or stubs
return instance[method].bind(instance)(payload);
},
opts: {
name: signature,
...opts,
},
});
});
});
});
return handlers;
});
}