Fix silent exit when a query runs on a connection left idle by a prompt
An idle Bun.SQL connection does not keep the event loop alive. After an inquirer prompt released stdin, the next query let Bun exit with code 0 mid-query: `schema` never applied confirmed destructive changes, and `admin` table views exited instead of showing results. Hold the loop open for the duration of every query and transaction. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Vendored
+7
@@ -15,6 +15,13 @@ type Param = {
|
|||||||
* and CRUD do not open a new connection per query.
|
* and CRUD do not open a new connection per query.
|
||||||
*/
|
*/
|
||||||
export declare function resolveClient(config?: BunMariaDBConfig): Bun.SQL | undefined;
|
export declare function resolveClient(config?: BunMariaDBConfig): Bun.SQL | undefined;
|
||||||
|
/**
|
||||||
|
* Keep the event loop alive until `fn` settles.
|
||||||
|
* An idle Bun.SQL connection does not hold the process open, so a query sent
|
||||||
|
* on it after something else released the loop (e.g. an inquirer prompt
|
||||||
|
* releasing stdin) lets Bun exit silently mid-query.
|
||||||
|
*/
|
||||||
|
export declare function holdEventLoop<T>(fn: () => Promise<T>): Promise<T>;
|
||||||
/**
|
/**
|
||||||
* # Main DB Handler Function
|
* # Main DB Handler Function
|
||||||
*/
|
*/
|
||||||
|
|||||||
Vendored
+16
-1
@@ -16,6 +16,21 @@ export function resolveClient(config) {
|
|||||||
}
|
}
|
||||||
return grabMariaDBClient({ config });
|
return grabMariaDBClient({ config });
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Keep the event loop alive until `fn` settles.
|
||||||
|
* An idle Bun.SQL connection does not hold the process open, so a query sent
|
||||||
|
* on it after something else released the loop (e.g. an inquirer prompt
|
||||||
|
* releasing stdin) lets Bun exit silently mid-query.
|
||||||
|
*/
|
||||||
|
export async function holdEventLoop(fn) {
|
||||||
|
const timer = setInterval(() => { }, 1 << 30);
|
||||||
|
try {
|
||||||
|
return await fn();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Convert driver rows to plain objects.
|
* Convert driver rows to plain objects.
|
||||||
* - BigInt -> number (or string when outside the safe integer range)
|
* - BigInt -> number (or string when outside the safe integer range)
|
||||||
@@ -54,7 +69,7 @@ export default async function dbHandler({ query, values, config, client }) {
|
|||||||
if (!CLIENT) {
|
if (!CLIENT) {
|
||||||
throw new Error(`Couldn't grab MariaDB Client.`);
|
throw new Error(`Couldn't grab MariaDB Client.`);
|
||||||
}
|
}
|
||||||
const res = await CLIENT.unsafe(query, values);
|
const res = await holdEventLoop(() => CLIENT.unsafe(query, values));
|
||||||
const count = res.count;
|
const count = res.count;
|
||||||
const res_array = normalizeRows(res);
|
const res_array = normalizeRows(res);
|
||||||
const last_insert_id = res_array?.[0] ? res_array[0]?.id : undefined;
|
const last_insert_id = res_array?.[0] ? res_array[0]?.id : undefined;
|
||||||
|
|||||||
Vendored
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
import dbHandler, { resolveClient } from "../db-handler";
|
import dbHandler, { holdEventLoop, resolveClient } from "../db-handler";
|
||||||
import sqlGenerator from "../../utils/sql-generator";
|
import sqlGenerator from "../../utils/sql-generator";
|
||||||
import sanitizeHtmlFields from "../../utils/sanitize-html-fields";
|
import sanitizeHtmlFields from "../../utils/sanitize-html-fields";
|
||||||
import { quoteIdentifier } from "../../utils/sql-identifier";
|
import { quoteIdentifier } from "../../utils/sql-identifier";
|
||||||
@@ -62,7 +62,7 @@ export default async function DbUpdate({ table, data, query, targetId, config, }
|
|||||||
* all in one transaction. Re-fetching by id still works when the update
|
* all in one transaction. Re-fetching by id still works when the update
|
||||||
* changes a column used in the WHERE clause.
|
* changes a column used in the WHERE clause.
|
||||||
*/
|
*/
|
||||||
return await CLIENT.begin(async (tx) => {
|
return await holdEventLoop(() => CLIENT.begin(async (tx) => {
|
||||||
const matched_res = await dbHandler({
|
const matched_res = await dbHandler({
|
||||||
query: `SELECT id FROM ${quoteIdentifier(table)} ${whereClause} FOR UPDATE`,
|
query: `SELECT id FROM ${quoteIdentifier(table)} ${whereClause} FOR UPDATE`,
|
||||||
values: sqlQueryObj.whereValues,
|
values: sqlQueryObj.whereValues,
|
||||||
@@ -111,7 +111,7 @@ export default async function DbUpdate({ table, data, query, targetId, config, }
|
|||||||
},
|
},
|
||||||
db_res: res.db_res,
|
db_res: res.db_res,
|
||||||
};
|
};
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
if (error instanceof UpdateAbort) {
|
if (error instanceof UpdateAbort) {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@moduletrace/bun-mariadb",
|
"name": "@moduletrace/bun-mariadb",
|
||||||
"version": "1.0.23",
|
"version": "1.0.24",
|
||||||
"description": "Schema-driven MariaDB manager for Bun",
|
"description": "Schema-driven MariaDB manager for Bun",
|
||||||
"author": "Benjamin Toby",
|
"author": "Benjamin Toby",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
+16
-1
@@ -42,6 +42,21 @@ export function resolveClient(config?: BunMariaDBConfig): Bun.SQL | undefined {
|
|||||||
return grabMariaDBClient({ config });
|
return grabMariaDBClient({ config });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep the event loop alive until `fn` settles.
|
||||||
|
* An idle Bun.SQL connection does not hold the process open, so a query sent
|
||||||
|
* on it after something else released the loop (e.g. an inquirer prompt
|
||||||
|
* releasing stdin) lets Bun exit silently mid-query.
|
||||||
|
*/
|
||||||
|
export async function holdEventLoop<T>(fn: () => Promise<T>): Promise<T> {
|
||||||
|
const timer = setInterval(() => {}, 1 << 30);
|
||||||
|
try {
|
||||||
|
return await fn();
|
||||||
|
} finally {
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert driver rows to plain objects.
|
* Convert driver rows to plain objects.
|
||||||
* - BigInt -> number (or string when outside the safe integer range)
|
* - BigInt -> number (or string when outside the safe integer range)
|
||||||
@@ -88,7 +103,7 @@ export default async function dbHandler<
|
|||||||
throw new Error(`Couldn't grab MariaDB Client.`);
|
throw new Error(`Couldn't grab MariaDB Client.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await CLIENT.unsafe(query, values);
|
const res = await holdEventLoop(() => CLIENT.unsafe(query, values));
|
||||||
|
|
||||||
const count = res.count;
|
const count = res.count;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import dbHandler, { resolveClient } from "../db-handler";
|
import dbHandler, { holdEventLoop, resolveClient } from "../db-handler";
|
||||||
import type {
|
import type {
|
||||||
BunMariaDBConfig,
|
BunMariaDBConfig,
|
||||||
DBResponseObject,
|
DBResponseObject,
|
||||||
@@ -103,7 +103,7 @@ export default async function DbUpdate<
|
|||||||
* all in one transaction. Re-fetching by id still works when the update
|
* all in one transaction. Re-fetching by id still works when the update
|
||||||
* changes a column used in the WHERE clause.
|
* changes a column used in the WHERE clause.
|
||||||
*/
|
*/
|
||||||
return await CLIENT.begin(async (tx) => {
|
return await holdEventLoop(() => CLIENT.begin(async (tx) => {
|
||||||
const matched_res = await dbHandler<{ id: number }>({
|
const matched_res = await dbHandler<{ id: number }>({
|
||||||
query: `SELECT id FROM ${quoteIdentifier(table)} ${whereClause} FOR UPDATE`,
|
query: `SELECT id FROM ${quoteIdentifier(table)} ${whereClause} FOR UPDATE`,
|
||||||
values: sqlQueryObj.whereValues as any,
|
values: sqlQueryObj.whereValues as any,
|
||||||
@@ -162,7 +162,7 @@ export default async function DbUpdate<
|
|||||||
},
|
},
|
||||||
db_res: res.db_res,
|
db_res: res.db_res,
|
||||||
} as DBResponseObject;
|
} as DBResponseObject;
|
||||||
});
|
}));
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error instanceof UpdateAbort) {
|
if (error instanceof UpdateAbort) {
|
||||||
return error.response;
|
return error.response;
|
||||||
|
|||||||
Reference in New Issue
Block a user