/** * Type-safe Query Builder Types * * Core types for building type-safe SQL queries with full TypeScript inference. * These types ensure that queries are validated at compile time. */ import type { Row as _Row } from '../types.js' // ============================================================================ // Column and Table Definition Types // ============================================================================ /** * Base column definition with type information */ export interface ColumnDefinition< TName extends string = string, TType = unknown, TNotNull extends boolean = boolean, TDefault extends boolean = boolean, > { readonly name: TName readonly dataType: TType readonly notNull: TNotNull readonly hasDefault: TDefault readonly _columnBrand: 'column' } /** * Create a column definition with inferred types */ export type Column< TName extends string, TType, TNotNull extends boolean = false, TDefault extends boolean = false, > = ColumnDefinition /** * Table definition with typed columns */ export interface TableDefinition< TName extends string = string, TColumns extends Record = Record, > { readonly tableName: TName readonly columns: TColumns readonly _tableBrand: 'table' } /** * Infer the row type from a table definition */ export type InferTableRow = { [K in keyof T['columns']]: InferColumnType } /** * Infer the TypeScript type from a column definition */ export type InferColumnType = T['notNull'] extends true ? T['dataType'] : T['dataType'] | null /** * Infer insert type (required fields only) */ export type InferInsertRow = { [K in keyof T['columns'] as RequiredInsertColumn extends true ? K : never]: T['columns'][K]['dataType'] } & { [K in keyof T['columns'] as RequiredInsertColumn extends true ? never : K]?: T['columns'][K]['dataType'] | null } type RequiredInsertColumn = T['notNull'] extends true ? T['hasDefault'] extends true ? false : true : false // ============================================================================ // Expression Types // ============================================================================ /** * SQL expression with type information */ export interface SQLExpression { readonly _type: TType readonly _brand: 'sql_expression' toSQL(): { sql: string; params: unknown[] } } /** * Column reference expression */ export interface ColumnRef< TTable extends string = string, TColumn extends string = string, TType = unknown, > extends SQLExpression { readonly table: TTable readonly column: TColumn readonly _brand: 'sql_expression' } /** * Literal value expression */ export interface LiteralExpr extends SQLExpression { readonly value: TType } /** * Binary comparison expression */ export interface ComparisonExpr extends SQLExpression { readonly left: SQLExpression readonly operator: ComparisonOperator readonly right: SQLExpression } export type ComparisonOperator = | '=' | '!=' | '<' | '<=' | '>' | '>=' | 'LIKE' | 'ILIKE' | 'IN' | 'NOT IN' | 'IS NULL' | 'IS NOT NULL' /** * Logical expression (AND, OR, NOT) */ export interface LogicalExpr extends SQLExpression { readonly operator: 'AND' | 'OR' | 'NOT' readonly expressions: SQLExpression[] } // ============================================================================ // Aggregate Expression Types // ============================================================================ /** * Aggregate function expression */ export interface AggregateExpr extends SQLExpression { readonly fn: 'COUNT' | 'SUM' | 'AVG' | 'MIN' | 'MAX' readonly column: SQLExpression | '*' readonly distinct?: boolean } // ============================================================================ // SELECT Query Types // ============================================================================ /** * Selection specification - what columns to select */ export type SelectionSpec = | '*' | (keyof T['columns'])[] | Record /** * Infer result type from selection spec */ export type InferSelectResult< T extends TableDefinition, TSelection extends SelectionSpec, > = TSelection extends '*' ? InferTableRow : TSelection extends (keyof T['columns'])[] ? Pick, TSelection[number]> : TSelection extends Record ? { [K in keyof TSelection]: TSelection[K] extends SQLExpression ? U : TSelection[K] extends keyof T['columns'] ? InferColumnType : never } : never /** * WHERE clause type - accepts boolean expressions */ export type WhereClause = | SQLExpression | ((columns: ColumnRefs) => SQLExpression) /** * Column references for a table */ export type ColumnRefs = { [K in keyof T['columns']]: ColumnRef } /** * ORDER BY specification */ export type OrderBySpec = | keyof T['columns'] | { column: keyof T['columns']; direction: 'ASC' | 'DESC' } | { column: keyof T['columns']; direction: 'ASC' | 'DESC'; nulls?: 'FIRST' | 'LAST' } // ============================================================================ // JOIN Types // ============================================================================ /** * Join types */ export type JoinType = 'INNER' | 'LEFT' | 'RIGHT' | 'FULL' /** * Join condition specification */ export type JoinCondition< TLeft extends TableDefinition, TRight extends TableDefinition, > = SQLExpression | { left: keyof TLeft['columns'] right: keyof TRight['columns'] } /** * Result type for joined tables */ export type JoinResult< TLeft extends TableDefinition, TRight extends TableDefinition, TJoinType extends JoinType, > = TJoinType extends 'LEFT' ? InferTableRow & { [K in TRight['tableName']]: InferTableRow | null } : TJoinType extends 'RIGHT' ? { [K in TLeft['tableName']]: InferTableRow | null } & InferTableRow : TJoinType extends 'FULL' ? { [K in TLeft['tableName']]: InferTableRow | null } & { [K in TRight['tableName']]: InferTableRow | null } : InferTableRow & InferTableRow /** * Combined table reference after join */ export interface JoinedTables< TLeft extends TableDefinition, TRight extends TableDefinition, TJoinType extends JoinType = 'INNER', > { left: TLeft right: TRight joinType: TJoinType condition: SQLExpression } // ============================================================================ // INSERT Types // ============================================================================ /** * Insert values - single or array of rows */ export type InsertValues = | InferInsertRow | InferInsertRow[] /** * On conflict action */ export type OnConflictAction = | 'DO NOTHING' | { doUpdate: Partial> } | { doUpdate: (row: InferTableRow) => Partial> } /** * Conflict target columns */ export type ConflictTarget = | (keyof T['columns'])[] | { constraint: string } // ============================================================================ // UPDATE Types // ============================================================================ /** * Update set values */ export type UpdateSet = Partial<{ [K in keyof T['columns']]: T['columns'][K]['dataType'] | SQLExpression }> // ============================================================================ // Query Builder Interfaces // ============================================================================ /** * Base query builder interface */ export interface QueryBuilder { /** * Get the generated SQL and parameters */ toSQL(): { sql: string; params: unknown[] } /** * Execute the query and return results */ execute(): Promise } /** * SELECT query builder interface */ export interface SelectQueryBuilder< T extends TableDefinition, TSelection extends SelectionSpec = '*', TResult = InferSelectResult[], > extends QueryBuilder { /** * Select specific columns or expressions */ select>( selection: TNewSelection ): SelectQueryBuilder[]> /** * Add WHERE clause */ where(condition: WhereClause): SelectQueryBuilder /** * Add AND condition to WHERE clause */ andWhere(condition: WhereClause): SelectQueryBuilder /** * Add OR condition to WHERE clause */ orWhere(condition: WhereClause): SelectQueryBuilder /** * Add ORDER BY clause */ orderBy(...specs: OrderBySpec[]): SelectQueryBuilder /** * Add LIMIT clause */ limit(count: number): SelectQueryBuilder /** * Add OFFSET clause */ offset(count: number): SelectQueryBuilder /** * Add GROUP BY clause */ groupBy(...columns: (keyof T['columns'])[]): SelectQueryBuilder /** * Add HAVING clause */ having(condition: SQLExpression): SelectQueryBuilder /** * Add DISTINCT */ distinct(): SelectQueryBuilder /** * Execute and return first result or null */ first(): Promise /** * Execute and return first result or throw */ firstOrThrow(): Promise } /** * JOIN query builder interface */ export interface JoinQueryBuilder< TLeft extends TableDefinition, TRight extends TableDefinition, TJoinType extends JoinType, TResult = JoinResult[], > extends QueryBuilder { /** * Add WHERE clause */ where(condition: SQLExpression): JoinQueryBuilder /** * Add ORDER BY clause */ orderBy(column: string, direction?: 'ASC' | 'DESC'): JoinQueryBuilder /** * Add LIMIT clause */ limit(count: number): JoinQueryBuilder /** * Select specific columns from joined tables */ select>( selection: TSelection ): JoinQueryBuilder ? U : never }[]> } /** * INSERT query builder interface */ export interface InsertQueryBuilder< T extends TableDefinition, TResult = { rowCount: number }, > extends QueryBuilder { /** * Add RETURNING clause */ returning(): InsertQueryBuilder[]> /** * Add RETURNING clause with specific columns */ returning( ...columns: TColumns ): InsertQueryBuilder, TColumns[number]>[]> /** * Add ON CONFLICT clause */ onConflict( target: ConflictTarget, action: OnConflictAction ): InsertQueryBuilder } /** * UPDATE query builder interface */ export interface UpdateQueryBuilder< T extends TableDefinition, TResult = { rowCount: number }, > extends QueryBuilder { /** * Add WHERE clause */ where(condition: WhereClause): UpdateQueryBuilder /** * Add RETURNING clause */ returning(): UpdateQueryBuilder[]> /** * Add RETURNING clause with specific columns */ returning( ...columns: TColumns ): UpdateQueryBuilder, TColumns[number]>[]> } /** * DELETE query builder interface */ export interface DeleteQueryBuilder< T extends TableDefinition, TResult = { rowCount: number }, > extends QueryBuilder { /** * Add WHERE clause */ where(condition: WhereClause): DeleteQueryBuilder /** * Add RETURNING clause */ returning(): DeleteQueryBuilder[]> /** * Add RETURNING clause with specific columns */ returning( ...columns: TColumns ): DeleteQueryBuilder, TColumns[number]>[]> } // ============================================================================ // Query Factory Interface // ============================================================================ /** * Main query builder factory interface */ export interface QueryBuilderFactory { /** * Start a SELECT query */ select(): { from(table: T): SelectQueryBuilder } /** * Start a SELECT query with specific columns */ select>( selection: TSelection ): { from(table: T): SelectQueryBuilder } /** * Start an INSERT query */ insert(table: T): { values(values: InsertValues): InsertQueryBuilder } /** * Start an UPDATE query */ update(table: T): { set(values: UpdateSet): UpdateQueryBuilder } /** * Start a DELETE query */ delete(table: T): DeleteQueryBuilder /** * Create a JOIN */ join< TLeft extends TableDefinition, TRight extends TableDefinition, >( left: TLeft, right: TRight, condition: JoinCondition ): JoinQueryBuilder /** * Create a LEFT JOIN */ leftJoin< TLeft extends TableDefinition, TRight extends TableDefinition, >( left: TLeft, right: TRight, condition: JoinCondition ): JoinQueryBuilder /** * Create a RIGHT JOIN */ rightJoin< TLeft extends TableDefinition, TRight extends TableDefinition, >( left: TLeft, right: TRight, condition: JoinCondition ): JoinQueryBuilder /** * Create a FULL OUTER JOIN */ fullJoin< TLeft extends TableDefinition, TRight extends TableDefinition, >( left: TLeft, right: TRight, condition: JoinCondition ): JoinQueryBuilder }