mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
@types/bun: Improvements to Bun.SQL (#19488)
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
This commit is contained in:
207
packages/bun-types/bun.d.ts
vendored
207
packages/bun-types/bun.d.ts
vendored
@@ -991,11 +991,9 @@ declare module "bun" {
|
||||
function fileURLToPath(url: URL | string): string;
|
||||
|
||||
/**
|
||||
* Fast incremental writer that becomes an `ArrayBuffer` on end().
|
||||
* Fast incremental writer that becomes an {@link ArrayBuffer} on end().
|
||||
*/
|
||||
class ArrayBufferSink {
|
||||
constructor();
|
||||
|
||||
start(options?: {
|
||||
asUint8Array?: boolean;
|
||||
/**
|
||||
@@ -1320,7 +1318,8 @@ declare module "bun" {
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
type SQLOptions = {
|
||||
|
||||
interface SQLOptions {
|
||||
/** Connection URL (can be string or URL object) */
|
||||
url?: URL | string;
|
||||
/** Database server hostname */
|
||||
@@ -1369,27 +1368,33 @@ declare module "bun" {
|
||||
bigint?: boolean;
|
||||
/** Automatic creation of prepared statements, defaults to true */
|
||||
prepare?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a SQL query that can be executed, with additional control methods
|
||||
* Extends Promise to allow for async/await usage
|
||||
*/
|
||||
interface SQLQuery extends Promise<any> {
|
||||
interface SQLQuery<T = any> extends Promise<T> {
|
||||
/** Indicates if the query is currently executing */
|
||||
active: boolean;
|
||||
|
||||
/** Indicates if the query has been cancelled */
|
||||
cancelled: boolean;
|
||||
|
||||
/** Cancels the executing query */
|
||||
cancel(): SQLQuery;
|
||||
cancel(): SQLQuery<T>;
|
||||
|
||||
/** Execute as a simple query, no parameters are allowed but can execute multiple commands separated by semicolons */
|
||||
simple(): SQLQuery;
|
||||
simple(): SQLQuery<T>;
|
||||
|
||||
/** Executes the query */
|
||||
execute(): SQLQuery;
|
||||
execute(): SQLQuery<T>;
|
||||
|
||||
/** Returns the raw query result */
|
||||
raw(): SQLQuery;
|
||||
raw(): SQLQuery<T>;
|
||||
|
||||
/** Returns only the values from the query result */
|
||||
values(): SQLQuery;
|
||||
values(): SQLQuery<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1407,65 +1412,117 @@ declare module "bun" {
|
||||
* Main SQL client interface providing connection and transaction management
|
||||
*/
|
||||
interface SQL {
|
||||
/** Creates a new SQL client instance
|
||||
* @example
|
||||
* const sql = new SQL("postgres://localhost:5432/mydb");
|
||||
* const sql = new SQL(new URL("postgres://localhost:5432/mydb"));
|
||||
*/
|
||||
new (connectionString: string | URL): SQL;
|
||||
/** Creates a new SQL client instance with options
|
||||
* @example
|
||||
* const sql = new SQL("postgres://localhost:5432/mydb", { idleTimeout: 1000 });
|
||||
*/
|
||||
new (connectionString: string | URL, options: SQLOptions): SQL;
|
||||
/** Creates a new SQL client instance with options
|
||||
* @example
|
||||
* const sql = new SQL({ url: "postgres://localhost:5432/mydb", idleTimeout: 1000 });
|
||||
*/
|
||||
new (options?: SQLOptions): SQL;
|
||||
/** Executes a SQL query using template literals
|
||||
* @example
|
||||
* const [user] = await sql`select * from users where id = ${1}`;
|
||||
*/
|
||||
(strings: string | TemplateStringsArray, ...values: any[]): SQLQuery;
|
||||
/**
|
||||
* Helper function to allow easy use to insert values into a query
|
||||
* Executes a SQL query using template literals
|
||||
* @example
|
||||
* const result = await sql`insert into users ${sql(users)} RETURNING *`;
|
||||
* ```ts
|
||||
* const [user] = await sql`select * from users where id = ${1}`;
|
||||
* ```
|
||||
*/
|
||||
(obj: any): SQLQuery;
|
||||
/** Commits a distributed transaction also know as prepared transaction in postgres or XA transaction in MySQL
|
||||
(strings: string[] | TemplateStringsArray, ...values: any[]): SQLQuery;
|
||||
|
||||
/**
|
||||
* Helper function for inserting an object into a query
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Insert an object
|
||||
* const result = await sql`insert into users ${sql(users)} RETURNING *`;
|
||||
*
|
||||
* // Or pick specific columns
|
||||
* const result = await sql`insert into users ${sql(users, "id", "name")} RETURNING *`;
|
||||
*
|
||||
* // Or a single object
|
||||
* const result = await sql`insert into users ${sql(user)} RETURNING *`;
|
||||
* ```
|
||||
*/
|
||||
<T extends { [Key in PropertyKey]: unknown }>(obj: T | T[] | readonly T[], ...columns: (keyof T)[]): SQLQuery;
|
||||
|
||||
/**
|
||||
* Helper function for inserting any serializable value into a query
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = await sql`SELECT * FROM users WHERE id IN ${sql([1, 2, 3])}`;
|
||||
* ```
|
||||
*/
|
||||
(obj: unknown): SQLQuery;
|
||||
|
||||
/**
|
||||
* Commits a distributed transaction also know as prepared transaction in postgres or XA transaction in MySQL
|
||||
*
|
||||
* @param name - The name of the distributed transaction
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* await sql.commitDistributed("my_distributed_transaction");
|
||||
* ```
|
||||
*/
|
||||
commitDistributed(name: string): Promise<void>;
|
||||
/** Rolls back a distributed transaction also know as prepared transaction in postgres or XA transaction in MySQL
|
||||
|
||||
/**
|
||||
* Rolls back a distributed transaction also know as prepared transaction in postgres or XA transaction in MySQL
|
||||
*
|
||||
* @param name - The name of the distributed transaction
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* await sql.rollbackDistributed("my_distributed_transaction");
|
||||
* ```
|
||||
*/
|
||||
rollbackDistributed(name: string): Promise<void>;
|
||||
|
||||
/** Waits for the database connection to be established
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* await sql.connect();
|
||||
* ```
|
||||
*/
|
||||
connect(): Promise<SQL>;
|
||||
/** Closes the database connection with optional timeout in seconds. If timeout is 0, it will close immediately, if is not provided it will wait for all queries to finish before closing.
|
||||
|
||||
/**
|
||||
* Closes the database connection with optional timeout in seconds. If timeout is 0, it will close immediately, if is not provided it will wait for all queries to finish before closing.
|
||||
*
|
||||
* @param options - The options for the close
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* await sql.close({ timeout: 1 });
|
||||
* ```
|
||||
*/
|
||||
close(options?: { timeout?: number }): Promise<void>;
|
||||
/** Closes the database connection with optional timeout in seconds. If timeout is 0, it will close immediately, if is not provided it will wait for all queries to finish before closing.
|
||||
* @alias close
|
||||
|
||||
/**
|
||||
* Closes the database connection with optional timeout in seconds. If timeout is 0, it will close immediately, if is not provided it will wait for all queries to finish before closing.
|
||||
* This is an alias of {@link SQL.close}
|
||||
*
|
||||
* @param options - The options for the close
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* await sql.end({ timeout: 1 });
|
||||
* ```
|
||||
*/
|
||||
end(options?: { timeout?: number }): Promise<void>;
|
||||
/** Flushes any pending operations */
|
||||
flush(): void;
|
||||
/** The reserve method pulls out a connection from the pool, and returns a client that wraps the single connection.
|
||||
* This can be used for running queries on an isolated connection.
|
||||
* Calling reserve in a reserved Sql will return a new reserved connection, not the same connection (behavior matches postgres package).
|
||||
|
||||
/**
|
||||
* Flushes any pending operations
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* sql.flush();
|
||||
* ```
|
||||
*/
|
||||
flush(): void;
|
||||
|
||||
/**
|
||||
* The reserve method pulls out a connection from the pool, and returns a client that wraps the single connection.
|
||||
* This can be used for running queries on an isolated connection.
|
||||
* Calling reserve in a reserved Sql will return a new reserved connection, not the same connection (behavior matches postgres package).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const reserved = await sql.reserve();
|
||||
* await reserved`select * from users`;
|
||||
* await reserved.release();
|
||||
@@ -1476,12 +1533,14 @@ declare module "bun" {
|
||||
* } finally {
|
||||
* await reserved.release();
|
||||
* }
|
||||
* //To make it simpler bun supportsSymbol.dispose and Symbol.asyncDispose
|
||||
*
|
||||
* // Bun supports Symbol.dispose and Symbol.asyncDispose
|
||||
* {
|
||||
* // always release after context (safer)
|
||||
* using reserved = await sql.reserve()
|
||||
* await reserved`select * from users`
|
||||
* // always release after context (safer)
|
||||
* using reserved = await sql.reserve()
|
||||
* await reserved`select * from users`
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
reserve(): Promise<ReservedSQL>;
|
||||
/** Begins a new transaction
|
||||
@@ -1626,6 +1685,45 @@ declare module "bun" {
|
||||
|
||||
[Symbol.asyncDispose](): Promise<any>;
|
||||
}
|
||||
const SQL: {
|
||||
/**
|
||||
* Creates a new SQL client instance
|
||||
*
|
||||
* @param connectionString - The connection string for the SQL client
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const sql = new SQL("postgres://localhost:5432/mydb");
|
||||
* const sql = new SQL(new URL("postgres://localhost:5432/mydb"));
|
||||
* ```
|
||||
*/
|
||||
new (connectionString: string | URL): SQL;
|
||||
|
||||
/**
|
||||
* Creates a new SQL client instance with options
|
||||
*
|
||||
* @param connectionString - The connection string for the SQL client
|
||||
* @param options - The options for the SQL client
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const sql = new SQL("postgres://localhost:5432/mydb", { idleTimeout: 1000 });
|
||||
* ```
|
||||
*/
|
||||
new (connectionString: string | URL, options: Omit<SQLOptions, "url">): SQL;
|
||||
|
||||
/**
|
||||
* Creates a new SQL client instance with options
|
||||
*
|
||||
* @param options - The options for the SQL client
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const sql = new SQL({ url: "postgres://localhost:5432/mydb", idleTimeout: 1000 });
|
||||
* ```
|
||||
*/
|
||||
new (options?: SQLOptions): SQL;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a reserved connection from the connection pool
|
||||
@@ -1697,21 +1795,14 @@ declare module "bun" {
|
||||
*
|
||||
* @category Database
|
||||
*/
|
||||
var sql: SQL;
|
||||
const sql: SQL;
|
||||
|
||||
/**
|
||||
* SQL client for PostgreSQL
|
||||
*
|
||||
* @category Database
|
||||
*/
|
||||
var postgres: SQL;
|
||||
|
||||
/**
|
||||
* The SQL constructor
|
||||
*
|
||||
* @category Database
|
||||
*/
|
||||
var SQL: SQL;
|
||||
const postgres: SQL;
|
||||
|
||||
/**
|
||||
* Generate and verify CSRF tokens
|
||||
|
||||
7
packages/bun-types/globals.d.ts
vendored
7
packages/bun-types/globals.d.ts
vendored
@@ -954,13 +954,6 @@ interface ErrorConstructor {
|
||||
*/
|
||||
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
||||
|
||||
/**
|
||||
* Optional override for formatting stack traces
|
||||
*
|
||||
* @see https://v8.dev/docs/stack-trace-api#customizing-stack-traces
|
||||
*/
|
||||
prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
|
||||
|
||||
/**
|
||||
* The maximum number of stack frames to capture.
|
||||
*/
|
||||
|
||||
@@ -179,16 +179,11 @@ function depromise<T>(_promise: Promise<T>): T {
|
||||
tsd.expectType<number>(proc.stdin);
|
||||
}
|
||||
tsd.expectAssignable<PipedSubprocess>(Bun.spawn([], { stdio: ["pipe", "pipe", "pipe"] }));
|
||||
tsd.expectNotAssignable<PipedSubprocess>(Bun.spawn([], { stdio: ["inherit", "inherit", "inherit"] }));
|
||||
tsd.expectAssignable<ReadableSubprocess>(Bun.spawn([], { stdio: ["ignore", "pipe", "pipe"] }));
|
||||
tsd.expectAssignable<ReadableSubprocess>(Bun.spawn([], { stdio: ["pipe", "pipe", "pipe"] }));
|
||||
tsd.expectNotAssignable<ReadableSubprocess>(Bun.spawn([], { stdio: ["pipe", "ignore", "pipe"] }));
|
||||
tsd.expectAssignable<WritableSubprocess>(Bun.spawn([], { stdio: ["pipe", "pipe", "pipe"] }));
|
||||
tsd.expectAssignable<WritableSubprocess>(Bun.spawn([], { stdio: ["pipe", "ignore", "inherit"] }));
|
||||
tsd.expectNotAssignable<WritableSubprocess>(Bun.spawn([], { stdio: ["ignore", "pipe", "pipe"] }));
|
||||
tsd.expectAssignable<NullSubprocess>(Bun.spawn([], { stdio: ["ignore", "inherit", "ignore"] }));
|
||||
tsd.expectAssignable<NullSubprocess>(Bun.spawn([], { stdio: [null, null, null] }));
|
||||
tsd.expectNotAssignable<ReadableSubprocess>(Bun.spawn([], {}));
|
||||
tsd.expectNotAssignable<PipedSubprocess>(Bun.spawn([], {}));
|
||||
|
||||
tsd.expectAssignable<SyncSubprocess<Bun.SpawnOptions.Readable, Bun.SpawnOptions.Readable>>(Bun.spawnSync([], {}));
|
||||
|
||||
192
test/integration/bun-types/fixture/sql.ts
Normal file
192
test/integration/bun-types/fixture/sql.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import { sql } from "bun";
|
||||
import { expectAssignable, expectType } from "./utilities";
|
||||
|
||||
{
|
||||
const postgres = new Bun.SQL();
|
||||
const id = 1;
|
||||
await postgres`select * from users where id = ${id}`;
|
||||
}
|
||||
|
||||
{
|
||||
const postgres = new Bun.SQL("postgres://localhost:5432/mydb");
|
||||
const id = 1;
|
||||
await postgres`select * from users where id = ${id}`;
|
||||
}
|
||||
|
||||
{
|
||||
const postgres = new Bun.SQL({ url: "postgres://localhost:5432/mydb" });
|
||||
const id = 1;
|
||||
await postgres`select * from users where id = ${id}`;
|
||||
}
|
||||
|
||||
{
|
||||
const postgres = new Bun.SQL();
|
||||
postgres("ok");
|
||||
}
|
||||
|
||||
const sql1 = new Bun.SQL();
|
||||
const sql2 = new Bun.SQL("postgres://localhost:5432/mydb");
|
||||
const sql3 = new Bun.SQL(new URL("postgres://localhost:5432/mydb"));
|
||||
const sql4 = new Bun.SQL({ url: "postgres://localhost:5432/mydb", idleTimeout: 1000 });
|
||||
|
||||
const query1 = sql1`SELECT * FROM users WHERE id = ${1}`;
|
||||
const query2 = sql2({ foo: "bar" });
|
||||
|
||||
query1.cancel().simple().execute().raw().values();
|
||||
|
||||
const _promise: Promise<any> = query1;
|
||||
|
||||
sql1.connect();
|
||||
sql1.close();
|
||||
sql1.end();
|
||||
sql1.flush();
|
||||
|
||||
const reservedPromise: Promise<Bun.ReservedSQL> = sql1.reserve();
|
||||
|
||||
sql1.begin(async txn => {
|
||||
txn`SELECT 1`;
|
||||
await txn.savepoint("sp", async sp => {
|
||||
sp`SELECT 2`;
|
||||
});
|
||||
});
|
||||
|
||||
sql1.transaction(async txn => {
|
||||
txn`SELECT 3`;
|
||||
});
|
||||
|
||||
sql1.begin("read write", async txn => {
|
||||
txn`SELECT 4`;
|
||||
});
|
||||
|
||||
sql1.transaction("read write", async txn => {
|
||||
txn`SELECT 5`;
|
||||
});
|
||||
|
||||
sql1.beginDistributed("foo", async txn => {
|
||||
txn`SELECT 6`;
|
||||
});
|
||||
|
||||
sql1.distributed("bar", async txn => {
|
||||
txn`SELECT 7`;
|
||||
});
|
||||
|
||||
sql1.unsafe("SELECT * FROM users");
|
||||
sql1.file("query.sql", [1, 2, 3]);
|
||||
|
||||
sql1.reserve().then(reserved => {
|
||||
reserved.release();
|
||||
reserved[Symbol.dispose]?.();
|
||||
reserved`SELECT 8`;
|
||||
});
|
||||
|
||||
sql1.begin(async txn => {
|
||||
txn.savepoint("sp", async sp => {
|
||||
sp`SELECT 9`;
|
||||
});
|
||||
});
|
||||
|
||||
sql1.begin(async txn => {
|
||||
txn.savepoint(async sp => {
|
||||
sp`SELECT 10`;
|
||||
});
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
sql1.commitDistributed();
|
||||
|
||||
// @ts-expect-error
|
||||
sql1.rollbackDistributed();
|
||||
|
||||
// @ts-expect-error
|
||||
sql1.file(123);
|
||||
|
||||
// @ts-expect-error
|
||||
sql1.unsafe(123);
|
||||
|
||||
// @ts-expect-error
|
||||
sql1.begin("read write", 123);
|
||||
|
||||
// @ts-expect-error
|
||||
sql1.transaction("read write", 123);
|
||||
|
||||
const sqlQueryAny: Bun.SQLQuery = {} as any;
|
||||
const sqlQueryNumber: Bun.SQLQuery<number> = {} as any;
|
||||
const sqlQueryString: Bun.SQLQuery<string> = {} as any;
|
||||
|
||||
expectAssignable<Promise<any>>(sqlQueryAny);
|
||||
expectAssignable<Promise<number>>(sqlQueryNumber);
|
||||
expectAssignable<Promise<string>>(sqlQueryString);
|
||||
|
||||
expectType(sqlQueryNumber).is<Bun.SQLQuery<number>>();
|
||||
expectType(sqlQueryString).is<Bun.SQLQuery<string>>();
|
||||
expectType(sqlQueryNumber).is<Bun.SQLQuery<number>>();
|
||||
|
||||
const queryA = sql`SELECT 1`;
|
||||
expectType(queryA).is<Bun.SQLQuery>();
|
||||
const queryB = sql({ foo: "bar" });
|
||||
expectType(queryB).is<Bun.SQLQuery>();
|
||||
|
||||
expectType(sql).is<Bun.SQL>();
|
||||
|
||||
const opts2: Bun.SQLOptions = { url: "postgres://localhost" };
|
||||
expectType(opts2).is<Bun.SQLOptions>();
|
||||
|
||||
const txCb: Bun.SQLTransactionContextCallback = async sql => [sql`SELECT 1`];
|
||||
const spCb: Bun.SQLSavepointContextCallback = async sql => [sql`SELECT 2`];
|
||||
expectType(txCb).is<Bun.SQLTransactionContextCallback>();
|
||||
expectType(spCb).is<Bun.SQLSavepointContextCallback>();
|
||||
|
||||
expectType(queryA.cancel()).is<Bun.SQLQuery>();
|
||||
expectType(queryA.simple()).is<Bun.SQLQuery>();
|
||||
expectType(queryA.execute()).is<Bun.SQLQuery>();
|
||||
expectType(queryA.raw()).is<Bun.SQLQuery>();
|
||||
expectType(queryA.values()).is<Bun.SQLQuery>();
|
||||
|
||||
declare const queryNum: Bun.SQLQuery<number>;
|
||||
expectType(queryNum.cancel()).is<Bun.SQLQuery<number>>();
|
||||
expectType(queryNum.simple()).is<Bun.SQLQuery<number>>();
|
||||
expectType(queryNum.execute()).is<Bun.SQLQuery<number>>();
|
||||
expectType(queryNum.raw()).is<Bun.SQLQuery<number>>();
|
||||
expectType(queryNum.values()).is<Bun.SQLQuery<number>>();
|
||||
|
||||
expectType(await queryNum.cancel()).is<number>();
|
||||
expectType(await queryNum.simple()).is<number>();
|
||||
expectType(await queryNum.execute()).is<number>();
|
||||
expectType(await queryNum.raw()).is<number>();
|
||||
expectType(await queryNum.values()).is<number>();
|
||||
|
||||
const _sqlInstance: Bun.SQL = Bun.sql;
|
||||
|
||||
expectType(sql({ name: "Alice", email: "alice@example.com" })).is<Bun.SQLQuery>();
|
||||
|
||||
expectType(
|
||||
sql([
|
||||
{ name: "Alice", email: "alice@example.com" },
|
||||
{ name: "Bob", email: "bob@example.com" },
|
||||
]),
|
||||
).is<Bun.SQLQuery>();
|
||||
|
||||
const user = { name: "Alice", email: "alice@example.com", age: 25 };
|
||||
expectType(sql(user, "name", "email")).is<Bun.SQLQuery>();
|
||||
|
||||
const users = [
|
||||
{ id: 1, name: "Alice" },
|
||||
{ id: 2, name: "Bob" },
|
||||
];
|
||||
expectType(sql(users, "id")).is<Bun.SQLQuery>();
|
||||
|
||||
expectType(sql([1, 2, 3])).is<Bun.SQLQuery>();
|
||||
|
||||
expectType(sql("users")).is<Bun.SQLQuery>();
|
||||
|
||||
// @ts-expect-error - missing key in object
|
||||
sql(user, "notAKey");
|
||||
|
||||
// @ts-expect-error - wrong type for key argument
|
||||
sql(user, 123);
|
||||
|
||||
// @ts-expect-error - array of objects, missing key
|
||||
sql(users, "notAKey");
|
||||
|
||||
// @ts-expect-error - array of numbers, extra key argument
|
||||
sql([1, 2, 3], "notAKey");
|
||||
@@ -33,5 +33,4 @@ export function expectType<T>(arg?: T) {
|
||||
}
|
||||
|
||||
export declare const expectAssignable: <T>(expression: T) => void;
|
||||
export declare const expectNotAssignable: <T>(expression: any) => void;
|
||||
export declare const expectTypeEquals: <T, S>(expression: T extends S ? (S extends T ? true : false) : false) => void;
|
||||
|
||||
Reference in New Issue
Block a user