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

@andyrmitchell/utils

v0.32.1

Published

A collection of small helpful functions.

Readme

Utils

A collection of small helpful functions.

Compatibility

Requires zod ^4 as a peer dependency (since v0.28.0). Projects still on zod 3 must upgrade to zod 4 before using this version.

Breaking changes in v0.28.0 (Zod 4 migration)

  • Peer dependency zod moved from ^3.23.8 to ^4.1.8.
  • The ./prettify-zod-v3-error export is renamed to ./prettify-zod-error; its functions prettifyZod3Error / prettifyZod3ErrorAsArray / prettifyZod3ErrorAsJson are renamed to prettifyZodError / prettifyZodErrorAsArray / prettifyZodErrorAsJson and now wrap Zod's native prettifyError. Issue message text follows Zod 4 defaults (paths are unchanged).

Rate limiting (fetch-pacer)

FetchPacer and FetchPacerMultiClient pace requests against a quota and back off when a service refuses one.

Reacting to a refusal

  • Retry-After is honoured. When a service names how long to wait — either as a count of seconds or as the moment to resume — that period is used instead of the calculated guess, which can only be shorter. Read it yourself with parseRetryAfterMs(headerValue).

  • treat_as_back_off identifies a refusal that does not announce itself as one. Some services reply 403 and explain in the body that the real reason was speed, which is indistinguishable from a permission failure without looking:

    treat_as_back_off: async (response) => {
        if( response.status!==403 ) return false;
        const body = await response.json();
        return body?.error?.status==='RESOURCE_EXHAUSTED';
    }

    It is given a clone, so reading the body here does not consume the caller's copy, and the response keeps the status the service actually sent. Return {minimumMs} to set a floor.

  • logBackOff(minimumMs?, clientId?) reports a refusal the pacer never saw. A batch call spends the cost of many requests at once and comes back a success even when parts inside it were rate limited, leaving nothing to pace on. getActiveBackOffForMs(clientId?) reads back how long requests are currently being held.

Keeping a retried request valid

fetch(url, options, ...) accepts a function for options, called afresh for every attempt. A retry can land minutes after the first try, so anything that goes stale in the meantime — an access token, an abort signal that has already fired — should be built there rather than captured up front:

pacer.fetch(url, async () => ({
    headers: { Authorization: `Bearer ${await getAccessToken()}` },
    signal: AbortSignal.timeout(30_000)
}));

Jitter

back_off_calculation.jitter varies each calculated pause by up to a fifth either side of its length, so clients that back off together do not return together. Because it runs in both directions, the average wait across many clients is still the calculated one, and a pause at max_single_back_off_ms still reaches that ceiling. A period the service named itself is never varied.

Previously jitter only ever lengthened a pause, and inverted to shorten it once the pause reached max_single_back_off_ms — so a run at the ceiling could wait up to 40% less than configured. Anything relying on those exact timings will now see different ones.

Building

  • npm run build_release Will run the lint (failing if it gives any warnings), build it, and deploy with np