UNPKG

saga-transaction-lib

Version:

A TypeScript library for implementing the Saga pattern to manage distributed transactions and complex workflows

124 lines (116 loc) 3.71 kB
'use strict'; var lodash = require('lodash'); class TransactionContext { constructor(data, metadata = {}) { this.data = data; this.metadata = metadata; this.successfulSteps = []; } addSuccessfulStep(step) { this.successfulSteps.unshift(step); } getSuccessfulSteps() { return [...this.successfulSteps]; } } class DefaultLogger { log(message) { console.log(`[Saga] ${message}`); } error(message, error) { console.error(`[Saga Error] ${message}`, error); } warn(message) { console.warn(`[Saga Warning] ${message}`); } debug(message) { console.debug(`[Saga Debug] ${message}`); } } class DefaultErrorHandler { constructor(logger) { this.logger = logger; } async handleError(error, context) { this.logger.error('Saga transaction failed', error); const successfulSteps = context.getSuccessfulSteps(); for (const step of successfulSteps) { try { this.logger.debug(`Compensating step: ${step.name}`); await step.compensate(context.data); } catch (compensationError) { this.logger.error(`Compensation failed for step ${step.name}`, compensationError); } } } } class Saga { constructor(options = {}) { var _a; this.isBreakStep = false; this.logger = options.logger || new DefaultLogger(); this.errorHandler = options.errorHandler || new DefaultErrorHandler(this.logger); this.shouldStopOnError = (_a = options.shouldStopOnError) !== null && _a !== void 0 ? _a : true; } async execute(context, steps) { const transactionContext = new TransactionContext(context); for (const step of steps) { if (this.isBreakStep) { break; } try { this.logger.debug(`Executing step: ${step.name}`); await step.invoke(context); transactionContext.addSuccessfulStep(step); this.logger.log(`Successfully completed step: ${step.name}`); } catch (error) { await this.handleStepError(error, transactionContext); if (this.shouldStopOnError) { throw error; } } } return transactionContext; } async handleStepError(error, context) { await this.errorHandler.handleError(error, context); } } function BeforeInvoke(fn) { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; descriptor.value = async function (...args) { if (typeof fn === 'function' && !(await fn({ instance: this, context: lodash.first(args), }))) { this.isBreakStep = true; return; } return originalMethod.apply(this, args); }; return descriptor; }; } function BeforeRevoke() { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args) { if (this.isBreakStep) { return; } return originalMethod.apply(this, args); }; return descriptor; }; } exports.BeforeInvoke = BeforeInvoke; exports.BeforeRevoke = BeforeRevoke; exports.DefaultErrorHandler = DefaultErrorHandler; exports.DefaultLogger = DefaultLogger; exports.Saga = Saga; exports.TransactionContext = TransactionContext;