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

@stanvanheumen/web-utils

v1.0.1

Published

Lightweight, type-safe utility functions for web development

Downloads

229

Readme

@stanvanheumen/web-utils

npm version npm downloads bundle size TypeScript Node license

A lightweight, zero-dependency utility library for web development. Provides type-safe helpers for arrays, strings, numbers, dates, files, and common regex patterns - fully tree-shakeable with ESM and CommonJS support.

Installation

npm install @stanvanheumen/web-utils

Usage

import { groupByArray, isEmail, blobToFile } from '@stanvanheumen/web-utils';

CommonJS is also supported:

const { groupByArray, isEmail, blobToFile } = require('@stanvanheumen/web-utils');

Environment Support

| Function | Browser | Node.js | |-------------------|:-------:|:-------:| | clamp | ✓ | ✓ | | round | ✓ | ✓ | | chunkArray | ✓ | ✓ | | groupByArray | ✓ | ✓ | | mapArray | ✓ | ✓ | | removeFromArray | ✓ | ✓ | | sortArray | ✓ | ✓ | | toggleArray | ✓ | ✓ | | isEmail | ✓ | ✓ | | isUrl | ✓ | ✓ | | slugify | ✓ | ✓ | | parseDate | ✓ | ✓ | | startOfDay | ✓ | ✓ | | endOfDay | ✓ | ✓ | | startOfMonth | ✓ | ✓ | | endOfMonth | ✓ | ✓ | | startOfYear | ✓ | ✓ | | endOfYear | ✓ | ✓ | | blobToFile | ✓ | ✗ ¹ | | Regex constants | ✓ | ✓ |

¹ blobToFile relies on the File Web API, which is not available in Node.js.


Table of Contents


API Reference

Number Utilities

clamp(value, min, max)

Clamps a number between min and max (inclusive).

clamp(5, 0, 10);  // → 5
clamp(-1, 0, 10); // → 0
clamp(11, 0, 10); // → 10

round(value, decimals?)

Rounds a number to the specified number of decimal places. Defaults to 0 (integer rounding).

round(3.14159);    // → 3
round(3.14159, 2); // → 3.14
round(3.14159, 4); // → 3.1416
round(1.005, 2);   // → 1.01

Array Utilities

chunkArray(items, size)

Splits an array into chunks of the specified size. The last chunk may be smaller than size.

chunkArray([1, 2, 3, 4, 5], 2);
// → [[1, 2], [3, 4], [5]]

Throws if size is less than 1.


groupByArray(items, key)

Groups array items by a field, returning a record that maps each key to all matching items.

const users = [
    { role: 'admin', name: 'Alice' },
    { role: 'user',  name: 'Bob' },
    { role: 'admin', name: 'Carol' },
];

groupByArray(users, 'role');
// → { admin: [{ role: 'admin', name: 'Alice' }, { role: 'admin', name: 'Carol' }], user: [...] }

mapArray(items, key)

Converts an array into a keyed record. When multiple items share the same key, the last one wins.

const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];

mapArray(users, 'id');
// → { '1': { id: 1, name: 'Alice' }, '2': { id: 2, name: 'Bob' } }

removeFromArray(items, index)

Returns a new array with the element at the given index removed. Supports negative indices (-1 removes the last element).

removeFromArray(['a', 'b', 'c'], 1);  // → ['a', 'c']
removeFromArray(['a', 'b', 'c'], -1); // → ['a', 'b']

Throws if the index is out of bounds. Never mutates the original array.


sortArray(items, criteria)

Sorts an array by one or more criteria. Supports string, number, boolean, and Date values. Multiple criteria act as tiebreakers applied in order.

import type { SortCriteria } from '@stanvanheumen/web-utils';

sortArray(users, { key: 'name' });
sortArray(users, [{ key: 'role' }, { key: 'name', direction: 'desc' }]);

SortCriteria<T> shape:

| Field | Type | Default | |-------------|--------------------------|---------| | key | keyof T | - | | direction | 'asc' or 'desc' | 'asc' |


toggleArray(items, item, compare?)

Adds an item if it is absent, or removes it if it is already present. Accepts an optional custom comparator.

toggleArray([1, 2, 3], 2); // → [1, 3]
toggleArray([1, 3],    2); // → [1, 3, 2]

// Custom comparator
toggleArray(users, { id: 1, name: 'Alice' }, (a, b) => a.id === b.id);

String Utilities

isEmail(value)

Returns true if value is a valid email address. Supports quoted local parts and IP address domains.

isEmail('[email protected]'); // → true
isEmail('not-an-email');     // → false

isUrl(value)

Returns true if value is a valid URL. Accepts http, https, ftp, and protocol-relative (//) URLs. Rejects private and reserved IP ranges.

isUrl('https://example.com/path?q=1'); // → true
isUrl('http://192.168.1.1');           // → false (private IP)
isUrl('example.com');                  // → false (no protocol)

slugify(value)

Converts a string into a URL-friendly slug. Normalises accented characters, lowercases, and replaces separators with hyphens. Passes null and undefined through unchanged.

slugify('Hello, World!'); // → 'hello-world'
slugify('Ångström Units'); // → 'angstrom-units'
slugify(null);            // → null

Date Utilities

parseDate(value)

Parses a value into a Date, returning null if the value cannot be interpreted as a valid date. Accepts null and undefined (both return null), Date objects, Unix millisecond timestamps, and ISO 8601 strings.

Supported string formats:

| Format | Example | Timezone | |-------------------------------|--------------------------------|-----------------| | YYYY-MM-DD | 2024-06-15 | Local midnight | | YYYY-MM-DDTHH:mm:ss | 2024-06-15T12:00:00 | Treated as UTC | | YYYY-MM-DDTHH:mm:ssZ | 2024-06-15T12:00:00Z | UTC | | YYYY-MM-DDTHH:mm:ss.sss | 2024-06-15T12:00:00.000 | Treated as UTC | | YYYY-MM-DDTHH:mm:ss.sssZ | 2024-06-15T12:00:00.000Z | UTC | | YYYY-MM-DDTHH:mm:ss±HH:mm | 2024-06-15T12:00:00+02:00 | Offset applied |

Any other string returns null.

parseDate('2024-06-15');                     // → Date (local midnight)
parseDate('2024-06-15T12:00:00Z');           // → Date (UTC noon)
parseDate('2024-06-15T12:00:00.000+02:00'); // → Date (with offset)
parseDate(1718449200000);                    // → Date from timestamp
parseDate('2024-99-99');                     // → null (invalid calendar date)
parseDate('June 15 2024');                   // → null (unrecognised format)
parseDate(null);                             // → null

startOfDay(date)

Returns a new Date set to midnight (00:00:00.000) in local time. Does not mutate the original.

startOfDay(new Date('2024-06-15T14:30:00')); // → 2024-06-15T00:00:00.000

endOfDay(date)

Returns a new Date set to 23:59:59.999 in local time. Does not mutate the original.

endOfDay(new Date('2024-06-15T08:00:00')); // → 2024-06-15T23:59:59.999

startOfMonth(date)

Returns a new Date set to the first day of the month at midnight in local time. Does not mutate the original.

startOfMonth(new Date('2024-06-15T14:30:00')); // → 2024-06-01T00:00:00.000

endOfMonth(date)

Returns a new Date set to the last day of the month at 23:59:59.999 in local time. Does not mutate the original.

endOfMonth(new Date('2024-06-15T14:30:00')); // → 2024-06-30T23:59:59.999
endOfMonth(new Date('2024-02-10T00:00:00')); // → 2024-02-29T23:59:59.999 (leap year)

startOfYear(date)

Returns a new Date set to January 1st at midnight in local time. Does not mutate the original.

startOfYear(new Date('2024-06-15T14:30:00')); // → 2024-01-01T00:00:00.000

endOfYear(date)

Returns a new Date set to December 31st at 23:59:59.999 in local time. Does not mutate the original.

endOfYear(new Date('2024-06-15T14:30:00')); // → 2024-12-31T23:59:59.999

File Utilities

blobToFile(blob, name, options?)

Converts a Blob into a File with the given filename and optional metadata.

const file1 = blobToFile(blob, 'photo.jpg');
const file2 = blobToFile(blob, 'photo.jpg', { type: 'image/jpeg', lastModified: Date.now() });

BlobToFileOptions:

| Field | Type | Default | |----------------|----------|------------------| | type | string | blob.type or '' | | lastModified | number | Date.now() |


Regex Constants

Pre-compiled regex patterns exported for direct use.

| Export | Matches | |-----------------|-------------------------------------------------| | REGEX_EMAIL | Valid email addresses | | REGEX_URL | Valid URLs (rejects private IP ranges) | | REGEX_INTEGER | Integer strings - no leading zeros, no decimals |

import { REGEX_EMAIL, REGEX_URL, REGEX_INTEGER } from '@stanvanheumen/web-utils';

REGEX_EMAIL.test('[email protected]'); // → true
REGEX_INTEGER.test('042');            // → false (leading zero)

License

MIT © Stan van Heumen