import { Database } from './Database.js' /** * Abstract class representing a database transaction. * Warning: These need to be arrow functions! * @class DatabaseTransaction */ export abstract class DatabaseTransaction extends Function { /** * A boolean flag indicating whether a certain feature is open or closed. * @type {boolean} */ protected _isOpen: boolean = false /** * A property representing a writer object. * @type {any} */ public readonly writer: any /** * A property representing a reader object. * @type {any} */ public readonly reader: any /** * A protected property representing a transaction. * @type {any} */ protected transaction: any /** * A protected property representing a database of type Database. * This property is accessible within the class and its subclasses. */ protected database: Database /** * Constructor for a class that interacts with a database using a writer and reader. * @param {any} writer - The object responsible for writing to the database. * @param {Database} database - The database to interact with. * @param {any} [reader] - The object responsible for reading from the database (optional). * @returns None */ protected constructor(writer: any, database: Database, reader?: any) { super('...args', 'return this.transaction(...args)') this.writer = writer this.database = database this.reader = reader } /** * Creates a proxy instance for the given subclass of DatabaseTransaction. * The proxy handles method binding and transaction execution based on the subclass state. * @param {T} subclass - The subclass of DatabaseTransaction to proxy. * @returns A proxied instance of the subclass with method binding and transaction execution logic. */ protected static proxyInstance(subclass: T): T { const bind = (target: any, key: string | symbol) => typeof target[key] === 'function' ? target[key].bind(target) : target[key] return new Proxy(subclass, { get(target: T, p: string | symbol): any { if (target[p] !== undefined) { return bind(target, p) } else if (target._isOpen) { return bind(target.transaction, p) } return undefined }, apply(target: T, thisArg: any, argArray: any[]): any { if (target._isOpen) { return target.transaction(...argArray) } throw new Error('Transaction is closed!') }, }) } /** * Check if the object is open. * @returns {boolean} - true if the object is open, false otherwise. */ public isOpen = (): boolean => { return this._isOpen } /** * Begins a transaction asynchronously. * @returns {Promise} A Promise that resolves when the transaction has begun. * @throws {Error} If the transaction is already open. */ public begin = async (): Promise => { if (this._isOpen) { throw new Error('Cannot begin, transaction is already opened!') } await this.doBegin() this._isOpen = true } /** * Asynchronously commits the transaction. * If the transaction is already closed, an error is thrown. * @returns Promise * @throws Error if the transaction is already closed */ public commit = async (): Promise => { if (!this._isOpen) { throw new Error('Cannot commit, transaction is already closed!') } await this.doCommit() this._isOpen = false } /** * Rollback the transaction by reverting any changes made within the transaction. * If the transaction is already closed, an error is thrown. * @returns Promise * @throws Error if the transaction is already closed. */ public rollback = async (): Promise => { if (!this._isOpen) { throw new Error('Cannot rollback, transaction is already closed!') } await this.doRollback() this._isOpen = false } /** * Closes the success modal, committing or rolling back changes based on the autoCommit setting. * If the modal is open and autoCommit is enabled, it will commit the changes. * If autoCommit is disabled, it will rollback the changes. * @returns {Promise} A promise that resolves once the commit or rollback operation is completed. */ public closeSuccess = async () => { if (this._isOpen) { if (this.database.config['autoCommit']) { await this.doCommit() } else { await this.doRollback() } } } /** * Closes the failure by performing a rollback if the failure is currently open. * @returns {Promise} A promise that resolves once the rollback is completed. */ public closeFailure = async () => { if (this._isOpen) { await this.doRollback() } } /** * Abstract method that should be implemented by subclasses to perform a commit operation. * @returns A Promise that resolves when the commit operation is completed. */ protected abstract doCommit: () => Promise /** * An abstract method that defines the rollback functionality. * This method should be implemented by subclasses to perform the actual rollback operation. * @returns A Promise that resolves when the rollback operation is completed. */ protected abstract doRollback: () => Promise /** * Abstract method that defines the beginning of an asynchronous operation. * @returns A Promise that resolves when the operation begins. */ protected abstract doBegin: () => Promise }