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

@totalsoft/process-manager

v1.1.2

Published

Process Manager Library

Readme

Process Manager Library

A TypeScript library for building stateful, event-driven processes using monadic composition patterns.

Installation

npm install @totalsoft/process-manager
# or
yarn add @totalsoft/process-manager

Overview

This library provides a functional approach to building complex event-driven workflows with:

  • Stateful computations that evolve over time
  • Event handling with pattern matching
  • Monadic composition for sequential and parallel workflows
  • Type-safe process definitions

Core Concepts

  • Process: A function that takes an instance ID and initial state, returns effects and a process state
  • ProcessState: Can be Succeeded, InProgress, or Failed
  • Events: External triggers that advance process execution
  • Effects: Side effects to be executed by the runtime

Simple Example

import { Process, Result, createRuntime } from '@totalsoft/process-manager';
import { createEventStore } from '@totalsoft/process-manager/event-store';

// Define your event types
type MyEvent = 
  | { type: 'UserLoggedIn', userId: string }
  | { type: 'DataReceived', data: number };

// Define your state
type MyState = {
  userId?: string;
  total: number;
};

// Create a simple process that waits for two events
const myProcess = Process.Do<string, MyState, MyEvent, string>(function* () {
  // Wait for user login
  const loginEvent = yield Process.waitForEvent<MyEvent, string>(event => {
    if (event.type === 'UserLoggedIn') {
      return Result.Ok(event.userId);
    }
    return null;
  });

  // Update state with user ID
  yield Process.setState<MyState>(state => ({
    ...state,
    userId: loginEvent
  }));

  // Wait for data
  const dataEvent = yield Process.waitForEvent<MyEvent, number>(event => {
    if (event.type === 'DataReceived') {
      return Result.Ok(event.data);
    }
    return null;
  });

  // Update state with total
  yield Process.setState<MyState>(state => ({
    ...state,
    total: state.total + dataEvent
  }));

  // Return final result
  return `User ${loginEvent} processed ${dataEvent} items`;
});

// Create event store and runtime
const eventStore = createEventStore({ type: "in-memory" });
const runtime = createRuntime(eventStore);

// Execute the process by handling events
const instanceId = 'process-1';
const stream = `myProcess-${instanceId}`;

// Send events to progress the process
await runtime.handleEvent(instanceId, 
  { type: 'UserLoggedIn', userId: 'user123' }, 
  stream, 
  myProcess
).execute();

await runtime.handleEvent(instanceId, 
  { type: 'DataReceived', data: 42 }, 
  stream, 
  myProcess
).execute();

// The process completes and returns the result
// Output: "User user123 processed 42 items"
// Final state: { userId: 'user123', total: 42 }

Process Composition

Sequential Composition with bind

Chain processes sequentially - the second process starts after the first completes:

const sequentialProcess = Process.bind(
  Process.waitForEvent<MyEvent, string>(e => 
    e.type === 'UserLoggedIn' ? Result.Ok(e.userId) : null
  ),
  (userId) => Process.bind(
    Process.waitForEvent<MyEvent, number>(e => 
      e.type === 'DataReceived' ? Result.Ok(e.data) : null
    ),
    (data) => Process.pure(`${userId}: ${data}`)
  )
);

Parallel Composition with both

Run two processes in parallel, waiting for both to complete:

const parallelProcess = Process.both(
  Process.waitForEvent<MyEvent, string>(e => 
    e.type === 'UserLoggedIn' ? Result.Ok(e.userId) : null
  ),
  Process.waitForEvent<MyEvent, number>(e => 
    e.type === 'DataReceived' ? Result.Ok(e.data) : null
  ),
  // State merger function
  (leftState, rightState) => ({
    userId: leftState.userId,
    total: rightState.total
  })
);

// Both processes execute concurrently
// Events can arrive in any order
// Result is a tuple: [userId, data]

Key Operations

| Operation | Purpose | Example | |-----------|---------|---------| | Process.Do | Generator-based sequential composition | Process.Do(function* () { ... }) | | Process.bind | Chain processes sequentially | bind(p1, (result) => p2) | | Process.both | Run processes in parallel | both(p1, p2, merger) | | Process.map | Transform result values | map(x => x * 2, process) | | Process.waitForEvent | Wait for specific event | waitForEvent(e => e.type === 'X' ? Ok(e) : null) | | Process.setState | Update process state | setState(s => ({ ...s, count: s.count + 1 })) | | Process.pure | Create completed process | pure(42) |

Effects

Effects represent side effects that need to be executed. The library provides an Effect class for managing asynchronous operations:

import { Effect } from '@totalsoft/process-manager';

// Create an effect from an async operation
const effect = Effect.fromAsync(async () => {
  const response = await fetch('https://api.example.com/data');
  return response.json();
});

// Execute the effect
const result = await effect.execute();

Custom Effects

The library also provides custom effects for common scenarios:

import { CustomEffects } from '@totalsoft/process-manager';

// HTTP GET request with event handling
const httpProcess = CustomEffects.httpGet(
  'https://api.example.com/users',
  (response) => ({ type: 'DataReceived', data: response }),
  (event) => event.type === 'DataReceived' ? Result.Ok(event.data) : null
);

Process Runtime

The process runtime manages the execution of processes and handles event delivery.

Creating a Runtime

import { createRuntime } from '@totalsoft/process-manager';
import { createEventStore } from '@totalsoft/process-manager/event-store';

// Create event store
const eventStore = createEventStore({ type: "in-memory" });

// Create runtime with event store
const runtime = createRuntime(eventStore);

Handling Events

// Handle events with automatic persistence and replay
const stream = `myProcess-${instanceId}`;
await runtime.handleEvent(instanceId, myEvent, stream, myProcess).execute();

The runtime automatically:

  • Persists events to the event store
  • Reconstructs process state from event history
  • Handles concurrency conflicts with automatic retry
  • Executes side effects

Event Store

The library provides an event store abstraction for persisting process events with support for event sourcing and optimistic concurrency control.

Event Store Options

Create an event store using the factory function:

import { createEventStore } from '@totalsoft/process-manager/event-store';

// In-memory store (for testing)
const inMemoryStore = createEventStore({ type: "in-memory" });

// SQL Server store (for production)
const sqlStore = createEventStore({ type: "mssql" });

// SQL Server with custom connection
import { ConnectionManager } from '@totalsoft/process-manager/infra/db-connection';
const connectionManager = new ConnectionManager({
  server: 'localhost',
  database: 'ProcessManager',
  user: 'sa',
  password: 'password'
});
const customSqlStore = createEventStore({ 
  type: "mssql", 
  connectionManager 
});

Event Store Features

  • Event Sourcing: All events are persisted in append-only streams
  • Optimistic Concurrency: Prevents lost updates with version-based concurrency control
  • Event Replay: Reconstruct process state by replaying event history
  • Stream Management: Create, read, and delete event streams
  • Multiple Backends: In-memory (testing) and SQL Server (production)

Event Store Interface

interface EventStore {
  // Append events to a stream
  appendEvent<TEvent>(
    stream: string,
    events: TEvent[],
    expectedVersion: number
  ): Effect<void>;

  // Load events from a stream
  loadEvents<TEvent>(
    stream: string,
    fromVersion?: number
  ): Effect<TEvent[]>;

  // Check if stream exists
  streamExists(stream: string): Effect<boolean>;

  // Delete a stream
  deleteStream(stream: string): Effect<void>;
}

Concurrency Control

The event store uses optimistic concurrency control to prevent lost updates:

try {
  // Append with expected version
  await eventStore.appendEvent(
    'user-123',
    [newEvent],
    expectedVersion // Current version of the stream
  ).execute();
} catch (error) {
  if (error instanceof ConcurrencyError) {
    // Handle conflict: reload, merge, retry
    console.log('Concurrent modification detected');
  }
}

The runtime automatically handles concurrency conflicts by retrying the operation.

Visualization

The library includes visual documentation:

Note: Download the HTML file and open it in your browser to view the interactive visualizations.