refactoring to remove a lot of junk

This commit is contained in:
Boki 2025-06-22 16:57:08 -04:00
parent 5318158e59
commit d858222af7
33 changed files with 505 additions and 367 deletions

157
libs/LIBRARY_STANDARDS.md Normal file
View file

@ -0,0 +1,157 @@
# Library Standards and Patterns
This document defines the standardized patterns for all libraries in the @stock-bot ecosystem.
## Export Patterns
### Standard: Named Exports Only
All libraries should use **named exports only**. Default exports have been removed for consistency and better tree-shaking.
**Example:**
```typescript
// ✅ Good - Named exports
export { createCache } from './cache';
export type { CacheOptions } from './types';
// ❌ Bad - Default export
export default createCache;
```
## Initialization Patterns
Libraries follow different initialization patterns based on their purpose:
### 1. Singleton with Global State
**Use for:** Global services that should have only one instance (config, logger)
**Example:** Config library
```typescript
let configInstance: ConfigManager | null = null;
export function initializeConfig(): AppConfig {
if (!configInstance) {
configInstance = new ConfigManager();
}
return configInstance.initialize();
}
export function getConfig(): AppConfig {
if (!configInstance) {
throw new Error('Config not initialized');
}
return configInstance.get();
}
```
### 2. Factory with Registry
**Use for:** Services that need instance reuse based on configuration (cache, logger instances)
**Example:** Cache library
```typescript
const cacheInstances = new Map<string, CacheProvider>();
export function createCache(options: CacheOptions): CacheProvider {
if (options.shared) {
const key = generateKey(options);
if (cacheInstances.has(key)) {
return cacheInstances.get(key)!;
}
const cache = new RedisCache(options);
cacheInstances.set(key, cache);
return cache;
}
return new RedisCache(options);
}
```
### 3. Pure Factory Functions
**Use for:** Services that need creation logic beyond simple instantiation
**Example:** Event bus with configuration processing
```typescript
export function createEventBus(config: EventBusConfig): EventBus {
// Process config, set defaults, etc.
const processedConfig = { ...defaultConfig, ...config };
return new EventBus(processedConfig);
}
```
**Note:** Simple instantiation doesn't need factories - use direct class instantiation or DI container.
### 4. Direct Class Exports
**Use for:** Simple utilities or services managed by DI container
**Example:** MongoDB library
```typescript
export { MongoDBClient } from './client';
// No factory function - let DI container handle instantiation
```
### 5. Singleton Classes
**Use for:** Manager classes that coordinate multiple instances
**Example:** QueueManager
```typescript
export class QueueManager {
private static instance: QueueManager | null = null;
static initialize(config: QueueConfig): QueueManager {
if (!QueueManager.instance) {
QueueManager.instance = new QueueManager(config);
}
return QueueManager.instance;
}
static getInstance(): QueueManager {
if (!QueueManager.instance) {
throw new Error('QueueManager not initialized');
}
return QueueManager.instance;
}
}
```
## Pattern Selection Guide
Choose the initialization pattern based on these criteria:
| Pattern | When to Use | Examples |
|---------|-------------|----------|
| **Singleton with Global State** | - One instance per process<br>- Stateful configuration<br>- Process-wide settings | config, logger setup |
| **Factory with Registry** | - Multiple instances with same config should share<br>- Connection pooling<br>- Resource optimization | cache, logger instances |
| **Pure Factory** | - Complex initialization logic<br>- Configuration processing needed<br>- Defaults to apply | event bus (if needed) |
| **Direct Class Export** | - DI container manages lifecycle<br>- Simple initialization<br>- No special setup needed | database clients (MongoDB, PostgreSQL, QuestDB), utilities |
| **Singleton Class** | - Coordinates multiple resources<br>- Central management point<br>- Graceful shutdown needed | QueueManager, ConnectionManager |
## Additional Standards
### Error Handling
- All libraries should throw descriptive errors
- Consider creating custom error classes for domain-specific errors
- Always include context in error messages
### Configuration
- Accept configuration through constructor/factory parameters
- Validate configuration using Zod schemas
- Provide sensible defaults where appropriate
### Testing
- All libraries must have unit tests
- Use consistent test file naming: `*.test.ts`
- Mock external dependencies
### Documentation
- Every library must have a README.md
- Include usage examples
- Document all public APIs with JSDoc
### TypeScript
- Export all public types
- Use strict TypeScript settings
- Avoid `any` types
### Dependencies
- Minimize external dependencies
- Use exact versions for critical dependencies
- Document peer dependencies clearly

View file

@ -8,9 +8,9 @@ import { createCache, type CacheProvider } from '@stock-bot/cache';
import type { IServiceContainer } from '@stock-bot/handlers';
import { getLogger, type Logger } from '@stock-bot/logger';
import { MongoDBClient } from '@stock-bot/mongodb';
import { createPostgreSQLClient, type PostgreSQLClient } from '@stock-bot/postgres';
import { PostgreSQLClient } from '@stock-bot/postgres';
import { ProxyManager } from '@stock-bot/proxy';
import { createQuestDBClient, type QuestDBClient } from '@stock-bot/questdb';
import { QuestDBClient } from '@stock-bot/questdb';
import { type QueueManager } from '@stock-bot/queue';
import { asFunction, asValue, createContainer, InjectionMode, type AwilixContainer } from 'awilix';
import { z } from 'zod';
@ -146,7 +146,7 @@ export function createServiceContainer(rawConfig: unknown): AwilixContainer<Serv
// Conditionally register PostgreSQL client
if (config.postgres?.enabled !== false) {
registrations.postgresClient = asFunction(({ postgresConfig, logger }) => {
return createPostgreSQLClient(
return new PostgreSQLClient(
{
host: postgresConfig.host,
port: postgresConfig.port,
@ -165,7 +165,7 @@ export function createServiceContainer(rawConfig: unknown): AwilixContainer<Serv
if (config.questdb?.enabled !== false) {
registrations.questdbClient = asFunction(({ questdbConfig, logger }) => {
console.log('Creating QuestDB client with config:', questdbConfig);
return createQuestDBClient(
return new QuestDBClient(
{
host: questdbConfig.host,
httpPort: questdbConfig.httpPort,

View file

@ -1,9 +1,9 @@
import { getLogger } from '@stock-bot/logger';
import { createJobHandler, handlerRegistry, type HandlerConfigWithSchedule } from '@stock-bot/types';
import { fetch } from '@stock-bot/utils';
import type { Collection } from 'mongodb';
import type { IServiceContainer } from '../types/service-container';
import type { ExecutionContext, IHandler } from '../types/types';
import type { Collection } from 'mongodb';
/**
* Abstract base class for all handlers with improved DI
@ -196,7 +196,7 @@ export abstract class BaseHandler implements IHandler {
*/
protected hasService(name: keyof IServiceContainer): boolean {
const service = this[name as keyof this];
return service != null;
return service !== null;
}
/**

View file

@ -9,6 +9,3 @@ export { Logger, getLogger, shutdownLoggers, setLoggerConfig } from './logger';
// Type definitions
export type { LogLevel, LogContext, LogMetadata, LoggerConfig } from './types';
// Default export
export { getLogger as default } from './logger';

View file

@ -45,6 +45,3 @@ export type {
export { RedisConnectionManager } from './connection-manager';
export { CacheKeyGenerator } from './key-generator';
export { RedisCache } from './redis-cache';
// Default export for convenience
export default createCache;

View file

@ -1,5 +0,0 @@
// This factory is no longer needed when using Awilix DI
// The MongoDBClient is now registered directly in the DI container
// See: libs/core/di/src/awilix-container.ts
export { MongoDBClient } from './client';

View file

@ -1,29 +0,0 @@
import { PostgreSQLClient } from './client';
import type { PostgreSQLClientConfig, PostgreSQLConnectionOptions, ConnectionEvents } from './types';
/**
* Factory function to create a PostgreSQL client instance
*/
export function createPostgreSQLClient(
config: PostgreSQLClientConfig,
logger?: any,
options?: PostgreSQLConnectionOptions,
events?: ConnectionEvents
): PostgreSQLClient {
return new PostgreSQLClient(config, logger, options, events);
}
/**
* Create and connect a PostgreSQL client
*/
export async function createAndConnectPostgreSQLClient(
config: PostgreSQLClientConfig,
logger?: any,
options?: PostgreSQLConnectionOptions,
events?: ConnectionEvents
): Promise<PostgreSQLClient> {
const client = createPostgreSQLClient(config, logger, options, events);
await client.connect();
return client;
}

View file

@ -33,10 +33,5 @@ export type {
DynamicPoolConfig,
} from './types';
// Factory functions
export {
createPostgreSQLClient,
createAndConnectPostgreSQLClient,
} from './factory';
// Singleton pattern removed - use factory functions instead
// Note: Factory functions removed - instantiate directly with new PostgreSQLClient()
// or use the Awilix DI container (recommended)

View file

@ -1,26 +0,0 @@
import { QuestDBClient } from './client';
import type { QuestDBClientConfig, QuestDBConnectionOptions } from './types';
/**
* Factory function to create a QuestDB client instance
*/
export function createQuestDBClient(
config: QuestDBClientConfig,
logger?: any,
options?: QuestDBConnectionOptions
): QuestDBClient {
return new QuestDBClient(config, logger, options);
}
/**
* Create and connect a QuestDB client
*/
export async function createAndConnectQuestDBClient(
config: QuestDBClientConfig,
logger?: any,
options?: QuestDBConnectionOptions
): Promise<QuestDBClient> {
const client = createQuestDBClient(config, logger, options);
await client.connect();
return client;
}

View file

@ -28,5 +28,5 @@ export type {
InsertResult,
} from './types';
// Utils
export { createQuestDBClient, createAndConnectQuestDBClient } from './factory';
// Note: Factory functions removed - instantiate directly with new QuestDBClient()
// or use the Awilix DI container (recommended)

View file

@ -7,7 +7,6 @@
import { afterEach, describe, expect, it } from 'bun:test';
import {
createQuestDBClient,
QuestDBClient,
QuestDBHealthMonitor,
QuestDBInfluxWriter,
@ -40,9 +39,17 @@ describe('QuestDB Client Integration', () => {
});
describe('Client Initialization', () => {
it('should create client with factory function', () => {
const factoryClient = createQuestDBClient();
expect(factoryClient).toBeInstanceOf(QuestDBClient);
it('should create client with constructor', () => {
const newClient = new QuestDBClient({
host: 'localhost',
httpPort: 9000,
pgPort: 8812,
influxPort: 9009,
database: 'questdb',
user: 'admin',
password: 'quest',
});
expect(newClient).toBeInstanceOf(QuestDBClient);
});
it('should initialize all supporting classes', () => {

View file

@ -10,7 +10,4 @@ export function createEventBus(config: EventBusConfig): EventBus {
// Re-export everything
export { EventBus } from './event-bus';
export * from './types';
// Default export
export default createEventBus;
export * from './types';