import { Knex } from 'knex'; export type KnexTransaction = Knex.Transaction; export type MockTransaction = null; export type UnleashTransaction = KnexTransaction | MockTransaction; export type TransactionCreator = ( scope: (trx: S) => void | Promise, ) => Promise; export const createKnexTransactionStarter = ( knex: Knex, ): TransactionCreator => { function transaction( scope: (trx: KnexTransaction) => void | Promise, ) { return knex.transaction(scope); } return transaction; }; export type DbServiceFactory = (db: Knex) => S; export type WithTransactional = S & { transactional: (fn: (service: S) => R) => Promise; }; export function withTransactional( serviceFactory: (db: Knex) => S, db: Knex, ): WithTransactional { const service = serviceFactory(db) as WithTransactional; service.transactional = async (fn: (service: S) => R) => db.transaction(async (trx: Knex.Transaction) => { const transactionalService = serviceFactory(trx); return fn(transactionalService); }); return service; } /** Just for testing purposes */ export function withFakeTransactional(service: S): WithTransactional { const serviceWithFakeTransactional = service as WithTransactional; serviceWithFakeTransactional.transactional = async ( fn: (service: S) => R, ) => fn(serviceWithFakeTransactional); return serviceWithFakeTransactional; }