UNPKG

flint-orm

Version:

Type-safe SQLite ORM for JavaScript

261 lines (260 loc) 12.2 kB
import type { ColumnDef } from '../schema/columns'; import type { Condition } from './conditions'; import type { AnyTable, InferRow, InsertRow } from '../schema/table'; import type { Executor } from '../executor'; /** * Narrow a row type to specific columns using a mapped type. * Unlike Pick, TypeScript eagerly evaluates { [K in C]: T[K] } to a concrete * shape in hover info: { id: string; name: string } instead of Pick<...>. */ export type NarrowRow<T, C extends keyof T> = { [K in C]: T[K]; }; /** * Force TypeScript to expand intersection/mapped types into a flat object * in hover info: Prettify<{ id: string } & { name: string }> → { id: string; name: string }. */ export type Prettify<T> = { [K in keyof T]: T[K]; } & {}; /** Anything that can produce SQL — used by `db.batch()`. */ export interface Executable { toSQL(): { sql: string; params: unknown[]; }; } /** * Full SELECT builder — created directly via `db.selectFrom(table)`. * Entry point: `db.selectFrom(table)`. * Returns `InferRow<T>[]` (all columns) from `execute()`. * Call `.columns()` to narrow — returns a `NarrowedSelectBuilder`. */ export declare class SelectBuilder<T extends AnyTable> implements Executable { #private; constructor(executor: Executor, tableName: string, table: T, conditions?: Condition[], selectedColumns?: (keyof InferRow<T>)[] | null, orderByClauses?: { column: ColumnDef<any, any>; direction: 'asc' | 'desc'; }[], limitValue?: number | null, offsetValue?: number | null, distinct?: boolean); /** Add a WHERE condition. Multiple calls stack. */ where(condition: Condition): SelectBuilder<T>; /** * Narrow which columns appear in the result. * Returns a `NarrowedSelectBuilder` with a clean `{ id: string; name: string }` return type. * * @example * db.selectFrom(users).columns(["id", "name"]).execute() * // ^? { id: string; name: string }[] */ columns<K extends keyof InferRow<T>>(keys: K[]): NarrowedSelectBuilder<T, K>; /** Return a single row or null instead of an array. Adds `LIMIT 1` to the SQL. */ single(): SingleSelectBuilder<T>; /** Return only distinct (unique) rows. */ distinct(): SelectBuilder<T>; /** Add an ORDER BY clause. Multiple calls stack. */ orderBy<K extends keyof InferRow<T>>(key: K, direction?: 'asc' | 'desc'): SelectBuilder<T>; /** Limit the number of results. */ limit(n: number): SelectBuilder<T>; /** Skip N rows before returning results. */ offset(n: number): SelectBuilder<T>; toSQL(): { sql: string; params: unknown[]; }; /** Execute the query and return all matching rows. */ execute(): Promise<InferRow<T>[]>; } /** * Narrowed SELECT builder — after `.columns()` has been called. * Returns `NarrowRow<InferRow<T>, C>[]` from `execute()` — a clean * `{ id: string; name: string }` shape, no Pick wrapper. */ export declare class NarrowedSelectBuilder<T extends AnyTable, C extends keyof InferRow<T>> implements Executable { #private; constructor(executor: Executor, tableName: string, table: T, conditions: Condition[], selectedColumns: C[], orderByClauses: { column: ColumnDef<any, any>; direction: 'asc' | 'desc'; }[], limitValue: number | null, offsetValue: number | null, distinct: boolean); where(condition: Condition): NarrowedSelectBuilder<T, C>; distinct(): NarrowedSelectBuilder<T, C>; single(): NarrowedSingleSelectBuilder<T, C>; orderBy<K extends keyof InferRow<T>>(key: K, direction?: 'asc' | 'desc'): NarrowedSelectBuilder<T, C>; limit(n: number): NarrowedSelectBuilder<T, C>; offset(n: number): NarrowedSelectBuilder<T, C>; toSQL(): { sql: string; params: unknown[]; }; /** Execute the query and return narrowed rows. */ execute(): Promise<Prettify<NarrowRow<InferRow<T>, C>>[]>; } /** * Single-row SELECT builder — after `.single()` on a `SelectBuilder`. * Returns `InferRow<T> | null` from `execute()`. */ export declare class SingleSelectBuilder<T extends AnyTable> implements Executable { #private; constructor(executor: Executor, tableName: string, table: T, conditions: Condition[], selectedColumns?: (keyof InferRow<T>)[] | null, orderByClauses?: { column: ColumnDef<any, any>; direction: 'asc' | 'desc'; }[], offsetValue?: number | null, distinct?: boolean); where(condition: Condition): SingleSelectBuilder<T>; orderBy<K extends keyof InferRow<T>>(key: K, direction?: 'asc' | 'desc'): SingleSelectBuilder<T>; offset(n: number): SingleSelectBuilder<T>; toSQL(): { sql: string; params: unknown[]; }; /** Returns a single row or null, never throws on empty results. */ execute(): Promise<InferRow<T> | null>; } /** * Narrowed single-row SELECT builder — after `.single()` on a `NarrowedSelectBuilder`. * Returns `NarrowRow<InferRow<T>, C> | null` from `execute()`. */ export declare class NarrowedSingleSelectBuilder<T extends AnyTable, C extends keyof InferRow<T>> implements Executable { #private; constructor(executor: Executor, tableName: string, table: T, conditions: Condition[], selectedColumns: C[], orderByClauses: { column: ColumnDef<any, any>; direction: 'asc' | 'desc'; }[], offsetValue: number | null, distinct: boolean); where(condition: Condition): NarrowedSingleSelectBuilder<T, C>; orderBy<K extends keyof InferRow<T>>(key: K, direction?: 'asc' | 'desc'): NarrowedSingleSelectBuilder<T, C>; offset(n: number): NarrowedSingleSelectBuilder<T, C>; toSQL(): { sql: string; params: unknown[]; }; /** Returns a single narrowed row or null. */ execute(): Promise<Prettify<NarrowRow<InferRow<T>, C>> | null>; } /** Phase 1 of a JOIN — only `.on()` is available. */ export interface JoinSelectStage1<Parent extends AnyTable> { on<Child extends AnyTable>(child: Child, condition: Condition): JoinBuilder<Parent, [Child]>; /** Auto-join: infer condition from foreign key references. */ on<Child extends AnyTable>(child: Child): JoinBuilder<Parent, [Child]>; } /** Full join builder — chain more `.on()` calls or call `.execute()`. */ export interface JoinBuilder<Parent extends AnyTable, Joined extends AnyTable[], ParentCols extends keyof InferRow<Parent> = keyof InferRow<Parent>> { on<NewChild extends AnyTable>(child: NewChild, condition: Condition): JoinBuilder<Parent, [...Joined, NewChild], ParentCols>; /** Auto-join: infer condition from foreign key references. */ on<NewChild extends AnyTable>(child: NewChild): JoinBuilder<Parent, [...Joined, NewChild], ParentCols>; columns<K extends keyof InferRow<Parent>>(keys: K[]): JoinBuilder<Parent, Joined, K>; where(condition: Condition): JoinBuilder<Parent, Joined, ParentCols>; orderBy<K extends keyof InferRow<Parent>>(key: K, direction?: 'asc' | 'desc'): JoinBuilder<Parent, Joined, ParentCols>; limit(n: number): JoinBuilder<Parent, Joined, ParentCols>; offset(n: number): JoinBuilder<Parent, Joined, ParentCols>; single(): SingleJoinBuilder<Parent, Joined, ParentCols>; toSQL(): { sql: string; params: unknown[]; }; execute(): Promise<JoinResult<Parent, Joined, ParentCols>[]>; } /** The result type for joined queries. Parent fields are narrowed by `.columns()`; each joined table's data is nested under its table name. */ export type JoinResult<Parent extends AnyTable, _Joined extends AnyTable[], ParentCols extends keyof InferRow<Parent> = keyof InferRow<Parent>> = Prettify<Pick<InferRow<Parent>, ParentCols>> & Record<string, unknown>; /** Phase 1 of an INSERT — only `.values()` is available. */ export interface InsertStage1<T extends AnyTable> { values(row: InsertRow<T>): InsertBuilder<T>; values(rows: InsertRow<T>[]): InsertBuilder<T>; } type OnConflictDoUpdate<T extends AnyTable> = { mode: 'update'; target: ColumnDef<any, any> | ColumnDef<any, any>[]; set: Partial<InferRow<T>>; }; type OnConflictStrategy<T extends AnyTable> = OnConflictDoNothing | OnConflictDoUpdate<T>; /** Full INSERT builder — available after `.values()` has been called. */ export declare class InsertBuilder<T extends AnyTable, R extends boolean = false, K extends keyof InferRow<T> = keyof InferRow<T>> implements Executable { #private; constructor(executor: Executor, tableName: string, table: T, rowOrRows: InsertRow<T> | InsertRow<T>[], returning?: boolean | K[], onConflict?: OnConflictStrategy<T>); /** * Return the inserted row(s) instead of void. * Pass an array of column names to narrow the result shape. * * @example * db.insert(users).values({ id: "u1", name: "Alice" }).returning() * db.insert(users).values({ id: "u1", name: "Alice" }).returning(["id", "name"]) */ returning(): InsertBuilder<T, true>; returning<NewK extends keyof InferRow<T>>(keys: NewK[]): InsertBuilder<T, true, NewK>; /** * On conflict, do nothing (ignore the insert). * * @example * db.insert(users).values(row).onConflictDoNothing() */ onConflictDoNothing(): InsertBuilder<T, R, K>; /** * On conflict, update specified columns with the proposed values. * * @example * db.insert(users).values(row).onConflictDoUpdate({ * target: users.id, * set: { name: "Alice" }, * }) */ onConflictDoUpdate<C extends ColumnDef<any, any>>(options: { target: C | C[]; set: Partial<InferRow<T>>; }): InsertBuilder<T, R, K>; toSQL(): { sql: string; params: unknown[]; }; execute(): Promise<R extends true ? Prettify<NarrowRow<InferRow<T>, K>>[] : { rowsAffected: number; }>; } /** Phase 1 of an UPDATE — only `.set()` is available. */ export interface UpdateStage1<T extends AnyTable> { set(partial: Partial<InferRow<T>>): UpdateBuilder<T>; } /** Full UPDATE builder — available after `.set()` has been called. */ export declare class UpdateBuilder<T extends AnyTable, R extends boolean = false, K extends keyof InferRow<T> = keyof InferRow<T>> implements Executable { #private; constructor(executor: Executor, tableName: string, table: T, set: Partial<InferRow<T>>, conditions?: Condition[], returning?: boolean | K[]); set(partial: Partial<InferRow<T>>): UpdateBuilder<T, R, K>; where(condition: Condition): UpdateBuilder<T, R, K>; /** * Return the updated row(s) instead of void. * Pass an array of column names to narrow the result shape. * * @example * db.update(users).set({ name: "Bob" }).where(eq(users.id, "u1")).returning() * db.update(users).set({ name: "Bob" }).where(eq(users.id, "u1")).returning(["id", "name"]) */ returning(): UpdateBuilder<T, true>; returning<NewK extends keyof InferRow<T>>(keys: NewK[]): UpdateBuilder<T, true, NewK>; toSQL(): { sql: string; params: unknown[]; }; execute(): Promise<R extends true ? Prettify<NarrowRow<InferRow<T>, K>>[] : { rowsAffected: number; }>; } /** Full DELETE builder — chain `.where()` calls then `.execute()`. */ export declare class DeleteBuilder<T extends AnyTable, R extends boolean = false, K extends keyof InferRow<T> = keyof InferRow<T>> implements Executable { #private; constructor(executor: Executor, tableName: string, table: T, conditions?: Condition[], returning?: boolean | K[]); where(condition: Condition): DeleteBuilder<T, R, K>; /** * Return the deleted row(s) instead of void. * Pass an array of column names to narrow the result shape. * * @example * db.delete(users).where(eq(users.id, "u1")).returning() * db.delete(users).where(eq(users.id, "u1")).returning(["id", "name"]) */ returning(): DeleteBuilder<T, true>; returning<NewK extends keyof InferRow<T>>(keys: NewK[]): DeleteBuilder<T, true, NewK>; toSQL(): { sql: string; params: unknown[]; }; execute(): Promise<R extends true ? Prettify<NarrowRow<InferRow<T>, K>>[] : { rowsAffected: number; }>; } export {};