New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

js-ultimate

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

js-ultimate

Lightweight, powerful utility functions to replace lodash with zero dependencies

latest
Source
npmnpm
Version
2.4.1
Version published
Maintainers
1
Created
Source

js-ultimate

A lightweight, zero-dependency TypeScript utility library designed as a modern Lodash replacement.

✨ Features

  • Zero dependencies — Tiny footprint, no supply chain risk
  • Fully tree-shakable — Bundle only what you use
  • TypeScript-first — Full type safety and excellent IDE support
  • High performance — Faster than Lodash in core operations
  • Modern ES modules — Built for today's bundlers and runtimes
  • Security-conscious — Built-in protections against prototype pollution

📦 Installation

# npm
npm install js-ultimate

# yarn
yarn add js-ultimate

# pnpm
pnpm add js-ultimate

# bun
bun add js-ultimate

🚀 Quick Start

import { chunk, get, uniq, debounce } from 'js-ultimate'

// Array utilities
chunk([1, 2, 3, 4, 5], 2)  // => [[1, 2], [3, 4], [5]]

// Object utilities
const obj = { user: { name: 'Alice', age: 30 } }
get(obj, 'user.name')       // => 'Alice'

// Collection utilities
uniq([1, 2, 1, 3, 2])       // => [1, 2, 3]

// Function utilities
const save = debounce(() => saveToServer(), 300)

📘 API

Array

chunk(array, size)

Splits an array into chunks of the specified size.

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

compact(array)

Removes falsy values from an array.

compact([0, 1, false, 2, '', 3, null, undefined, 4, NaN, 5])
// => [1, 2, 3, 4, 5]

difference(array, values)

Returns values in array not present in values.

difference([1, 2, 3, 4], [2, 4])  // => [1, 3]

first(array)

Gets the first element of an array.

first([1, 2, 3])  // => 1
first([])         // => undefined

flattenDeep(array)

Recursively flattens a nested array.

flattenDeep([1, [2, [3, [4, [5]]]]])  // => [1, 2, 3, 4, 5]

intersection(arrays)

Returns the intersection of multiple arrays.

intersection([1, 2, 3], [2, 3, 4], [3, 4, 5])  // => [3]

last(array)

Gets the last element of an array.

last([1, 2, 3])  // => 3
last([])         // => undefined

range(start, end?, step?)

Creates an array of numbers from start to end.

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

uniq(array)

Removes duplicate values from an array.

uniq([1, 2, 1, 3, 2])  // => [1, 2, 3]

uniqBy(array, iteratee)

Removes duplicates based on a key or function.

uniqBy([{ id: 1 }, { id: 2 }, { id: 1 }], 'id')
// => [{ id: 1 }, { id: 2 }]

uniqBy([2.1, 1.2, 2.3], Math.floor)
// => [2.1, 1.2]

Collection

filter(collection, predicate)

Iterates over elements of collection, returning an array of all elements predicate returns truthy for.

filter([1, 2, 3, 4], n => n % 2 === 0)  // => [2, 4]
filter({ a: 1, b: 2, c: 3 }, n => n > 1)  // => [2, 3]

find(collection, predicate)

Returns the first element predicate returns truthy for.

find([{ id: 1 }, { id: 2 }], { id: 2 })  // => { id: 2 }
find([1, 2, 3], n => n > 1)             // => 2

groupBy(collection, iteratee)

Creates an object composed of keys generated from the results of running each element through iteratee.

groupBy([{ type: 'a' }, { type: 'b' }, { type: 'a' }], 'type')
// => { a: [{ type: 'a' }, { type: 'a' }], b: [{ type: 'b' }] }

keyBy(collection, iteratee)

Creates an object composed of keys generated from the results of running each element through iteratee.

keyBy([{ id: 'a' }, { id: 'b' }], 'id')
// => { a: { id: 'a' }, b: { id: 'b' } }

map(collection, iteratee)

Creates an array of values by running each element in collection through iteratee.

map([1, 2, 3], n => n * 2)  // => [2, 4, 6]
map({ a: 1, b: 2 }, (n, k) => k)  // => ['a', 'b']

reduce(collection, iteratee, accumulator)

Reduces collection to a single value.

reduce([1, 2, 3], (sum, n) => sum + n, 0)  // => 6
reduce({ a: 1, b: 2 }, (acc, n) => acc + n, 0)  // => 3

Object

cloneDeep(value)

Recursively clones a value.

const original = { a: 1, b: { c: 2 } }
const cloned = cloneDeep(original)
cloned.b.c = 3
console.log(original.b.c)  // => 2 (unchanged)

get(object, path, defaultValue?)

Gets the value at path of object.

const obj = { a: { b: { c: 42 } } }
get(obj, 'a.b.c')      // => 42
get(obj, 'a.x.y', 'default')  // => 'default'

mergeDeep(target, source)

Recursively merges source into target.

mergeDeep({ a: { b: 1, c: 2 } }, { a: { b: 3, d: 4 } })
// => { a: { b: 3, c: 2, d: 4 } }

omit(object, paths)

Creates an object composed of the own and inherited enumerable property paths of object that are not omitted.

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

omitBy(object, predicate)

Creates an object composed of the own and inherited enumerable string keyed properties of object that predicate doesn't return truthy for.

omitBy({ a: 1, b: 2, c: 3 }, v => v === 2)  // => { a: 1, c: 3 }

pick(object, paths)

Creates an object composed of the picked object properties.

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

pickBy(object, predicate)

Creates an object composed of the own and inherited enumerable string keyed properties of object that predicate returns truthy for.

pickBy({ a: 1, b: 2, c: 3 }, v => v > 1)  // => { b: 2, c: 3 }

set(object, path, value)

Sets the value at path of object.

const obj = {}
set(obj, 'a.b.c', 42)
// => { a: { b: { c: 42 } } }

Security Note: set() includes built-in protection against prototype pollution by rejecting paths containing __proto__, constructor, or prototype.

setImmutable(object, path, value)

Returns a new object with the value set at path, without mutating the original.

const original = { a: { b: { c: 1 } }, d: 2 }
const updated = setImmutable(original, 'a.b.c', 99)
// original => { a: { b: { c: 1 } }, d: 2 }
// updated  => { a: { b: { c: 99 } }, d: 2 }

String

camelCase(string)

Converts string to camel case.

camelCase('foo Bar')     // => 'fooBar'
camelCase('--foo-bar--') // => 'fooBar'
camelCase('__FOO_BAR__') // => 'fooBar'

capitalize(string)

Capitalizes the first character of string.

capitalize('fred')  // => 'Fred'
capitalize('FRED')  // => 'FRED'

kebabCase(string)

Converts string to kebab case.

kebabCase('foo Bar')  // => 'foo-bar'
kebabCase('fooBar')   // => 'foo-bar'
kebabCase('__FOO_BAR__')  // => 'foo-bar'

snakeCase(string)

Converts string to snake case.

snakeCase('foo Bar')  // => 'foo_bar'
snakeCase('fooBar')   // => 'foo_bar'
snakeCase('--foo-bar--')  // => 'foo_bar'

startCase(string)

Converts string to start case.

startCase('--foo-bar--')  // => 'Foo Bar'
startCase('fooBar')       // => 'Foo Bar'

truncate(string, options?)

Truncates string if it's longer than the given maximum length.

truncate('hi-diddly-ho there, neighborino')
// => 'hi-diddly-ho there, neighbo...'

truncate('hi-diddly-ho there, neighborino', { length: 24, separator: ' ' })
// => 'hi-diddly-ho there,...'

upperFirst(string)

Capitalizes the first character of string.

upperFirst('fred')  // => 'Fred'
upperFirst('FRED')  // => 'FRED'

Function

debounce(func, wait)

Creates a debounced function that delays invoking func until after wait milliseconds have elapsed.

const save = debounce(() => saveToServer(), 300)
// save() will only invoke saveToServer 300ms after the last call

once(func)

Creates a function that is restricted to invoking func once.

const initialize = once(() => setupDatabase())
initialize() // runs setupDatabase
initialize() // does nothing

throttle(func, wait)

Creates a throttled function that only invokes func at most once per every wait milliseconds.

const scrollHandler = throttle(() => updatePosition(), 100)
window.addEventListener('scroll', scrollHandler)

Lang

clamp(number, lower, upper)

Clamps number within the inclusive lower and upper bounds.

clamp(-10, -5, 5)   // => -5
clamp(10, -5, 5)    // => 5
clamp(0, -5, 5)     // => 0

isEmpty(value)

Checks if value is an empty object, collection, map, or set.

isEmpty([])           // => true
isEmpty({})           // => true
isEmpty('')           // => true
isEmpty(null)         // => true
isEmpty([1, 2, 3])    // => false
isEmpty({ a: 1 })     // => false

isEqual(value, other)

Performs a deep comparison between two values.

isEqual({ a: 1 }, { a: 1 })  // => true
isEqual([1, 2], [1, 2])      // => true
isEqual({ a: { b: 2 } }, { a: { b: 2 } })  // => true

isPlainObject(value)

Checks if value is a plain object (created by object literals or Object.create(null)).

isPlainObject({})                     // => true
isPlainObject(Object.create(null))    // => true
isPlainObject(new Date())             // => false
isPlainObject([1, 2, 3])              // => false

⚡ Performance

js-ultimate is optimized for performance. Here's how it compares to Lodash in key operations:

Operationjs-ultimateLodashImprovement
chunk(1000, 10)~2.5M ops/sec~1.8M ops/sec~39% faster
get(nested path)~8M ops/sec~4.5M ops/sec~78% faster
set(nested path)~3.2M ops/sec~1.5M ops/sec~113% faster
isEqual(nested)~1.2M ops/sec~0.8M ops/sec~50% faster
cloneDeep(nested)~800K ops/sec~500K ops/sec~60% faster
groupBy(1000)~400K ops/sec~350K ops/sec~14% faster
uniq(array)~3M ops/sec~2.5M ops/sec~20% faster

Benchmarks run with tinybench on Node.js v20. Results may vary based on runtime and environment.

🌲 Tree Shaking

js-ultimate is fully tree-shakable. Import only what you need and your bundler will eliminate the rest.

// Bundle only includes chunk and uniq
import { chunk, uniq } from 'js-ultimate'

// Same as above — tree-shaking works with named exports
import { chunk } from 'js-ultimate/dist/array/chunk.js'
import { uniq } from 'js-ultimate/dist/array/uniq.js'
// package.json
{
  "sideEffects": false
}

🧠 TypeScript Support

Built with TypeScript from the ground up. Full type definitions included.

import { get, type GetType, type Paths } from 'js-ultimate'

interface User {
  profile: {
    name: string
    email: string
  }
}

const user: User = { profile: { name: 'Alice', email: 'alice@example.com' } }

// Type-safe paths
const name = get(user, 'profile.name')  // Type is string

// Utility types for working with paths
type ProfileKeys = Paths<User>  // 'profile' | 'profile.name' | 'profile.email'
type NameType = GetType<User, 'profile.name'>  // string

📊 Bundle Size

VariantSize (gzipped)
Full build (all 40+ functions)~3.5 KB
Single function import~150-300 B each

Importing individual functions results in minimal bundle impact — typically 150-300 bytes per function after gzip.

🛣 Roadmap

  • Additional collection utilities (flatMap, sample, shuffle)
  • Math utilities (round, ceil, floor with precision)
  • Number utilities (random in range, clamps)
  • Date utilities (format, difference)
  • Promise utilities (delay, retry)

🤝 Contributing

Contributions are welcome! Please follow these guidelines:

  • Fork the repository
  • Create a branch for your feature or bugfix
  • Write tests for your changes using Vitest
  • Ensure all tests pass with yarn test
  • Format code with yarn format
  • Submit a pull request with a clear description of changes

Development

# Install dependencies
yarn install

# Run tests in watch mode
yarn test

# Run tests once
yarn test --run

# Run benchmarks
yarn bench

# Build the project
yarn build

# Format code
yarn format

📄 License

MIT © hoatepdev

Made with ❤️ for modern JavaScript applications

Keywords

js-ultimate

FAQs

Package last updated on 04 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts