UNPKG

@warlock.js/cascade

Version:

ORM for managing databases

165 lines (164 loc) 5.39 kB
import {AsyncLocalStorage}from'async_hooks';const ROLLBACK_SYMBOL = Symbol("rollback"); const COMMIT_SYMBOL = Symbol("commit"); class Database { /** * MongoDB Internal Database instance */ database; /** * Current Connection */ connection; sessionsContainer = new AsyncLocalStorage(); /** * Execute operations within a transaction (automatic commit/rollback) * * This is the recommended method for most use cases. * Automatically commits on success, rolls back on errors. * * @example * const user = await database.transaction(async (session) => { * const user = await User.create({ name: "John" }, { session }); * const order = await Order.create({ userId: user.id }, { session }); * return user; // Auto commits on success * }); */ async transaction(callback, sessionOptions = { defaultTransactionOptions: { readPreference: "primary", readConcern: { level: "local" }, writeConcern: { w: "majority" }, }, }) { const session = this.connection.client.startSession(sessionOptions); try { const result = await this.sessionsContainer.run({ session, database: this, }, async () => { return await session.withTransaction(async () => { return await callback(session); }); }); return result; } finally { // Always end session after withTransaction completes await session.endSession(); } } /** * Start a transaction with manual control * * Use this when you need explicit control over commit/rollback based on business logic. * Returns true if committed, false if rolled back. * * @example * const committed = await database.startTransaction(async ({ session, commit, rollback }) => { * const user = await User.create({ name: "John" }, { session }); * const order = await Order.create({ userId: user.id }, { session }); * * // Conditional rollback based on business logic * if (order.total > 10000) { * console.log("Order too large, rolling back"); * return rollback; // Explicit rollback * } * * if (user.isBanned) { * console.log("User banned, rolling back"); * return rollback; // Explicit rollback * } * * return commit; // Explicit commit * }); * * if (committed) { * console.log("Transaction committed successfully"); * } else { * console.log("Transaction was rolled back"); * } */ async startTransaction(callback, sessionOptions = { defaultTransactionOptions: { readPreference: "primary", readConcern: { level: "local" }, writeConcern: { w: "majority" }, }, }) { const session = this.connection.client.startSession(sessionOptions); try { let shouldRollback = false; await this.sessionsContainer.run({ session, database: this, }, async () => { await session.withTransaction(async () => { const output = await callback({ session, commit: COMMIT_SYMBOL, rollback: ROLLBACK_SYMBOL, }); if (output === ROLLBACK_SYMBOL) { shouldRollback = true; // Throw error to trigger rollback in withTransaction throw new Error("TRANSACTION_ROLLBACK_REQUESTED"); } // Return value for withTransaction to commit return output; }); }); return !shouldRollback; // true if committed, false if rolled back } catch (error) { // Check if it was an intentional rollback if (error?.message === "TRANSACTION_ROLLBACK_REQUESTED") { return false; // Rolled back successfully } // Re-throw actual errors throw error; } finally { // Always end session after withTransaction completes await session.endSession(); } } /** * Get active session */ getActiveSession() { return this.sessionsContainer.getStore(); } /** * Set connection instance */ setConnection(connection) { this.connection = connection; return this; } /** * Set database instance */ setDatabase(database) { this.database = database; return this; } /** * Get database collection instance */ collection(collection) { return this.database.collection(collection); } /** * List collection names */ async listCollectionNames() { return (await this.database.listCollections().toArray()).map(collection => collection.name); } /** * Drop database */ async drop() { return await this.database.dropDatabase(); } } const database = new Database();export{Database,database};//# sourceMappingURL=database.js.map