https://github.com/alessiofrittoli/chain-functions
Functions chaining made easy
https://github.com/alessiofrittoli/chain-functions
chaining
Last synced: about 2 months ago
JSON representation
Functions chaining made easy
- Host: GitHub
- URL: https://github.com/alessiofrittoli/chain-functions
- Owner: alessiofrittoli
- License: mit
- Created: 2024-12-21T19:25:42.000Z (6 months ago)
- Default Branch: master
- Last Pushed: 2025-03-21T11:50:09.000Z (3 months ago)
- Last Synced: 2025-03-28T16:21:19.182Z (2 months ago)
- Topics: chaining
- Language: TypeScript
- Homepage: https://npmjs.com/package/@alessiofrittoli/chain-functions
- Size: 305 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: license.md
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Chain Functions ⛓️
[![NPM Latest Version][version-badge]][npm-url] [![Coverage Status][coverage-badge]][coverage-url] [![Socket Status][socket-badge]][socket-url] [![NPM Monthly Downloads][downloads-badge]][npm-url] [![Dependencies][deps-badge]][deps-url]
[![GitHub Sponsor][sponsor-badge]][sponsor-url]
[version-badge]: https://img.shields.io/npm/v/%40alessiofrittoli%2Fchain-functions
[npm-url]: https://npmjs.org/package/%40alessiofrittoli%2Fchain-functions
[coverage-badge]: https://coveralls.io/repos/github/alessiofrittoli/chain-functions/badge.svg
[coverage-url]: https://coveralls.io/github/alessiofrittoli/chain-functions
[socket-badge]: https://socket.dev/api/badge/npm/package/@alessiofrittoli/chain-functions
[socket-url]: https://socket.dev/npm/package/@alessiofrittoli/chain-functions/overview
[downloads-badge]: https://img.shields.io/npm/dm/%40alessiofrittoli%2Fchain-functions.svg
[deps-badge]: https://img.shields.io/librariesio/release/npm/%40alessiofrittoli%2Fchain-functions
[deps-url]: https://libraries.io/npm/%40alessiofrittoli%2Fchain-functions[sponsor-badge]: https://img.shields.io/static/v1?label=Fund%20this%20package&message=%E2%9D%A4&logo=GitHub&color=%23DB61A2
[sponsor-url]: https://github.com/sponsors/alessiofrittoli## 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](#getting-started)
- [API Reference](#api-reference)
- [`Chain` class](#chain-class)
- [Types](#types)
- [Key Features](#key-features)
- [Examples](#examples)
- [Development](#development)
- [ESLint](#eslint)
- [Jest](#jest)
- [Contributing](#contributing)
- [Security](#security)
- [Credits](#made-with-)---
### Getting started
Run the following command to start using `chain-functions` in your projects:
```bash
npm i @alessiofrittoli/chain-functions
```or using `pnpm`
```bash
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.
Parameters
| Parameter | Type | Default | Description |
|-----------|----------------------|---------|-------------|
| `chain` | `ChainFactory` | - | The chain of functions to execute. This must be an array of functions (`ChainLink`), where the last function is of type LastChainLink. See [Types](#types) section for further informations about. |
| `index` | `number` | `0` | (Optional) The starting index for execution. |---
Returns
Type: `T | U`
The result of the chain execution, which matches the type of the chain's functions (`T` or `U`).
See [Types](#types) section for further informations about.
---
Throws
`Error` if no function is found at the specified index.
---
Example
```ts
import { Chain } from '@alessiofrittoli/chain-functions'
import type { ChainLink, LastChainLink, ChainFactory } from '@alessiofrittoli/chain-functions/types'type ChainFunction = () => string
const function1: ChainLink = next => () => `1-${ next() }`
const function2: ChainLink = next => () => `2-${ next() }`
const function3: LastChainLink = () => () => 'end'const chain: ChainFactory = [ 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`.
This method is primarily used internally by the `Chain.functions()` method to determine when the chain execution should terminate.
Parameters
| Parameter | Type | Default | Description |
|-----------|----------------------|---------|-------------|
| `chain` | `ChainFactory` | - | The chain of functions. See [Types](#types) section for further informations about. |
| `fn` | `ChainLink \| LastChainLink` | - | The function to type cast. This can be either a regular chain link or the last chain link. See [Types](#types) section for further informations about. |
| `index` | `number` | `0` | (Optional) The current index of the function in the `Chain.functions()` recursion. |---
Returns
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`
Represents a single link in a chain of functions.
Parameters
| Parameter | Type | Description |
|-----------|------|-------------------------------------------------------------------------|
| `next` | `T` | The next function in the chain. Its return type must be of type of `T`. |---
Returns
Type: `T`
A function that can be invoked as part of the chain.
---
##### `LastChainLink`
Represents the last link in a chain of functions. Unlike `ChainLink`, it does not accept a `next` parameter.
Returns
Type: `T`
A function that can be invoked as the final step in the chain.
---
##### `ChainFactory`
Represents the complete chain of functions as an array.
###### Structure
- Can contain any number of `ChainLink` functions.
- The last element in the array must be a `LastChainLink`.---
### 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
```ts
// importing the main `Chain` class
import { Chain } from '@alessiofrittoli/chain-functions'
// importing types
import type { ChainLink, LastChainLink, ChainFactory } from '@alessiofrittoli/chain-functions/types'
```Basic usage
```ts
// define the chain link function type
type ChainFunction = () => string// declare chain link functions
const function1: ChainLink = next => () => `1-${ next() }`
const function2: ChainLink = next => () => `2-${ next() }`
// declare the last chain function
const function3: LastChainLink = () => () => 'end'// declare the chain array
const chain: ChainFactory = [ function1, function2, function3 ]
// execute the chain array
const result = Chain.functions( chain )()console.log( result ) // Output: '1-2-end'
```---
Advance usage
```ts
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 = 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 = next => props => {
props.secondFunction = trueif ( 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 = () => props => {
props.thirdFunction = true
return props
}// declare the chain array
const chain: ChainFactory = [ 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,
// }
```---
`LastChainLink` with custom return type
```ts
type ChainFunction = () => string
type LastChainFunction = () => booleanconst function1: ChainLink = next => () => `1-${ next() }`
const function2: ChainLink = next => () => `2-${ next() }`
const function3: LastChainLink = () => () => trueconst chain: ChainFactory = [ function1, function2, function3 ]
const result = Chain.functions( chain )()console.log( result ) // Outputs: '1-2-true'
```---
`ChainLink` functions with promises
```ts
type ChainFunction = () => string | Promiseconst function1: ChainLink = next => async () => {
// simulate a long task running
await new Promise( resolve => setTimeout( resolve, 5000 ) )
return `1-${ next() }`
}
const function2: ChainLink = next => (
// this function is executed once `function1` Promise get resolved.
() => `2-${ next() }`
)
const function3: LastChainLink = () => () => 'end'const chain: ChainFactory = [ function1, function2, function3 ]
const result = Chain.functions( chain )() // `result` is now a promiseconsole.log( await result ) // Outputs: '1-2-end'
```---
Next.js middleware chain
```ts
// src/middleware.tsimport { NextMiddleware, NextResponse } from 'next/server'
import { Chain } from '@alessiofrittoli/chain-functions'
import type { ChainFactory, ChainLink, LastChainLink } from '@alessiofrittoli/chain-functions/types'type Middleware = ChainLink
type LastMiddleware = () => NextResponse
type MiddlewareFactory = ChainFactoryconst middleware1: Middleware = next => (
async ( request, event ) => {
const { nextUrl } = requestif ( 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 = () => () => 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
```bash
npm install
```or using `pnpm`
```bash
pnpm i
```#### Build the source code
Run the following command to test and build code for distribution.
```bash
pnpm build
```#### [ESLint](https://www.npmjs.com/package/eslint)
warnings / errors check.
```bash
pnpm lint
```#### [Jest](https://npmjs.com/package/jest)
Run all the defined test suites by running the following:
```bash
# Run tests and watch file changes.
pnpm test:watch# Run tests in a CI environment.
pnpm test:ci
```- See [`package.json`](./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.
```bash
test:coverage:serve
```---
### Contributing
Contributions are truly welcome!
Please refer to the [Contributing Doc](./CONTRIBUTING.md) for more information on how to start contributing to this project.
Help keep this project up to date with [GitHub Sponsor][sponsor-url].
[![GitHub Sponsor][sponsor-badge]][sponsor-url]
---
### 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 ☕
![]()
Alessio Frittoli
https://alessiofrittoli.it |
[email protected]