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

pure-effect

v0.3.0

Published

A tiny, zero-dependency effect system for writing pure, testable JavaScript without mocks.

Readme

Pure Effect

Pure Effect is a tiny, zero-dependency effect system for writing pure, testable JavaScript without mocks.

It implements the "Functional Core, Imperative Shell" pattern, allowing you to decouple your business logic from external side effects like database calls or API requests. Instead of executing side effects immediately, your functions return Commands which are executed later by an interpreter.

Pure Effect comes with JSDoc type annotations, so it can be used with TypeScript as well.

Installation

npm install pure-effect

Usage

Here is a complete example of a User Registration flow.

import { Success, Failure, Command, effectPipe, runEffect } from 'pure-effect';

const validateRegistration = (input) => {
    if (!input.email.includes('@')) return Failure('Invalid email.');
    if (input.password.length < 8) return Failure('Password too short.');
    return Success(input);
};

// These functions do NOT run the DB call. They return a Command object.
// The 'next' function defines what happens with the result of the async call.
const findUser = (email) => {
    const cmdFindUser = () => db.findUser(email); // The work to do later
    const next = (user) => Success(user); // Wrap result in Success
    return Command(cmdFindUser, next);
};

const saveUser = (input) => {
    const cmdSaveUser = () => db.saveUser(input);
    const next = (saved) => Success(saved);
    return Command(cmdSaveUser, next);
};

const ensureEmailAvailable = (user) => {
    return user ? Failure('Email already in use.') : Success(true);
};

// The Pipeline uses arrow functions to capture 'input' from the scope where needed.
const registerUserFlow = (input) =>
    effectPipe(
        validateRegistration,
        () => findUser(input.email),
        ensureEmailAvailable,
        () => saveUser(input)
    )(input);

// The Imperative Shell
async function registerUser() {
    // logic is just a data structure until we pass it to runEffect
    const logic = registerUserFlow(input);

    // runEffect performs the actual async work
    const result = await runEffect(logic, 'registerUser');

    if (result.type === 'Success') {
        console.log('User created:', result.value);
    } else {
        console.error('Error:', result.error);
    }
}

Testing Without Mocks

The biggest benefit of Pure Effect is testability. Because registerUserFlow returns a data structure (a tree of objects) instead of running a Promise, you can test your logic without mocking the database.

// 1. Test Validation Failure
const badInput = { email: 'bad-email', password: '123' };
const result = registerUserFlow(badInput);

assert.deepEqual(result, Failure('Invalid email format.', badInput));
// ✅ Logic tested instantly, no async needed.

// 2. Test Flow Intent (Introspection)
const goodInput = { email: '[email protected]', password: 'password123' };
const step1 = registerUserFlow(goodInput);

// Check if the first thing the code does is try to find a user
assert.equal(step1.type, 'Command');
assert.equal(step1.cmd.name, 'cmdFindUser');

// Check if the next thing the code will do is to save a user
const step2 = step1.next(null);
assert.equal(step2.type, 'Command');
assert.equal(step2.cmd.name, 'cmdSaveUser');
// ✅ We verified the *intent* of the code without touching a real DB.

API Reference

Success(value)

Returns an object { type: 'Success', value }. Represents a successful computation.

Failure(error)

Returns an object { type: 'Failure', error, initialInput }. Represents a failed computation. Stops the pipeline immediately.

Command(cmdFn, nextFn)

Returns an object { type: 'Command', cmd, next }.

  • cmdFn: A function (sync or async) that performs the side effect.
  • nextFn: A function that receives the result of cmdFn and returns the next Effect (Success, Failure, or another Command).

effectPipe(...functions)

A combinator that runs functions in sequence. It automatically handles unpacking Success values and passing them to the next function. If a Failure occurs, the pipe stops.

runEffect(effect, flowName = '')

The interpreter. It takes an effect object, executes any nested Commands recursively using async/await, and returns the final Success or Failure. It also accepts an optional flowName that comes in handy for telemetry.


configureTelemetry(options)

A configuration function that injects observability, tracing, or logging interceptors into the runEffect interpreter. By default, Pure Effect executes with zero overhead. By providing onRun and onStep callbacks, you can wrap pipeline executions and individual commands (e.g., inside OpenTelemetry spans).

Please see opentelemetry-example.js to see a quick example.

  • onRun (effect, pipeline, flowName)
    Fires once per runEffect call. It wraps the entire workflow execution.

    • effect: The initial state of the effect tree (useful for extracting initialInput).
    • pipeline: The actual interpreter. You must await pipeline() inside this callback to run the logic.
    • flowName: The optional name of the workflow passed to runEffect.
  • onStep (name, type, op)
    Fires every time a Command is executed.

    • name: The name of the command function (e.g., cmdFindUser).
    • type: Effect type.
    • op: The actual side-effect function. You must await op() inside this callback and return its result.