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

@zero.sc/util

v0.0.4

Published

The small functions every service rewrites — measured, not guessed.

Readme


The problem this solves

Nobody needs another utility library. What this is actually about is that the same twenty lines get retyped in every codebase, and the retypings disagree with each other.

That claim is measured, not assumed. Across the services in this ecosystem: thirty-two had their own clamp, fifteen had truncate, twelve had uuid, eleven had sleep, seven had slugify, seven had retry. Then the copies were compared. One formatBytes preserved the sign of a negative; the rest folded it to "0 B", so every deletion delta rendered as zero. Half the uuid implementations were built from Math.random(). Most slugify implementations stripped with [^a-z0-9], which erases a Korean title completely and leaves an empty slug — and empty slugs collide.

None of those are hard problems. They are just problems that get solved slightly wrong, in private, over and over.

Install

npm install @zero.sc/util

No dependencies. Everything is a pure function or a plain closure, so a bundler keeps only what you import.

Sixty seconds

import { clamp, formatBytes, retry, slugify, sleep, truncate } from '@zero.sc/util';

clamp(5, 10, 0);            // 5   — inverted bounds are still a range
formatBytes(-2048);         // '-2.0 KiB'
slugify('Café Crème');      // 'cafe-creme' — and a Hangul title survives intact
truncate('👨‍👩‍👧‍👦 family', 3);  // no broken glyph at the cut

await retry(() => fetch(url).then((r) => r.json()), { attempts: 3 });
await sleep(200, signal);   // cancellable

What's inside

| Group | Exports | |-------|---------| | Numbers | clamp · normalize · lerp · round | | Format | formatBytes · formatDuration · formatNumber | | Text | slugify · truncate · graphemeLength · collapseWhitespace | | Time | sleep · timeAgo · toIso | | Functions | debounce · throttle · once | | Retry | retry · backoff · NonRetryable | | Identifiers | uuid · shortId |

What the copies got wrong

Each of these is a test in the suite, not a claim in a paragraph.

  • clamp with inverted bounds. Math.min(Math.max(v, min), max) returns min when the bounds are swapped, silently. And NaN compares false against everything, so it passes straight through to the screen.
  • round at the floating-point boundary. Math.round(1.005 * 100) / 100 is 1, not 1.01.
  • formatBytes and negatives. Also the unit: dividing by 1024 and labelling it KB is the wrong label — that is KiB. Both bases are available; they are never mixed.
  • slugify and non-Latin titles. Latin diacritics come off, ideographs and Hangul stay. Decomposing to NFD and forgetting to recompose splits Hangul into six jamo where there were two syllables — a string that looks similar and is not the same.
  • truncate and grapheme clusters. slice splits a surrogate pair and leaves ; it also breaks the joins in family emoji into a different picture.
  • sleep without cancellation. An aborted request leaves the timer alive to wake a path that was already abandoned. Inside a retry loop that leak accumulates.
  • debounce without cancel. A timer that fires after unmount calls setState on a component that is gone — and the usual next move is to reinvent an isMounted flag instead.
  • throttle dropping the last event. The position where scrolling stopped never lands, so the view freezes one frame behind.
  • retry without jitter. Every worker that failed together returns together; the retry becomes the second outage. And retrying a 400 three times cannot change the answer.
  • uuid from Math.random(). As an idempotency key or session identifier, predictability is the vulnerability.

Honest limits

  • retry is for idempotent calls. Retrying a payment or a send creates duplicates. The library cannot know which yours is, and it does not guess.
  • formatNumber and timeAgo follow the runtime locale when you do not name one. A server and a browser can then disagree. Pass locale where that matters.
  • truncate needs Intl.Segmenter for full grapheme correctness. Without it the fallback counts code points — surrogate pairs stay whole, joined emoji do not.
  • No collection helpers. No groupBy, no chunk, no deep clone. Those were not among the things being retyped, and adding them would make this the library nobody needs.
  • This is 0.0.x. The names are the part most likely to move.

Around it

| | | |---|---| | Docs | kit.zero.sc | | Events, which uses this for backoff | @zero.sc/events | | Calling services | @zero.sc/sdk | | Components | @zero.sc/ui | | Everything | @zero.sc |

License

MIT OR Zero License v1.0 — take whichever you prefer. Choosing MIT is enough; nothing further is required of you.

The Zero name, marks and logos are not covered — build anything you like with this code, just don't present it as a Zero product.

Copyright (c) 2026 Zero. Source Code begins at Zero.