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

@andreww2012/unutils

v0.2.0

Published

A big collection of utilities coming from different utility libraries and us, targeting modern environments.

Readme

@andreww2012/unutils npm

A growing collection of general-purpose utilities and TypeScript types, mostly consolidated from existing popular packages with improved UX/DX, bug fixes and improvements in functionality.

Features

  • 🌱 Zero dependencies: everything is bundled;
  • 🌳 Tree-shakeable: unused utilities are dropped from your bundle;
  • ✨ More than 250 utilities and 200 types.

Installation

Minimum supported runtimes:

pnpm i -D @andreww2012/unutils
npm i -D @andreww2012/unutils
yarn add -D @andreww2012/unutils

Usage

Import any function or type from the main entrypoint (except for runtime-specific utilities):

import {mapKeys} from '@andreww2012/unutils';

or from a specific entrypoint:

import {every} from '@andreww2012/unutils/iterable';

See individual symbols' JSDoc for detailed documentation.

| Entrypoint | Description | Available in main? | | ----------- | ------------------------------------------------------ | ------------------ | | array | Array and tuple manipulation | ✅ | | async | Promises, mutexes, timeouts and concurrency | ✅ | | dom | Selector-typed DOM lookups | ❌ | | function | Function composition, currying and partial application | ✅ | | id | Unique ID generation | ✅ | | iterable | Generic iterable traversal and aggregation | ✅ | | json | JSON parsing and serialization | ✅ | | map | Map querying, insertion and transformation | ✅ | | math | Numeric aggregation, interpolation and randomness | ✅ | | misc | Miscellaneous helpers that fit no other group | ✅ | | object | Object merging, picking and deep-path access | ✅ | | predicate | Type guards and value predicates | ✅ | | regex | Type-safe RegExp and pattern escaping | ✅ | | runtime | Runtime environment detection | ✅ | | server | Node-only helpers (filesystem, process, terminal) | ❌ | | set | Set querying and transformation | ✅ | | string | String casing, splitting and affixing | ✅ | | traverse | Recursive visitor traversal of nested values | ✅ | | ts | TypeScript runtime and compile-time assertions | ✅ | | types | Type-only utility types | ✅ | | value | Cloning, equality and type inspection for any value | ✅ |

Global augmentation entrypoints

Entrypoints whose name ends in .global export nothing at all. They are imported for their effect instead: each one augments an existing global type, so the improvement reaches every call site in your project without a single one of them changing.

import '@andreww2012/unutils/dom/query-selector-typed.global';

document.querySelector('button#submit'); // HTMLButtonElement | null

One import anywhere in your project is enough — a declaration file or an entry module is the usual place. They are strictly opt-in: no other entrypoint pulls them in, so nothing changes until you add the import yourself. Where several of them augment the same thing, import at most one, otherwise their declarations merge into each other.

| Entrypoint | Description | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | dom/query-selector-typed.global | Types the native querySelector, querySelectorAll and closest from the CSS selector literal they are given, on every ParentNode and Element. An unknown tag or a malformed selector falls back to Element. The same typing is available as ordinary functions from dom, should you prefer not to touch globals | | dom/query-selector-typed-strict.global | As above, except a malformed selector resolves to never rather than falling back, turning a typo into a compile error instead of a runtime throw. Mutually exclusive with the entrypoint above |

Functions & types

Packages

Each bundled package has its own page under docs/packages/, listing every utility it provides and how it maps into @andreww2012/unutils (renames, consolidations, and what was skipped).

| Package | What we take from it | | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | es-toolkit | Broad lodash-style toolkit — the bulk of the array, object, function, math, predicate and string groups (plus es-toolkit/compat), lazy iterable* helpers and bigint math | | remeda | Data-first utilities with strong tuple/literal typing — many array/object helpers | | @antfu/utils | Small general-purpose helpers (e.g. ensurePrefix, toForwardSlashes) | | arkregex | Type-safe RegExp that infers the match and captures from a literal pattern | | dedent | Tagged-template / function dedent that handles interpolations | | destr | Forgiving and strict JSON parsing (jsonParseSafe / jsonParse) | | devalue | Serialize/revive Date/Map/Set/BigInt, circular refs and nested promises (not plain JSON) | | lossless-json | Precision-preserving JSON parse/stringify (bigint and decimals) | | ms-ts | Compile-time duration-string → milliseconds conversion (ms / Ms) | | nanoid | Secure, URL-safe unique ID generation — the id group / @andreww2012/unutils/id | | neotraverse | Recursive visitor traversal of nested values — the traverse group / @andreww2012/unutils/traverse | | safe-stable-stringify | Deterministic, circular-safe JSON.stringify | | string-ts | Type-level-aware string helpers — literal-preserving case conversion, native-method wrappers and object-key transforms | | ts-extras | Thin, strongly-typed wrappers over native methods | | type-fest | Type-only utility types (the types group / @andreww2012/unutils/types) | | typed-query-selector | Infers the element type from a CSS selector literal — the dom group / @andreww2012/unutils/dom | | yieldable-json | Non-blocking async JSON parse/stringify |

Candidate libraries on the radar. These cover JSON manipulation axes (repair, partial parsing, diff, pointer/patch) not yet provided — adding them would expand @andreww2012/unutils beyond parsing and serialization.

| Library | Axis | Status | Notes | | --------------------------------------------------- | ------------------------------- | ------ | ------------------------------------------------------------------------------------------------ | | jsonrepair | Repair malformed JSON | 🚧 | Fixes unquoted keys, trailing commas, comments, fenced blocks, etc.; complements jsonParseSafe | | partial-json | Parse incomplete/truncated JSON | 🚧 | Best-effort value from a partial buffer (e.g. streaming LLM output); configurable Allow mask | | jsondiffpatch | Document diff/patch | 🚧 | diff/patch/unpatch/reverse with array-move detection; core has no runtime dependency | | @hyperjump/json-pointer | JSON Pointer (RFC 6901) | 🚧 | Get/set/remove a value by /a/b/0 path | | immutable-json-patch | JSON Patch (RFC 6902) | 🚧 | Immutable apply/compare/revert; pairs with JSON Pointer | | JSON Merge Patch (RFC 7386) | Merge patch | 🚧 | No well-maintained typed library exists; a custom implementation would be the likely route |

JSONPath (querying) was considered and set aside: the dominant jsonpath-plus has a history of RCE advisories, and the axis is broad/lower-demand. Relaxed syntax (JSON5/JSONC), canonical/RFC 8785 output, and bigint-safe stringify are already covered by existing entries or native features.

Custom functions

| Our function group and name | Notes | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | array/arrayAt | Consolidates ts-extras' arrayAt (precise tuple typing for literal indices) with es-toolkit's at (also accepts a list of indices) | | array/arrayChunks | Consolidates fixed-size chunk with a by-key variant; pass an iteratee to group runs of consecutive same-key elements | | array/arrayDifference | Consolidates difference/differenceBy/differenceWith; optional mapper or comparator | | array/arrayDrop | Consolidates drop/dropWhile; pass a predicate to drop while it holds | | array/arrayDropFirstBy | Drops the count "smallest" elements by one or more OrderRules (via remeda's dropFirstBy), O(n) without sorting; kept elements keep their order | | array/arrayDropRight | Consolidates dropRight/dropRightWhile; pass a predicate to drop while it holds | | array/arrayFill | Consolidates fill/toFilled; pass {copy: true} to return a new array | | array/arrayFilter | Tuple-aware Array#filter (via remeda); a tuple + (inferred) type guard yields the refined tuple ([1, 2, 3] filtered by !== 2 → [1, 3]) not (1 \| 3)[] | | array/arrayFirstBy | First element by one or more OrderRules (via remeda's firstBy), O(n) without sorting; min/max + tie-breakers; non-empty tuples never give undefined | | array/arrayHasMaxElements | Type-guard: a literal maximum narrows to ArrayWithMaxLength (every length still possible, never if none is); non-literal returns a plain boolean | | array/arrayHasMinElements | Type-guard (via remeda's hasAtLeast): a literal minimum narrows to a known-minimum tuple; non-literal returns a plain boolean | | array/arrayify | Wraps non-arrays (tuple-preserving); nullish input returns [] (or [value] with wrapNullish) | | array/arrayIntersection | Consolidates intersection/intersectionBy/intersectionWith; optional mapper or comparator | | array/arrayIsSubset | Consolidates isSubset/isSubsetWith; optional mapper or comparator | | array/arrayMap | Tuple-preserving Array#map (via remeda); a fixed-length tuple maps to a same-length tuple instead of widening to U[] | | array/arrayMapWithAccumulator | Prefix scan (via remeda's mapWithFeedback): threads an accumulator, returning every intermediate state (same length, tuple-preserving) | | array/arrayNthBy | Element at the index-th position in the order defined by OrderRules (via remeda's nthBy), O(n) without sorting; out-of-bounds → undefined | | array/arrayPartition | Single predicate gives a binary [matched, unmatched] (guard-narrowing); multiple predicates give an n-way partition | | array/arrayPurgeValues | Consolidates pull/pullAllBy/pullAllWith; mutating; optional mapper or comparator | | array/arrayRankBy | Rank an item would have by one or more OrderRules — count of elements sorting before it (via remeda's rankBy), O(n); item need not be present | | array/arrayReverse | Tuple-preserving reverse (via remeda + type-fest's ArrayReverse); a fixed-length tuple reverses to a same-length tuple instead of widening to a union array | | array/arraySample | Consolidates sample/sampleSize; pass a size for multiple elements, {withReplacement: true} to allow repeats | | array/arraySort | Length-preserving non-mutating sort by comparator (via remeda); a fixed-length tuple keeps its length (slots widen to the element union) | | array/arraySplit | Consolidates remeda's splitAt/splitWhen into [before, after]; pass a predicate to split at the first match; tuple-preserving for literal indices | | array/arraySwapIndexes | Swaps the elements at two indices (tuple-preserving via remeda); negative indices count from the end; out-of-bounds/NaN returns an unchanged copy | | array/arraySymmetricDifference | Consolidates xor/xorBy/xorWith; renamed to the set-theory term | | array/arrayTakeFirstBy | The count "smallest" elements by one or more OrderRules (via remeda's takeFirstBy), O(n) without sorting | | array/arrayTakeWhile | Consolidates takeWhile/takeRightWhile; pass true to walk from the end | | array/arrayTranspose | Consolidates zip/unzip/zipWith/unzipWith; optional (...column) => value iteratee | | array/arrayUnion | Consolidates union/unionBy/unionWith; optional mapper or comparator | | array/arrayUnique | Consolidates uniq/uniqBy/uniqWith; optional mapper or comparator | | array/flatMap | Defaults to deep flattening; pass a finite depth for a specific level | | array/flatten | Defaults to deep flattening; pass a finite depth for a specific level | | array/maxBy | Element with the largest getValue result; the result may be a number or a bigint | | array/minBy | Element with the smallest getValue result; the result may be a number or a bigint | | array/sortedArrayIndexOf | Binary search for an existing value in a sorted array; returns its index or -1; {rightmost?} | | array/sortedArrayInsertionIndex | Binary-search insertion point into a sorted array; {iteratee?, rightmost?} | | array/sortedArrayInsertionIndexWith | Binary-search insertion index via a monotonic predicate (via remeda's sortedIndexWith); any comparable condition, not just number/string keys | | dom/closestTyped | Typed closest; the matched ancestor's type is inferred from the selector literal | | dom/querySelectorAllTyped | Typed querySelectorAll returning a real array instead of a NodeList; optional search root | | dom/querySelectorTyped | Typed querySelector; optional search root defaulting to document; .as<T>() for custom elements | | function/curry | Consolidates curry/curryRight; pass true to collect arguments right-to-left | | function/flow | Consolidates flow/flowRight; functions as an array; pass true for right-to-left | | function/flowAsync | Async flow (from es-toolkit/fp): each function gets the awaited result of the previous one; functions as an array; pass true for right-to-left | | function/mapTimes | Renamed times; iteratee required; clearer than Array.from({length}, ...) | | function/maybeCall | Resolves a MaybeFn (value or getter); calls it with forwarded args when it is a function, else returns it as-is | | function/partial | Consolidates partial/partialRight; args as an array; pass true to pre-apply trailing | | function/toDisposable | Consolidates defer/deferAsync: wraps a cleanup callback for using/await using; an async callback gives an AsyncDisposable only | | iterable/countBy | - | | iterable/every | Generalized from set/every; simple (value) => boolean callback | | iterable/find | Generalized from set/find; simple (value) => boolean callback | | iterable/forEach | Generalized from set/forEach; simple (value) => void callback | | iterable/iterableCartesianProduct | Lazy Cartesian product of any iterables; the first one may be infinite | | iterable/iterableChunks | Lazy fixed-size chunks of any iterable | | iterable/iterableCount | Counts the elements of any iterable | | iterable/iterableDropWhile | Lazy dropWhile over any iterable | | iterable/iterableFirst | First element of any iterable (renamed head); closes the iterator | | iterable/iterablePartition | [matched, unmatched] split of any iterable; a type guard narrows both arrays | | iterable/iterableScan | Lazy running accumulation (prefix scan) of any iterable | | iterable/iterableTakeWhile | Lazy takeWhile over any iterable | | iterable/iterableTranspose | Lazy zip of an array of iterables; stops at the shortest one | | iterable/iterableUnique | Lazy uniqBy over any iterable; the mapper is optional | | iterable/keyedBy | - | | iterable/reduce | Generalized from set/reduce; simple (acc, value) => acc callback | | iterable/slidingWindow | - | | iterable/some | Generalized from set/some; simple (value) => boolean callback | | json/jsonParse | Strict, throwing JSON.parse upgrade; decodes standalone tokens; blocks prototype pollution | | json/jsonParseAsync | Promisified non-blocking JSON.parse (yields to the event loop); yieldable-json wrapper | | json/jsonParseSafe | Forgiving parse that never throws; falls back to the original input when it cannot be parsed | | json/jsonStringifyAsync | Promisified non-blocking JSON.stringify (yields to the event loop); yieldable-json wrapper | | json/structuredParse | Consolidates devalue's parse/unflatten; accepts the serialized string or its already-JSON.parsed form | | map/mapGetOrInsert | Reads key, inserting defaultValue first when it is missing; the return is non-optional, so mapGetOrInsert(map, key, []).push(item) is a one-liner. Mirrors the native Map#getOrInsert, not yet widely available | | map/mapGetOrInsertComputed | Lazy counterpart: the factory receives key and runs only on a miss, so every missing key gets a freshly built default. Mirrors the native Map#getOrInsertComputed | | math/bigintMedian | Consolidates bigint median/medianBy; accepts any iterable; throws for an empty input | | math/bigintSum | Consolidates bigint sum/sumBy; accepts any iterable (single-pass); 0n for an empty input | | math/clamp | Number and bigint overloads | | math/interpolate | Renamed lerp; (min, max, t, clamp = false); extrapolates unless clamp is true | | math/isInRange | Renamed inRange; number and bigint overloads | | math/mapRange | Renamed remap; (value, inputRange, outputRange, clamp = false); affine range remap, extrapolates unless clamp | | math/max | Single-pass over any iterable of numbers and/or bigints; returns undefined for an empty input | | math/mean | Generalized to any iterable (single-pass); optional (item) => number selector | | math/median | Generalized to any iterable (materialized + sorted); optional (item) => number selector | | math/min | Single-pass over any iterable of numbers and/or bigints; returns undefined for an empty input | | math/randomIntExclusive | Random integer in the exclusive range [min, max) (or [0, max)); strict literal-union return for small literal bounds (via remeda's randomInteger) | | math/randomIntInclusive | Random integer in the inclusive range [min, max] (or [0, max]); strict literal-union return for small literal bounds (via remeda's randomInteger) | | math/range | Number and bigint overloads | | math/rangeRight | Number and bigint overloads | | math/sum | Generalized to any iterable (single-pass); optional (item, index) => number selector | | misc/generateMailtoLink | Builds a percent-encoded mailto: link; recipients in the path by default ({recipientsAsQueryParam} for a to query param), cc/bcc/subject/body as params | | object/assignDefaults | Consolidates defaults/defaultsDeep/toDefaulted; single source or array, {deep, copy} modes | | object/findObjectKey | Consolidates findKey/findLastKey; pass true to scan from the end | | object/hasPath | Deep-path existence check; pass {inherited: true} to include the prototype chain | | object/mapObjectValuesByKey | Transforms each value by a per-key function (via remeda's evolve); untouched keys are kept, result stays precisely typed | | object/mergeDeep | Consolidates merge/mergeWith/toMerged; optional mergeValues customizer and {copy} mode; precise merged type without a customizer | | object/mergeObjects | Shallow-merges a non-empty object array into one (via remeda's mergeAll); precise positional merge type | | object/objectFromEntriesDeep | Deep-path Object.fromEntries; builds nested objects/arrays from [path, value] entries | | object/objectFromKeys | Builds an object from an array of keys via a mapper (via remeda's fromKeys); literal keys yield a precisely-typed record | | object/objectUpsertProperty | Returns a copy with one property inserted-or-overwritten, precisely typed (via remeda's addProp) | | object/omit | Consolidates omit/omitBy; top-level keys, deep paths, or a predicate | | object/pick | Consolidates pick/pickBy; top-level keys, deep paths, or a predicate | | object/setByPath | Consolidates set/setWith; mutating deep-path write with optional container customizer | | object/setByPathImmutable | Immutable, statically-typed deep set (via remeda's setPath); path & value type-checked against the object, no container creation | | object/swapObjectKeysValues | Consolidates invert/invertBy; pass an iteratee to group colliding keys into arrays | | object/swapObjectProperties | Swaps the values (and types) of two properties (via remeda's swapProps); object analog of arraySwapIndexes | | object/toPathSegments | Parses a path string into segments (via remeda's stringToPath); a literal string yields a precisely-typed tuple, numeric segments as numbers | | object/updateByPath | Consolidates update/updateWith; mutating deep-path update with optional container customizer | | predicate/isBuffer | Enhanced with a value is Buffer type guard | | predicate/isIn | ts-extras' objectHasIn with (key, object) argument order (reads like key in object); narrows the object | | predicate/isKeyIn | ts-extras' keyIn with (key, object) argument order; narrows the key to those present in the object | | predicate/isTruthy | Truthy type guard whose narrowing subtracts the falsy members (unlike .filter(Boolean), which keeps the type) | | server/readFileSafe | Reads a file, returning null on a missing file (ENOENT) but rethrowing other errors; asBinary for a raw Buffer | | string/ensurePrefix | (value, prefix) (subject-first); prepends prefix only when missing | | string/ensureSuffix | (value, suffix) (subject-first); appends suffix only when missing | | string/stringReverse | Reverses a string by grapheme (via es-toolkit's reverseString, so emoji stay intact); literal inputs get the precise reversed type | | string/toCamelCase | camelCase; es-toolkit runtime (Unicode-aware) + string-ts literal type. Renamed from es-toolkit's camelCase | | string/toConstantCase | CONSTANT_CASE; es-toolkit runtime + string-ts literal type | | string/toDelimiterCase | Joins words with a delimiter, preserving case (via es-toolkit's words); literal inputs get the precise type | | string/toKebabCase | kebab-case; es-toolkit runtime + string-ts literal type | | string/toLowerCase | Space-joined lower-case words; es-toolkit runtime + string-ts literal type | | string/toPascalCase | PascalCase; es-toolkit runtime + string-ts literal type | | string/toSnakeCase | snake_case; es-toolkit runtime + string-ts literal type | | string/toTitleCase | Title-cases a string (via remeda's toTitleCase); literal inputs get the precise title-cased type; preserves consecutive capitals by default. Replaces toStartCase | | string/toUpperCase | Space-joined UPPER-CASE words; es-toolkit runtime + string-ts literal type | | string/toWords | Splits a string into words (via es-toolkit); literal inputs get the precise word tuple | | ts/allUnionMembers | Compile-time guard that a tuple lists every member of a union exactly once (no missing, extra or duplicate); {readonly} keeps the tuple readonly | | value/cloneDeep | Consolidates cloneDeep/cloneDeepWith; optional customizer | | value/cloneShallow | Consolidates clone/cloneWith; optional customizer | | value/isEqual | Consolidates isEqual/isEqualWith; optional customizer | | value/isObjectMatching | Consolidates isMatch/isMatchWith; deep partial match; optional customizer | | value/typeOf | Enhanced typeof: lowercase for primitives and null, the Symbol.toStringTag class tag for objects (Array, Date, …) |

Custom types

| Our type group and name | Notes | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | types/ArrayWithMaxLength | Union of every shape an array can take with at most N elements, keeping per-position types; impossible shapes drop out (never if all do) | | types/Falsy | Union of the literal-representable falsy values (false, 0, 0n, '', null, undefined); NaN excluded (no literal type) | | types/Truthy | Complement of Falsy: removes the literal falsy members from a type (Truthy<0 \| 1> is 1) |

License

MIT (See LICENSE file for the full license text).

Bundled packages are licensed under their own terms — see THIRD_PARTY_NOTICES.md.

Contributors