UNPKG

axiodb

Version:

The Pure JavaScript Alternative to SQLite. Embedded NoSQL database for Node.js with MongoDB-style queries, zero native dependencies, built-in InMemoryCache, and web GUI. Perfect for desktop apps, CLI tools, and embedded systems. No compilation, no platfor

232 lines 9.14 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable @typescript-eslint/no-explicit-any */ const outers_1 = require("outers"); const response_helper_1 = __importDefault(require("../../Helper/response.helper")); const Transaction_service_1 = __importDefault(require("./Transaction.service")); /** * Session provides MongoDB-like session management for transactions. * Supports automatic retry, timeout handling, and the withTransaction pattern. */ class Session { constructor(collectionPath, isEncrypted = false, encryptionKey, options = {}) { var _a, _b, _c; this.isActive = true; this.currentTransaction = null; this.sessionId = new outers_1.ClassBased.UniqueGenerator(20).RandomWord(true); this.collectionPath = collectionPath; this.isEncrypted = isEncrypted; this.encryptionKey = encryptionKey; this.startTime = Date.now(); this.ResponseHelper = new response_helper_1.default(); // Default options this.options = { defaultTimeout: (_a = options.defaultTimeout) !== null && _a !== void 0 ? _a : 60000, retryWrites: (_b = options.retryWrites) !== null && _b !== void 0 ? _b : true, maxRetries: (_c = options.maxRetries) !== null && _c !== void 0 ? _c : 3, }; } /** * Gets the session ID */ getSessionId() { return this.sessionId; } /** * Checks if the session is still active */ isSessionActive() { if (!this.isActive) return false; if (Date.now() - this.startTime > this.options.defaultTimeout) { this.isActive = false; return false; } return true; } /** * Starts a new transaction within this session. * Only one transaction can be active at a time per session. * * @returns A new Transaction instance * @throws Error if session is inactive or another transaction is active */ startTransaction() { if (!this.isSessionActive()) { throw new Error("Session has expired or is inactive"); } if (this.currentTransaction) { throw new Error("A transaction is already active in this session. Commit or rollback first."); } this.currentTransaction = new Transaction_service_1.default(this.collectionPath, this.isEncrypted, this.encryptionKey); return this.currentTransaction; } /** * Executes a callback function within a transaction context. * Automatically commits on success or rolls back on error. * Supports automatic retry on transient errors. * * @param callback - Async function that receives the transaction and performs operations * @returns Promise resolving to success/error result * * @example * ```typescript * const session = collection.startSession(); * await session.withTransaction(async (txn) => { * await txn.insert({ name: 'Alice' }); * await txn.update({ name: 'Bob' }, { age: 30 }); * }); * ``` */ withTransaction(callback) { return __awaiter(this, void 0, void 0, function* () { if (!this.isSessionActive()) { return this.ResponseHelper.Error("Session has expired or is inactive"); } let lastError = null; let attempts = 0; while (attempts < this.options.maxRetries) { attempts++; try { // Create a new transaction const txn = this.startTransaction(); // Execute the callback yield callback(txn); // Commit the transaction const result = yield txn.commit(); // Clear current transaction reference this.currentTransaction = null; if ('status' in result && result.status === true) { return result; } // If commit failed but not due to timeout, don't retry if ('message' in result && !this.isRetryableError(result.message)) { return result; } lastError = result; } catch (error) { // Ensure transaction is rolled back on error if (this.currentTransaction) { try { yield this.currentTransaction.rollback(); } catch (_a) { // Ignore rollback errors } this.currentTransaction = null; } // Check if error is retryable if (!this.options.retryWrites || !this.isRetryableError(error.message)) { return this.ResponseHelper.Error(error.message || error); } lastError = error; } // Wait before retry with exponential backoff if (attempts < this.options.maxRetries) { yield this.sleep(Math.min(100 * Math.pow(2, attempts - 1), 1000)); } } return this.ResponseHelper.Error(`Transaction failed after ${attempts} attempts: ${(lastError === null || lastError === void 0 ? void 0 : lastError.message) || lastError}`); }); } /** * Commits the current transaction if one is active. * * @returns Promise resolving to success/error result */ commitTransaction() { return __awaiter(this, void 0, void 0, function* () { if (!this.currentTransaction) { return this.ResponseHelper.Error("No active transaction to commit"); } try { const result = yield this.currentTransaction.commit(); this.currentTransaction = null; return result; } catch (error) { this.currentTransaction = null; return this.ResponseHelper.Error(error.message || error); } }); } /** * Rolls back the current transaction if one is active. * * @returns Promise resolving to success/error result */ abortTransaction() { return __awaiter(this, void 0, void 0, function* () { if (!this.currentTransaction) { return this.ResponseHelper.Error("No active transaction to abort"); } try { const result = yield this.currentTransaction.rollback(); this.currentTransaction = null; return result; } catch (error) { this.currentTransaction = null; return this.ResponseHelper.Error(error.message || error); } }); } /** * Ends the session and cleans up resources. * Any active transaction will be rolled back. */ endSession() { return __awaiter(this, void 0, void 0, function* () { if (this.currentTransaction) { try { yield this.currentTransaction.rollback(); } catch (_a) { // Ignore rollback errors during cleanup } this.currentTransaction = null; } this.isActive = false; }); } /** * Determines if an error is retryable (transient) */ isRetryableError(message) { if (!message) return false; const retryablePatterns = [ 'timeout', 'lock', 'deadlock', 'conflict', 'busy', 'temporary', 'EBUSY', 'EAGAIN' ]; const lowerMessage = message.toLowerCase(); return retryablePatterns.some(pattern => lowerMessage.includes(pattern)); } /** * Simple sleep utility */ sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } exports.default = Session; //# sourceMappingURL=Session.service.js.map