import * as queries from '../db/queries.constant'; export class QueryBuilder { readonly sql: string; readonly queryParams: unknown[]; /** * Create a QueryBuilder from a query constant path. * * @param queryPath Path to query in queries.constant.ts (e.g., 'MARKET_CALLS_QUERIES.SELECT_ALL') * @param params Parameters to bind (? placeholders in SQL) */ constructor(queryPath: string, params: unknown[] = []) { this.sql = this.lookupQuery(queryPath); this.queryParams = params; // Validate parameter count matches placeholders const placeholderCount = (this.sql.match(/\?/g) || []).length; if (this.queryParams.length !== placeholderCount) { throw new Error( `Parameter mismatch for query "${queryPath}": expected ${placeholderCount}, got ${this.queryParams.length}`, ); } } /** * Look up a query from queries.constant.ts. * Supports nested paths like "MARKET_CALLS_QUERIES.SELECT_ALL". * * @param queryPath Path to query (e.g., 'MARKET_CALLS_QUERIES.SELECT_ALL') * @returns The SQL query string * @throws Error if query not found */ private lookupQuery(queryPath: string): string { const parts = queryPath.split('.'); // Navigate through the nested objects let current: any = queries; for (const part of parts) { if (!(part in current)) { throw new Error( `Query not found: "${queryPath}". Make sure it exists in queries.constant.ts`, ); } current = current[part]; } if (typeof current !== 'string') { throw new Error(`Invalid query: "${queryPath}" must be a string, got ${typeof current}`); } // Clean up the SQL (remove extra whitespace) return current.trim(); } }