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

diflow-js

v1.0.1

Published

Dependency-injected flows for browser & Node

Downloads

27

Readme

diflow-js – Complete Library Guide

Dependency-injected flows & message bus helpers for TypeScript projects of any size


📚 Table of Contents

  1. Introduction
  2. Quick Start
  3. Core Concepts
  4. API Reference
  5. End-to-End Example
  6. Extending diflow-js
  7. FAQ

Introduction

diflow-js is built around the Command/Query Responsibility Segregation (CQRS) pattern and the principle of keeping all side-effects behind well-typed adapters.
The library ships a handful of minimal primitives—no decorators, no Reflect-metadata, <1 kB gzipped—that you can compose into rich, dependency-injected data flows that work the same way in the browser, Node, or serverless.

Primary Goal – Source Visibility
Whenever a module mutates state through a command, diflow-js lets you see every possible source of that change at a glance. Each command includes a strongly-typed, human-readable source field injected by CommandSource, so mutation origins stay explicit, traceable, and easy to audit in large codebases.

If you are new to CQRS or dependency-injected flows, start with the Quick Start; afterwards dive into the API Reference where every class and interface is documented with code samples.


Quick Start

Install:

npm i diflow-js

Create a command type and a matching response type:

// types.ts
export type CreateDeckPayload = { name: string };
export type CreateDeckResponse = { newDeckId: number };

export type CreateDeckCommand = {
  type: 'CreateDeck';
  source: string;   // filled automatically by CommandSource
  payload: CreateDeckPayload;
};

Wire a command source with a transport and register a handler:

import { CommandSource } from 'diflow-js';
import { LocalTransport } from 'diflow-js';
import { CreateDeckCommand, CreateDeckResponse } from './types';

const deckSource = new CommandSource<CreateDeckCommand, CreateDeckResponse>(
  'DeckModal',              // human-readable source name
  new LocalTransport(),     // process-local transport implementation
);

deckSource.registerHandler(async (cmd) => {
  console.log(`Saving deck '${cmd.payload.name}'…`);
  /* …persist to DB… */
  return { newDeckId: 42 };
});


```ts
// sync handler → immediate result
const id = deckSource.trigger({
  type: 'CreateDeck',
  payload: { name: 'Physics' },
});
console.log(id); // → 42

// async handler → Promise
const asyncId = await deckSource.trigger({
  type: 'CreateDeck',
  payload: { name: 'Chemistry' },
});
console.log(asyncId); // → 43

For queries you would use QueryBus + LocalQueryTransport, see the End-to-End Example.


Core Concepts

Command vs Query

  • Command – expresses an intention to change the system. Commands are sent via CommandSource and handled exactly once.
  • Query – asks the system for information without mutating it. Queries are sent via QueryBus and answered with a response.

Transports

A transport moves commands or queries between producers and handlers. diflow-js ships with in-process transports:

  • LocalTransport – for commands (sync and async)
  • LocalQueryTransport – for queries

You can implement your own transports (e.g. NATS, RabbitMQ, HTTP) by adhering to ITransport / IQueryTransport.


API Reference

Class: CommandSource

new CommandSource<TCommand, TResponse>(name, transport)

| Param | Type | Description | |-------|------|-------------| | name | string | Human-readable identifier automatically injected into every command (source field). | | transport | ITransport<TCommand, TResponse> | Transport implementation used to deliver commands. |

Methods

  • registerHandler(handler) – subscribe a single sync or async function that will process every incoming command.
  • trigger(command) – send a command (minus the source field, which is filled in for you) and either return the handler’s response immediately or return a Promise depending on whether the handler is synchronous or asynchronous.

Class: QueryBus

Analogous to CommandSource but for read-only queries.

  • registerHandler(handler) – subscribe a single query handler.
  • trigger(query) – send a query and await its response.

Interface: ITransport

type TCommandReturn<T> = T | Promise<T>;

interface ITransport<TCommand, TResponse> {
  send(cmd: TCommand): TCommandReturn<TResponse>;
  on(handler: (cmd: TCommand) => TCommandReturn<TResponse>): void;
}

Implement this interface to add new command transports.


Interface: IQueryTransport

Same contract as ITransport but for queries.


Class: LocalTransport

A zero-dependency, in-memory implementation of ITransport suited for tests, serverless functions, and monoliths.


Class: LocalQueryTransport

In-memory query counterpart of LocalTransport.


End-to-End Example

The repository ships two runnable examples:

node example/Example.ts            # command example
node example/GetDecksQuery.ts      # query example (to be implemented)

Below is a condensed version of the command example:

import { CommandSource, LocalTransport } from 'diflow-js';
import { TCreateDeckCommand, TCreateDeckResponse } from './CreateDeckCommand';

const source = new CommandSource<TCreateDeckCommand, TCreateDeckResponse>(
  'Modal',
  new LocalTransport(),
);

source.registerHandler(async (cmd) => {
  console.log(`[LOCAL HANDLER] ${cmd.payload.name}`);
  return { newDeckId: 777 };
});

const res = await source.trigger({
  type: 'CreateDeck',
  payload: { name: 'Physics' },
});
console.log(res.newDeckId);

Extending diflow-js

  • New transports – implement ITransport/IQueryTransport to plug in NATS, RabbitMQ, or WebSockets.
  • Middlewares – wrap your transport’s send / on methods to add logging, retries, or metrics.
  • Type utilities – use Omit<TCommand, 'source'> (as seen in CommandSource.trigger) to prevent callers from polluting the source field.

FAQ

Q: Can I register multiple handlers per command type?
Not at the moment—registerHandler stores exactly one handler. If you need fan-out, create a custom transport or emit events instead of commands.

Q: Where are the flows and adapters mentioned in the top-level README?
diflow-js’ long-term roadmap includes higher-level helpers (defineFlow, defineAdapter). The current NPM package provides the low-level message-bus primitives described above.

Q: Does it work in the browser?
Yes! All shipped code is ECMAScript-compliant and free of Node-specific APIs.


Happy coding – and may your flows be evergreen! ✨