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

durabull

v0.2.7

Published

A durable workflow engine built on top of BullMQ and Redis

Downloads

22

Readme

🐃 Durabull

Durable, replay-safe workflow orchestration for TypeScript & Node.js — powered by BullMQ and Redis.

Generator-based workflows that combine the elegance of Laravel Workflow with the reliability of Temporal.


✨ Overview

Durabull brings generator-based workflow orchestration to TypeScript. Author workflows as async *execute() coroutines, orchestrate idempotent activities, and run them safely on top of BullMQ and Redis — with full deterministic replay guarantees.


🚀 Quick Start

1. Install dependencies

npm install durabull bullmq ioredis
# or
pnpm add durabull bullmq ioredis

2. Configure Durabull

import { Durabull } from 'durabull';

const durabull = new Durabull({
  redisUrl: process.env.REDIS_URL ?? 'redis://127.0.0.1:6379',
  queues: {
    workflow: 'durabull-workflow',
    activity: 'durabull-activity',
  },
  serializer: 'json',
  pruneAge: '30 days',
  // Optional: Queue routing for multi-tenant support
  // The context object is passed from WorkflowStub.make(WorkflowClass, { context: { ... } })
  queueRouter: (workflowName, context) => {
    const tenant = context?.tenantId;
    return tenant ? {
      workflow: `tenant-${tenant}-workflow`,
      activity: `tenant-${tenant}-activity`,
    } : undefined;
  },
  // Optional: Lifecycle hooks for observability
  lifecycleHooks: {
    workflow: {
      onStart: async (id, name, args) => console.log(`Workflow ${name} started`),
      onComplete: async (id, name, output) => console.log(`Workflow ${name} completed`),
      onFailed: async (id, name, error) => console.error(`Workflow ${name} failed`, error),
    },
  },
  // logger: optional structured logger with info/warn/error/debug methods
});

durabull.setActive();

3. Create an Activity

import { Activity } from 'durabull';

export class SayHello extends Activity<[string], string> {
  tries = 3;
  timeout = 5; // seconds

  async execute(name: string): Promise<string> {
    return `Hello, ${name}!`;
  }
}

4. Create a Workflow

import { Workflow, ActivityStub } from 'durabull';
import { SayHello } from './SayHello';

export class GreetingWorkflow extends Workflow<[string], string> {
  async *execute(name: string) {
    const message = yield ActivityStub.make(SayHello, name);
    return message;
  }
}

5. Execute the Workflow

import { WorkflowStub } from 'durabull';
import { GreetingWorkflow } from './GreetingWorkflow';

const wf = await WorkflowStub.make(GreetingWorkflow);
await wf.start('World');

console.log(await wf.output()); // "Hello, World!"

🪝 Webhooks

Expose workflows via HTTP using createWebhookRouter.

import { createWebhookRouter, TokenAuthStrategy } from 'durabull';
import { GreetingWorkflow } from './GreetingWorkflow';

const router = createWebhookRouter({
  authStrategy: new TokenAuthStrategy('my-secret-token'),
});

router.registerWebhookWorkflow('greeting', GreetingWorkflow);

// Use with Express/Fastify/etc.
app.post('/webhooks/*', async (req, res) => {
  const response = await router.handle({
    method: req.method,
    path: req.path,
    headers: req.headers,
    body: req.body,
  });
  res.status(response.statusCode).send(response.body);
});

🧠 Why Durabull?

| Capability | Description | | -------------------------------- | ------------------------------------------------------------------------ | | 🧩 Generator-based workflows | Use async *execute() and yield for deterministic orchestration. | | ⚙️ Idempotent activities | Encapsulate retries, backoff, and heartbeats for safe IO. | | ⏳ Deterministic replay | Rebuild workflow state from event history — crash-safe and restart-safe. | | 💬 Signals & Queries | Interact dynamically with live workflows via decorators. | | 🧵 Saga & Compensation | Built-in support for distributed transactions. | | ⏱ Timers & Await | Durable timers via WorkflowStub.timer() and WorkflowStub.await(). | | 🩺 Observability | Full event history, heartbeats, and pruning controls. | | 🪝 Webhooks | Trigger workflows and signals via HTTP with pluggable auth. |


💡 Core Concepts

🧭 Workflows

Extend Workflow and implement async *execute().

Use ActivityStub.make() or ActivityStub.all() to orchestrate sequential or parallel work.

import { Workflow, ActivityStub } from 'durabull';
import { ChargeCard, EmailReceipt } from './activities';

export class CheckoutWorkflow extends Workflow<[string, number], string> {
  async *execute(orderId, amount) {
    const chargeId = yield ActivityStub.make(ChargeCard, orderId, amount);
    yield ActivityStub.make(EmailReceipt, orderId, chargeId);
    return chargeId;
  }
}

⚡ Activities

Extend Activity and implement execute(). Configure retry logic and call this.heartbeat() for long-running jobs.

import { Activity, NonRetryableError } from 'durabull';

export class ChargeCard extends Activity<[string, number], string> {
  tries = 5;
  timeout = 15;

  async execute(orderId, amount) {
    const res = await paymentGateway.charge(orderId, amount);
    if (!res.ok) throw new NonRetryableError(res.error);
    return res.chargeId;
  }
}

🧩 Examples

Run any example from the /examples directory:

npm run example:greeting          # Basic workflow + activity

🧭 Documentation

  • 📘 DOCS.md — Full Durabull guide for TypeScript users
  • 💡 examples/ — Complete working examples
  • 🧪 tests/ — Jest test suite covering all core behaviors

🤝 Contributing

We welcome contributions!

  1. Fork the repository
  2. Create a new branch (feat/my-feature)
  3. Write tests and ensure npm test passes
  4. Lint code (npm run lint)
  5. Open a pull request with a clear description

License

Durabull is open-source software licensed under the MIT License. © 2025 Durabull contributors.