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

@mj-studio/js-util

v1.1.24

Published

Custom JavaScript Utilities for MJ Studio

Downloads

20,683

Readme

@mj-studio/js-util

ogimage-1260-630

JS Check

A comprehensive collection of JavaScript utility functions for modern development. Written in TypeScript with full type safety and extensive JSDoc documentation.

Installation

yarn add @mj-studio/js-util
npm install @mj-studio/js-util

API Reference

String

camelCase(str: string): string

Converts a snake_case or kebab-case string to camelCase

camelCase('user_name') // Returns: 'userName'
camelCase('user-name') // Returns: 'userName'

capitalize(str: string): string

Capitalizes the first character of a string

capitalize('hello') // Returns: 'Hello'
capitalize('hello world') // Returns: 'Hello world'

lastMatchIndex(str: string, match: string): number

Finds the last occurrence index of a substring in a string

lastMatchIndex('hello world hello', 'hello') // Returns: 12

snakeCase(str: string): string

Converts a string to snake_case format

snakeCase('userName') // Returns: 'user_name'
snakeCase('getUserById') // Returns: 'get_user_by_id'

Object

camelCaseObject(objOrArr: JSONCandidate): JSONCandidate

Recursively converts all object keys to camelCase

camelCaseObject({ user_name: 'John', user_age: 30 })
// Returns: { userName: 'John', userAge: 30 }

replaceJsonKeysRecursively<T extends JSONCandidate>(objOrArr: T, options: Partial<Omit<ReplaceJsonKeyRecursivelyOption, "keyFilter">>): T

Recursively replaces all object keys in a JSON structure using a replacer function or mapping

replaceJsonKeysRecursively({ old_key: 'value' }, { replacer: { 'old_key': 'new_key' } })
// Returns: { new_key: 'value' }

reverseObjectKeyValues<T extends Record<string, string | number>>(obj: T): T | Record<string, string>

Reverses the keys and values of an object

reverseObjectKeyValues({ a: '1', b: '2' })
// Returns: { '1': 'a', '2': 'b' }

snakeCaseObject(objOrArr: JSONCandidate): JSONCandidate

Recursively converts all object keys to snake_case

snakeCaseObject({ userName: 'John', userAge: 30 })
// Returns: { user_name: 'John', user_age: 30 }

replaceJsonValuesRecursively<T extends JSONCandidate>(objOrArr: T, options: Partial<Omit<ReplaceJsonKeysOptions, "keyFilter">>): T

Recursively replaces values in a JSON structure based on key matching

replaceJsonValuesRecursively({ name: 'John', age: 30 }, { replacer: { age: 25 } })
// Returns: { name: 'John', age: 25 }

Array

doBatch<T, R>(list: T[], work: (list: T[], batchIndex: number) => R, batchCount: number): R[]

Processes an array in batches and returns results from each batch

doBatch([1,2,3,4,5,6], (batch) => batch.reduce((sum, n) => sum + n, 0), 3)
// Processes: [1,2,3], [4,5,6] -> Returns: [6, 15]

groupByArray<T, K extends string | number>(collection: T[], getKey: ((element: T) => K) | K): T[][]

Groups array elements into subarrays based on a key

groupByArray(users, user => user.age)
// Returns: [[users with age 25], [users with age 30]]

groupByObject<T, K extends string | number>(collection: T[], getKey: ((element: T) => K) | K): GroupByObject<T, K>

Groups array elements into an object based on a key

groupByObject(users, user => user.age)
// Returns: { 25: [users with age 25], 30: [users with age 30] }

generateArray(size: number): number[]

Generates an array of consecutive numbers from 0 to size-1

generateArray(5) // Returns: [0, 1, 2, 3, 4]

lastOf<T>(arr: T[]): T

Gets the last element of an array

lastOf([1, 2, 3, 4]) // Returns: 4

randomItem<T>(source: T[]): T

Selects a random element from an array

randomItem([1, 2, 3, 4, 5]) // Returns: random number between 1-5

toggled<T>(arr: T[], element: T): T[]

Toggles an element in an array - adds if not present, removes if present

toggled([1, 2, 3], 4) // Returns: [1, 2, 3, 4]

unique<T>(arr: T[]): T[]

Removes duplicate values from an array

unique([1, 2, 2, 3, 3, 4])
// Returns: [1, 2, 3, 4]

uniqueBy<T, K>(arr: T[], getKey: (value: T) => K): T[]

Removes duplicate elements from an array by a selected key.

uniqueBy(
  [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 1, name: 'Alice v2' },
  ],
  (item) => item.id,
)
// Returns: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]

Promise

withMinimumResolveTime<T>(minimumMilli: number, promise: Promise<T>): Promise<T>

Ensures a Promise takes at least a minimum amount of time to resolve

const result = await withMinimumResolveTime(1000, fetchData())
// Guarantees at least 1 second delay for UX (loading spinners)

withTimeout<T>(milli: number, promise: Promise<T>): Promise<T>

Adds a timeout to a Promise, rejecting if the timeout is exceeded

const result = await withTimeout(5000, fetchUser(userId))
// Throws error if fetchUser takes more than 5 seconds

Type Check

is.number(candidate: any): candidate is number

Checks whether the candidate is a valid number.

is.number(42) // true

is.string(candidate: any): candidate is string

Checks whether the candidate is a string.

is.string('hello') // true

is.integerString(candidate: any): candidate is string

Checks whether the candidate is an integer string.

is.integerString('42') // true

is.numberString(candidate: any): candidate is string

Checks whether the candidate is a numeric string.

is.numberString('3.14') // true

is.null(candidate: any): candidate is null

Checks whether the candidate is null.

is.null(null) // true

is.undefined(candidate: any): candidate is undefined

Checks whether the candidate is undefined.

is.undefined(undefined) // true

is.nullOrUndefined(candidate: any): candidate is undefined | null

Checks whether the candidate is null or undefined.

is.nullOrUndefined(undefined) // true

is.falsy<T>(candidate: T | Falsy): candidate is Falsy

Checks whether the candidate is falsy.

is.falsy(0) // true

is.truthy<T>(candidate: T | Falsy): candidate is T

Checks whether the candidate is truthy.

is.truthy('hello') // true

is.function<T extends Func, R extends unknown>(candidate: T | R): candidate is T

Checks whether the candidate is a function.

is.function(() => 'hello') // true

is.object(candidate: any): candidate is Record<string, unknown>

Checks whether the candidate is a non-null object.

is.object({ value: 1 }) // true

is.plainObject(candidate: any): candidate is Record<string, unknown>

Checks whether the candidate is a plain object.

is.plainObject({ value: 1 }) // true

is.array<T>(candidate: any): candidate is Array<T>

Checks whether the candidate is an array.

is.array([1, 2, 3]) // true

is.boolean(candidate: any): candidate is boolean

Checks whether the candidate is a boolean.

is.boolean(false) // true

is.promise<T>(p: Promise<T> | any): p is Promise<T>

Checks whether the candidate is a promise.

is.promise(Promise.resolve(1)) // true

is.primitive(candidate: unknown): candidate is string | number | boolean | null | undefined

Checks whether the candidate is a primitive value.

is.primitive('hello') // true

is.notEmptyString(candidate: any): candidate is string

Checks whether the candidate is a non-empty string.

is.notEmptyString('hello') // true

is.emptyString(candidate: any): boolean

Checks whether the candidate is an empty string.

is.emptyString('') // true

is.emptyArray(candidate: any): boolean

Checks whether the candidate is an empty array.

is.emptyArray([]) // true

is.notEmptyArray<T>(candidate: any): candidate is Array<T>

Checks whether the candidate is a non-empty array.

is.notEmptyArray([1, 2, 3]) // true

Filter

filterJsonKeys(x: JSONCandidate, filter: Filter): JSONCandidate

Filters a JSON structure to include only objects/arrays containing specified keys

filterJsonKeys({ name: 'John', age: 30, city: 'NYC' }, ['name', 'age'])
// Returns: { name: 'John', age: 30 }

filterNonNullish<T>(source: T[]): Exclude<T, null | undefined>[]

Filters out null and undefined values from an array

filterNonNullish([1, null, 2, undefined, 3]) // Returns: [1, 2, 3]

filterNonNullishKeys<T extends object>(source: T, options?: Options): T

Filters out object keys with null, undefined, or empty string values

filterNonNullishKeys({ a: 1, b: null, c: undefined, d: 'hello' })
// Returns: { a: 1, d: 'hello' }

removeValueByKeyInObject<T extends Record<string | number, any>>(v: T, key: (string | number) | (string | number)[]): T

Removes specified keys from an object and returns a new object

removeValueByKeyInObject({ a: 1, b: 2, c: 3 }, 'b') // Returns: { a: 1, c: 3 }

Number

numberWithComma(x?: number): string

Adds comma separators to a number for better readability

numberWithComma(1234567) // Returns: '1,234,567'

padZero(number: number | undefined, len?: number): string

Pads a number with leading zeros to reach the specified length

padZero(5) // Returns: '05'
padZero(5, 3) // Returns: '005'

toFixed(number: number | undefined, fractionDigits: number, defaultString?: string): string

Safely formats a number to a specified number of decimal places

toFixed(3.14159, 2) // Returns: '3.14'
toFixed(5, 0) // Returns: '5'

toFixedIfNeed(number: number | undefined, fractionDigits: number, defaultString?: string): string

Formats a number to a fixed decimal places, removing trailing zeros

toFixedIfNeed(3.1000, 4) // Returns: '3.1'
toFixedIfNeed(5.0, 2) // Returns: '5'

toSiUnitString(n: number): string

Converts a number to a readable string with SI unit suffixes (K, M)

toSiUnitString(1500) // Returns: '1.5K'
toSiUnitString(2500000) // Returns: '2.5M'

clamp(value: number, min: number, max: number): number

Clamps a number between a minimum and maximum value

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

Time

setIntervalWithTimeout(callback: (clear: () => void) => any, intervalMs: number): () => void

Creates a repeating timeout that can be cleared from within the callback

const stop = setIntervalWithTimeout((clear) => {
  console.log('Running...')
  if (someCondition) clear()
}, 1000)

TimeoutHandler.clear(): void

Clears the current timeout and marks the handler as cleared.

const handler = new TimeoutHandler()
handler.clear()

parseSecond(totalSecond?: number): Result

Parses total seconds into structured time components

parseSecond(3661) // Returns: { totalDay: 0, totalHour: 1, totalMinute: 61, onlyHour: 1, onlyMinute: 1, onlySecond: 1 }

SecFormat.get(type: SecFormats): Formatter

Returns the formatter for the given second format.

const formatter = SecFormat.get('mm:ss')
formatter(90) // Returns: '01:30'

SecFormat.format(totalSeconds: number, type: SecFormats): string

Formats total seconds with the given second format.

SecFormat.format(3661, 'hh:mm:ss') // Returns: '01:01:01'

SecFormat.invalidateIntervalSec(type: SecFormats): number

Returns the cache invalidation interval for the given second format.

SecFormat.invalidateIntervalSec('mm:ss') // Returns: 1

formatSec(totalSeconds: number, type: SecFormats): string

Alias for SecFormat.format - formats seconds into time string

formatSec(3661, 'hh:mm:ss') // Returns: '01:01:01'
formatSec(90, 'mm:ss') // Returns: '01:30'

createTimer(): { clear: () => void; timeout: (fn: () => void, duration: number, { clear: clearOtherTimers }?: Options) => () => void; }

Creates a timer utility that manages multiple timeouts with optional clearing

const timer = createTimer()
timer.timeout(() => console.log('Hello'), 1000)
timer.clear() // Clears all timeouts

createTimer().clear(): void

Clears every timeout created by this timer instance.

const timer = createTimer()
timer.clear()

createTimer().timeout(fn: () => void, duration: number, { clear: clearOtherTimers }?: Options): () => void

Schedules a timeout and optionally clears earlier timeouts first.

const timer = createTimer()
timer.timeout(() => console.log('Hello'), 1000)

Math

interpolate({ value, inputRange, outputRange, extrapolate, }: { value: number; inputRange: [number, number]; outputRange: [number, number]; extrapolate?: "clamp" | "extend"; }): number

Maps a value from one range to another range with optional extrapolation control

interpolate({ value: 50, inputRange: [0, 100], outputRange: [0, 1] }) // Returns: 0.5
interpolate({ value: 150, inputRange: [0, 100], outputRange: [0, 1], extrapolate: 'clamp' }) // Returns: 1
interpolate({ value: 25, inputRange: [0, 100], outputRange: [100, 0] }) // Returns: 75

interpolateColor({ value, inputRange, outputRange, }: { value: number; inputRange: [number, number]; outputRange: [string, string]; }): string

Interpolates between two hex colors based on a value within an input range

interpolateColor({ value: 50, inputRange: [0, 100], outputRange: ['#ff0000', '#00ff00'] }) // Returns: '#808000'
interpolateColor({ value: 0, inputRange: [0, 100], outputRange: ['#000000', '#ffffff'] }) // Returns: '#000000'
interpolateColor({ value: 100, inputRange: [0, 100], outputRange: ['#000000', '#ffffff'] }) // Returns: '#ffffff'

Misc

formatJson(a: any): string

Converts a value to a formatted JSON string representation

formatJson({ name: 'John', age: 30 }) // Returns: '{\n  "name": "John",\n  "age": 30\n}'

TypeScript Support

All functions include comprehensive TypeScript type definitions and JSDoc documentation for excellent IDE support.

Contributing

See CONTRIBUTING.md for contribution guidelines.

License

MIT © MJ Studio