/**
* Wraps a Kysely transaction such that any `Err` returned from the callback results in a rollback.
* Any exceptions thrown will result in an `Err<"UnknownTransactionError">`.
* @param db The Kysely database instance.
* @param cb The transaction handler which returns a `Result`.
* @returns The Result from the transaction callback, or an `Err<"UnknownTransactionError">`.
*/
export default async function transactResult<V, E extends string>(
db: Kysely<Tables>,
cb: (trx: Transaction<Tables>) => Promise<Result<V, E>>
): Promise<Result<V, E | "UnknownTransactionError">> {
class InvokedRollback extends Error {
constructor(public err: Err<E>) {
super();
}
}
try {
const transactionOkValue = await db.transaction().execute(async trx => {
const returnValue = await cb(trx);
if (returnValue.isErr()) {
logger.error("Transaction handler returned an Err:", returnValue.error);
// make Kysely rollback this transaction
throw new InvokedRollback(returnValue);
}
return returnValue;
});
return transactionOkValue;
} catch (error) {
if (error instanceof InvokedRollback) {
logger.error("Transaction rolled back.");
return error.err as Err<E>;
} else {
// Another unknown error thrown inside the transaction handler.
logger.error(
"Unknown error thrown inside transaction handler. This may indicate a missing Err check:",
error
);
return new Err("UnknownTransactionError");
}
}
}