modern decodators
This commit is contained in:
parent
8405f44bd9
commit
931f212ec7
6 changed files with 262 additions and 150 deletions
23
apps/data-ingestion/src/handlers/index.ts
Normal file
23
apps/data-ingestion/src/handlers/index.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* Handler auto-registration
|
||||||
|
* Import all handlers here to trigger auto-registration
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { IDataIngestionServices } from '@stock-bot/di';
|
||||||
|
import { QMHandler } from './qm/qm.handler';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and register all handlers
|
||||||
|
*/
|
||||||
|
export function initializeAllHandlers(services: IDataIngestionServices): void {
|
||||||
|
// QM Handler
|
||||||
|
const qmHandler = new QMHandler(services);
|
||||||
|
qmHandler.register();
|
||||||
|
|
||||||
|
// TODO: Add other handlers here as they're converted
|
||||||
|
// const webShareHandler = new WebShareHandler(services);
|
||||||
|
// webShareHandler.register();
|
||||||
|
|
||||||
|
// const ibHandler = new IBHandler(services);
|
||||||
|
// ibHandler.register();
|
||||||
|
}
|
||||||
|
|
@ -1,32 +1,17 @@
|
||||||
|
import type { IDataIngestionServices } from '@stock-bot/di';
|
||||||
import {
|
import {
|
||||||
BaseHandler,
|
BaseHandler,
|
||||||
Handler,
|
Handler,
|
||||||
Operation,
|
Operation,
|
||||||
QueueSchedule,
|
QueueSchedule,
|
||||||
type ExecutionContext,
|
type ExecutionContext
|
||||||
type HandlerConfigWithSchedule
|
|
||||||
} from '@stock-bot/handlers';
|
} from '@stock-bot/handlers';
|
||||||
import { handlerRegistry, createJobHandler } from '@stock-bot/types';
|
|
||||||
import type { IDataIngestionServices, IExecutionContext } from '@stock-bot/di';
|
|
||||||
import type { SymbolSpiderJob } from './shared/types';
|
import type { SymbolSpiderJob } from './shared/types';
|
||||||
|
|
||||||
@Handler('qm')
|
@Handler('qm')
|
||||||
export class QMHandler extends BaseHandler {
|
export class QMHandler extends BaseHandler {
|
||||||
constructor(services: IDataIngestionServices) {
|
constructor(services: IDataIngestionServices) {
|
||||||
super(services);
|
super(services); // Handler name read from @Handler decorator
|
||||||
}
|
|
||||||
|
|
||||||
async execute(operation: string, input: unknown, context: ExecutionContext): Promise<unknown> {
|
|
||||||
switch (operation) {
|
|
||||||
case 'create-sessions':
|
|
||||||
return await this.createSessions(input, context);
|
|
||||||
case 'search-symbols':
|
|
||||||
return await this.searchSymbols(input, context);
|
|
||||||
case 'spider-symbol-search':
|
|
||||||
return await this.spiderSymbolSearch(input as SymbolSpiderJob, context);
|
|
||||||
default:
|
|
||||||
throw new Error(`Unknown operation: ${operation}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation('create-sessions')
|
@Operation('create-sessions')
|
||||||
|
|
@ -36,26 +21,44 @@ export class QMHandler extends BaseHandler {
|
||||||
description: 'Create and maintain QM sessions'
|
description: 'Create and maintain QM sessions'
|
||||||
})
|
})
|
||||||
async createSessions(input: unknown, context: ExecutionContext): Promise<unknown> {
|
async createSessions(input: unknown, context: ExecutionContext): Promise<unknown> {
|
||||||
// Direct access to typed dependencies
|
this.logger.info('Creating QM sessions with new DI pattern...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check existing sessions in MongoDB
|
||||||
const sessionsCollection = this.mongodb.collection('qm_sessions');
|
const sessionsCollection = this.mongodb.collection('qm_sessions');
|
||||||
|
|
||||||
// Get existing sessions
|
|
||||||
const existingSessions = await sessionsCollection.find({}).toArray();
|
const existingSessions = await sessionsCollection.find({}).toArray();
|
||||||
this.logger.info('Found existing QM sessions', { count: existingSessions.length });
|
|
||||||
|
|
||||||
// Cache session count for monitoring
|
this.logger.info('Current QM sessions', {
|
||||||
|
existing: existingSessions.length,
|
||||||
|
action: 'creating_new_sessions'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cache session stats for monitoring
|
||||||
await this.cache.set('qm-sessions-count', existingSessions.length, 3600);
|
await this.cache.set('qm-sessions-count', existingSessions.length, 3600);
|
||||||
|
await this.cache.set('last-session-check', new Date().toISOString(), 1800);
|
||||||
|
|
||||||
return { success: true, existingCount: existingSessions.length };
|
// For now, just return the current state
|
||||||
|
// TODO: Implement actual session creation logic using new DI pattern
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
existingSessions: existingSessions.length,
|
||||||
|
message: 'QM session check completed'
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Failed to create QM sessions', { error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation('search-symbols')
|
@Operation('search-symbols')
|
||||||
async searchSymbols(input: unknown, context: ExecutionContext): Promise<unknown> {
|
async searchSymbols(_input: unknown, _context: ExecutionContext): Promise<unknown> {
|
||||||
// Direct access to typed dependencies
|
this.logger.info('Searching QM symbols with new DI pattern...');
|
||||||
|
try {
|
||||||
|
// Check existing symbols in MongoDB
|
||||||
const symbolsCollection = this.mongodb.collection('qm_symbols');
|
const symbolsCollection = this.mongodb.collection('qm_symbols');
|
||||||
|
|
||||||
// Get symbols from database
|
|
||||||
const symbols = await symbolsCollection.find({}).limit(100).toArray();
|
const symbols = await symbolsCollection.find({}).limit(100).toArray();
|
||||||
|
|
||||||
this.logger.info('QM symbol search completed', { count: symbols.length });
|
this.logger.info('QM symbol search completed', { count: symbols.length });
|
||||||
|
|
||||||
if (symbols && symbols.length > 0) {
|
if (symbols && symbols.length > 0) {
|
||||||
|
|
@ -69,12 +72,19 @@ export class QMHandler extends BaseHandler {
|
||||||
symbols: symbols.slice(0, 10), // Return first 10 symbols as sample
|
symbols: symbols.slice(0, 10), // Return first 10 symbols as sample
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
// No symbols found - this is expected initially
|
||||||
|
this.logger.info('No QM symbols found in database yet');
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: true,
|
||||||
message: 'No symbols found',
|
message: 'No symbols found yet - database is empty',
|
||||||
count: 0,
|
count: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Failed to search QM symbols', { error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation('spider-symbol-search')
|
@Operation('spider-symbol-search')
|
||||||
|
|
@ -108,57 +118,19 @@ export class QMHandler extends BaseHandler {
|
||||||
spiderJobId
|
spiderJobId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize and register the QM provider with new DI pattern
|
/**
|
||||||
export function initializeQMProviderNew(services: IDataIngestionServices) {
|
* Provide payloads for scheduled jobs
|
||||||
// Create handler instance with new DI
|
*/
|
||||||
const handler = new QMHandler(services);
|
protected getScheduledJobPayload(operation: string): any {
|
||||||
|
if (operation === 'spiderSymbolSearch') {
|
||||||
// Register with legacy format for backward compatibility
|
return {
|
||||||
const qmProviderConfig: HandlerConfigWithSchedule = {
|
|
||||||
name: 'qm',
|
|
||||||
operations: {
|
|
||||||
'create-sessions': createJobHandler(async (payload) => {
|
|
||||||
const context = handler.createExecutionContext('queue', { source: 'queue', timestamp: Date.now() });
|
|
||||||
return await handler.execute('create-sessions', payload, context);
|
|
||||||
}),
|
|
||||||
'search-symbols': createJobHandler(async (payload) => {
|
|
||||||
const context = handler.createExecutionContext('queue', { source: 'queue', timestamp: Date.now() });
|
|
||||||
return await handler.execute('search-symbols', payload, context);
|
|
||||||
}),
|
|
||||||
'spider-symbol-search': createJobHandler(async (payload: SymbolSpiderJob) => {
|
|
||||||
const context = handler.createExecutionContext('queue', { source: 'queue', timestamp: Date.now() });
|
|
||||||
return await handler.execute('spider-symbol-search', payload, context);
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
|
|
||||||
scheduledJobs: [
|
|
||||||
{
|
|
||||||
type: 'session-management',
|
|
||||||
operation: 'create-sessions',
|
|
||||||
cronPattern: '0 */15 * * *', // Every 15 minutes
|
|
||||||
priority: 7,
|
|
||||||
immediately: true,
|
|
||||||
description: 'Create and maintain QM sessions',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'qm-maintnance',
|
|
||||||
operation: 'spider-symbol-search',
|
|
||||||
payload: {
|
|
||||||
prefix: null,
|
prefix: null,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
source: 'qm',
|
source: 'qm',
|
||||||
maxDepth: 4
|
maxDepth: 4
|
||||||
},
|
|
||||||
cronPattern: '0 0 * * 0', // Every Sunday at midnight
|
|
||||||
priority: 10,
|
|
||||||
immediately: true,
|
|
||||||
description: 'Comprehensive symbol search using QM API',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
handlerRegistry.registerWithSchedule(qmProviderConfig);
|
return undefined;
|
||||||
handler.logger.debug('QM provider registered successfully with new DI pattern');
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,19 +9,19 @@ import { Hono } from 'hono';
|
||||||
import { cors } from 'hono/cors';
|
import { cors } from 'hono/cors';
|
||||||
|
|
||||||
// Library imports
|
// Library imports
|
||||||
import { getLogger, setLoggerConfig, shutdownLoggers } from '@stock-bot/logger';
|
|
||||||
import { Shutdown } from '@stock-bot/shutdown';
|
|
||||||
import { ProxyManager } from '@stock-bot/utils';
|
|
||||||
import {
|
import {
|
||||||
createDataIngestionServices,
|
createDataIngestionServices,
|
||||||
disposeDataIngestionServices,
|
disposeDataIngestionServices,
|
||||||
type IDataIngestionServices
|
type IDataIngestionServices
|
||||||
} from '@stock-bot/di';
|
} from '@stock-bot/di';
|
||||||
|
import { getLogger, setLoggerConfig, shutdownLoggers } from '@stock-bot/logger';
|
||||||
|
import { Shutdown } from '@stock-bot/shutdown';
|
||||||
import { handlerRegistry } from '@stock-bot/types';
|
import { handlerRegistry } from '@stock-bot/types';
|
||||||
|
import { ProxyManager } from '@stock-bot/utils';
|
||||||
|
|
||||||
// Local imports
|
// Local imports
|
||||||
import { createRoutes } from './routes/create-routes';
|
import { createRoutes } from './routes/create-routes';
|
||||||
import { initializeQMProviderNew } from './handlers/qm/qm.handler';
|
import { initializeAllHandlers } from './handlers';
|
||||||
|
|
||||||
const config = initializeServiceConfig();
|
const config = initializeServiceConfig();
|
||||||
console.log('Data Service Configuration:', JSON.stringify(config, null, 2));
|
console.log('Data Service Configuration:', JSON.stringify(config, null, 2));
|
||||||
|
|
@ -84,19 +84,14 @@ async function initializeServices() {
|
||||||
// Initialize handlers with new DI pattern
|
// Initialize handlers with new DI pattern
|
||||||
logger.debug('Initializing data handlers with new DI pattern...');
|
logger.debug('Initializing data handlers with new DI pattern...');
|
||||||
|
|
||||||
// Initialize QM handler with new pattern
|
// Auto-register all handlers
|
||||||
initializeQMProviderNew(services);
|
initializeAllHandlers(services);
|
||||||
|
|
||||||
// TODO: Convert other handlers to new pattern
|
|
||||||
// initializeWebShareProviderNew(services);
|
|
||||||
// initializeIBProviderNew(services);
|
|
||||||
// initializeProxyProviderNew(services);
|
|
||||||
|
|
||||||
logger.info('Data handlers initialized with new DI pattern');
|
logger.info('Data handlers initialized with new DI pattern');
|
||||||
|
|
||||||
// Create scheduled jobs from registered handlers
|
// Create scheduled jobs from registered handlers
|
||||||
logger.debug('Creating scheduled jobs from registered handlers...');
|
logger.debug('Creating scheduled jobs from registered handlers...');
|
||||||
const allHandlers = handlerRegistry.getAllHandlers();
|
const allHandlers = handlerRegistry.getAllHandlersWithSchedule();
|
||||||
|
|
||||||
let totalScheduledJobs = 0;
|
let totalScheduledJobs = 0;
|
||||||
for (const [handlerName, config] of allHandlers) {
|
for (const [handlerName, config] of allHandlers) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
import type { IDataIngestionServices, IExecutionContext } from '@stock-bot/di';
|
import type { IDataIngestionServices, IExecutionContext } from '@stock-bot/di';
|
||||||
import type { IHandler, ExecutionContext } from '../types/types';
|
import type { IHandler, ExecutionContext } from '../types/types';
|
||||||
|
import { handlerRegistry, createJobHandler, type HandlerConfigWithSchedule } from '@stock-bot/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for all handlers with improved DI
|
* Abstract base class for all handlers with improved DI
|
||||||
|
|
@ -8,9 +9,13 @@ import type { IHandler, ExecutionContext } from '../types/types';
|
||||||
*/
|
*/
|
||||||
export abstract class BaseHandler implements IHandler {
|
export abstract class BaseHandler implements IHandler {
|
||||||
protected readonly logger;
|
protected readonly logger;
|
||||||
|
private handlerName: string;
|
||||||
|
|
||||||
constructor(protected readonly services: IDataIngestionServices) {
|
constructor(protected readonly services: IDataIngestionServices, handlerName?: string) {
|
||||||
this.logger = getLogger(this.constructor.name);
|
this.logger = getLogger(this.constructor.name);
|
||||||
|
// Read handler name from decorator first, then fallback to parameter or class name
|
||||||
|
const constructor = this.constructor as any;
|
||||||
|
this.handlerName = constructor.__handlerName || handlerName || this.constructor.name.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience getters for common services
|
// Convenience getters for common services
|
||||||
|
|
@ -20,18 +25,35 @@ export abstract class BaseHandler implements IHandler {
|
||||||
protected get queue() { return this.services.queue; }
|
protected get queue() { return this.services.queue; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main execution method - must be implemented by subclasses
|
* Main execution method - automatically routes to decorated methods
|
||||||
* Works with queue (events commented for future)
|
* Works with queue (events commented for future)
|
||||||
*/
|
*/
|
||||||
abstract execute(operation: string, input: unknown, context: ExecutionContext): Promise<unknown>;
|
async execute(operation: string, input: unknown, context: ExecutionContext): Promise<unknown> {
|
||||||
|
const constructor = this.constructor as any;
|
||||||
|
const operations = constructor.__operations || [];
|
||||||
|
|
||||||
|
// Find the operation metadata
|
||||||
|
const operationMeta = operations.find((op: any) => op.name === operation);
|
||||||
|
if (!operationMeta) {
|
||||||
|
throw new Error(`Unknown operation: ${operation}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the method from the instance and call it
|
||||||
|
const method = (this as any)[operationMeta.method];
|
||||||
|
if (typeof method !== 'function') {
|
||||||
|
throw new Error(`Operation method '${operationMeta.method}' not found on handler`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await method.call(this, input, context);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue helper methods - now type-safe and direct
|
* Queue helper methods - now type-safe and direct
|
||||||
*/
|
*/
|
||||||
protected async scheduleOperation(operation: string, payload: unknown, delay?: number): Promise<void> {
|
protected async scheduleOperation(operation: string, payload: unknown, delay?: number): Promise<void> {
|
||||||
const queue = this.services.queue.getQueue(this.constructor.name.toLowerCase());
|
const queue = this.services.queue.getQueue(this.handlerName);
|
||||||
const jobData = {
|
const jobData = {
|
||||||
handler: this.constructor.name.toLowerCase(),
|
handler: this.handlerName,
|
||||||
operation,
|
operation,
|
||||||
payload
|
payload
|
||||||
};
|
};
|
||||||
|
|
@ -58,6 +80,64 @@ export abstract class BaseHandler implements IHandler {
|
||||||
// await eventBus.publish(eventName, payload);
|
// await eventBus.publish(eventName, payload);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register this handler using decorator metadata
|
||||||
|
* Automatically reads @Handler, @Operation, and @QueueSchedule decorators
|
||||||
|
*/
|
||||||
|
register(): void {
|
||||||
|
const constructor = this.constructor as any;
|
||||||
|
const handlerName = constructor.__handlerName || this.handlerName;
|
||||||
|
const operations = constructor.__operations || [];
|
||||||
|
const schedules = constructor.__schedules || [];
|
||||||
|
|
||||||
|
// Create operation handlers from decorator metadata
|
||||||
|
const operationHandlers: Record<string, any> = {};
|
||||||
|
for (const op of operations) {
|
||||||
|
operationHandlers[op.name] = createJobHandler(async (payload) => {
|
||||||
|
const context: ExecutionContext = {
|
||||||
|
type: 'queue',
|
||||||
|
metadata: { source: 'queue', timestamp: Date.now() }
|
||||||
|
};
|
||||||
|
return await this.execute(op.name, payload, context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create scheduled jobs from decorator metadata
|
||||||
|
const scheduledJobs = schedules.map((schedule: any) => {
|
||||||
|
// Find the operation name from the method name
|
||||||
|
const operation = operations.find((op: any) => op.method === schedule.operation);
|
||||||
|
return {
|
||||||
|
type: `${handlerName}-${schedule.operation}`,
|
||||||
|
operation: operation?.name || schedule.operation,
|
||||||
|
cronPattern: schedule.cronPattern,
|
||||||
|
priority: schedule.priority || 5,
|
||||||
|
immediately: schedule.immediately || false,
|
||||||
|
description: schedule.description || `${handlerName} ${schedule.operation}`,
|
||||||
|
payload: this.getScheduledJobPayload?.(schedule.operation),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const config: HandlerConfigWithSchedule = {
|
||||||
|
name: handlerName,
|
||||||
|
operations: operationHandlers,
|
||||||
|
scheduledJobs,
|
||||||
|
};
|
||||||
|
|
||||||
|
handlerRegistry.registerWithSchedule(config);
|
||||||
|
this.logger.debug('Handler registered using decorator metadata', {
|
||||||
|
handlerName,
|
||||||
|
operations: operations.length,
|
||||||
|
schedules: schedules.length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this method to provide payloads for scheduled jobs
|
||||||
|
* @param operation The operation name that needs a payload
|
||||||
|
* @returns The payload for the scheduled job, or undefined
|
||||||
|
*/
|
||||||
|
protected getScheduledJobPayload?(operation: string): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lifecycle hooks - can be overridden by subclasses
|
* Lifecycle hooks - can be overridden by subclasses
|
||||||
*/
|
*/
|
||||||
|
|
@ -67,6 +147,7 @@ export abstract class BaseHandler implements IHandler {
|
||||||
async onDispose?(): Promise<void>;
|
async onDispose?(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized handler for operations that have scheduled jobs
|
* Specialized handler for operations that have scheduled jobs
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
// Simple decorators for handler registration
|
// Modern TC39 Stage 3 decorators for handler registration
|
||||||
// These are placeholders for now - can be enhanced with reflection later
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler decorator - marks a class as a handler
|
* Handler decorator - marks a class as a handler
|
||||||
* @param name Handler name for registration
|
* @param name Handler name for registration
|
||||||
*/
|
*/
|
||||||
export function Handler(name: string) {
|
export function Handler(name: string) {
|
||||||
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
|
return function <T extends { new (...args: any[]): {} }>(
|
||||||
// Store handler name on the constructor for future use
|
target: T,
|
||||||
(constructor as any).__handlerName = name;
|
context: ClassDecoratorContext
|
||||||
return constructor;
|
) {
|
||||||
|
// Store handler name on the constructor
|
||||||
|
(target as any).__handlerName = name;
|
||||||
|
(target as any).__needsAutoRegistration = true;
|
||||||
|
|
||||||
|
console.log('Handler decorator applied', { name, className: context.name });
|
||||||
|
return target;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,16 +23,38 @@ export function Handler(name: string) {
|
||||||
* @param name Operation name
|
* @param name Operation name
|
||||||
*/
|
*/
|
||||||
export function Operation(name: string) {
|
export function Operation(name: string) {
|
||||||
return function (target: any, propertyName: string, descriptor?: PropertyDescriptor) {
|
return function (
|
||||||
// Store operation metadata for future use
|
_target: Function,
|
||||||
if (!target.constructor.__operations) {
|
context: ClassMethodDecoratorContext
|
||||||
target.constructor.__operations = [];
|
) {
|
||||||
}
|
const methodName = String(context.name);
|
||||||
target.constructor.__operations.push({
|
|
||||||
name,
|
console.log('Operation decorator applied', {
|
||||||
method: propertyName,
|
operationName: name,
|
||||||
|
methodName,
|
||||||
|
contextName: context.name,
|
||||||
|
contextKind: context.kind
|
||||||
});
|
});
|
||||||
return descriptor;
|
|
||||||
|
// Use context.addInitializer to run code when the class is constructed
|
||||||
|
context.addInitializer(function(this: any) {
|
||||||
|
const constructor = this.constructor as any;
|
||||||
|
if (!constructor.__operations) {
|
||||||
|
constructor.__operations = [];
|
||||||
|
}
|
||||||
|
constructor.__operations.push({
|
||||||
|
name,
|
||||||
|
method: methodName,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Operation registered via initializer', {
|
||||||
|
name,
|
||||||
|
methodName,
|
||||||
|
className: constructor.name
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Don't return anything - just modify metadata
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,17 +71,26 @@ export function QueueSchedule(
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
return function (target: any, propertyName: string, descriptor?: PropertyDescriptor) {
|
return function (
|
||||||
// Store schedule metadata for future use
|
_target: Function,
|
||||||
if (!target.constructor.__schedules) {
|
context: ClassMethodDecoratorContext
|
||||||
target.constructor.__schedules = [];
|
) {
|
||||||
|
const methodName = String(context.name);
|
||||||
|
|
||||||
|
// Use context.addInitializer to run code when the class is constructed
|
||||||
|
context.addInitializer(function(this: any) {
|
||||||
|
const constructor = this.constructor as any;
|
||||||
|
if (!constructor.__schedules) {
|
||||||
|
constructor.__schedules = [];
|
||||||
}
|
}
|
||||||
target.constructor.__schedules.push({
|
constructor.__schedules.push({
|
||||||
operation: propertyName,
|
operation: methodName,
|
||||||
cronPattern,
|
cronPattern,
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
return descriptor;
|
});
|
||||||
|
|
||||||
|
// Don't return anything - just modify metadata
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,12 @@
|
||||||
// Override root settings for application builds
|
// Override root settings for application builds
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"types": ["bun-types"]
|
"types": ["bun-types"],
|
||||||
|
// Modern TC39 decorators configuration
|
||||||
|
"experimentalDecorators": false,
|
||||||
|
"emitDecoratorMetadata": false,
|
||||||
|
"useDefineForClassFields": true
|
||||||
|
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue