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

@alphinex/utils

v1.1.0

Published

Pure, framework-agnostic utility functions (date, number, string, array, object, class merging).

Readme

@alphinex/utils

Pure, framework-agnostic utility functions used across the platform: class name merging, number/date formatting, string casing, array/object helpers, and timing helpers. Nothing here depends on React or any other package — safe to import from app code, server code, or any other package in the repo.

cn

Merges class names via clsx, then resolves conflicting Tailwind utilities with tailwind-merge so the last one wins instead of both ending up in the class list. Every @alphinex/ui component's className prop is merged through this:

import { cn } from "@alphinex/utils";

cn("px-2 py-1", isActive && "bg-accent", "px-4"); // -> "py-1 bg-accent px-4" (last px-* wins)

formatCurrency / formatNumber / formatPercent

Thin wrappers around Intl.NumberFormat, defaulting locale to "en-US". All other Intl.NumberFormat options pass through:

import { formatCurrency, formatNumber, formatPercent } from "@alphinex/utils";

formatCurrency(1234.5); // "$1,234.50"
formatCurrency(1234.5, { locale: "en-GB", currency: "GBP" }); // "£1,234.50"
formatNumber(1234567, { maximumFractionDigits: 0 }); // "1,234,567"
formatPercent(0.256, { maximumFractionDigits: 1 }); // "25.6%"

formatDate / formatRelativeTime

formatDate wraps Intl.DateTimeFormat, accepting a Date, epoch number, or ISO string. It defaults to { year: "numeric", month: "short", day: "numeric" }, and merges any options you pass on top of those defaults rather than replacing them:

import { formatDate, formatRelativeTime } from "@alphinex/utils";

formatDate(new Date()); // "Aug 5, 2026"
formatDate(new Date(), { weekday: "long" }); // "Wednesday, Aug 5, 2026" — weekday is added, not swapped in

Known limitation: because year/month/day are always merged in underneath your own options, you can't use formatDate to produce a time-only or otherwise fully custom format that omits them — passing e.g. { hour: "2-digit", minute: "2-digit" } still gets the date parts prepended. For time-only or fully custom output, call Intl.DateTimeFormat directly instead of formatDate.

formatRelativeTime formats a date against "now" using Intl.RelativeTimeFormat, auto-selecting the largest sensible unit:

const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
formatRelativeTime(fiveMinutesAgo); // "5 minutes ago"

String helpers

capitalize, toCamelCase, toKebabCase, toPascalCase, truncate. The case converters share a word-splitter that handles camelCase, snake_case, and kebab-case input alike:

import { capitalize, toCamelCase, toKebabCase, toPascalCase, truncate } from "@alphinex/utils";

capitalize("hello world"); // "Hello world"
toCamelCase("user_first_name"); // "userFirstName"
toKebabCase("UserFirstName"); // "user-first-name"
toPascalCase("user-first-name"); // "UserFirstName"
truncate("A very long description", 12); // "A very lo…"

Array helpers

chunk, groupBy, unique, uniqueBy:

import { chunk, groupBy, unique, uniqueBy } from "@alphinex/utils";

chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
unique([1, 2, 2, 3]); // [1, 2, 3]
uniqueBy(users, (u) => u.email); // first user per unique email
groupBy(orders, (o) => o.status); // { pending: [...], shipped: [...] }

chunk throws a RangeError if size <= 0.

Object helpers

isEmptyObject, omit, pick — plain, type-safe object shaping:

import { isEmptyObject, omit, pick } from "@alphinex/utils";

pick(user, ["id", "email"]); // { id, email }
omit(user, ["password"]); // everything except password
isEmptyObject({}); // true

Timing helpers

debounce and throttle, both returning a wrapped function with a .cancel() method (Cancelable):

import { debounce, throttle } from "@alphinex/utils";

const search = debounce((query: string) => runSearch(query), 300);
input.addEventListener("input", (e) => search((e.target as HTMLInputElement).value));
// search.cancel() to abort a pending call, e.g. on unmount

const onScroll = throttle(() => updateScrollPosition(), 100);
window.addEventListener("scroll", onScroll);

debounce delays invocation until waitMs have elapsed since the last call. throttle runs at most once per waitMs, with a trailing call scheduled for the end of the window if calls kept coming in during it.

See documentation/ARCHITECTURE.md for the full package contract, dependency rules, and roadmap placement.

formatBytes

import { formatBytes } from "@alphinex/utils";

formatBytes(1536); // "1.5 KB"
formatBytes(1024, { standard: "binary" }); // "1 KiB"
formatBytes(1_234_567, { decimals: 2 }); // "1.23 MB"

Defaults to the 1000-based KB/MB units, not 1024-based KiB/MiB. That distinction is not pedantry: a "5 MB max upload" almost always means 5,000,000 bytes, and checking it against 5 × 1024² silently rejects files the user was told were small enough.