async-toolbelt
v0.1.8
Published
- [async-toolbelt](#async-toolbelt) - [Key Features](#key-features) - [Installation](#installation) - [Usage Examples](#usage-examples) - [1. Composing Functions with `pipe`](#1-composing-functions-with-pipe) - [2. Adding Side Effects with `
Downloads
849
Readme
async-toolbelt
A lightweight utility library for composing, transforming, and augmenting functions (synchronous or asynchronous) in JavaScript/TypeScript. Provides a unified API, full TypeScript support (including up to 18 chained transformations), and automatically selects ESM or CJS based on your environment.
Key Features
- Composable Pipelines – Chain any combination of sync or async functions with
pipe
. - Side Effects – Insert logging or debugging without altering results using
tap
. - Function Wrapping – Extend or augment one or more functions with
wrapWithTap
. - Mapping & Transformation – Apply a single transformation using
map
. - TypeScript Ready – Includes comprehensive type definitions and overloads.
Installation
Use your preferred package manager:
Bun:
bun add --exact async-toolbelt
pnpm:
pnpm add --save-exact async-toolbelt
Yarn:
yarn add --exact async-toolbelt
npm:
npm install --save-exact async-toolbelt
Usage Examples
While these functions do not have an “Async” suffix, they handle both synchronous and asynchronous code seamlessly. Any sync function is wrapped in a promise under the hood.
1. Composing Functions with pipe
import { pipe } from 'async-toolbelt';
// Async example
const addOneAsync = async (n: number) => n + 1;
const squareAsync = async (n: number) => n * n;
// Sync example
const addOneSync = (n: number) => n + 1;
const squareSync = (n: number) => n * n;
// Async pipeline
pipe(2, addOneAsync, squareAsync).then((result) => {
console.log(result); // 9
});
// Mixed pipeline
pipe(2, addOneSync, squareAsync).then((result) => {
console.log(result); // 9
});
// Fully sync pipeline (still returns a promise)
pipe(2, addOneSync, squareSync).then((result) => {
console.log(result); // 9
});
2. Adding Side Effects with tap
import { tap } from 'async-toolbelt';
const logValue = (val: unknown) => {
console.log('Received:', val);
};
tap(logValue)('Hello World');
// Logs: "Received: Hello World"
// Returns a Promise resolved to "Hello World"
3. Wrapping Functions with wrapWithTap
import { wrapWithTap } from 'async-toolbelt';
// Wrap multiple functions at once
const wrappedFunctions = wrapWithTap([
async (val: string) => console.log('First effect:', val),
(val: string) => console.log('Second effect:', val.toUpperCase()),
]);
// Each function logs and then returns the original input
wrappedFunctions[0]('hello'); // Logs "First effect: hello"
wrappedFunctions[1]('hello'); // Logs "Second effect: HELLO"
4. Transforming Values with map
import { map } from 'async-toolbelt';
const doubleAsync = async (n: number) => n * 2;
const doubleSync = (n: number) => n * 2;
// Using an async function
map(doubleAsync)(3).then((res) => {
console.log('Async result:', res); // 6
});
// Using a sync function
map(doubleSync)(5).then((res) => {
console.log('Sync result:', res); // 10
});
5. Long Pipeline Example
Here’s a more declarative example illustrating how pipe
can simplify a multi-step workflow. In real-world scenarios, you might fetch data, transform it, log progress, and handle both sync and async steps without deeply nested callbacks.
import { pipe, tap, map } from 'async-toolbelt';
// Mock async function to fetch user data
const fetchUser = async (id: number) => {
// Simulate network delay
await new Promise((resolve) => setTimeout(resolve, 100));
return { id, name: 'Alice', email: '[email protected]' };
};
// Convert user data to a string (sync function)
const userToString = (user: { id: number; name: string; email: string }) =>
`[User #${user.id}]: ${user.name} <${user.email}>`;
// Log a message (sync or async)
const logMessage = (msg: string) => {
console.log(`[LOG] ${msg}`);
};
// Asynchronously send notification
const sendNotification = async (message: string) => {
await new Promise((resolve) => setTimeout(resolve, 50));
return `Notification sent with message: "${message}"`;
};
// Build a pipeline that fetches a user, logs it, transforms to string, sends notification
pipe(
1, // userId
fetchUser, // Step 1: fetch user data (async)
tap((u) => console.log('Fetched user:', u)), // Step 2: side-effect
userToString, // Step 3: convert to string (sync)
tap(logMessage), // Step 4: additional side-effect
sendNotification // Step 5: final async operation
).then((result) => {
console.log(result);
// Logs something like:
// Fetched user: { id: 1, name: 'Alice', ...}
// [LOG] [User #1]: Alice <[email protected]>
// Notification sent with message: "[User #1]: Alice <[email protected]>"
});
In this pipeline:
fetchUser
retrieves user data (async).tap(...)
logs the raw user.userToString
transforms it to a string (sync).- Another
tap(logMessage)
logs the user string. sendNotification
finalizes the process (async).
The promise-based design keeps everything composable, flat, and declarative without nested callbacks or complicated promise chains.
API Reference
pipe(input, ...fns)
Executes a sequence of sync or async functions on an initial value. Supports up to 18 functions.
- Parameters
input
: initial value...fns
: array of sync or async functions
- Returns
APromise
resolving to the final result
tap(fn)
Inserts a side effect (e.g., logging) without changing the input.
- Parameters
fn
: a sync or async function that receives the input
- Returns
A function that returns the original input as aPromise
wrapWithTap(fns)
Wraps an array of functions so each function executes a side effect while returning the original input.
- Parameters
fns
: an array of sync or async functions
- Returns
An array of tapped functions
map(fn)
Transforms an input with a single sync or async function.
- Parameters
fn
: a function taking the input and returning a new value
- Returns
A function that, given an input, returns aPromise
of the transformed value
Development (Bun)
This project uses Bun as the development environment.
Clone the repository:
git clone https://github.com/yourusername/async-toolbelt.git cd async-toolbelt
Install dependencies (via Bun):
bun install
Build the project:
bun run build
Run tests:
bun test
Contributing
Contributions are welcome. Please open issues or submit pull requests for features or improvements.
License
Distributed under the MIT License.
Support
If you find this library valuable, please consider starring the repository on GitHub.