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

@alessiofrittoli/chain-functions

v2.5.0

Published

Functions chaining made easy

Readme

Chain Functions ⛓️

NPM Latest Version Coverage Status Socket Status NPM Monthly Downloads Dependencies

GitHub Sponsor

Functions chaining made easy

The Chain class provides a utility for managing and executing chains of functions. Each function in the chain can optionally invoke the next function, enabling a flexible and composable flow of execution. This is particularly useful for scenarios such as middleware processing, data transformations, or handling asynchronous operations in a structured manner.

Table of Contents


Getting started

Run the following command to start using chain-functions in your projects:

npm i @alessiofrittoli/chain-functions

or using pnpm

pnpm i @alessiofrittoli/chain-functions

API Reference

Chain class

A utility class for managing and executing chains of functions.

Static Methods
Chain.functions()

Recursively executes a chain of functions.

| Parameter | Type | Default | Description | | --------- | ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | chain | ChainFactory<T,U> | - | The chain of functions to execute. | | | | | This must be an array of functions (ChainLink<T>), where the last function is of type LastChainLink. See Types section for further informations about. | | index | number | 0 | (Optional) The starting index for execution. |


Type: T | U

The result of the chain execution, which matches the type of the chain's functions (T or U).

See Types section for further informations about.


Error if no function is found at the specified index.


import { Chain } from "@alessiofrittoli/chain-functions";
import type {
  ChainLink,
  LastChainLink,
  ChainFactory,
} from "@alessiofrittoli/chain-functions/types";

type ChainFunction = () => string;

const function1: ChainLink<ChainFunction> = (next) => () => `1-${next()}`;
const function2: ChainLink<ChainFunction> = (next) => () => `2-${next()}`;
const function3: LastChainLink<ChainFunction> = () => () => "end";

const chain: ChainFactory<ChainFunction> = [function1, function2, function3];
const result = Chain.functions(chain)();

console.log(result); // Output: '1-2-end'

Chain.isLast()

Determines if the given function is the last function in the chain needed to type cast the last function with LastChainLink<U>.

This method is primarily used internally by the Chain.functions() method to determine when the chain execution should terminate.

| Parameter | Type | Default | Description | | --------- | -------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | chain | ChainFactory<T,U> | - | The chain of functions. See Types section for further informations about. | | fn | ChainLink<T>\|LastChainLink<U> | - | The function to type cast. This can be either a regular chain link or the last chain link. See Types section for further informations about. | | index | number | 0 | (Optional) The current index of the function in the Chain.functions() recursion. |


Type: boolean

Returns true if the given function is the last function in the chain, false otherwise.


Types

ChainFunction

Represents any callable function that can be invoked as part of the chain.

This is used internally to type cast other types template parameters.

ChainLink<T extends ChainFunction = ChainFunction>

Represents a single link in a chain of functions.

| Parameter | Type | Description | | --------- | ---- | ----------------------------------------------------------------------- | | next | T | The next function in the chain. Its return type must be of type of T. |


Type: T

A function that can be invoked as part of the chain.


LastChainLink<T extends ChainFunction = ChainFunction>

Represents the last link in a chain of functions. Unlike ChainLink, it does not accept a next parameter.

Type: T

A function that can be invoked as the final step in the chain.


ChainFactory<T extends ChainFunction = ChainFunction, U extends ChainFunction = T>

Represents the complete chain of functions as an array.

Structure
  • Can contain any number of ChainLink<T> functions.
  • The last element in the array must be a LastChainLink<U>.

Key Features

  • Chain link functions are highly customizeable.
  • Chain link functions can be async functions.
  • The last chain link could return a different type (U) other than T from a standard ChainLink.

Examples

Importing the library

// importing the main `Chain` class
import { Chain } from "@alessiofrittoli/chain-functions";
// importing types
import type {
  ChainLink,
  LastChainLink,
  ChainFactory,
} from "@alessiofrittoli/chain-functions/types";
// define the chain link function type
type ChainFunction = () => string;

// declare chain link functions
const function1: ChainLink<ChainFunction> = (next) => () => `1-${next()}`;
const function2: ChainLink<ChainFunction> = (next) => () => `2-${next()}`;
// declare the last chain function
const function3: LastChainLink<ChainFunction> = () => () => "end";

// declare the chain array
const chain: ChainFactory<ChainFunction> = [function1, function2, function3];
// execute the chain array
const result = Chain.functions(chain)();

console.log(result); // Output: '1-2-end'

type ChainFunctionProps = {
  someProperty: string;
  firstFunction?: boolean;
  secondFunction?: boolean;
  thirdFunction?: boolean;
};
// define the chain link function type
type ChainFunction = (props: ChainFunctionProps) => ChainFunctionProps;

// declare chain link functions
const function1: ChainLink<ChainFunction> = (next) => (props) => {
  // edit properties
  props.someProperty = "Edited by 1st function";
  props.firstFunction = true;
  // call the next function in the chain
  return next(props);
};

const function2: ChainLink<ChainFunction> = (next) => (props) => {
  props.secondFunction = true;

  if (props.someProperty === "Edited by 1st function") {
    // stop chain execution if some condition is met.
    return props;
  }

  // call the next function in the chain
  return next(props);
};

// declare the last chain function
const function3: LastChainLink<ChainFunction> = () => (props) => {
  props.thirdFunction = true;
  return props;
};

// declare the chain array
const chain: ChainFactory<ChainFunction> = [function1, function2, function3];
// declare the initial state
const initialState: ChainFunctionProps = {
  someProperty: "Initial value",
  firstFunction: false,
  secondFunction: false,
  thirdFunction: false,
};
// execute the chain array with initial state
const result = Chain.functions(chain)(initialState);

console.log(result);
// Output: {
//  someProperty    : 'Edited by 1st function',
//  firstFunction   : true,
//  secondFunction  : true,
//  thirdFunction   : false,
// }

type ChainFunction = () => string;
type LastChainFunction = () => boolean;

const function1: ChainLink<ChainFunction> = (next) => () => `1-${next()}`;
const function2: ChainLink<ChainFunction> = (next) => () => `2-${next()}`;
const function3: LastChainLink<LastChainFunction> = () => () => true;

const chain: ChainFactory<ChainFunction, LastChainFunction> = [
  function1,
  function2,
  function3,
];
const result = Chain.functions(chain)();

console.log(result); // Outputs: '1-2-true'

type ChainFunction = () => string | Promise<string>;

const function1: ChainLink<ChainFunction> = (next) => async () => {
  // simulate a long task running
  await new Promise<void>((resolve) => setTimeout(resolve, 5000));
  return `1-${next()}`;
};
// this function is executed once `function1` Promise get resolved.
const function2: ChainLink<ChainFunction> = (next) => () => `2-${next()}`;
const function3: LastChainLink<ChainFunction> = () => () => "end";

const chain: ChainFactory<ChainFunction> = [function1, function2, function3];
const result = Chain.functions(chain)(); // `result` is now a promise

console.log(await result); // Outputs: '1-2-end'

// src/middleware.ts

import { NextMiddleware, NextResponse } from "next/server";
import { Chain } from "@alessiofrittoli/chain-functions";
import type {
  ChainFactory,
  ChainLink,
  LastChainLink,
} from "@alessiofrittoli/chain-functions/types";

type Middleware = ChainLink<NextMiddleware>;
type LastMiddleware = () => NextResponse<unknown>;
type MiddlewareFactory = ChainFactory<NextMiddleware, LastMiddleware>;

const middleware1: Middleware = (next) => async (request, event) => {
  const { nextUrl } = request;

  if (nextUrl === "...") {
    const rewriteUrl = "...";
    return NextResponse.rewrite(rewriteUrl);
  }

  return next(request, event);
};

const middleware2: Middleware = (next) => async (request, event) => {
  const response = await next(request, event);

  // do something with `response` returned by the next middleware.
  // ...

  return response;
};

// ensures `NextResponse.next()` is called if no one stops the chain.
const lastMiddleware: LastChainLink<LastMiddleware> = () => () =>
  NextResponse.next();

const middlewares: MiddlewareFactory = [
  middleware1,
  middleware2,
  lastMiddleware,
];

export const config = {
  matcher: [
    /**
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    "/((?!api/|_next|.*\\..*).*)",
  ],
};

// note that we do not execute the chain like in the previous examples since Next.js is responsible for the execution, providing `request` and `event` parameters to the `middleware` functions.
export default Chain.functions(middlewares);

Development

Install depenendencies

npm install

or using pnpm

pnpm i

Build the source code

Run the following command to test and build code for distribution.

pnpm build

ESLint

warnings / errors check.

pnpm lint

Jest

Run all the defined test suites by running the following:

# Run tests and watch file changes.
pnpm test:watch

# Run tests in a CI environment.
pnpm test:ci

Run tests with coverage.

An HTTP server is then started to serve coverage files from ./coverage folder.

⚠️ You may see a blank page the first time you run this command. Simply refresh the browser to see the updates.

test:coverage:serve

Contributing

Contributions are truly welcome!

Please refer to the Contributing Doc for more information on how to start contributing to this project.

Help keep this project up to date with GitHub Sponsor.

GitHub Sponsor


Security

If you believe you have found a security vulnerability, we encourage you to responsibly disclose this and NOT open a public issue. We will investigate all legitimate reports. Email [email protected] to disclose any security vulnerabilities.

Made with ☕