diff --git a/apps/stock/data-ingestion/src/handlers/ceo/actions/get-channels.action.ts b/apps/stock/data-ingestion/src/handlers/ceo/actions/get-channels.action.ts index e4c6b40..f506e02 100644 --- a/apps/stock/data-ingestion/src/handlers/ceo/actions/get-channels.action.ts +++ b/apps/stock/data-ingestion/src/handlers/ceo/actions/get-channels.action.ts @@ -75,7 +75,7 @@ export async function getChannels(this: CeoHandler, payload: number | undefined) this.logger.info(`Fetched CEO channels for page ${page}/${totalPages}`); return { page, totalPages }; - } catch (error) { + } catch (error: any) { this.logger.error(`Error fetching CEO channels for page ${page} with proxy ${proxy}:`, error); throw new Error(`Failed to fetch CEO channels: ${error.message}`); } diff --git a/apps/stock/data-ingestion/src/handlers/ceo/ceo.handler.ts b/apps/stock/data-ingestion/src/handlers/ceo/ceo.handler.ts index a2603f2..26ea79f 100644 --- a/apps/stock/data-ingestion/src/handlers/ceo/ceo.handler.ts +++ b/apps/stock/data-ingestion/src/handlers/ceo/ceo.handler.ts @@ -6,10 +6,11 @@ import { ScheduledOperation, } from '@stock-bot/handlers'; import { getChannels, getPosts, getShorts, updateUniqueSymbols } from './actions'; +import type { DataIngestionServices } from '../../types'; @Handler('ceo') @Disabled() -export class CeoHandler extends BaseHandler { +export class CeoHandler extends BaseHandler { constructor(services: any) { super(services); // Handler name read from @Handler decorator } diff --git a/apps/stock/data-ingestion/src/handlers/ib/actions/fetch-exchanges-and-symbols.action.ts b/apps/stock/data-ingestion/src/handlers/ib/actions/fetch-exchanges-and-symbols.action.ts index f24e883..5992601 100644 --- a/apps/stock/data-ingestion/src/handlers/ib/actions/fetch-exchanges-and-symbols.action.ts +++ b/apps/stock/data-ingestion/src/handlers/ib/actions/fetch-exchanges-and-symbols.action.ts @@ -1,4 +1,3 @@ -import type { IServiceContainer } from '@stock-bot/handlers'; import { fetchExchanges } from './fetch-exchanges.action'; import { fetchSession } from './fetch-session.action'; import { fetchSymbols } from './fetch-symbols.action'; diff --git a/apps/stock/data-ingestion/src/handlers/qm/actions/corporate-actions.action.ts b/apps/stock/data-ingestion/src/handlers/qm/actions/corporate-actions.action.ts index 7510945..dfaf970 100644 --- a/apps/stock/data-ingestion/src/handlers/qm/actions/corporate-actions.action.ts +++ b/apps/stock/data-ingestion/src/handlers/qm/actions/corporate-actions.action.ts @@ -2,10 +2,11 @@ * QM Corporate Actions - Fetch and update dividends, splits, and earnings together */ -import type { BaseHandler, ExecutionContext } from '@stock-bot/handlers'; +import type { ExecutionContext } from '@stock-bot/handlers'; +import type { QMHandler } from '../qm.handler'; import { QM_CONFIG, QM_SESSION_IDS } from '../shared/config'; -import { QMSessionManager } from '../shared/session-manager'; import { QMOperationTracker } from '../shared/operation-tracker'; +import { QMSessionManager } from '../shared/session-manager'; // Cache tracker instance let operationTracker: QMOperationTracker | null = null; @@ -13,7 +14,7 @@ let operationTracker: QMOperationTracker | null = null; /** * Get or initialize the operation tracker */ -async function getOperationTracker(handler: BaseHandler): Promise { +async function getOperationTracker(handler: QMHandler): Promise { if (!operationTracker) { const { initializeQMOperations } = await import('../shared/operation-registry'); operationTracker = await initializeQMOperations(handler.mongodb, handler.logger); @@ -26,7 +27,7 @@ async function getOperationTracker(handler: BaseHandler): Promise { +async function getOperationTracker(handler: QMHandler): Promise { if (!operationTracker) { const { initializeQMOperations } = await import('../shared/operation-registry'); operationTracker = await initializeQMOperations(handler.mongodb, handler.logger); @@ -25,7 +26,7 @@ async function getOperationTracker(handler: BaseHandler): Promise { +async function getOperationTracker(handler: QMHandler): Promise { if (!operationTracker) { const { initializeQMOperations } = await import('../shared/operation-registry'); operationTracker = await initializeQMOperations(handler.mongodb, handler.logger); @@ -25,7 +26,7 @@ async function getOperationTracker(handler: BaseHandler): Promise { +async function getOperationTracker(handler: QMHandler): Promise { if (!operationTracker) { const { initializeQMOperations } = await import('../shared/operation-registry'); operationTracker = await initializeQMOperations(handler.mongodb, handler.logger); @@ -26,7 +27,7 @@ async function getOperationTracker(handler: BaseHandler): Promise); // TODO: Update with correct intraday endpoint const apiUrl = `${QM_CONFIG.BASE_URL}/datatool/intraday.json?${searchParams.toString()}`; @@ -157,7 +158,7 @@ export async function updateIntradayBars( * This handles both initial crawls and regular updates */ export async function scheduleIntradayUpdates( - this: BaseHandler, + this: QMHandler, input: { limit?: number; mode?: 'crawl' | 'update'; // crawl for historical, update for recent @@ -259,7 +260,6 @@ export async function scheduleIntradayUpdates( crawlState: { finished: false, oldestDateReached: new Date(startDate.getTime() - daysToFetch * 24 * 60 * 60 * 1000), - lastCrawlDirection: 'backward' } }); } else { diff --git a/apps/stock/data-ingestion/src/handlers/qm/actions/prices.action.ts b/apps/stock/data-ingestion/src/handlers/qm/actions/prices.action.ts index 8333895..4195790 100644 --- a/apps/stock/data-ingestion/src/handlers/qm/actions/prices.action.ts +++ b/apps/stock/data-ingestion/src/handlers/qm/actions/prices.action.ts @@ -2,10 +2,11 @@ * QM Prices Actions - Fetch and update daily price data */ -import type { BaseHandler, ExecutionContext } from '@stock-bot/handlers'; +import type { ExecutionContext } from '@stock-bot/handlers'; +import type { QMHandler } from '../qm.handler'; import { QM_CONFIG, QM_SESSION_IDS } from '../shared/config'; -import { QMSessionManager } from '../shared/session-manager'; import { QMOperationTracker } from '../shared/operation-tracker'; +import { QMSessionManager } from '../shared/session-manager'; // Cache tracker instance let operationTracker: QMOperationTracker | null = null; @@ -13,7 +14,7 @@ let operationTracker: QMOperationTracker | null = null; /** * Get or initialize the operation tracker */ -async function getOperationTracker(handler: BaseHandler): Promise { +async function getOperationTracker(handler: QMHandler): Promise { if (!operationTracker) { const { initializeQMOperations } = await import('../shared/operation-registry'); operationTracker = await initializeQMOperations(handler.mongodb, handler.logger); @@ -25,7 +26,7 @@ async function getOperationTracker(handler: BaseHandler): Promise { const { sessionId, sessionType = 'LOOKUP' } = input || {}; @@ -108,7 +108,7 @@ export async function createSession( const sessionUrl = `${QM_CONFIG.BASE_URL}${QM_CONFIG.SESSION_PATH}/${sessionId}`; // Build request options - const sessionRequest: BunRequestInit = { + const sessionRequest = { proxy: proxyUrl || undefined, headers: getQmHeaders(), }; diff --git a/apps/stock/data-ingestion/src/handlers/qm/actions/symbol-info.action.ts b/apps/stock/data-ingestion/src/handlers/qm/actions/symbol-info.action.ts index 288175c..b350979 100644 --- a/apps/stock/data-ingestion/src/handlers/qm/actions/symbol-info.action.ts +++ b/apps/stock/data-ingestion/src/handlers/qm/actions/symbol-info.action.ts @@ -2,10 +2,11 @@ * QM Symbol Info Actions - Fetch and update symbol metadata */ -import type { BaseHandler, ExecutionContext } from '@stock-bot/handlers'; +import type { ExecutionContext } from '@stock-bot/handlers'; +import type { QMHandler } from '../qm.handler'; import { QM_CONFIG, QM_SESSION_IDS } from '../shared/config'; -import { QMSessionManager } from '../shared/session-manager'; import { QMOperationTracker } from '../shared/operation-tracker'; +import { QMSessionManager } from '../shared/session-manager'; // Cache tracker instance let operationTracker: QMOperationTracker | null = null; @@ -13,7 +14,7 @@ let operationTracker: QMOperationTracker | null = null; /** * Get or initialize the operation tracker */ -async function getOperationTracker(handler: BaseHandler): Promise { +async function getOperationTracker(handler: QMHandler): Promise { if (!operationTracker) { const { initializeQMOperations } = await import('../shared/operation-registry'); operationTracker = await initializeQMOperations(handler.mongodb, handler.logger); @@ -26,7 +27,7 @@ async function getOperationTracker(handler: BaseHandler): Promise 0) { - await this.mongodb.batchUpsert('qmExchanges', exchanges, ['exchange']); + await this.mongodb?.batchUpsert('qmExchanges', exchanges, ['exchange']); this.logger.debug('Stored exchanges from spider search', { count: exchanges.length }); @@ -136,7 +137,7 @@ export async function spiderSymbol( * Search QM symbols API directly */ export async function searchSymbols( - this: BaseHandler, + this: QMHandler, input: { query: string }, _context?: ExecutionContext ): Promise { diff --git a/apps/stock/data-ingestion/src/handlers/qm/qm.handler.ts b/apps/stock/data-ingestion/src/handlers/qm/qm.handler.ts index db8d918..3d31e43 100644 --- a/apps/stock/data-ingestion/src/handlers/qm/qm.handler.ts +++ b/apps/stock/data-ingestion/src/handlers/qm/qm.handler.ts @@ -4,6 +4,7 @@ import { Operation, ScheduledOperation, } from '@stock-bot/handlers'; +import type { DataIngestionServices } from '../../types'; import { checkSessions, createSession, @@ -25,7 +26,7 @@ import { import { initializeQMOperations } from './shared/operation-registry'; @Handler('qm') -export class QMHandler extends BaseHandler { +export class QMHandler extends BaseHandler { constructor(services: any) { super(services); // Handler name read from @Handler decorator // Initialize operations after super() so services are available diff --git a/apps/stock/data-ingestion/src/handlers/qm/shared/operation-tracker.ts b/apps/stock/data-ingestion/src/handlers/qm/shared/operation-tracker.ts index fd0ee2e..18222a5 100644 --- a/apps/stock/data-ingestion/src/handlers/qm/shared/operation-tracker.ts +++ b/apps/stock/data-ingestion/src/handlers/qm/shared/operation-tracker.ts @@ -3,7 +3,7 @@ * Supports dynamic operation registration with auto-indexing */ -import type { MongoDBClient, Logger } from '@stock-bot/types'; +import type { Logger, MongoDBClient } from '@stock-bot/types'; import type { IntradayCrawlSymbol, QMOperationConfig } from './types'; export class QMOperationTracker { @@ -118,7 +118,7 @@ export class QMOperationTracker { await this.mongodb.updateOne(this.collectionName, { symbol }, update); - this.logger.trace('Updated symbol operation', { + this.logger.debug('Updated symbol operation', { symbol, operation: operationName, status: data.status diff --git a/apps/stock/data-ingestion/src/types.ts b/apps/stock/data-ingestion/src/types.ts new file mode 100644 index 0000000..4b7b2b6 --- /dev/null +++ b/apps/stock/data-ingestion/src/types.ts @@ -0,0 +1,25 @@ +/** + * Data Ingestion Service Types + * Defines required services for data ingestion handlers + */ + +import type { ServiceTypes } from '@stock-bot/types'; + +/** + * Services required by data ingestion handlers + * Makes specific services non-nullable since they're always enabled + */ +export type DataIngestionServices = ServiceTypes & { + // Always required services + logger: NonNullable; + mongodb: NonNullable; + cache: NonNullable; + queue: NonNullable; + queueManager: NonNullable; + browser: NonNullable; + proxy: NonNullable; + // Optional services remain optional + postgres: ServiceTypes['postgres']; + questdb: ServiceTypes['questdb']; + globalCache: ServiceTypes['globalCache']; +}; \ No newline at end of file diff --git a/apps/stock/data-ingestion/tsconfig.json b/apps/stock/data-ingestion/tsconfig.json index 0ac761f..5f0e071 100644 --- a/apps/stock/data-ingestion/tsconfig.json +++ b/apps/stock/data-ingestion/tsconfig.json @@ -1,18 +1,18 @@ { - "extends": "../../tsconfig.app.json", + "extends": "../../../tsconfig.app.json", "references": [ - { "path": "../../libs/core/types" }, - { "path": "../../libs/core/config" }, - { "path": "../../libs/core/logger" }, - { "path": "../../libs/core/di" }, - { "path": "../../libs/core/handlers" }, - { "path": "../../libs/data/cache" }, - { "path": "../../libs/data/mongodb" }, - { "path": "../../libs/data/postgres" }, - { "path": "../../libs/data/questdb" }, - { "path": "../../libs/services/queue" }, - { "path": "../../libs/services/shutdown" }, - { "path": "../../libs/utils" }, + { "path": "../../../libs/core/types" }, + { "path": "../../../libs/core/config" }, + { "path": "../../../libs/core/logger" }, + { "path": "../../../libs/core/di" }, + { "path": "../../../libs/core/handlers" }, + { "path": "../../../libs/core/cache" }, + { "path": "../../../libs/data/mongodb" }, + { "path": "../../../libs/data/postgres" }, + { "path": "../../../libs/data/questdb" }, + { "path": "../../../libs/core/queue" }, + { "path": "../../../libs/core/shutdown" }, + { "path": "../../../libs/utils" }, { "path": "../config" } ] } diff --git a/apps/stock/data-pipeline/tsconfig.json b/apps/stock/data-pipeline/tsconfig.json index cb4b0dc..3346965 100644 --- a/apps/stock/data-pipeline/tsconfig.json +++ b/apps/stock/data-pipeline/tsconfig.json @@ -1,14 +1,14 @@ { - "extends": "../../tsconfig.app.json", + "extends": "../../../tsconfig.app.json", "references": [ - { "path": "../../libs/core/types" }, - { "path": "../../libs/core/config" }, - { "path": "../../libs/core/logger" }, - { "path": "../../libs/data/cache" }, - { "path": "../../libs/services/queue" }, - { "path": "../../libs/data/mongodb" }, - { "path": "../../libs/data/postgres" }, - { "path": "../../libs/data/questdb" }, - { "path": "../../libs/services/shutdown" } + { "path": "../../../libs/core/types" }, + { "path": "../../../libs/core/config" }, + { "path": "../../../libs/core/logger" }, + { "path": "../../../libs/core/cache" }, + { "path": "../../../libs/core/queue" }, + { "path": "../../../libs/data/mongodb" }, + { "path": "../../../libs/data/postgres" }, + { "path": "../../../libs/data/questdb" }, + { "path": "../../../libs/core/shutdown" } ] } diff --git a/apps/stock/web-api/src/types.ts b/apps/stock/web-api/src/types.ts new file mode 100644 index 0000000..4a28ae9 --- /dev/null +++ b/apps/stock/web-api/src/types.ts @@ -0,0 +1,25 @@ +/** + * Web API Service Types + * Defines required services for web API handlers + */ + +import type { ServiceTypes } from '@stock-bot/types'; + +/** + * Services required by web API handlers + * Makes specific services non-nullable since they're always enabled + */ +export type WebApiServices = ServiceTypes & { + // Always required services + logger: NonNullable; + mongodb: NonNullable; + postgres: NonNullable; + cache: NonNullable; + queue: NonNullable; + queueManager: NonNullable; + // Optional services remain optional + browser: ServiceTypes['browser']; + proxy: ServiceTypes['proxy']; + questdb: ServiceTypes['questdb']; + globalCache: ServiceTypes['globalCache']; +}; \ No newline at end of file diff --git a/apps/stock/web-api/tsconfig.json b/apps/stock/web-api/tsconfig.json index cb4b0dc..3346965 100644 --- a/apps/stock/web-api/tsconfig.json +++ b/apps/stock/web-api/tsconfig.json @@ -1,14 +1,14 @@ { - "extends": "../../tsconfig.app.json", + "extends": "../../../tsconfig.app.json", "references": [ - { "path": "../../libs/core/types" }, - { "path": "../../libs/core/config" }, - { "path": "../../libs/core/logger" }, - { "path": "../../libs/data/cache" }, - { "path": "../../libs/services/queue" }, - { "path": "../../libs/data/mongodb" }, - { "path": "../../libs/data/postgres" }, - { "path": "../../libs/data/questdb" }, - { "path": "../../libs/services/shutdown" } + { "path": "../../../libs/core/types" }, + { "path": "../../../libs/core/config" }, + { "path": "../../../libs/core/logger" }, + { "path": "../../../libs/core/cache" }, + { "path": "../../../libs/core/queue" }, + { "path": "../../../libs/data/mongodb" }, + { "path": "../../../libs/data/postgres" }, + { "path": "../../../libs/data/questdb" }, + { "path": "../../../libs/core/shutdown" } ] } diff --git a/libs/core/handlers/src/base/BaseHandler.ts b/libs/core/handlers/src/base/BaseHandler.ts index 330665e..898589c 100644 --- a/libs/core/handlers/src/base/BaseHandler.ts +++ b/libs/core/handlers/src/base/BaseHandler.ts @@ -40,41 +40,41 @@ export interface JobScheduleOptions { * Abstract base class for all handlers with improved DI * Provides common functionality and structure for queue/event operations */ -export abstract class BaseHandler implements IHandler { +export abstract class BaseHandler implements IHandler { // Direct service properties - flattened for cleaner access with proper types - readonly logger: ServiceTypes['logger']; - readonly cache: ServiceTypes['cache']; - readonly globalCache: ServiceTypes['globalCache']; - readonly queueManager: ServiceTypes['queueManager']; - readonly queue: ServiceTypes['queue']; // Specific queue for this handler - readonly proxy: ServiceTypes['proxy']; - readonly browser: ServiceTypes['browser']; - readonly mongodb: ServiceTypes['mongodb']; - readonly postgres: ServiceTypes['postgres']; - readonly questdb: ServiceTypes['questdb']; + readonly logger: TServices['logger']; + readonly cache: TServices['cache']; + readonly globalCache: TServices['globalCache']; + readonly queueManager: TServices['queueManager']; + readonly queue!: TServices['queue']; // Specific queue for this handler - initialized if queueManager exists + readonly proxy: TServices['proxy']; + readonly browser: TServices['browser']; + readonly mongodb: TServices['mongodb']; + readonly postgres: TServices['postgres']; + readonly questdb: TServices['questdb']; private handlerName: string; - constructor(services: IServiceContainer, handlerName?: string) { + constructor(services: TServices, handlerName?: string) { // 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(); // Flatten all services onto the handler instance - this.logger = getLogger(this.constructor.name); - this.cache = services.cache; - this.globalCache = services.globalCache; - this.queueManager = services.queueManager; - this.proxy = services.proxy; - this.browser = services.browser; - this.mongodb = services.mongodb; - this.postgres = services.postgres; - this.questdb = services.questdb; + this.logger = getLogger(this.constructor.name) as TServices['logger']; + this.cache = services.cache as TServices['cache']; + this.globalCache = services.globalCache as TServices['globalCache']; + this.queueManager = services.queueManager as TServices['queueManager']; + this.proxy = services.proxy as TServices['proxy']; + this.browser = services.browser as TServices['browser']; + this.mongodb = services.mongodb as TServices['mongodb']; + this.postgres = services.postgres as TServices['postgres']; + this.questdb = services.questdb as TServices['questdb']; // Get the specific queue for this handler if (this.queueManager) { - this.queue = this.queueManager.getQueue(this.handlerName); + this.queue = this.queueManager.getQueue(this.handlerName) as TServices['queue']; } } diff --git a/tsconfig.app.json b/tsconfig.app.json index 009565a..774bfcb 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -6,12 +6,11 @@ "composite": true, "incremental": true, "types": ["bun-types"], - // Modern TC39 Stage 3 decorators (TypeScript 5+ default) - "experimentalDecorators": false, + // Use legacy decorators for compatibility with the handler system + "experimentalDecorators": true, "emitDecoratorMetadata": true, // Suppress decorator-related type checking issues due to Bun's hybrid implementation - "skipLibCheck": true, - "suppressImplicitAnyIndexErrors": true + "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]