flint-orm
Version:
Type-safe SQLite ORM for JavaScript
261 lines (260 loc) • 12.2 kB
TypeScript
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 {};