
Security News
GitHub Actions Pricing Whiplash: Self-Hosted Actions Billing Change Postponed
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.
@alessiofrittoli/chain-functions
Advanced tools
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.
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
Chain classA utility class for managing and executing chains of functions.
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.
ChainFunctionRepresents 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.
ChainLink<T> functions.LastChainLink<U>.async functions.U) other than T from a standard ChainLink.// 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);
npm install
or using pnpm
pnpm i
Run the following command to test and build code for distribution.
pnpm build
warnings / errors check.
pnpm lint
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
package.json file scripts for more info.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
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.
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 security@alessiofrittoli.it to disclose any security vulnerabilities.
|
|
|
FAQs
Functions chaining made easy
We found that @alessiofrittoli/chain-functions demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.