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

@wtfalch/utils

v0.1.0

Published

Small dependency-free helpers, one subpath per category: datetime, json, encoding, id, text, html, csv, format, sql, http, and node-only env, cli, crypto, node-http.

Readme

@wtfalch/utils

Small helpers with no runtime dependencies, one subpath per category. There is no root export: import the category you need, so a caller pulls in only that.

import { toIso } from '@wtfalch/utils/datetime';
import { csvField } from '@wtfalch/utils/csv';

Node 22 or newer. The categories marked "any runtime" use only web-standard APIs and run in Node, browsers and edge runtimes. The "Node only" ones import node: modules.

The package holds no credentials, makes no network request and opens no socket. It never reads the environment on import.

Categories

| Subpath | Runs | Functions | |---|---|---| | datetime | any runtime | toIso, toIsoOrNull, toDate, toDateOrNull, toIsoDate, sleep | | json | any runtime | canonicalJson | | encoding | any runtime | toHex | | id | any runtime | isUuid | | text | any runtime | slugify | | html | any runtime | escapeHtml | | csv | any runtime | csvField | | format | any runtime | formatBytes | | sql | any runtime | sqlIdent, sqlStringLiteral | | http | any runtime | jsonResponse, readJsonBody, parseBearer, BodyError | | env | Node only | requiredEnv | | cli | Node only | parseFlags, readStdin | | crypto | Node only | sha256Hex, safeEqual | | node-http | Node only | readNodeBody |

Functions

datetime

  • toIso(value: string | Date): string is an ISO 8601 UTC instant. A string is always normalised, so a Postgres timestamp such as 2026-10-01 12:00:00+00 becomes 2026-10-01T12:00:00.000Z. Throws RangeError on an invalid date.
  • toIsoOrNull(value) is toIso, with null and undefined giving null.
  • toDate(value: Date | string): Date returns a Date as is and parses a string.
  • toDateOrNull(value) is toDate, with null giving null.
  • toIsoDate(value: Date | string | null | undefined): string | null is a UTC calendar date, YYYY-MM-DD. A date-only string passes through. A timestamp string is converted to its UTC day, never passed through as a timestamp.
  • sleep(ms: number, signal?: AbortSignal): Promise<void> resolves after ms, or at once when signal aborts. It never rejects; the caller checks signal.aborted.

json

  • canonicalJson(value: unknown): string is deterministic JSON: keys sorted at every depth by UTF-16 code unit (never by locale), no whitespace, arrays in order. Safe to hash. It throws TypeError for anything JSON cannot hold (undefined anywhere, functions, symbols, bigints, NaN, Infinity, class instances, cycles, nesting past 1000 levels), so a hash is never taken over a value a JSON column could not have held. The tests carry byte-for-byte vectors: inputs run through the three existing hash-chain implementations (all three gave the same bytes), plus the RFC 8785 reference fixtures copied unchanged from the keys package's test data.

encoding

  • toHex(bytes: Uint8Array | ArrayBuffer): string is lower-case hex.

id

  • isUuid(value: unknown): value is string accepts the 8-4-4-4-12 form, any version, either case.

text

  • slugify(input, { maxLength = 128, fallback = '' }) lower-cases, folds accents (Café gives cafe), transliterates æ ø ß and similar, and joins words with single hyphens. Other scripts are dropped. The cap never leaves a hyphen at the end. Returns fallback when nothing is left.

html

  • escapeHtml(value: string): string escapes & < > " '. Safe in element text and in a quoted attribute value; not safe in a script, a style or an unquoted attribute.

csv

  • csvField(value: string | null | undefined): string quotes a field when it holds a comma, quote or line break (RFC 4180). A field a spreadsheet would run as a formula (=, +, -, @, even after leading spaces or control characters, or opening with a tab, carriage return or line feed) gets a leading '. The tests include vectors from the existing copies' tests; where those copies only checked the first character, the tests assert the safer output.

format

  • formatBytes(bytes: number): string gives 512 B, 48.0 KB, 2.4 MB, 3.0 GB, 5.0 TB. Throws RangeError for a negative or non-finite count.

sql

  • sqlIdent(name: string): string double-quotes an identifier.
  • sqlStringLiteral(value: string): string single-quotes a string.

Both double any quote inside and throw RangeError on a NUL byte (sqlIdent also on an empty name). Prefer bound parameters wherever a statement allows them.

http

These build and read Request and Response objects. They open no socket.

  • jsonResponse(body, status = 200, headers?) is a JSON Response with cache-control: no-store unless headers sets its own.
  • readJsonBody(request, { maxBytes }) reads a JSON body and never holds more than maxBytes. The cap is counted on the stream, so a missing or false content-length does not bypass it. It throws BodyError with a reason: content_type, missing, too_large, invalid_encoding or invalid_json. The caller maps the reason to its own response.
  • parseBearer(header, { maxLength = 8192 }) returns the token from Authorization: Bearer <token>, or null. The scheme is case-insensitive, one space follows it, and the token has no whitespace. Any key prefix check stays with the caller.
  • BodyError is the error class above.

env (Node only)

  • requiredEnv(name, env = process.env): string returns the value. An unset variable and an empty one both throw Error('<name> is required').

cli (Node only)

  • parseFlags(argv, usage): Map<string, string> reads --name value pairs. The last repeat wins. Anything else throws Error(usage).
  • readStdin(stream = process.stdin): Promise<string> reads all of a stream as UTF-8, untrimmed.

crypto (Node only)

  • sha256Hex(input: string | Uint8Array): string is the hex SHA-256 digest.
  • safeEqual(a: string, b: string): boolean compares in constant time. Both sides are hashed first, so length is not revealed.

node-http (Node only)

  • readNodeBody(req: IncomingMessage, maxBytes: number): Promise<Buffer> reads a Node request body, counting bytes. Over the cap it stops buffering, discards the rest (bounded in time and size), then rejects with BodyError('too_large'), so the caller's 413 reaches the client.

Status

Published: no

Version 0.1.0 is built and tested. Publishing happens on a v* tag.

Licence

MIT.