UNPKG

node-sagas-orchestrator

Version:

Library for handling distributed transactions using an orchestrator

239 lines (227 loc) 6.89 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.heap = {})); })(this, (function (exports) { 'use strict'; class SagaCompensationFailed extends Error { originalError; constructor(e) { super(e.message); this.stack = e.stack; this.originalError = e; } } class SagaExecutionFailed extends Error { originalError; constructor(e) { super(e.message); this.stack = e.stack; this.originalError = e; } } exports.SagaStates = void 0; (function (SagaStates) { SagaStates["New"] = "New"; SagaStates["InProgress"] = "In progress"; SagaStates["InCompensation"] = "In compensation"; SagaStates["Complete"] = "Complete"; SagaStates["CompensationComplete"] = "Compensation complete"; SagaStates["CompensationError"] = "Compensation error"; })(exports.SagaStates || (exports.SagaStates = {})); class Saga { sagaFlow; state; invokeError; compensationError; constructor(sagaFlow) { this.sagaFlow = sagaFlow; this.state = exports.SagaStates.New; } getState() { return this.state; } async execute() { this.state = exports.SagaStates.InProgress; try { await this.sagaFlow.invoke(); this.state = exports.SagaStates.Complete; } catch (e) { this.state = exports.SagaStates.InCompensation; this.invokeError = e; await this.runCompensationFlow(); throw new SagaExecutionFailed(e); } } async runCompensationFlow() { try { await this.sagaFlow.compensate(); this.state = exports.SagaStates.CompensationComplete; } catch (e) { this.state = exports.SagaStates.CompensationError; this.compensationError = e; throw new SagaCompensationFailed(e); } } } class Step { invocation; compensation; name; key; constructor(name = '') { this.name = name; } setInvocation(method) { this.invocation = method; } setCompensation(method) { this.compensation = method; } setKey(key) { this.key = key; } getKey() { return this.key; } async invoke(sagaContextWrapper) { if (this.invocation) { return this.invocation(sagaContextWrapper); } } async compensate(sagaContextWrapper) { if (this.compensation) { return this.compensation(sagaContextWrapper); } } getName() { return this.name; } } class SagaContext { steps; _context; currentStepIndex = -1; disabledSteps = new Set(); constructor(steps = [], _context = null) { this.steps = steps; this._context = _context; } set context(ctx) { this._context = ctx; } get context() { return this._context; } set currentStep(index) { this.currentStepIndex = index; } get currentStep() { return this.currentStepIndex; } disableStep(key) { this.disabledSteps.add(key); } enableStep(key) { this.disabledSteps.delete(key); } isStepDisabled(key) { return this.disabledSteps.has(key); } } class SagaContextMediator { sagaContext; constructor(sagaContext) { this.sagaContext = sagaContext; } disableStep(key) { return this.sagaContext.disableStep(key); } enableStep(key) { return this.sagaContext.enableStep(key); } isStepDisable(key) { return this.sagaContext.isStepDisabled(key); } getContext() { return this.sagaContext.context; } setContext(context) { return (this.sagaContext.context = context); } } class SagaFlow { steps; compensationSteps = []; context; sagaContextMediator; constructor(steps = [], ctx = new SagaContext()) { this.steps = steps; this.context = ctx; this.sagaContextMediator = new SagaContextMediator(this.context); } async invoke() { for (const step of this.steps) { if (!this.context.isStepDisabled(step.getKey())) { this.compensationSteps.push(step); await step.invoke(this.sagaContextMediator); } } } async compensate() { for (const step of this.compensationSteps.reverse()) { await step.compensate(this.sagaContextMediator); } } } class Factory { createSaga(steps, ctx) { return new Saga(this.createSagaFlow(steps, ctx)); } createSagaFlow(steps, ctx) { return new SagaFlow(steps, ctx); } createStep(name = '') { return new Step(name); } } class SagaBuilder { currentStep; steps = []; factory = new Factory(); context; setFactory(factory) { this.factory = factory; } step(name = '') { this.currentStep = this.factory.createStep(name); this.steps.push(this.currentStep); return this; } invoke(method) { this.currentStep.setInvocation(method); return this; } withCompensation(method) { this.currentStep.setCompensation(method); return this; } withKey(key) { this.currentStep.setKey(key); return this; } setContext(ctx) { this.context = ctx; return this; } build() { const ctx = new SagaContext(this.steps, this.context); return this.factory.createSaga(this.steps, ctx); } } exports.Saga = Saga; exports.SagaBuilder = SagaBuilder; exports.SagaCompensationFailed = SagaCompensationFailed; exports.SagaExecutionFailed = SagaExecutionFailed; }));