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 🙏

© 2025 – Pkg Stats / Ryan Hefner

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:

  1. fetchUser retrieves user data (async).
  2. tap(...) logs the raw user.
  3. userToString transforms it to a string (sync).
  4. Another tap(logMessage) logs the user string.
  5. 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
    A Promise 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 a Promise

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 a Promise of the transformed value

Development (Bun)

This project uses Bun as the development environment.

  1. Clone the repository:

    git clone https://github.com/yourusername/async-toolbelt.git
    cd async-toolbelt
  2. Install dependencies (via Bun):

    bun install
  3. Build the project:

    bun run build
  4. 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.