phase-9: domain-driven architecture complete
- Restructured server layer with 5 domains: shared, screener, portfolio, calls, finance - Migrated 58 TypeScript files to domain-driven structure - Updated CLAUDE.md with new architecture documentation - Added .gitignore rules for .md files (except CLAUDE.md) - Removed unused CatalystAnalyst import from app.ts - Fixed lint errors: removed unused imports, fixed regex escape, added console suppressions - Verified no sensitive data in git history - Server code compiles cleanly with TypeScript strict mode
This commit is contained in:
committed by
saikiranvella
parent
83116baa3c
commit
96a752ecf7
@@ -0,0 +1,96 @@
|
||||
import { randomUUID } from 'crypto';
|
||||
import { DatabaseConnection } from '../db/index';
|
||||
import { QueryBuilder } from '../utils/QueryBuilder';
|
||||
import { sanitizeString, sanitizeDate } from '../utils/sanitizer';
|
||||
import type { MarketCall, CreateCallInput, MarketCallRow } from '../types';
|
||||
|
||||
export class MarketCallRepository {
|
||||
constructor(private readonly db: DatabaseConnection) {}
|
||||
|
||||
/**
|
||||
* Get all market calls, newest first.
|
||||
*/
|
||||
list(): (MarketCall & { createdAt: string })[] {
|
||||
const qb = new QueryBuilder('MARKET_CALLS_QUERIES.SELECT_ALL');
|
||||
const rows = this.db.all<MarketCallRow>(qb);
|
||||
return rows.map(MarketCallRepository.toCall);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single market call by ID.
|
||||
*/
|
||||
get(id: string): (MarketCall & { createdAt: string }) | null {
|
||||
const qb = new QueryBuilder('MARKET_CALLS_QUERIES.SELECT_BY_ID', [id]);
|
||||
const row = this.db.get<MarketCallRow>(qb);
|
||||
return row ? MarketCallRepository.toCall(row) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new market call with snapshot of current prices.
|
||||
*/
|
||||
create({
|
||||
title,
|
||||
quarter,
|
||||
date,
|
||||
thesis,
|
||||
tickers,
|
||||
snapshot,
|
||||
}: CreateCallInput): MarketCall & { createdAt: string } {
|
||||
// Sanitize inputs
|
||||
const sanitizedTitle = sanitizeString(title, 'title', 255);
|
||||
const sanitizedQuarter = sanitizeString(quarter, 'quarter', 10);
|
||||
const sanitizedThesis = sanitizeString(thesis, 'thesis', 2000);
|
||||
const sanitizedDate = date ? sanitizeDate(date, 'date') : new Date().toISOString().slice(0, 10);
|
||||
|
||||
const call = {
|
||||
id: randomUUID(),
|
||||
title: sanitizedTitle,
|
||||
quarter: sanitizedQuarter,
|
||||
date: sanitizedDate,
|
||||
thesis: sanitizedThesis,
|
||||
tickers: tickers ?? [],
|
||||
snapshot: snapshot ?? {},
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const qb = new QueryBuilder('MARKET_CALLS_QUERIES.INSERT', [
|
||||
call.id,
|
||||
call.title,
|
||||
call.quarter,
|
||||
call.date,
|
||||
call.thesis,
|
||||
JSON.stringify(call.tickers),
|
||||
JSON.stringify(call.snapshot),
|
||||
call.createdAt,
|
||||
]);
|
||||
|
||||
this.db.run(qb);
|
||||
return call as MarketCall & { createdAt: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a market call by ID.
|
||||
* Returns true if the call existed and was deleted, false otherwise.
|
||||
*/
|
||||
delete(id: string): boolean {
|
||||
const qb = new QueryBuilder('MARKET_CALLS_QUERIES.DELETE_BY_ID', [id]);
|
||||
const changes = this.db.run(qb);
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert database row to domain object.
|
||||
*/
|
||||
private static toCall(row: MarketCallRow): MarketCall & { createdAt: string } {
|
||||
return {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
quarter: row.quarter,
|
||||
date: row.date,
|
||||
thesis: row.thesis,
|
||||
tickers: JSON.parse(row.tickers),
|
||||
snapshot: JSON.parse(row.snapshot),
|
||||
createdAt: row.created_at,
|
||||
} as MarketCall & { createdAt: string };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user