JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 290
  • Score
    100M100P100Q98165F
  • License MIT

Workflow and State Machines for NestJS

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@jescrich/nestjs-workflow) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    NestJS Workflow & State Machine

    A flexible workflow engine built on top of NestJS framework, enabling developers to create, manage, and execute complex workflows in their Node.js applications.

    Features

    • Workflow Definitions: Define workflows using a simple, declarative syntax
    • State Management: Track and persist workflow states
    • Event-Driven Architecture: Built on NestJS's event system for flexible workflow triggers
    • Transition Rules: Configure complex transition conditions between workflow states
    • Extensible: Easily extend with custom actions, conditions, and triggers
    • TypeScript Support: Full TypeScript support with strong typing
    • Integration Friendly: Seamlessly integrates with existing NestJS applications

    Installation

    npm install @jescrich/nestjs-workflow

    Or using yarn:

    yarn add @jescrich/nestjs-workflow

    Quick Start

    Module Registration

    import { Module } from '@nestjs/common';
    import { WorkflowModule } from '@jescrich/nestjs-workflow';
    
    @Module({
      imports: [
        WorkflowModule.forRoot({
          storage: {
            type: 'memory'
          }
        }),
      ],
    })
    export class AppModule {}

    Define a Workflow

    import { WorkflowDefinition } from '@jescrich/nestjs-workflow';
    
    // Define your entity and state/event enums
    export enum OrderEvent {
      Create = 'order.create',
      Submit = 'order.submit',
      Update = 'order.update',
      Complete = 'order.complete',
      Fail = 'order.fail',
      Cancel = 'order.cancel',
    }
    
    export enum OrderStatus {
      Pending = 'pending',
      Processing = 'processing',
      Completed = 'completed',
      Failed = 'failed',
    }
    
    export class Order {
      urn: string;
      name: string;
      price: number;
      items: string[];
      status: OrderStatus;
    }
    
    // Create workflow definition
    const orderWorkflowDefinition = (entity: Order): WorkflowDefinition<Order, any, OrderEvent, OrderStatus> => {
      return {
        FinalStates: [OrderStatus.Completed, OrderStatus.Failed],
        IdleStates: [OrderStatus.Pending, OrderStatus.Processing, OrderStatus.Completed, OrderStatus.Failed],
        Transitions: [
          {
            from: OrderStatus.Pending,
            to: OrderStatus.Processing,
            event: OrderEvent.Submit,
            conditions: [(entity: Order, payload: any) => entity.price > 10],
          },
          {
            from: OrderStatus.Pending,
            to: OrderStatus.Pending,
            event: OrderEvent.Update,
            actions: [
              (entity: Order, payload: any) => {
                entity.price = payload.price;
                entity.items = payload.items;
                return Promise.resolve(entity);
              },
            ],
          },
          {
            from: OrderStatus.Processing,
            to: OrderStatus.Completed,
            event: OrderEvent.Complete,
          },
          {
            from: OrderStatus.Processing,
            to: OrderStatus.Failed,
            event: OrderEvent.Fail,
          },
        ],
        FailedState: OrderStatus.Failed,
        Entity: {
          new: () => new Order(),
          update: async (entity: Order, status: OrderStatus) => {
            entity.status = status;
            return entity;
          },
          load: async (urn: string) => {
            return entity;
          },
          status: (entity: Order) => entity.status,
          urn: function (entity: Order): string {
            return entity.urn;
          },
        },
      };
    };

    Use the Workflow in a Service

    import { Injectable } from '@nestjs/common';
    import { WorkflowService } from '@jescrich/nestjs-workflow';
    import { Order, OrderEvent, OrderStatus } from './order.model';
    
    @Injectable()
    export class OrderService {
      constructor(
        private readonly workflowService: WorkflowService<Order, any, OrderEvent, OrderStatus>,
      ) {}
      
      async createOrder() {
        const order = new Order();
        order.urn = 'urn:order:123';
        order.name = 'Order 123';
        order.price = 100;
        order.items = ['Item 1', 'Item 2', 'Item 3'];
        order.status = OrderStatus.Pending;
        
        // Initialize workflow with order entity
        const workflowDefinition = orderWorkflowDefinition(order);
        const workflow = new WorkflowService<Order, any, OrderEvent, OrderStatus>(workflowDefinition);
        
        return order;
      }
      
      async submitOrder(urn: string) {
        // Emit an event to trigger workflow transition
        const result = await this.workflowService.emit({ 
          urn: urn, 
          event: OrderEvent.Submit 
        });
        
        return result;
      }
      
      async updateOrder(urn: string, price: number, items: string[]) {
        // Emit an event with payload to update the order
        const result = await this.workflowService.emit({
          urn: urn,
          event: OrderEvent.Update,
          payload: {
            price: price,
            items: items,
          },
        });
        
        return result;
      }
    }

    Configuration Options

    The WorkflowModule.forRoot() method accepts the following configuration options:

    interface WorkflowModuleOptions {
      storage: {
        type: 'memory' | 'database';
        options?: any;
      };
    }

    Advanced Usage

    For more advanced usage, including custom actions, conditions, and event handling, please check the documentation.