Source: editing/transactionStack.js

/**
 * Класс SessionStack
 */
class TransactionStack {
    /**
     * Конструктор стека
     * @param params
     */
    constructor(params) {
        this.stack = params.stack || new Array();
    }

    /**
     * Добавление элемента (транзакции) в конец массив
     * @param item - {TransactionItem} - объект транзакции
     * @returns {Number} - длина стека
     */
    push(item) {
        return this.stack.push(item);
    }

    /**
     * Удаление последнего элемента из стека
     * @returns {TransactionItem} - удаляемый объект транзакции
     */
    pop() {
        return this.stack.pop();
    }

    /**
     * Удаление транзакции по индексу в стеке
     * @param index
     */
    removeItemByIndex(index) {
        let self = this;
        let stack = self.getStack();
        stack = stack.filter((item, i) => {
            return index !== i;
        });
        this.stack = stack;
    }

    /**
     * Удаление транзакции по идентификатору в стеке
     * @param index
     */
    removeItemById(id) {
        let self = this;
        let stack = self.getStack();
        stack = stack.filter((item) => {
            return item.guid !== id;
        });
        this.stack = stack;
    }

    /**
     * Получение объектов стека
     * @returns {*|Array} - массив транзакций
     */
    getStack() {
        return this.stack;
    }

    /**
     * Применение транзакции, заданной по индексу в стеке
     * @param index (number) - индекс транзакции в стеке
     * @returns {Promise.<boolean>} - результат применения транзакции
     */
    async commit(index) {
        let self = this;
        let item = self.getStack()[index];
        let commitResult = false;
        try {
            commitResult = await item.execute();
        }
        catch (e) {
            commitResult = undefined;
        }
        if (commitResult) {
            self.stack = self.stack.filter((item) => {
                return item.getState() !== true;
            });
        }
        return commitResult;
    }

    /**
     * Применение транзакции, заданной по ее id (guid - вычисляется автоматически при создании транзакции)
     * @param id (guid) - идентификатор транзакции
     * @returns {Promise.<boolean>} - результат применения транзакции
     */
    async commitById(id) {
        let self = this;
        let items = self.getStack();
        let item = items.find((transactionItem) => {
            return transactionItem.getId() === id;
        });

        if (!item) return undefined;

        let commitResult = false;
        try {
            commitResult = await item.execute();
        }
        catch (e) {
            commitResult = undefined;
        }
        if (commitResult) {
            self.stack = self.stack.filter((item) => {
                return item.getState() !== true;
            });
        }
        return commitResult;
    }

    /**
     * Применение всех транзакций
     * @returns {Promise.<Array>} - результаты применения транзакций {result: false/true, transactionId - идентификатор}
     */
    async commitAll() {
        let self = this;
        let commitResults = [];
        for (let i = 0; i < self.getStack().length; i++) {
            let stack = self.getStack();
            let guid = stack[i].getId();
            let result = await self.commit(i);
            commitResults.push({
                transactionId: guid,
                result: result,
                state: result.state
            });
        }
        return commitResults;
    }

    /**
     * Очистка стека транзакций
     * @returns {*|Array} - новый пустой стек транзакций
     */
    clear() {
        this.stack = new Array();
        return this.stack;
    }
}

let stack;

function init() {
    stack = new TransactionStack({});
    return stack;
}

function getStack() {
    if (!stack) stack = init();
    return stack;
}

module.exports = getStack();