moved folders around
This commit is contained in:
parent
4f89affc2b
commit
36cb84b343
202 changed files with 1160 additions and 660 deletions
|
|
@ -1,23 +0,0 @@
|
|||
export { ConnectionFactory } from './connection-factory';
|
||||
export { ServiceContainer, createServiceContainer } from './service-container';
|
||||
export { PoolSizeCalculator } from './pool-size-calculator';
|
||||
|
||||
export type {
|
||||
ConnectionPoolConfig,
|
||||
MongoDBPoolConfig,
|
||||
PostgreSQLPoolConfig,
|
||||
CachePoolConfig,
|
||||
ConnectionFactoryConfig,
|
||||
ConnectionPool,
|
||||
PoolMetrics,
|
||||
ConnectionFactory as IConnectionFactory,
|
||||
} from './types';
|
||||
|
||||
export type {
|
||||
ServiceRegistration,
|
||||
ServiceResolver,
|
||||
} from './service-container';
|
||||
|
||||
export type {
|
||||
PoolSizeRecommendation,
|
||||
} from './pool-size-calculator';
|
||||
|
|
@ -150,6 +150,10 @@ export function getProviderConfig(provider: string) {
|
|||
return (providers as Record<string, unknown>)[provider];
|
||||
}
|
||||
|
||||
export function getQueueConfig() {
|
||||
return getConfig().queue;
|
||||
}
|
||||
|
||||
// Export environment helpers
|
||||
export function isDevelopment(): boolean {
|
||||
return getConfig().environment === 'development';
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"extends": "../../tsconfig.lib.json",
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import { getLogger, type Logger } from '@stock-bot/logger';
|
||||
import { MongoDBClient, createMongoDBClient, type ConnectionEvents } from '@stock-bot/mongodb-client';
|
||||
import { PostgreSQLClient, createPostgreSQLClient } from '@stock-bot/postgres-client';
|
||||
import { MongoDBClient, createMongoDBClient, type ConnectionEvents } from '@stock-bot/mongodb';
|
||||
import { PostgreSQLClient, createPostgreSQLClient } from '@stock-bot/postgres';
|
||||
import { createCache, type CacheProvider } from '@stock-bot/cache';
|
||||
import { QueueManager } from '@stock-bot/queue';
|
||||
import type {
|
||||
ConnectionFactory as IConnectionFactory,
|
||||
ConnectionPool,
|
||||
|
|
@ -9,6 +10,7 @@ import type {
|
|||
MongoDBPoolConfig,
|
||||
PostgreSQLPoolConfig,
|
||||
CachePoolConfig,
|
||||
QueuePoolConfig,
|
||||
PoolMetrics,
|
||||
} from './types';
|
||||
|
||||
|
|
@ -189,7 +191,51 @@ export class ConnectionFactory implements IConnectionFactory {
|
|||
}
|
||||
}
|
||||
|
||||
getPool(type: 'mongodb' | 'postgres' | 'cache', name: string): ConnectionPool<any> | undefined {
|
||||
createQueue(poolConfig: QueuePoolConfig): ConnectionPool<QueueManager> {
|
||||
const key = `queue:${poolConfig.name}`;
|
||||
|
||||
if (this.pools.has(key)) {
|
||||
this.logger.debug('Reusing existing queue manager', { name: poolConfig.name });
|
||||
return this.pools.get(key)!;
|
||||
}
|
||||
|
||||
this.logger.info('Creating queue manager', {
|
||||
name: poolConfig.name,
|
||||
});
|
||||
|
||||
try {
|
||||
// Initialize or get existing QueueManager instance
|
||||
const queueManager = QueueManager.getOrInitialize(poolConfig.config);
|
||||
|
||||
const pool: ConnectionPool<QueueManager> = {
|
||||
name: poolConfig.name,
|
||||
client: queueManager,
|
||||
metrics: this.createInitialMetrics(),
|
||||
health: async () => {
|
||||
try {
|
||||
// Check if QueueManager is initialized
|
||||
queueManager.getQueueNames();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
dispose: async () => {
|
||||
// QueueManager handles its own shutdown
|
||||
await queueManager.shutdown();
|
||||
this.pools.delete(key);
|
||||
},
|
||||
};
|
||||
|
||||
this.pools.set(key, pool);
|
||||
return pool;
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to create queue manager', { name: poolConfig.name, error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
getPool(type: 'mongodb' | 'postgres' | 'cache' | 'queue', name: string): ConnectionPool<any> | undefined {
|
||||
const key = `${type}:${name}`;
|
||||
return this.pools.get(key);
|
||||
}
|
||||
6
libs/core/di/index.ts
Normal file
6
libs/core/di/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Export all dependency injection components
|
||||
export * from './service-container';
|
||||
export * from './connection-factory';
|
||||
export * from './operation-context';
|
||||
export * from './pool-size-calculator';
|
||||
export * from './types';
|
||||
|
|
@ -11,9 +11,9 @@
|
|||
import { createCache, type CacheProvider } from '@stock-bot/cache';
|
||||
import { getLogger, type Logger } from '@stock-bot/logger';
|
||||
import { getDatabaseConfig } from '@stock-bot/config';
|
||||
import type { ServiceResolver } from '@stock-bot/connection-factory';
|
||||
import type { MongoDBClient } from '@stock-bot/mongodb-client';
|
||||
import type { PostgreSQLClient } from '@stock-bot/postgres-client';
|
||||
import type { ServiceResolver } from './service-container';
|
||||
import type { MongoDBClient } from '@stock-bot/mongodb';
|
||||
import type { PostgreSQLClient } from '@stock-bot/postgres';
|
||||
|
||||
export interface OperationContextOptions {
|
||||
handlerName: string;
|
||||
|
|
@ -28,6 +28,7 @@ export class OperationContext {
|
|||
private _mongodb?: MongoDBClient;
|
||||
private _postgres?: PostgreSQLClient;
|
||||
private _cache?: CacheProvider;
|
||||
private _queue?: any; // Type will be QueueManager but we avoid import for circular deps
|
||||
|
||||
private static sharedCache: CacheProvider | null = null;
|
||||
private static parentLoggers = new Map<string, Logger>();
|
||||
|
|
@ -92,16 +93,45 @@ export class OperationContext {
|
|||
return this._postgres!;
|
||||
}
|
||||
|
||||
// Lazy load QueueManager
|
||||
get queue(): any {
|
||||
if (!this._queue) {
|
||||
if (this.container) {
|
||||
try {
|
||||
this._queue = this.container.resolve('queue');
|
||||
} catch (error) {
|
||||
this.logger.warn('Failed to resolve QueueManager from container, falling back to singleton', { error });
|
||||
this._queue = this.getLegacyQueueManager();
|
||||
}
|
||||
} else {
|
||||
this._queue = this.getLegacyQueueManager();
|
||||
}
|
||||
}
|
||||
return this._queue!;
|
||||
}
|
||||
|
||||
// Legacy method for QueueManager
|
||||
private getLegacyQueueManager(): any {
|
||||
try {
|
||||
// Dynamic import to avoid TypeScript issues during build
|
||||
const { QueueManager } = require('@stock-bot/queue');
|
||||
return QueueManager.getInstance();
|
||||
} catch (error) {
|
||||
this.logger.warn('QueueManager not initialized, queue operations may fail', { error });
|
||||
throw new Error('QueueManager not available');
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy method for backward compatibility
|
||||
private getLegacyDatabaseClient(type: 'mongodb' | 'postgres'): any {
|
||||
try {
|
||||
if (type === 'mongodb') {
|
||||
// Dynamic import to avoid TypeScript issues during build
|
||||
const { getMongoDBClient } = require('@stock-bot/mongodb-client');
|
||||
const { getMongoDBClient } = require('@stock-bot/mongodb');
|
||||
return getMongoDBClient();
|
||||
} else {
|
||||
// Dynamic import to avoid TypeScript issues during build
|
||||
const { getPostgreSQLClient } = require('@stock-bot/postgres-client');
|
||||
const { getPostgreSQLClient } = require('@stock-bot/postgres');
|
||||
return getPostgreSQLClient();
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -1,14 +1,19 @@
|
|||
{
|
||||
"name": "@stock-bot/connection-factory",
|
||||
"name": "@stock-bot/di",
|
||||
"version": "1.0.0",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/logger": "workspace:*",
|
||||
"@stock-bot/mongodb-client": "workspace:*",
|
||||
"@stock-bot/postgres-client": "workspace:*",
|
||||
"@stock-bot/mongodb": "workspace:*",
|
||||
"@stock-bot/postgres": "workspace:*",
|
||||
"@stock-bot/cache": "workspace:*",
|
||||
"@stock-bot/queue": "workspace:*",
|
||||
"mongodb": "^6.3.0",
|
||||
"pg": "^8.11.3"
|
||||
},
|
||||
|
|
@ -99,12 +99,22 @@ export class ServiceContainer implements ServiceResolver {
|
|||
}
|
||||
}
|
||||
|
||||
// Helper to create pre-configured containers for services
|
||||
// Enhanced service container factory with infrastructure services
|
||||
export function createServiceContainer(
|
||||
serviceName: string,
|
||||
connectionFactory: ConnectionFactory
|
||||
connectionFactory: ConnectionFactory,
|
||||
config?: any
|
||||
): ServiceContainer {
|
||||
const container = new ServiceContainer(serviceName);
|
||||
|
||||
// Register configuration if provided
|
||||
if (config) {
|
||||
container.register({
|
||||
name: 'config',
|
||||
factory: () => config,
|
||||
singleton: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Register connection factories
|
||||
container.register({
|
||||
|
|
@ -143,5 +153,63 @@ export function createServiceContainer(
|
|||
singleton: true,
|
||||
});
|
||||
|
||||
container.register({
|
||||
name: 'queue',
|
||||
factory: () => {
|
||||
const pool = connectionFactory.createQueue({
|
||||
name: 'default',
|
||||
config: {} as any, // Config injected by factory
|
||||
});
|
||||
return pool.client;
|
||||
},
|
||||
singleton: true,
|
||||
});
|
||||
|
||||
// Register ProxyManager
|
||||
container.register({
|
||||
name: 'proxyManager',
|
||||
factory: async () => {
|
||||
const { ProxyManager } = await import('@stock-bot/utils');
|
||||
await ProxyManager.initialize();
|
||||
return ProxyManager.getInstance();
|
||||
},
|
||||
singleton: true,
|
||||
dispose: async (proxyManager) => {
|
||||
// ProxyManager handles its own cleanup
|
||||
if (proxyManager && typeof proxyManager.shutdown === 'function') {
|
||||
await proxyManager.shutdown();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Register Browser service
|
||||
container.register({
|
||||
name: 'browser',
|
||||
factory: async () => {
|
||||
const { Browser } = await import('@stock-bot/browser');
|
||||
return Browser;
|
||||
},
|
||||
singleton: true,
|
||||
dispose: async (browser) => {
|
||||
if (browser && typeof browser.close === 'function') {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Register HttpClient with default configuration
|
||||
container.register({
|
||||
name: 'httpClient',
|
||||
factory: async () => {
|
||||
const { createHttpClient } = await import('@stock-bot/http');
|
||||
return createHttpClient({
|
||||
timeout: 30000,
|
||||
retries: 3,
|
||||
userAgent: 'stock-bot/1.0',
|
||||
});
|
||||
},
|
||||
singleton: true,
|
||||
});
|
||||
|
||||
return container;
|
||||
}
|
||||
21
libs/core/di/tsconfig.json
Normal file
21
libs/core/di/tsconfig.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./",
|
||||
"outDir": "./dist",
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"types": ["node", "bun-types"]
|
||||
},
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"references": [
|
||||
{ "path": "../config" },
|
||||
{ "path": "../logger" },
|
||||
{ "path": "../../data/mongodb" },
|
||||
{ "path": "../../data/postgres" },
|
||||
{ "path": "../../data/cache" },
|
||||
{ "path": "../../services/queue" }
|
||||
]
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import type { MongoDBClientConfig } from '@stock-bot/mongodb-client';
|
||||
import type { PostgreSQLClientConfig } from '@stock-bot/postgres-client';
|
||||
import type { CacheOptions } from '@stock-bot/cache';
|
||||
import type { QueueManagerConfig } from '@stock-bot/queue';
|
||||
|
||||
export interface ConnectionPoolConfig {
|
||||
name: string;
|
||||
|
|
@ -24,6 +25,10 @@ export interface CachePoolConfig extends ConnectionPoolConfig {
|
|||
config: CacheOptions;
|
||||
}
|
||||
|
||||
export interface QueuePoolConfig extends ConnectionPoolConfig {
|
||||
config: QueueManagerConfig;
|
||||
}
|
||||
|
||||
export interface ConnectionFactoryConfig {
|
||||
service: string;
|
||||
environment: 'development' | 'production' | 'test';
|
||||
|
|
@ -31,6 +36,7 @@ export interface ConnectionFactoryConfig {
|
|||
mongodb?: Partial<MongoDBPoolConfig>;
|
||||
postgres?: Partial<PostgreSQLPoolConfig>;
|
||||
cache?: Partial<CachePoolConfig>;
|
||||
queue?: Partial<QueuePoolConfig>;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +61,8 @@ export interface ConnectionFactory {
|
|||
createMongoDB(config: MongoDBPoolConfig): Promise<ConnectionPool<any>>;
|
||||
createPostgreSQL(config: PostgreSQLPoolConfig): Promise<ConnectionPool<any>>;
|
||||
createCache(config: CachePoolConfig): ConnectionPool<any>;
|
||||
getPool(type: 'mongodb' | 'postgres' | 'cache', name: string): ConnectionPool<any> | undefined;
|
||||
createQueue(config: QueuePoolConfig): ConnectionPool<any>;
|
||||
getPool(type: 'mongodb' | 'postgres' | 'cache' | 'queue', name: string): ConnectionPool<any> | undefined;
|
||||
listPools(): Array<{ type: string; name: string; metrics: PoolMetrics }>;
|
||||
disposeAll(): Promise<void>;
|
||||
}
|
||||
22
libs/core/handlers/package.json
Normal file
22
libs/core/handlers/package.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "@stock-bot/handlers",
|
||||
"version": "1.0.0",
|
||||
"description": "Universal handler system for queue and event-driven operations",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rimraf dist",
|
||||
"test": "bun test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/logger": "workspace:*",
|
||||
"@stock-bot/di": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"typescript": "^5.3.0",
|
||||
"bun-types": "^1.2.15"
|
||||
}
|
||||
}
|
||||
69
libs/core/handlers/src/base/BaseHandler.ts
Normal file
69
libs/core/handlers/src/base/BaseHandler.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import type { ServiceContainer } from '@stock-bot/di';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { IHandler, ExecutionContext } from '../types/types';
|
||||
|
||||
/**
|
||||
* Abstract base class for all handlers
|
||||
* Provides common functionality and structure for queue/event operations
|
||||
*/
|
||||
export abstract class BaseHandler implements IHandler {
|
||||
protected readonly logger;
|
||||
|
||||
constructor(protected readonly container: ServiceContainer) {
|
||||
this.logger = getLogger(this.constructor.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main execution method - must be implemented by subclasses
|
||||
* Works with queue (events commented for future)
|
||||
*/
|
||||
abstract execute(operation: string, input: unknown, context: ExecutionContext): Promise<unknown>;
|
||||
|
||||
/**
|
||||
* Queue helper methods
|
||||
*/
|
||||
protected async scheduleOperation(operation: string, payload: unknown, delay?: number): Promise<void> {
|
||||
const queue = await this.container.resolveAsync('queue');
|
||||
await queue.add(operation, payload, { delay });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a service from the container
|
||||
*/
|
||||
protected async getService<T>(serviceName: string): Promise<T> {
|
||||
return await this.container.resolveAsync<T>(serviceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event methods - commented for future
|
||||
*/
|
||||
// protected async publishEvent(eventName: string, payload: unknown): Promise<void> {
|
||||
// const eventBus = await this.container.resolveAsync('eventBus');
|
||||
// await eventBus.publish(eventName, payload);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Lifecycle hooks - can be overridden by subclasses
|
||||
*/
|
||||
async onInit?(): Promise<void>;
|
||||
async onStart?(): Promise<void>;
|
||||
async onStop?(): Promise<void>;
|
||||
async onDispose?(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized handler for operations that have scheduled jobs
|
||||
*/
|
||||
export abstract class ScheduledHandler extends BaseHandler {
|
||||
/**
|
||||
* Get scheduled job configurations for this handler
|
||||
* Override in subclasses to define schedules
|
||||
*/
|
||||
getScheduledJobs?(): Array<{
|
||||
operation: string;
|
||||
cronPattern: string;
|
||||
priority?: number;
|
||||
immediately?: boolean;
|
||||
description?: string;
|
||||
}>;
|
||||
}
|
||||
86
libs/core/handlers/src/decorators/decorators.ts
Normal file
86
libs/core/handlers/src/decorators/decorators.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// Simple decorators for handler registration
|
||||
// These are placeholders for now - can be enhanced with reflection later
|
||||
|
||||
/**
|
||||
* Handler decorator - marks a class as a handler
|
||||
* @param name Handler name for registration
|
||||
*/
|
||||
export function Handler(name: string) {
|
||||
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
|
||||
// Store handler name on the constructor for future use
|
||||
(constructor as any).__handlerName = name;
|
||||
return constructor;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation decorator - marks a method as an operation
|
||||
* @param name Operation name
|
||||
*/
|
||||
export function Operation(name: string) {
|
||||
return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
|
||||
// Store operation metadata for future use
|
||||
if (!target.constructor.__operations) {
|
||||
target.constructor.__operations = [];
|
||||
}
|
||||
target.constructor.__operations.push({
|
||||
name,
|
||||
method: propertyName,
|
||||
});
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue schedule decorator - marks an operation as scheduled
|
||||
* @param cronPattern Cron pattern for scheduling
|
||||
* @param options Additional scheduling options
|
||||
*/
|
||||
export function QueueSchedule(
|
||||
cronPattern: string,
|
||||
options?: {
|
||||
priority?: number;
|
||||
immediately?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
) {
|
||||
return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
|
||||
// Store schedule metadata for future use
|
||||
if (!target.constructor.__schedules) {
|
||||
target.constructor.__schedules = [];
|
||||
}
|
||||
target.constructor.__schedules.push({
|
||||
operation: propertyName,
|
||||
cronPattern,
|
||||
...options,
|
||||
});
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
|
||||
// Future event decorators - commented for now
|
||||
// export function EventListener(eventName: string) {
|
||||
// return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
|
||||
// if (!target.constructor.__eventListeners) {
|
||||
// target.constructor.__eventListeners = [];
|
||||
// }
|
||||
// target.constructor.__eventListeners.push({
|
||||
// eventName,
|
||||
// method: propertyName,
|
||||
// });
|
||||
// return descriptor;
|
||||
// };
|
||||
// }
|
||||
|
||||
// export function EventPublisher(eventName: string) {
|
||||
// return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
|
||||
// if (!target.constructor.__eventPublishers) {
|
||||
// target.constructor.__eventPublishers = [];
|
||||
// }
|
||||
// target.constructor.__eventPublishers.push({
|
||||
// eventName,
|
||||
// method: propertyName,
|
||||
// });
|
||||
// return descriptor;
|
||||
// };
|
||||
// }
|
||||
26
libs/core/handlers/src/index.ts
Normal file
26
libs/core/handlers/src/index.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Base handler classes
|
||||
export { BaseHandler, ScheduledHandler } from './base/BaseHandler';
|
||||
|
||||
// Handler registry
|
||||
export { handlerRegistry } from './registry/HandlerRegistry';
|
||||
|
||||
// Types
|
||||
export type {
|
||||
ExecutionContext,
|
||||
IHandler,
|
||||
JobHandler,
|
||||
ScheduledJob,
|
||||
HandlerConfig,
|
||||
HandlerConfigWithSchedule,
|
||||
TypedJobHandler,
|
||||
HandlerMetadata,
|
||||
OperationMetadata,
|
||||
} from './types/types';
|
||||
|
||||
export { createJobHandler } from './types/types';
|
||||
|
||||
// Decorators
|
||||
export { Handler, Operation, QueueSchedule } from './decorators/decorators';
|
||||
|
||||
// Future exports - commented for now
|
||||
// export { EventListener, EventPublisher } from './decorators/decorators';
|
||||
191
libs/core/handlers/src/registry/HandlerRegistry.ts
Normal file
191
libs/core/handlers/src/registry/HandlerRegistry.ts
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import type { JobHandler, HandlerConfig, HandlerConfigWithSchedule, ScheduledJob } from '../types/types';
|
||||
|
||||
const logger = getLogger('handler-registry');
|
||||
|
||||
class HandlerRegistry {
|
||||
private handlers = new Map<string, HandlerConfig>();
|
||||
private handlerSchedules = new Map<string, ScheduledJob[]>();
|
||||
|
||||
/**
|
||||
* Register a handler with its operations (simple config)
|
||||
*/
|
||||
register(handlerName: string, config: HandlerConfig): void {
|
||||
logger.info(`Registering handler: ${handlerName}`, {
|
||||
operations: Object.keys(config),
|
||||
});
|
||||
|
||||
this.handlers.set(handlerName, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler with operations and scheduled jobs (full config)
|
||||
*/
|
||||
registerWithSchedule(config: HandlerConfigWithSchedule): void {
|
||||
logger.info(`Registering handler with schedule: ${config.name}`, {
|
||||
operations: Object.keys(config.operations),
|
||||
scheduledJobs: config.scheduledJobs?.length || 0,
|
||||
});
|
||||
|
||||
this.handlers.set(config.name, config.operations);
|
||||
|
||||
if (config.scheduledJobs && config.scheduledJobs.length > 0) {
|
||||
this.handlerSchedules.set(config.name, config.scheduledJobs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a handler for a specific handler and operation
|
||||
*/
|
||||
getHandler(handler: string, operation: string): JobHandler | null {
|
||||
const handlerConfig = this.handlers.get(handler);
|
||||
if (!handlerConfig) {
|
||||
logger.warn(`Handler not found: ${handler}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const jobHandler = handlerConfig[operation];
|
||||
if (!jobHandler) {
|
||||
logger.warn(`Operation not found: ${handler}:${operation}`, {
|
||||
availableOperations: Object.keys(handlerConfig),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
return jobHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all scheduled jobs from all handlers
|
||||
*/
|
||||
getAllScheduledJobs(): Array<{ handler: string; job: ScheduledJob }> {
|
||||
const allJobs: Array<{ handler: string; job: ScheduledJob }> = [];
|
||||
|
||||
for (const [handlerName, jobs] of this.handlerSchedules) {
|
||||
for (const job of jobs) {
|
||||
allJobs.push({
|
||||
handler: handlerName,
|
||||
job,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return allJobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scheduled jobs for a specific handler
|
||||
*/
|
||||
getScheduledJobs(handler: string): ScheduledJob[] {
|
||||
return this.handlerSchedules.get(handler) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a handler has scheduled jobs
|
||||
*/
|
||||
hasScheduledJobs(handler: string): boolean {
|
||||
return this.handlerSchedules.has(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered handlers with their configurations
|
||||
*/
|
||||
getHandlerConfigs(): Array<{ name: string; operations: string[]; scheduledJobs: number }> {
|
||||
return Array.from(this.handlers.keys()).map(name => ({
|
||||
name,
|
||||
operations: Object.keys(this.handlers.get(name) || {}),
|
||||
scheduledJobs: this.handlerSchedules.get(name)?.length || 0,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all handlers with their full configurations for queue manager registration
|
||||
*/
|
||||
getAllHandlers(): Map<string, { operations: HandlerConfig; scheduledJobs?: ScheduledJob[] }> {
|
||||
const result = new Map<
|
||||
string,
|
||||
{ operations: HandlerConfig; scheduledJobs?: ScheduledJob[] }
|
||||
>();
|
||||
|
||||
for (const [name, operations] of this.handlers) {
|
||||
const scheduledJobs = this.handlerSchedules.get(name);
|
||||
result.set(name, {
|
||||
operations,
|
||||
scheduledJobs,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered handlers
|
||||
*/
|
||||
getHandlers(): string[] {
|
||||
return Array.from(this.handlers.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get operations for a specific handler
|
||||
*/
|
||||
getOperations(handler: string): string[] {
|
||||
const handlerConfig = this.handlers.get(handler);
|
||||
return handlerConfig ? Object.keys(handlerConfig) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a handler exists
|
||||
*/
|
||||
hasHandler(handler: string): boolean {
|
||||
return this.handlers.has(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a handler has a specific operation
|
||||
*/
|
||||
hasOperation(handler: string, operation: string): boolean {
|
||||
const handlerConfig = this.handlers.get(handler);
|
||||
return handlerConfig ? operation in handlerConfig : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a handler
|
||||
*/
|
||||
unregister(handler: string): boolean {
|
||||
this.handlerSchedules.delete(handler);
|
||||
return this.handlers.delete(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all handlers
|
||||
*/
|
||||
clear(): void {
|
||||
this.handlers.clear();
|
||||
this.handlerSchedules.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registry statistics
|
||||
*/
|
||||
getStats(): { handlers: number; totalOperations: number; totalScheduledJobs: number } {
|
||||
let totalOperations = 0;
|
||||
let totalScheduledJobs = 0;
|
||||
|
||||
for (const config of this.handlers.values()) {
|
||||
totalOperations += Object.keys(config).length;
|
||||
}
|
||||
|
||||
for (const jobs of this.handlerSchedules.values()) {
|
||||
totalScheduledJobs += jobs.length;
|
||||
}
|
||||
|
||||
return {
|
||||
handlers: this.handlers.size,
|
||||
totalOperations,
|
||||
totalScheduledJobs,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const handlerRegistry = new HandlerRegistry();
|
||||
73
libs/core/handlers/src/types/types.ts
Normal file
73
libs/core/handlers/src/types/types.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import type { ServiceContainer } from '@stock-bot/di';
|
||||
|
||||
// Simple execution context - mostly queue for now
|
||||
export interface ExecutionContext {
|
||||
type: 'queue'; // | 'event' - commented for future
|
||||
serviceContainer: ServiceContainer;
|
||||
metadata: {
|
||||
source?: string;
|
||||
jobId?: string;
|
||||
attempts?: number;
|
||||
timestamp: number;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
// Simple handler interface
|
||||
export interface IHandler {
|
||||
execute(operation: string, input: unknown, context: ExecutionContext): Promise<unknown>;
|
||||
}
|
||||
|
||||
// Job handler type for queue operations
|
||||
export interface JobHandler<TPayload = unknown, TResult = unknown> {
|
||||
(payload: TPayload): Promise<TResult>;
|
||||
}
|
||||
|
||||
// Scheduled job configuration
|
||||
export interface ScheduledJob<T = unknown> {
|
||||
type: string;
|
||||
operation: string;
|
||||
payload?: T;
|
||||
cronPattern: string;
|
||||
priority?: number;
|
||||
description?: string;
|
||||
immediately?: boolean;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
// Handler configuration
|
||||
export interface HandlerConfig {
|
||||
[operation: string]: JobHandler;
|
||||
}
|
||||
|
||||
// Handler configuration with schedule
|
||||
export interface HandlerConfigWithSchedule {
|
||||
name: string;
|
||||
operations: Record<string, JobHandler>;
|
||||
scheduledJobs?: ScheduledJob[];
|
||||
}
|
||||
|
||||
// Type-safe wrapper for creating job handlers
|
||||
export type TypedJobHandler<TPayload, TResult = unknown> = (payload: TPayload) => Promise<TResult>;
|
||||
|
||||
// Helper to create type-safe job handlers
|
||||
export function createJobHandler<TPayload = unknown, TResult = unknown>(
|
||||
handler: TypedJobHandler<TPayload, TResult>
|
||||
): JobHandler<unknown, TResult> {
|
||||
return async (payload: unknown): Promise<TResult> => {
|
||||
return handler(payload as TPayload);
|
||||
};
|
||||
}
|
||||
|
||||
// Handler metadata for decorators (future)
|
||||
export interface HandlerMetadata {
|
||||
name: string;
|
||||
operations: OperationMetadata[];
|
||||
}
|
||||
|
||||
export interface OperationMetadata {
|
||||
name: string;
|
||||
schedules?: string[];
|
||||
// eventListeners?: string[]; // Future
|
||||
// eventPublishers?: string[]; // Future
|
||||
}
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
{ "path": "../config" },
|
||||
{ "path": "../logger" },
|
||||
{ "path": "../mongodb-client" },
|
||||
{ "path": "../postgres-client" },
|
||||
{ "path": "../cache" }
|
||||
{ "path": "../di" }
|
||||
]
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"extends": "../../tsconfig.lib.json",
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"extends": "../../tsconfig.lib.json",
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
12
libs/data/cache/tsconfig.json
vendored
Normal file
12
libs/data/cache/tsconfig.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
{ "path": "../../core/logger" }
|
||||
]
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ A comprehensive MongoDB client library for the Stock Bot trading platform, desig
|
|||
## Usage
|
||||
|
||||
```typescript
|
||||
import { MongoDBClient } from '@stock-bot/mongodb-client';
|
||||
import { MongoDBClient } from '@stock-bot/mongodb';
|
||||
|
||||
// Initialize client
|
||||
const mongoClient = new MongoDBClient();
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@stock-bot/mongodb-client",
|
||||
"name": "@stock-bot/mongodb",
|
||||
"version": "1.0.0",
|
||||
"description": "MongoDB client library for Stock Bot platform",
|
||||
"main": "dist/index.js",
|
||||
13
libs/data/mongodb/tsconfig.json
Normal file
13
libs/data/mongodb/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
{ "path": "../../core/logger" },
|
||||
{ "path": "../../core/types" }
|
||||
]
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ A comprehensive PostgreSQL client library for the Stock Bot trading platform, de
|
|||
## Usage
|
||||
|
||||
```typescript
|
||||
import { PostgreSQLClient } from '@stock-bot/postgres-client';
|
||||
import { PostgreSQLClient } from '@stock-bot/postgres';
|
||||
|
||||
// Initialize client
|
||||
const pgClient = new PostgreSQLClient();
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@stock-bot/postgres-client",
|
||||
"name": "@stock-bot/postgres",
|
||||
"version": "1.0.0",
|
||||
"description": "PostgreSQL client library for Stock Bot platform",
|
||||
"main": "dist/index.js",
|
||||
13
libs/data/postgres/tsconfig.json
Normal file
13
libs/data/postgres/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
{ "path": "../../core/logger" },
|
||||
{ "path": "../../core/types" }
|
||||
]
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ A comprehensive QuestDB client library for the Stock Bot trading platform, optim
|
|||
## Usage
|
||||
|
||||
```typescript
|
||||
import { QuestDBClient } from '@stock-bot/questdb-client';
|
||||
import { QuestDBClient } from '@stock-bot/questdb';
|
||||
|
||||
// Initialize client
|
||||
const questClient = new QuestDBClient();
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@stock-bot/questdb-client",
|
||||
"name": "@stock-bot/questdb",
|
||||
"version": "1.0.0",
|
||||
"description": "QuestDB client library for Stock Bot platform",
|
||||
"main": "dist/index.js",
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue