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

hearty-helper

v2.1.0

Published

A comprehensive TypeScript utility library — numbers, strings, arrays, objects, validators, and more. Your single source of utility functions.

Readme

hearty-helper

NPM version License: MIT

A comprehensive TypeScript utility library -- numbers, strings, arrays, objects, validators, and more. Your single source of utility functions.

Note: Date/time utilities live in the separate hearty-datetime-helper package.

Installation

npm install hearty-helper

Quick Start

// ESM
import { formatCurrency, chunk, deepMerge, isEmail } from 'hearty-helper'

// CommonJS
const { formatCurrency, chunk, deepMerge, isEmail } = require('hearty-helper')

API Reference

Number Formatters

formatCurrency(num, currency?, locale?)

Formats a number as a currency string using Intl.NumberFormat.

formatCurrency(1234.56)                    // "$1,234.56"
formatCurrency(1234.56, "EUR", "de-DE")    // "1.234,56 €"

formatCompact(num)

Formats a number in compact notation.

formatCompact(1000)        // "1K"
formatCompact(1500000)     // "1.5M"
formatCompact(1000000000)  // "1B"

clamp(num, min, max)

Clamps a number between min and max.

clamp(15, 0, 10)  // 10
clamp(-5, 0, 10)  // 0

roundTo(num, decimals)

Rounds to N decimal places.

roundTo(3.14159, 2)  // 3.14

toOrdinal(num)

Converts to ordinal string.

toOrdinal(1)   // "1st"
toOrdinal(2)   // "2nd"
toOrdinal(11)  // "11th"
toOrdinal(23)  // "23rd"

isEven(num) / isOdd(num)

Check if a number is even or odd.

percentage(value, total)

Calculate percentage.

percentage(25, 200)  // 12.5

sum(numbers[]) / average(numbers[])

Sum or average an array of numbers.

sum([1, 2, 3, 4])      // 10
average([1, 2, 3, 4])   // 2.5

padNumber(num, length)

Pad with leading zeros.

padNumber(5, 3)   // "005"
padNumber(42, 5)  // "00042"

toFixedNumber(num, decimals)

Like toFixed but returns a number.

toFixedNumber(1.2345, 2)  // 1.23

inRange(num, start, end)

Check if number is within range (inclusive).

inRange(5, 1, 10)  // true

formatBytes(bytes, decimals?)

Format bytes into human-readable string.

formatBytes(1024)     // "1 KB"
formatBytes(1048576)  // "1 MB"

randomInt(min, max)

Random integer between min and max (inclusive).

randomInt(1, 10)  // e.g. 7

Regex Validators

isEmail(str)

isEmail("[email protected]")  // true
isEmail("invalid")           // false

isURL(str)

isURL("https://example.com")  // true
isURL("not-a-url")            // false

isIPv4(str) / isIPv6(str)

isIPv4("192.168.1.1")  // true
isIPv6("::1")          // false (simplified; full form required)

isHexColor(str)

isHexColor("#fff")     // true
isHexColor("#ffffff")  // true
isHexColor("#ggg")     // false

isCreditCard(str)

Validates using the Luhn algorithm.

isCreditCard("4532015112830366")  // true

isStrongPassword(str)

Min 8 chars, uppercase, lowercase, number, special char.

isStrongPassword("Abcdef1!")  // true
isStrongPassword("weak")      // false

isUUID(str)

isUUID("550e8400-e29b-41d4-a716-446655440000")  // true

isSlug(str)

isSlug("hello-world")  // true
isSlug("Hello World")  // false

isMACAddress(str)

isMACAddress("00:1A:2B:3C:4D:5E")  // true

isLatLong(str)

isLatLong("40.7128,-74.0060")  // true

isPostalCode(str, locale?)

Supports US (default), UK, CA, DE, FR, IN, JP, AU.

isPostalCode("12345")         // true (US)
isPostalCode("SW1A 1AA", "UK")  // true
isPostalCode("500001", "IN")    // true

isPhoneNumber(str)

Basic international phone number validation.

isPhoneNumber("+14155552671")  // true

String Utilities

isAlphanumeric(str)

isAlphanumeric("abc123")   // true
isAlphanumeric("abc@123")  // false

removeSpaces(str)

Removes all whitespace.

removeSpaces("hello world")  // "helloworld"

getRandomString(length?)

Random alphanumeric string (default length: 10).

getRandomString(8)  // e.g. "aB3xK9mQ"

getTimestamp()

Current Unix timestamp in milliseconds.

capitalize(str)

capitalize("hello")  // "Hello"

capitalizeWords(str)

capitalizeWords("hello world")  // "Hello World"

camelCase(str) / snakeCase(str) / kebabCase(str) / pascalCase(str)

camelCase("hello world")   // "helloWorld"
snakeCase("helloWorld")    // "hello_world"
kebabCase("helloWorld")    // "hello-world"
pascalCase("hello world")  // "HelloWorld"

truncate(str, length, suffix?)

truncate("Hello World", 8)  // "Hello..."

slugify(str)

slugify("Hello World!")  // "hello-world"

escapeHtml(str) / unescapeHtml(str)

escapeHtml('<script>alert("xss")</script>')
// "&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;"

reverseString(str)

reverseString("hello")  // "olleh"

countOccurrences(str, substr)

countOccurrences("hello world hello", "hello")  // 2

isPalindrome(str)

isPalindrome("racecar")  // true

mask(str, visibleCount?, maskChar?)

mask("1234567890")      // "******7890"
mask("1234567890", 2)   // "********90"

wordCount(str)

wordCount("hello world")  // 2

stripHtml(str)

stripHtml("<p>Hello <b>World</b></p>")  // "Hello World"

initials(str)

initials("John Doe")  // "JD"

URL Utilities

getUrlParams(url?)

Parses URL query parameters into an object.

getUrlParams("https://example.com?name=sri&page=2")
// { name: "sri", page: "2" }

parseQueryString(str)

parseQueryString("?name=sri&page=2")  // { name: "sri", page: "2" }

buildQueryString(obj)

buildQueryString({ name: "sri", page: 2 })  // "name=sri&page=2"

Object Utilities

isObject(data)

isObject({ a: 1 })  // true
isObject([])         // false
isObject(null)       // false

getSizeOfObject(data)

getSizeOfObject({ a: 1, b: 2 })  // 2
getSizeOfObject([])               // null

isObjectEmpty(data)

isObjectEmpty({})        // true
isObjectEmpty({ a: 1 })  // false

deepClone(obj)

Deep clone using structuredClone.

const clone = deepClone({ a: { b: 1 } })

pick(obj, keys[])

pick({ a: 1, b: 2, c: 3 }, ["a", "c"])  // { a: 1, c: 3 }

omit(obj, keys[])

omit({ a: 1, b: 2, c: 3 }, ["b"])  // { a: 1, c: 3 }

deepMerge(target, ...sources)

deepMerge({ a: { b: 1 } }, { a: { c: 2 } })
// { a: { b: 1, c: 2 } }

flattenObject(obj, separator?)

flattenObject({ a: { b: { c: 1 } } })  // { "a.b.c": 1 }

unflattenObject(obj, separator?)

unflattenObject({ "a.b.c": 1 })  // { a: { b: { c: 1 } } }

invertObject(obj)

invertObject({ a: "1", b: "2" })  // { "1": "a", "2": "b" }

mapKeys(obj, fn) / mapValues(obj, fn)

mapKeys({ a: 1 }, k => k.toUpperCase())  // { A: 1 }
mapValues({ a: 1 }, v => v * 10)          // { a: 10 }

hasPath(obj, path)

hasPath({ a: { b: { c: 1 } } }, "a.b.c")  // true
hasPath({ a: 1 }, "a.b.c")                 // false

getPath(obj, path, defaultValue?)

getPath({ a: { b: 42 } }, "a.b")         // 42
getPath({ a: 1 }, "a.b.c", "default")    // "default"

setPath(obj, path, value)

Returns a new object with the value set at the path.

setPath({ a: { b: 1 } }, "a.c", 2)  // { a: { b: 1, c: 2 } }

Array Utilities

unique(arr)

unique([1, 2, 2, 3])  // [1, 2, 3]

uniqueBy(arr, key)

uniqueBy([{ id: 1 }, { id: 2 }, { id: 1 }], "id")
// [{ id: 1 }, { id: 2 }]

chunk(arr, size)

chunk([1, 2, 3, 4, 5], 2)  // [[1, 2], [3, 4], [5]]

flatten(arr, depth?)

flatten([[1, 2], [3, [4]]])     // [1, 2, 3, [4]]
flatten([[1, 2], [3, [4]]], 2)  // [1, 2, 3, 4]

shuffle(arr)

Fisher-Yates shuffle. Returns a new array.

groupBy(arr, key)

groupBy([{ type: "a" }, { type: "b" }, { type: "a" }], "type")
// { a: [...], b: [...] }

sortBy(arr, key, order?)

sortBy([{ n: 3 }, { n: 1 }], "n")         // [{ n: 1 }, { n: 3 }]
sortBy([{ n: 3 }, { n: 1 }], "n", "desc") // [{ n: 3 }, { n: 1 }]

intersection(arr1, arr2) / difference(arr1, arr2) / union(arr1, arr2)

intersection([1, 2, 3], [2, 3, 4])  // [2, 3]
difference([1, 2, 3], [2, 3, 4])    // [1]
union([1, 2], [2, 3])               // [1, 2, 3]

compact(arr)

compact([0, 1, false, 2, "", null])  // [1, 2]

sample(arr)

Returns a random element.

range(start, end, step?)

range(0, 5)      // [0, 1, 2, 3, 4]
range(0, 10, 3)  // [0, 3, 6, 9]

partition(arr, predicate)

partition([1, 2, 3, 4], n => n % 2 === 0)  // [[2, 4], [1, 3]]

zip(...arrays) / unzip(arr)

zip([1, 2], ["a", "b"])         // [[1, "a"], [2, "b"]]
unzip([[1, "a"], [2, "b"]])     // [[1, 2], ["a", "b"]]

first(arr, n?) / last(arr, n?)

first([1, 2, 3])     // 1
first([1, 2, 3], 2)  // [1, 2]
last([1, 2, 3])      // 3
last([1, 2, 3], 2)   // [2, 3]

countBy(arr, key)

countBy(["apple", "banana", "avocado"], s => s[0])
// { a: 2, b: 1 }

minBy(arr, key) / maxBy(arr, key)

minBy([{ n: 3 }, { n: 1 }, { n: 2 }], "n")  // { n: 1 }
maxBy([{ n: 3 }, { n: 1 }, { n: 2 }], "n")  // { n: 3 }

JSON Utilities

isJson(str)

isJson('{"name":"sri"}')  // true
isJson('not json')        // false

Misc Utilities

debounce(fn, delay)

Creates a debounced function.

const search = debounce(query => fetchResults(query), 300)

throttle(fn, delay)

Creates a throttled function.

const onScroll = throttle(handleScroll, 100)

sleep(ms)

Promise-based delay.

await sleep(1000)

retry(fn, retries?, delay?)

Retry an async function.

const data = await retry(() => fetchData(), 3, 1000)

memoize(fn)

Basic memoization.

const cached = memoize(expensiveFunction)

pipe(...fns) / compose(...fns)

const transform = pipe(
  (x: number) => x + 1,
  (x: number) => x * 2,
)
transform(3)  // 8

const transform2 = compose(
  (x: number) => x + 1,
  (x: number) => x * 2,
)
transform2(3)  // 7

deepEqual(a, b)

Deep equality check.

deepEqual({ a: { b: 1 } }, { a: { b: 1 } })  // true

generateId(length?)

URL-safe unique ID (default 21 chars).

generateId()    // e.g. "V1StGXR8_Z5jdHi6B-myT"
generateId(10)  // e.g. "aBc1D_e2Fg"

noop()

A no-op function that does nothing.

times(n, fn)

times(3, i => i * 2)  // [0, 2, 4]

isEmpty(value)

Checks if a value is empty (string, array, object, null, undefined).

isEmpty("")         // true
isEmpty([])         // true
isEmpty({})         // true
isEmpty(null)       // true
isEmpty("hello")    // false

Session Utilities (Browser only)

getSessionId(sessionKey)

Gets or creates a session ID in sessionStorage.

Cookie Utilities (Browser only)

setCookie(options) / getCookie(name)

setCookie({ cname: 'theme', cvalue: 'dark', expires: 7 })
getCookie('theme')  // 'dark'

HTTP Utilities

sendHttpRequest<T>(url, options?)

const data = await sendHttpRequest('https://api.example.com/users')

Tech Stack

| Tool | Purpose | |------|---------| | TypeScript | Type-safe source code | | tsup | Bundling (ESM + CJS dual output) | | Vitest | Testing | | Node.js 18+ | Minimum runtime |

Development

npm install
npm test
npm run test:watch
npm run build
npm run lint

Migration from v1

v2 is a major rewrite in TypeScript with modern tooling. Key changes:

  • ESM + CJS dual exports -- works with both import and require
  • TypeScript types included -- full type definitions shipped with the package
  • sendHttpRequest now uses Fetch API -- returns a Promise instead of using callbacks
  • setCookie no longer hardcodes a domain -- you must provide domain explicitly
  • getRandomNumber renamed to getTimestamp -- old name still works as a deprecated alias
  • geturlParams renamed to getUrlParams -- old name still works as a deprecated alias

Author

Srinivas N -- [email protected]

License

MIT