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

@rom98m/utils

v1.0.1

Published

Various TS utils

Downloads

129

Readme

Various TS utils

Installation

npm install --save @rom98m/utils

Usage

The utils are shipped as ESM files:

import { AsyncBatch } from "@rom98m/utils"

const batch = new AsyncBatch({ batchSize: 10 })
for (let i = 0; i <= 1e4; i++) {
  batch.add(() => fetch(`https://some.site/post?id=${i}`))
}
batch
  .run()
  .then(res => /* ... */)
  .catch(err => /* ... */)

Array utils

asArray(maybeArray: T | T[]) => T[]

Wraps given argument in array if needed.

/**
 * @param {T | T[]} maybeArray Value (might be arra y or a single one).
 * @returns {T[]}
 */
asArray(5)        // => [5]
asArray([5])      // => [5] // left as is

last<T>(array: T[]) => T

Returns the last element of given array.

/**
 * @param {T[]} array Given array.
 * @returns {T} Last element or undefined (in case of empty array).
 */
last([1, 2, 3])     // => 3
last("fdsa")        // => "a"

partition<T>(array: T[], partitionSize: number): T[][]

Split the array into partitions of given size.

/**
 * @param {T[]} array
 * @param {number} partitionSize
 * @returns {T[][]}
 */
partition([1, 2, 3, 4, 5], 2)       // => [[1, 2],  [3, 4],  [5]]

partitionBy<T>(array: T[], predicate: (item: T, index: number, arr: T[]) => boolean): [T[], T[]]

Splits the array into 2 buckets:

  • Left bucket contains all elements where the predicate() returned true;
  • Right bucket contains all elements where the predicate() returned false.
/**
 * @param {T[]} array Given array.
 * @param {(item: T, index: number, arr: T[]) => boolean} predicate A method that is applied to each element.
 *                                                                  If returns true, the element appears in the left bucket,
 *                                                                  otherwise in the right one.
 * @returns {[T[], T[]]}
 */
partitionBy([1, 2, 4, 5, 5], n => n % 2 === 0)      // => [[2, 4],  [1, 3, 5]]

Async utils

class AsyncBatch<T = any>

A container for async tasks that should be run in batches. For instance, there are 1000 requests to a remote server:

  • running them sequentially is too slow;
  • running them all at once can overwhelm/DoS the server.

But running (for instance) a sequence of 40 batches of 25 simultanious requests might be a good balance:

parallel(25_requests)
  .then(() => parallel(25_requests))
  .then(() => parallel(25_requests))
  // ...
  .then(() => parallel(25_requests))

Constructor

new AsyncBatch({ batchSize = 10 })

Creates a new container for async tasks.

/**
 * @param {object?} [param={}] Configuration of the task container.
 * @param {number} [param.batchSize=10] The size of the batch
 *                                      (number of task to run simultaneously).
 */
const batch = new AsyncBatch({ batchSize: 5 })
const batch = new AsyncBatch()

Instance fileds and methods

Status flags (readonly):

  • .isRunning: boolean
  • .isFinished: boolean
  • .totalTasks: number returns the total number of tasks added

.results: AsyncResult<T>[][]

Readonly.
Keeps the refetence to the array of results.
Useful when results are referenced outside the .run().then(res => {/* ... */}).

⚠️ Watch out!
The array is filled dynamically upon task execution (during the run() routine).
Make sure to reference the .results after the .run() is finished.

.add(...tasks: Array<() => Promise<T>>)

Add given tasks to the container.

/**
 * @param {...() => Promise<T>} tasks Tasks to add.
 * @returns {AsyncBatch} The instance of the AsyncBatch (`this`) to make the method chainable.
 * @throws When trying to add the task to a running or finished batch.
 */
batch
  .add(() => fetch(/* ... */))
  .add(() => fetch(/* ... */))

const tasks = postIds
  .map(id => () => fetch(`https://some.site/post?id=${id}`))
  //         ^^^^^ the function is returned!
batch.add(...tasks)

run(): Promise<AsyncResult<T>[][]>

Run all tasks in batches.

/**
 * @returns {Promise<AsyncResult<T>[][]>} Promise that resolves with all task results
 *                                       combined in sub-arrays (per batch).
 * @throws When trying to run the batch for the 2nd time.
 */
batch
  .run()
  .then(res => {
    res.flat() // because it's a 2D array of per-batch results
    res.forEach(({ error, result, batch, task }) => {
      console.info(`Batch ${batch}, task ${task}: ${error ? "failed" : "succeeded"}:`, error || result)
    })
  })
  .catch(err => /* ... */)

Auxiliary types

/**
 * A wrapper for the result of an async task.
 */
type AsyncResult<T> = {
  error: undefined | string | object
  result: T
  batch: number
  task: number
}

Math/Random utils

random(from: number, to?: number): number

Returns random int from the interval [min(from, to) .. max(from, to)).

/**
 * @param {number} from
 * @param {number?} [to] Optional. If omitted consider `random(0, from)`.
 * @returns {number}
 */
random(-5, 5)
random(5, -5)       // order does not matter
random(5)           // equal to `random(0, 5)`

pickRandom<T>(array: T[] | string): typeof array extends T[] ? T : string

Picks a random value from given array. Works against string as well, picking random character.

/**
 * @param {T[] | string} arrayLike
 * @returns {T | string}
 */
pickRandom([1, 2, 3, 4, 5])         // => some number
pickRandom("ACBDEF")                // => some char

withProbability(probability: number): boolean

Return true with given % of probability.

/**
 * @param {number} probability Probability percentage (0..100).
 * @returns {boolean}
 */
if (withProbability(25)) turn(withProbability(50) ? "left" : "right")
moveForward()

randomString(length: number, chars = dictionary.englishLower): string

Generates random string of given length.

/**
 * @param {number} length
 * @param {string?} chars Optional. Define the chars dictionary for generation.
*                         If omitted, lower English (`a`..`z`) letter are used.
 * @returns {string}
 */
randomString(5)
randomString(10, dictionary.englishLower + dictionary.englishUpper + dictionary.numbers)

Auxiliary export: dictionaries for randomString()

export const dictionary = {
  englishLower: "abcdefghijklmnopqrstuvwxyz",
  englishUpper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
  numbers: "1234567890",
}

Time utils

wait(ms: number): Promise<void>

Returns a Promise which resolves after given time.

/**
 * @param {number} ms Time (milliseconds).
 * @returns {Promise<void>}
 */
wait(100).then(() => console.log("After 100ms"))

debounce(fn: (...args: any[]) => void, intervalMs: number): typeof fn

Ensures that given function is invoked not too often. Think of eliminating "noisy" calls:

  interval
  :      :      :      :      :      :      :
--:------:------:------:------:------:------:--> time
  :      :      :      :      :      :      :
  A B  C : D E F:G     H    I :      :    J :    normal (noisy) function calls
  :      :      :      :      :      :      :
  A      C      F      H      I      :    J :    debounced calls
  :      :      :      :      :      :      :
/**
 * @param {(...args: any[]) => void} fn (Repeatable) action to debounce.
 * @param {number} intervalMs Minimal interval between consecutive calls.
 * @returns {(...args: any[]) => void} Debounced function.
 */
const debouncedSend((input) => {
  fetch({ url: "/api/...", method: "POST", data: { input } })
    .then(res => /* ... */)
    .catch(err => /* ... */)
}, 500)

<input type="text" onchange="debouncedSend(this.value)" />

Various/Unsorted utils

*times(n: number): Iterable<number>

Return an iterable that runs given number of times.

/**
 * @param {number} n
 * @returns {Generator<number> & { do((val: number) => void): void }}
 */
for (const x of times(5)) {
  // Do something
}

// Alternative syntax:
times(5).do(n => conosle.info(n))