"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.contextStorage = void 0;
exports.transaction = transaction;
const globals_js_1 = require("../globals.js");
const DatabaseTransaction_ts_1 = require("./DatabaseTransaction.js");
const async_hooks_1 = require("async_hooks");
exports.contextStorage = new async_hooks_1.AsyncLocalStorage();
/**
 * Start and run a new transaction. This can be called with a request to hold the transaction, or a new request object will be created
 * @param ctx
 * @param callback
 * @returns
 */
function transaction(ctx, callback) {
    let context;
    let asyncStorageContext;
    if (typeof ctx === 'function') {
        // optional first argument, handle case of no request
        callback = ctx;
        asyncStorageContext = exports.contextStorage.getStore();
        context = asyncStorageContext ?? {};
    }
    else {
        // request argument included, but null or undefined, so maybe create a new one
        context = ctx ?? (asyncStorageContext = exports.contextStorage.getStore()) ?? {};
    }
    if (typeof callback !== 'function') {
        throw new TypeError('Callback function must be provided to transaction');
    }
    if (context?.transaction?.open === DatabaseTransaction_ts_1.TRANSACTION_STATE.OPEN && typeof callback === 'function') {
        return callback(context.transaction); // nothing to be done, already in open transaction
    }
    const transaction = new DatabaseTransaction_ts_1.DatabaseTransaction();
    context.transaction = transaction;
    if (context.timestamp)
        transaction.timestamp = context.timestamp;
    if (context.replicatedConfirmation)
        transaction.replicatedConfirmation = context.replicatedConfirmation;
    transaction.setContext(context);
    // create a resource cache so that multiple requests to the same resource return the same resource
    if (!context.resourceCache)
        context.resourceCache = [];
    let result;
    try {
        result =
            context.isExplicit || asyncStorageContext
                ? callback(transaction)
                : exports.contextStorage.run(context, () => callback(transaction));
        if (result?.then) {
            return result.then(onComplete, onError);
        }
    }
    catch (error) {
        onError(error);
    }
    return onComplete(result);
    // when the transaction function completes, run this to commit the transaction
    function onComplete(result) {
        const committed = transaction.commit({ doneWriting: true });
        if (committed.then) {
            return committed.then(() => {
                return result;
            });
        }
        else {
            return result;
        }
    }
    // if the transaction function throws an error, we abort
    function onError(error) {
        transaction.abort();
        throw error;
    }
}
(0, globals_js_1._assignPackageExport)('transaction', transaction);
transaction.commit = function (contextSource) {
    const transaction = (contextSource.getContext?.() || contextSource)?.transaction;
    if (!transaction)
        throw new Error('No active transaction is available to commit');
    return transaction.commit();
};
transaction.abort = function (contextSource) {
    const transaction = (contextSource.getContext?.() || contextSource)?.transaction;
    if (!transaction)
        throw new Error('No active transaction is available to abort');
    return transaction.abort();
};
//# sourceMappingURL=transaction.js.map