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

redux-orchestrate

v1.3.0

Published

Redux middleware for coordinating actions and handling side effects.

Readme

Build Status codecov NPM Status NPM Status

Redux Orchestrate

Simple alternative to redux-saga or redux-observable.

Rather than using generators or Observables, most common operations are defined with a simple config object.

Installation

npm install --save redux-orchestrate

Usage

import { createStore, applyMiddleware } from 'redux'
import orchestrate from 'redux-orchestrate'
import reducer from './reducers'

const processManager = [
  // process manager logic
]

// pass rules directly to middleware
const store = createStore(reducer, applyMiddleware(orchestrate(processManager)))

Tranform

In case of action(s) X -> dispatch action(s) Y

const processManager = [
  {
    case: [
      SEND_MESSAGE_BUTTON_CLICKED,
      MESSAGE_INPUT_ENTER_KEY_PRESSED
    ],
    dispatch: ADD_MESSAGE
  }
]

Cascade

In case of action(s) X -> dispatch action(s) Y

In case of action(s) Y -> dispatch action(s) Z

const processManager = [
  {
    case: [
      SEND_MESSAGE_BUTTON_CLICKED,
      MESSAGE_INPUT_ENTER_KEY_PRESSED
    ],
    dispatch: ADD_MESSAGE
  },
  {
    case: ADD_MESSAGE,
    dispatch: [
      ANOTHER_ACTION,
      ONE_MORE
    ]
  }
]

Delay

In case of action(s) X -> wait for k miliseconds -> dispatch action(s) Y

const processManager = [
  {
    case: [
      SEND_MESSAGE_BUTTON_CLICKED,
      MESSAGE_INPUT_ENTER_KEY_PRESSED
    ],
    delay: 500
    dispatch: ADD_MESSAGE
  }
]

Debounce

In case of action(s) X -> debounce for k miliseconds -> dispatch action(s) Y

const processManager = [
  {
    case: [
      SEND_MESSAGE_BUTTON_CLICKED,
      MESSAGE_INPUT_ENTER_KEY_PRESSED
    ],
    debounce: 500
    dispatch: ADD_MESSAGE
  }
]

Dispatch Logic

In case of action(s) X -> perform logic using orignal action and state -> dispatch action(s) Y

const processManager = [
  {
    case: [
      SEND_MESSAGE_BUTTON_CLICKED,
      MESSAGE_INPUT_ENTER_KEY_PRESSED
    ],
    dispatch: (action, state) => {
      if (state.canAddMessage) {
        return { ...action, type: ADD_MESSAGE }
      }
    }
  }
]

Ajax Request

In case of action(s) X -> make an ajax request ->

-> in case of success -> dispatch Y

-> in case of failure -> dispatch Z

const processManager = [
  {
    case: ADD_MESSAGE,
    get: {
      url: 'https://server.com',
      onSuccess: MESSAGE_SENT,
      onFail: MESSAGE_SENDING_ERROR,
    }
  }
]

post request using action.payload:

const processManager = [
  {
    case: ADD_MESSAGE,
    post: action => ({
      url: 'https://server.com/new',
      data: {
        content: action.payload
      },
      onSuccess: { type: MESSAGE_SENT, id: action.id },
      onFail: { type: MESSAGE_SENDING_ERROR, id: action.id }
    })
  }
]

making use od res and err response object from onSuccess and onFail:

const processManager = [
  {
    case: ADD_MESSAGE,
    post: action => ({
      url: 'https://server.com/new',
      data: {
        content: action.payload
      },
      onSuccess: res => ({
        type: MESSAGE_SENT,
        dataFromRes: res.data
        id: action.id
      }),
      onFail: err => ({
        type: MESSAGE_SENDING_ERROR,
        errorMessage: err.message
        id: action.id
      })
    })
  }
]

Request Cancelation

In case of action(s) X -> make an ajax request ->

in case of action(s) Y -> cancel ajax request

const processManager = [
  {
    case: ADD_MESSAGE,
    post: {
      url: `http://server.com`,
      cancelWhen: [
        STOP_SENDING
      ],
      onSuccess: MESSAGE_SENT
    }
  }
]

Autocomplete example

Now let's say we need to implement an autocomplete feature. In short, these are feature requirements:

  • Any time the user changes an input field, make a network request
  • If network request is not completed, but user had changed the input field again, cancel the previous request
  • Don't spam "suggestion server". Make the request when user had stopped typing, by debouncing its events.
const processManager = [
  {
    case: SEARCH_INPUT_CHARACTER_ENTERED,   // in case user has changed an input field
    debounce: 500,                          // wait for user to stop typing (debouncing by 500ms)
    get: action => ({
      url: `http://s.co/${action.payload}`, // make a get request to a "suggestion server"
      cancelWhen: [
        SEARCH_INPUT_CHARACTER_ENTERED,     // in case user starts typing again, cancel request
        SEARCH_INPUT_BLURED                 // in case user is not using an input field, cancel request
      ],
      onSuccess: res => ({
        type: AUTOCOMPLETE_SUGGESTION,      // if query was successful, dispatch an event
        payload: res.data
      })
    })
  }
]

Cascade - more complex example

const processManager = [
  {
    case: ADD_MESSAGE,
    post: (action, state) => ({
      url: 'https://chat.app.com/new',
      data: {
        content: action.payload
      },
      onSuccess: () => {
        if (state.canMarkAsSent) {
          return { ...action, type: MESSAGE_SENT }
        } else {
          return { ...action, type: FOR_SOME_REASON_THIS_IS_DISPATHCED }
        }
       }
    })
  },
  {
    case: FOR_SOME_REASON_THIS_IS_DISPATHCED
    post: (action, state) => ({
      url: 'https://what.is.happening',
      data: {
        content: action.payload
      },
      onSuccess: MESSAGE_SENT,
      onFail: MESSAGE_SENDING_ERROR
    })
  }
]

Dynamically added rules

Sometimes you may wish to add rules dynamically after middleware has been applied:

const processManager = [
  // initial rules
]
const orchestrateMiddleware = orchestrate(processManager)

const store = createStore(reducer, applyMiddleware(orchestrateMiddleware))
orchestrateMiddleware.addRules([
  // additional rules added dynamically
])

FAQ

Ok, but what about other kind of async operations?

This middleware is not an attempt to solve all your problems. If you need to handle more complex async operations which are better solved by some other tools (generators, observables), then you should use middlewares that supports them or define your own (it's not that hard).

Also, don't forget that you can combine multiple middlewares.

Note: additional operators could be supported in the future (but only if they don't significantly complicate the existing API).

Can I use custom headers or similar options for ajax requests?

Yes.

redux-orchestrate uses axios for making network requests.

All options passed in request (or aliases like post, get, etc.) is mapped with axios request config

What is a process manager?

Config object which defines the middleware logic is here reffered as "process manager".

This term is borrowed from CQRS/ES terminology where the same concept is also referred as "saga" - "a piece of code that coordinates and routes messages between bounded contexts and aggregates".

Why "orchestrate"?

Term "orchestrate" is used to reffer to a single, central point for coordinating multiple entities and making them less coupled.

This is a broad term, usually used in service-oriented arhitectures and compared with its opossite concept - "choreography"

API

Applying middleware:

orchestrate(processManager, options)

Process Manager

The main array of objects defining action coordination.

const processManager = [
  {
    case: [
      IN_CASE_THIS_EVENT_IS_DISPATCHED,
      OR_THIS_EVENT
    ],
    dispatch: DISPATCH_THAT_EVENT,
    debounce: 500,
    delay: 500,
    request: {
      method: 'get',
      url: 'url',
      cancelWhen: [
        IF_REQUEST_IS_PENDING_CANCEL_IT_WHEN_THIS_IS_DISPATCHED,
        OR_THIS
      ],
      onSuccess: DISPATCH_THIS_IF_AJAX_SUCCEDED
      onFail: DISPATCH_THIS_IF_AJAX_FAILED,
      // other axios props
    }
  }
]

Case

Proceed with dispatching or making a request if action type is matched with the one defined in case.

{
  // string
  case: 'EVENT',
  // array
  case: [
    'EVENT_1',
    'EVENT_2'
  ],
  // function
  case: (action, state) => `PREFIX_${action.type}`
}

Dispatch

Synchronously dispatch an action

{
  // string
  dispatch: 'EVENT', // dispatch action results in { type: 'EVENT' }
  // function
  dispatch: (action, state) => ({ type: `PREFIX_${action.type}` })
}

Request

Make an ajax request using axios library.

{
  // object
  request: {
    method: 'get',
    url: 'url',
    cancelWhen: [
      'IF_REQUEST_IS_PENDING_CANCEL_IT_WHEN_THIS_IS_DISPATCHED',
      'OR_THIS'
    ],
    onSuccess: 'DISPATCH_THIS_IF_AJAX_SUCCEDED'
    onFail: 'DISPATCH_THIS_IF_AJAX_FAILED',
    // other axios props
  },
  // function
  request: (action, state) => { ... }
}

For convenience aliases have been provided for all supported request methods:

{
  post: { ... },
  get: { ... },
  del: { ... },
  head: { ... },
  options: { ... },
  put: { ... }
  patch: { ... }
}

Debounce

Dispatch event or make a request, after an action is debounced

{
  // integer
  debounce: 500, // in ms
  // function
  debounce: (action, state) => state.debounceConfig
}

Delay

Dispatch event or make a request, after an action is delayed

{
  // integer
  delay: 500, // in ms
  // function
  delay: (action, state) => state.delayConfig
}

Options

Validate

If defined, no events will reach a reducer unless it's defined in a process manager.

{
  validate: false // default
}