174 lines
No EOL
5.3 KiB
TypeScript
174 lines
No EOL
5.3 KiB
TypeScript
/**
|
|
* Auto-registration utilities for handlers
|
|
* Automatically discovers and registers handlers based on file patterns
|
|
*/
|
|
|
|
import { getLogger } from '@stock-bot/logger';
|
|
import type { IServiceContainer } from '../types/service-container';
|
|
import { BaseHandler } from '../base/BaseHandler';
|
|
import { readdirSync, statSync } from 'fs';
|
|
import { join, relative } from 'path';
|
|
|
|
const logger = getLogger('handler-auto-register');
|
|
|
|
/**
|
|
* Recursively find all handler files in a directory
|
|
*/
|
|
function findHandlerFiles(dir: string, pattern = '.handler.'): string[] {
|
|
const files: string[] = [];
|
|
|
|
function scan(currentDir: string) {
|
|
const entries = readdirSync(currentDir);
|
|
|
|
for (const entry of entries) {
|
|
const fullPath = join(currentDir, entry);
|
|
const stat = statSync(fullPath);
|
|
|
|
if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
|
|
scan(fullPath);
|
|
} else if (stat.isFile() && entry.includes(pattern) && entry.endsWith('.ts')) {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
scan(dir);
|
|
return files;
|
|
}
|
|
|
|
/**
|
|
* Extract handler classes from a module
|
|
*/
|
|
function extractHandlerClasses(module: any): Array<new (services: IServiceContainer) => BaseHandler> {
|
|
const handlers: Array<new (services: IServiceContainer) => BaseHandler> = [];
|
|
|
|
for (const key of Object.keys(module)) {
|
|
const exported = module[key];
|
|
|
|
// Check if it's a class that extends BaseHandler
|
|
if (
|
|
typeof exported === 'function' &&
|
|
exported.prototype &&
|
|
exported.prototype instanceof BaseHandler
|
|
) {
|
|
handlers.push(exported);
|
|
}
|
|
}
|
|
|
|
return handlers;
|
|
}
|
|
|
|
/**
|
|
* Auto-register all handlers in a directory
|
|
* @param directory The directory to scan for handlers
|
|
* @param services The service container to inject into handlers
|
|
* @param options Configuration options
|
|
*/
|
|
export async function autoRegisterHandlers(
|
|
directory: string,
|
|
services: IServiceContainer,
|
|
options: {
|
|
pattern?: string;
|
|
exclude?: string[];
|
|
dryRun?: boolean;
|
|
} = {}
|
|
): Promise<{ registered: string[]; failed: string[] }> {
|
|
const { pattern = '.handler.', exclude = [], dryRun = false } = options;
|
|
const registered: string[] = [];
|
|
const failed: string[] = [];
|
|
|
|
try {
|
|
logger.info('Starting auto-registration of handlers', { directory, pattern });
|
|
|
|
// Find all handler files
|
|
const handlerFiles = findHandlerFiles(directory, pattern);
|
|
logger.debug(`Found ${handlerFiles.length} handler files`, { files: handlerFiles });
|
|
|
|
// Process each handler file
|
|
for (const file of handlerFiles) {
|
|
const relativePath = relative(directory, file);
|
|
|
|
// Skip excluded files
|
|
if (exclude.some(ex => relativePath.includes(ex))) {
|
|
logger.debug(`Skipping excluded file: ${relativePath}`);
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
// Import the module
|
|
const module = await import(file);
|
|
const handlerClasses = extractHandlerClasses(module);
|
|
|
|
if (handlerClasses.length === 0) {
|
|
logger.warn(`No handler classes found in ${relativePath}`);
|
|
continue;
|
|
}
|
|
|
|
// Register each handler class
|
|
for (const HandlerClass of handlerClasses) {
|
|
const handlerName = HandlerClass.name;
|
|
|
|
if (dryRun) {
|
|
logger.info(`[DRY RUN] Would register handler: ${handlerName} from ${relativePath}`);
|
|
registered.push(handlerName);
|
|
} else {
|
|
logger.info(`Registering handler: ${handlerName} from ${relativePath}`);
|
|
|
|
// Create instance and register
|
|
const handler = new HandlerClass(services);
|
|
handler.register();
|
|
|
|
registered.push(handlerName);
|
|
logger.info(`Successfully registered handler: ${handlerName}`);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
logger.error(`Failed to process handler file: ${relativePath}`, { error });
|
|
failed.push(relativePath);
|
|
}
|
|
}
|
|
|
|
logger.info('Auto-registration complete', {
|
|
totalFiles: handlerFiles.length,
|
|
registered: registered.length,
|
|
failed: failed.length
|
|
});
|
|
|
|
return { registered, failed };
|
|
} catch (error) {
|
|
logger.error('Auto-registration failed', { error });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a handler registry that auto-discovers handlers
|
|
*/
|
|
export function createAutoHandlerRegistry(services: IServiceContainer) {
|
|
return {
|
|
/**
|
|
* Register all handlers from a directory
|
|
*/
|
|
async registerDirectory(directory: string, options?: Parameters<typeof autoRegisterHandlers>[2]) {
|
|
return autoRegisterHandlers(directory, services, options);
|
|
},
|
|
|
|
/**
|
|
* Register handlers from multiple directories
|
|
*/
|
|
async registerDirectories(directories: string[], options?: Parameters<typeof autoRegisterHandlers>[2]) {
|
|
const results = {
|
|
registered: [] as string[],
|
|
failed: [] as string[]
|
|
};
|
|
|
|
for (const dir of directories) {
|
|
const result = await autoRegisterHandlers(dir, services, options);
|
|
results.registered.push(...result.registered);
|
|
results.failed.push(...result.failed);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
};
|
|
} |