import { Kysely, Transaction } from 'kysely' import { KyselyDatabase } from './KyselyDatabase.js' import { Database } from '../../Database.js' import { DatabaseTransaction } from '../../DatabaseTransaction.js' /** * Represents a transaction for querying a database with a specific schema. * @param DBSchema - The schema of the database. * @type {KyselyTransactionImpl & Transaction} */ export type KyselyTransaction = KyselyTransactionImpl & Transaction /** * Represents a transaction in a Postgres database. */ export class KyselyTransactionImpl extends DatabaseTransaction { /** * A readonly property representing a writer for a Kysely object. * @type {Kysely} */ public readonly writer!: Kysely /** * A readonly property representing a reader of type Kysely. * This property allows reading data of any type using the Kysely interface. */ public readonly reader!: Kysely /** * A protected property representing a transaction of type any. */ protected transaction!: Transaction /** * A protected property representing a database instance. * @type {Database>} */ protected database!: Database> /** * A private property that represents a deferred object. * @type {Deferred} */ private txWrapper!: Deferred /** * Constructs a new instance of the class. * @param {Kysely} delegate - The delegate object for the database query. * @param {KyselyDatabase} database - The database object for the query. * @param {Kysely} [reader] - An optional reader object for the query. * @returns None */ private constructor( delegate: Kysely, database: KyselyDatabase, reader?: Kysely ) { super(delegate, database, reader) } /** * Creates a new database transaction for the given database schema. * @param {Kysely} connection - The connection object for the transaction. * @param {KyselyDatabase} database - The database object for the transaction. * @param {Kysely} [reader] - An optional reader object for the transaction. * @returns {Promise>} A promise that resolves to the created transaction. */ public static async newTransaction( connection: Kysely, database: KyselyDatabase, reader?: Kysely ): Promise> { const tx = new KyselyTransactionImpl(connection, database, reader) await tx.begin() return DatabaseTransaction.proxyInstance(tx) as any } /** * Asynchronously begins a transaction using a writer and resolves the transaction once it is executed. * @returns A Promise that resolves with the transaction object once the transaction is executed. */ protected doBegin = async () => { this.txWrapper = new Deferred() return new Promise(resolve => { this.writer .transaction() .execute(async trx => { this.transaction = trx resolve(trx) // resolve with tx return this.txWrapper.promise // wait for wrapper to be solved }) .catch(() => { // Don't do anything here. Just swallow the exception. }) }) } /** * Executes the commit operation by resolving the transaction wrapper and returning a resolved Promise. * @returns A Promise that resolves to null. */ protected doCommit = () => { this.txWrapper.resolve(null) return Promise.resolve() } /** * Performs a rollback operation by rejecting the transaction wrapper with an error. * @returns A resolved Promise after the rollback operation is completed. */ protected doRollback = () => { this.txWrapper.reject(new Error('Rollback')) return Promise.resolve() } } /** * Represents a deferred promise that can be resolved or rejected at a later time. * @template T - The type of the value that the promise will resolve to. */ class Deferred { /** * A private readonly Promise object that resolves to type T. */ private readonly _promise: Promise /** * A private property that holds a function to resolve a Promise with a value of type T. * @param {T | PromiseLike} value - The value or promise to be resolved. */ private _resolve?: (value: T | PromiseLike) => void /** * A function that can be called to reject a promise with an optional reason. * @param {any} [reason] - An optional reason for rejecting the promise. */ private _reject?: (reason?: any) => void /** * Constructor for creating a new Promise instance with resolve and reject functions. * @constructor */ constructor() { this._promise = new Promise((resolve, reject) => { this._reject = reject this._resolve = resolve }) } /** * Getter method to retrieve the Promise object. * @returns {Promise} A Promise object of type T. */ public get promise(): Promise { return this._promise } /** * Resolves the Promise with the given value. * @param {T | PromiseLike} value - The value to resolve the Promise with. * @returns void */ public resolve(value: T | PromiseLike): void { if (this._resolve) { this._resolve(value) } } /** * Rejects the Promise with the given reason, if the reject function is available. * @param {any} reason - The reason for rejecting the Promise. * @returns void */ public reject(reason?: any): void { if (this._reject) { this._reject(reason) } } }