npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zola_do/workflow-engine

v0.2.9

Published

State machine workflow converter for NestJS

Downloads

874

Readme

@zola_do/workflow-engine

npm version npm downloads License: ISC

State machine workflow converter for NestJS. Converts UI JSON (node/edge graph) to XState-compatible state machine definitions.

Overview

@zola_do/workflow-engine provides:

  • UI JSON Conversion — Transform React Flow / node-editor JSON to state machines
  • XState Compatibility — Output suitable for XState or similar libraries
  • Node Type Support — Handles start, parallel, and custom node types
  • State Transitions — Automatic edge-to-transition mapping

Installation

# Install individually
npm install @zola_do/workflow-engine

# Or via meta package
npm install @zola_do/nestjs-shared

Quick Start

1. Register Service

import { Module } from '@nestjs/common';
import { WorkflowEngineService } from '@zola_do/workflow-engine';

@Module({
  providers: [WorkflowEngineService],
  exports: [WorkflowEngineService],
})
export class WorkflowModule {}

2. Convert Workflow

import { Injectable } from '@nestjs/common';
import { WorkflowEngineService } from '@zola_do/workflow-engine';

@Injectable()
export class WorkflowController {
  constructor(private readonly workflowEngine: WorkflowEngineService) {}

  @Post('workflows/:id/compile')
  async compileWorkflow(@Param('id') id: string) {
    const uiJson = await this.workflowService.getUiJson(id);

    const stateMachine = this.workflowEngine.convertToStateMachine(uiJson);

    return {
      definition: stateMachine,
      xstateReady: true,
    };
  }
}

Workflow Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                    Workflow Conversion Flow                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  UI Editor (React Flow)                                             │
│  ┌─────────────────────────────────────────┐                        │
│  │  nodes: [                               │                        │
│  │    { id: '1', type: 'start', ... },    │                        │
│  │    { id: '2', type: 'task', ... },      │                        │
│  │    { id: '3', type: 'end', ... }        │                        │
│  │  ]                                      │                        │
│  │  edges: [                                │                        │
│  │    { source: '1', target: '2' },        │                        │
│  │    { source: '2', target: '3' }         │                        │
│  │  ]                                      │                        │
│  └────────────────────┬────────────────────┘                        │
│                       │                                               │
│                       │ convertToStateMachine()                       │
│                       ▼                                               │
│  ┌─────────────────────────────────────────┐                        │
│  │  State Machine Definition               │                        │
│  │                                         │                        │
│  │  {                                      │                        │
│  │    id: 'workflow-uuid',                 │                        │
│  │    initial: 'Start',                    │                        │
│  │    states: {                            │                        │
│  │      Start: {                          │                        │
│  │        on: { NEXT: 'Task' }            │                        │
│  │      },                                 │                        │
│  │      Task: {                            │                        │
│  │        on: { DONE: 'End' }              │                        │
│  │      },                                 │                        │
│  │      End: { type: 'final' }             │                        │
│  │    }                                    │                        │
│  │  }                                      │                        │
│  └─────────────────────────────────────────┘                        │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

Input Format

Node Types

interface WorkflowNode {
  id: string;
  type: 'start' | 'end' | 'task' | 'condition' | 'parallel' | string;
  data: {
    label?: string;
    [key: string]: any;
  };
  position?: { x: number; y: number };
}

Edge Types

interface WorkflowEdge {
  id?: string;
  source: string; // Source node ID
  target: string; // Target node ID
  label?: string; // Event/transition name
  data?: {
    condition?: string; // For conditional edges
    [key: string]: any;
  };
}

Example UI JSON

const uiJson = {
  nodes: [
    {
      id: 'node-1',
      type: 'start',
      data: { label: 'Start' },
      position: { x: 100, y: 100 },
    },
    {
      id: 'node-2',
      type: 'task',
      data: { label: 'Review Application' },
      position: { x: 300, y: 100 },
    },
    {
      id: 'node-3',
      type: 'condition',
      data: { label: 'Approved?' },
      position: { x: 500, y: 100 },
    },
    {
      id: 'node-4',
      type: 'task',
      data: { label: 'Send Approval Email' },
      position: { x: 700, y: 50 },
    },
    {
      id: 'node-5',
      type: 'task',
      data: { label: 'Send Rejection Email' },
      position: { x: 700, y: 150 },
    },
    {
      id: 'node-6',
      type: 'end',
      data: { label: 'End' },
      position: { x: 900, y: 100 },
    },
  ],
  edges: [
    { source: 'node-1', target: 'node-2' },
    { source: 'node-2', target: 'node-3' },
    { source: 'node-3', target: 'node-4', label: 'YES' },
    { source: 'node-3', target: 'node-5', label: 'NO' },
    { source: 'node-4', target: 'node-6' },
    { source: 'node-5', target: 'node-6' },
  ],
};

Output Format

State Machine Structure

interface StateMachine {
  id: string; // UUID
  initial: string; // Initial state name
  states: {
    [stateName: string]: {
      type?: 'final' | 'atomic';
      on?: {
        [event: string]: string | StateMachine;
      };
      meta?: {
        type?: string;
        [key: string]: any;
      };
      states?: {
        [nestedState: string]: any;
      };
    };
  };
}

Example Output

const stateMachine = {
  id: '550e8400-e29b-41d4-a716-446655440000',
  initial: 'Start',
  states: {
    Start: {
      on: { NEXT: 'Review Application' },
      meta: { type: 'start' },
    },
    'Review Application': {
      on: { NEXT: 'Approved?' },
      meta: { type: 'task' },
    },
    'Approved?': {
      on: {
        YES: 'Send Approval Email',
        NO: 'Send Rejection Email',
      },
      meta: { type: 'condition' },
    },
    'Send Approval Email': {
      on: { NEXT: 'End' },
      meta: { type: 'task' },
    },
    'Send Rejection Email': {
      on: { NEXT: 'End' },
      meta: { type: 'task' },
    },
    End: {
      type: 'final',
      meta: { type: 'end' },
    },
  },
};

Node Type Handling

Start Node

{
  id: 'node-1',
  type: 'start',
  data: { label: 'Start' },
}
// Becomes: initial state in output

End Node

{
  id: 'node-2',
  type: 'end',
  data: { label: 'End' },
}
// Becomes: { type: 'final' } state

Task Node

{
  id: 'node-3',
  type: 'task',
  data: { label: 'Process Order' },
}
// Becomes: { on: { NEXT: 'nextState' }, meta: { type: 'task' } }

Condition Node

{
  id: 'node-4',
  type: 'condition',
  data: { label: 'Is Valid?' },
}
// Becomes: { on: { YES: 'trueState', NO: 'falseState' } }

Parallel Node

{
  id: 'node-5',
  type: 'parallel',
  data: { label: 'Parallel Tasks' },
}
// Becomes: { type: 'parallel', states: { ... } }

Real-World Examples

Approval Workflow

const approvalWorkflow = {
  nodes: [
    { id: '1', type: 'start', data: { label: 'Submit' } },
    { id: '2', type: 'task', data: { label: 'Manager Review' } },
    { id: '3', type: 'condition', data: { label: 'Approved?' } },
    { id: '4', type: 'task', data: { label: 'Send Approval' } },
    { id: '5', type: 'task', data: { label: 'Notify Rejection' } },
    { id: '6', type: 'end', data: { label: 'Done' } },
  ],
  edges: [
    { source: '1', target: '2' },
    { source: '2', target: '3' },
    { source: '3', target: '4', label: 'YES' },
    { source: '3', target: '5', label: 'NO' },
    { source: '4', target: '6' },
    { source: '5', target: '6' },
  ],
};

const stateMachine = workflowEngine.convertToStateMachine(approvalWorkflow);

Multi-Step Form

const formWorkflow = {
  nodes: [
    { id: 's1', type: 'start', data: { label: 'Step 1' } },
    { id: 's2', type: 'task', data: { label: 'Personal Info' } },
    { id: 's3', type: 'task', data: { label: 'Address' } },
    { id: 's4', type: 'task', data: { label: 'Payment' } },
    { id: 's5', type: 'condition', data: { label: 'Valid?' } },
    { id: 's6', type: 'end', data: { label: 'Complete' } },
  ],
  edges: [
    { source: 's1', target: 's2' },
    { source: 's2', target: 's3' },
    { source: 's3', target: 's4' },
    { source: 's4', target: 's5' },
    { source: 's5', target: 's6', label: 'YES' },
    { source: 's5', target: 's2', label: 'NO' }, // Go back
  ],
};

XState Integration

The output is compatible with XState:

import { createMachine } from 'xstate';
import { interpret } from 'xstate';

const machine = createMachine(stateMachine);

const actor = interpret(machine).start();

actor.send({ type: 'NEXT' }); // Transition to next state
actor.send({ type: 'YES' }); // Transition on condition

Full XState Example

import { createMachine, interpret } from 'xstate';

@Controller('workflows')
export class WorkflowExecutionController {
  @Post(':id/execute')
  async executeWorkflow(@Param('id') id: string) {
    const definition = await this.workflowService.getCompiledDefinition(id);
    const machine = createMachine(definition);

    const actor = interpret(machine)
      .onTransition((state) => {
        console.log('Current state:', state.value);
      })
      .start();

    // Execute workflow steps
    const result = await this.executeSteps(actor, definition);

    return result;
  }

  private async executeSteps(actor: any, definition: any) {
    const states = Object.keys(definition.states);
    for (const state of states) {
      if (definition.states[state].type === 'final') {
        break;
      }
      await this.performStateAction(state);
      actor.send({ type: 'NEXT' });
    }
    return { completed: true };
  }
}

API Reference

Service

class WorkflowEngineService {
  convertToStateMachine(uiJson: UiJson): StateMachine;
}

Types

interface UiJson {
  nodes: WorkflowNode[];
  edges: WorkflowEdge[];
}

interface WorkflowNode {
  id: string;
  type: string;
  data: Record<string, any>;
  position?: { x: number; y: number };
}

interface WorkflowEdge {
  id?: string;
  source: string;
  target: string;
  label?: string;
  data?: Record<string, any>;
}

interface StateMachine {
  id: string;
  initial: string;
  states: Record<string, any>;
}

Troubleshooting

Q: Node label not used?

Ensure nodes have a data.label property:

// ❌ No label
{ id: '1', type: 'start' }

// ✅ With label
{ id: '1', type: 'start', data: { label: 'Start' } }

Q: Edge transitions not working?

Check edge labels match expected events:

// For condition nodes, edge labels become transition events
{ source: '3', target: '4', label: 'YES' }
// Triggers: state.on.YES

Q: Parallel nodes not handled?

Parallel nodes create nested state configurations:

{
  type: 'parallel',
  states: {
    trackA: { initial: 'a1', states: { a1: {}, a2: {} } },
    trackB: { initial: 'b1', states: { b1: {}, b2: {} } },
  },
}

Related Packages

None - standalone workflow package

License

ISC

Community