@eroyeroy/utils - v0.1.0
    Preparing search index...

    @eroyeroy/utils - v0.1.0

    @eroyeroy/utils

    A fully typed TypeScript utility library with tree-shakeable subpath imports and zero runtime dependencies.

    npm

    npm i @eroyeroy/utils
    

    pnpm

    pnpm add @eroyeroy/utils
    

    yarn

    yarn add @eroyeroy/utils
    

    bun

    bun add @eroyeroy/utils
    

    Import from the root or from any subpath:

    // Full import
    import { groupBy, isEmail, formatBytes } from "@eroyeroy/utils"

    // Subpath import (smallest bundle)
    import { groupBy } from "@eroyeroy/utils/array"
    import { isEmail } from "@eroyeroy/utils/validation"
    import { formatBytes } from "@eroyeroy/utils/format"
    Module Subpath Functions
    Array @eroyeroy/utils/array 34
    Guards @eroyeroy/utils/guards 14
    String @eroyeroy/utils/string 11
    Object @eroyeroy/utils/object 9
    Number @eroyeroy/utils/number 10
    Async @eroyeroy/utils/async 4
    Function @eroyeroy/utils/function 6
    URL @eroyeroy/utils/url 7
    Format @eroyeroy/utils/format 4
    Date @eroyeroy/utils/date 16
    Validation @eroyeroy/utils/validation 10

    import { ... } from "@eroyeroy/utils/array"
    

    Typed Array.prototype.includes. Narrows the type of value to T[number] on true.

    const roles = ["admin", "user"] as const
    arrayIncludes(roles, "admin") // => true, typed as "admin" | "user"

    Average of a numeric key. Returns 0 for empty arrays.

    averageBy([{ score: 10 }, { score: 20 }, { score: 30 }], "score") // => 20
    

    Splits into chunks of at most size elements.

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

    Removes null and undefined.

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

    Counts occurrences of each value of key.

    countBy([{ role: "admin" }, { role: "user" }, { role: "admin" }], "role")
    // => { admin: 2, user: 1 }

    Removes the first count elements.

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

    Finds the first item where item[key] === value.

    findArrayItemByKey([{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }], "id", 2)
    // => { id: 2, name: "Bob" }

    Returns the first element (typed as non-undefined for non-empty tuples).

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

    Flattens one level of nesting.

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

    Groups items by the value of a key.

    groupBy([{ role: "admin", name: "Alice" }, { role: "user", name: "Bob" }], "role")
    // => { admin: [{ role: "admin", name: "Alice" }], user: [{ role: "user", name: "Bob" }] }

    Returns true if any item matches item[key] === value.

    hasArrayItemByKey([{ id: 1 }, { id: 2 }], "id", 2) // => true
    

    Inserts at index (defaults to end).

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

    Type guard for arrays.

    isArray([1, 2, 3]) // => true
    

    Returns true if value is an array with zero elements.

    isEmptyArray([]) // => true
    

    Returns true if value is an array with at least one element.

    isFilledArray([1]) // => true
    

    Returns the last element (typed as non-undefined for non-empty tuples).

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

    Transforms an array to a record keyed by key.

    mapArrayToRecord([{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }], "id")
    // => { 1: { id: 1, name: "Alice" }, 2: { id: 2, name: "Bob" } }

    Returns the item with the maximum value of key.

    maxBy([{ age: 25 }, { age: 30 }, { age: 20 }], "age") // => { age: 30 }
    

    Returns the item with the minimum value of key.

    minBy([{ age: 25 }, { age: 30 }, { age: 20 }], "age") // => { age: 20 }
    

    Moves an item to a new position.

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

    Splits into [matched, unmatched].

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

    Creates a range of integers.

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

    Removes all occurrences of an item by reference.

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

    Removes all items where item[key] === value.

    removeArrayItemByKey([{ id: 1 }, { id: 2 }, { id: 1 }], "id", 1)
    // => [{ id: 2 }]

    Replaces all items equal by reference.

    replaceArrayItem([1, 2, 3, 2], 2, 99) // => [1, 99, 3, 99]
    

    Replaces all items matching item[key] === value. Accepts a function.

    replaceArrayItemByKey([{ id: 1, x: 0 }], "id", 1, item => ({ ...item, x: 99 }))
    // => [{ id: 1, x: 99 }]

    Fisher-Yates shuffle. Accepts a custom RNG for deterministic results.

    shuffleArray([1, 2, 3, 4, 5]) // => e.g. [3, 1, 5, 2, 4]
    

    Sorts by key ("asc" or "desc"). Supports strings, numbers, booleans, and Dates.

    sortBy([{ age: 30 }, { age: 20 }, { age: 25 }], "age")        // ascending
    sortBy([{ age: 30 }, { age: 20 }, { age: 25 }], "age", "desc") // descending

    Sum of a numeric key.

    sumBy([{ price: 10 }, { price: 20 }, { price: 30 }], "price") // => 60
    

    Returns the first count elements.

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

    Removes the item if present, appends if absent. Useful for selection sets.

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

    Removes duplicate values, preserving insertion order.

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

    Removes duplicates based on a key, keeping the first occurrence.

    uniqueBy([{ id: 1, v: "a" }, { id: 1, v: "b" }, { id: 2, v: "c" }], "id")
    // => [{ id: 1, v: "a" }, { id: 2, v: "c" }]

    Updates all items matching item[key] === value. Accepts a partial object or function.

    updateArrayItemByKey([{ id: 1, x: 0 }, { id: 2, x: 0 }], "id", 1, { x: 99 })
    // => [{ id: 1, x: 99 }, { id: 2, x: 0 }]

    updateArrayItemByKey([{ id: 1, x: 5 }], "id", 1, item => ({ ...item, x: item.x * 2 }))
    // => [{ id: 1, x: 10 }]

    import { ... } from "@eroyeroy/utils/guards"
    
    Function Description
    isString(value) value is string
    isNumber(value) value is number (excludes NaN)
    isBoolean(value) value is boolean
    isNull(value) value is null
    isUndefined(value) value is undefined
    isNullish(value) value is null | undefined
    isDefined(value) value is T — useful for array.filter(isDefined)
    isObject(value) Plain object (not null, not array)
    isFunction(value) value is (...args) => unknown
    isDate(value) value instanceof Date
    isError(value) value instanceof Error
    isSymbol(value) value is symbol
    isBigInt(value) value is bigint
    isRegExp(value) value instanceof RegExp
    [1, null, 2, undefined].filter(isDefined) // => [1, 2]
    

    import { ... } from "@eroyeroy/utils/string"
    
    Function Description Example
    capitalize(str) Uppercases the first character "hello""Hello"
    camelCase(str) Converts to camelCase "hello_world""helloWorld"
    snakeCase(str) Converts to snake_case "helloWorld""hello_world"
    kebabCase(str) Converts to kebab-case "HelloWorld""hello-world"
    pascalCase(str) Converts to PascalCase "hello-world""HelloWorld"
    truncate(str, max, suffix?) Truncates to max chars truncate("Hello, World!", 8)"Hello..."
    slugify(str) URL-friendly slug "Hello, World!""hello-world"
    reverseString(str) Reverses characters "hello""olleh"
    maskString(str, visible?, char?) Masks all but last N chars maskString("1234567890")"******7890"
    countWords(str) Counts whitespace-separated words "hello world"2
    isEmptyString(str) True if empty or only whitespace "" / " "true
    isFilledString(str) True if has non-whitespace content "hi"true

    import { ... } from "@eroyeroy/utils/object"
    
    Function Description
    pick(obj, keys) Returns a new object with only the specified keys
    omit(obj, keys) Returns a new object with the specified keys excluded
    objectKeys(obj) Typed Object.keys
    objectValues(obj) Typed Object.values
    objectEntries(obj) Typed Object.entries
    mapValues(obj, fn) Maps values, preserving keys
    filterValues(obj, predicate) Filters entries by value
    isEmptyObject(obj) True if no own enumerable keys
    isFilledObject(obj) True if at least one own enumerable key
    pick({ a: 1, b: 2, c: 3 }, ["a", "c"])         // => { a: 1, c: 3 }
    omit({ a: 1, b: 2, c: 3 }, ["b"]) // => { a: 1, c: 3 }
    mapValues({ a: 1, b: 2 }, v => v * 2) // => { a: 2, b: 4 }
    filterValues({ a: 1, b: 2, c: 3 }, v => v > 1) // => { b: 2, c: 3 }

    import { ... } from "@eroyeroy/utils/number"
    
    Function Description Example
    clamp(value, min, max) Clamps to [min, max] clamp(15, 0, 10)10
    inRange(value, min, max) Checks inclusive range inRange(5, 0, 10)true
    roundTo(value, precision) Rounds to N decimal places roundTo(1.2345, 2)1.23
    isInteger(value) True if safe integer isInteger(42)true
    isFloat(value) True if finite non-integer isFloat(1.5)true
    isPositive(value) True if > 0 isPositive(1)true
    isNegative(value) True if < 0 isNegative(-1)true
    isZero(value) True if === 0 isZero(0)true
    randomBetween(min, max) Random float in [min, max) randomBetween(1, 10)
    percentageOf(value, total) Calculates percentage percentageOf(1, 4)25

    import { ... } from "@eroyeroy/utils/async"
    

    Pauses for ms milliseconds.

    await sleep(1000)
    

    Retries an async function on failure with optional exponential backoff.

    const data = await retry(() => fetch("/api").then(r => r.json()), {
    retries: 3,
    delay: 200,
    backoff: 2,
    })

    Rejects with an error if the promise doesn't resolve within ms.

    const result = await withTimeout(fetch("/api"), 5000)
    

    Runs async tasks with an optional concurrency limit. Preserves result order.

    const results = await parallel([
    () => fetchUser(1),
    () => fetchUser(2),
    () => fetchUser(3),
    ], 2) // at most 2 concurrent requests

    import { ... } from "@eroyeroy/utils/function"
    

    Delays invocation until wait ms have passed since the last call.

    const handleInput = debounce((value: string) => search(value), 300)
    

    Limits invocation to at most once per limit ms.

    const handleScroll = throttle(() => updatePosition(), 100)
    

    Caches results by serialized arguments.

    const expensiveCalc = memoize((n: number) => n * n)
    expensiveCalc(4) // computed
    expensiveCalc(4) // cached

    Calls fn only on the first invocation; returns the cached result thereafter.

    const init = once(() => createConnection())
    init() // creates connection
    init() // returns cached connection

    Does nothing. Useful as a default callback.

    element.addEventListener("click", noop)
    

    Returns the value unchanged. Useful as a default transform or selector.

    [1, 2, 3].map(identity) // => [1, 2, 3]
    

    import { ... } from "@eroyeroy/utils/url"
    
    Function Description
    parseUrl(url) Parses to URL or returns null on failure
    buildUrl(base, params) Appends query params (skips null/undefined values)
    getQueryParam(url, param) Gets a single query param or null
    setQueryParam(url, param, value) Sets/replaces a query param
    removeQueryParam(url, param) Removes a query param
    isValidUrl(url) Returns true for valid absolute URLs
    joinUrl(base, path) Joins URL and path, normalizing slashes
    buildUrl("https://api.example.com", { page: 1, q: "hello", sort: null })
    // => "https://api.example.com/?page=1&q=hello"

    getQueryParam("https://example.com?q=hello", "q") // => "hello"
    joinUrl("https://example.com/api/", "/users") // => "https://example.com/api/users"

    import { ... } from "@eroyeroy/utils/format"
    
    formatBytes(0)          // => "0 B"
    formatBytes(1024) // => "1.00 KB"
    formatBytes(1048576, 1) // => "1.0 MB"
    formatDuration(500)      // => "500ms"
    formatDuration(5000) // => "5s"
    formatDuration(90000) // => "1m 30s"
    formatDuration(3660000) // => "1h 1m"
    formatRelativeTime(new Date(Date.now() - 5 * 60 * 1000))     // => "5 minutes ago"
    formatRelativeTime(new Date(Date.now() + 24 * 60 * 60 * 1000)) // => "in 1 day"
    formatPlural(1, "item")             // => "item"
    formatPlural(3, "item") // => "items"
    formatPlural(2, "person", "people") // => "people"

    import { ... } from "@eroyeroy/utils/date"
    

    All functions return new Date objects and do not mutate inputs.

    Function Description
    isValidDate(value) Type guard — Date instance with a valid time
    addDays(date, days) Adds days (negative to subtract)
    addMonths(date, months) Adds months
    addYears(date, years) Adds years
    diffInDays(dateA, dateB) Whole days from A to B (negative if B < A)
    diffInMonths(dateA, dateB) Whole months from A to B
    startOfDay(date) 00:00:00.000 of the same day (local time)
    endOfDay(date) 23:59:59.999 of the same day (local time)
    startOfMonth(date) First day of the month at midnight
    endOfMonth(date) Last moment of the month
    isSameDay(dateA, dateB) True if same calendar day
    isBefore(date, referenceDate) True if date < referenceDate
    isAfter(date, referenceDate) True if date > referenceDate
    isToday(date) True if today
    isPast(date) True if in the past
    isFuture(date) True if in the future
    addDays(new Date("2024-01-01"), 7)                   // => 2024-01-08
    diffInDays(new Date("2024-01-01"), new Date("2024-01-08")) // => 7
    isBefore(new Date("2024-01-01"), new Date("2024-06-01")) // => true

    import { ... } from "@eroyeroy/utils/validation"
    
    Function Description
    isEmail(value) Simple local@domain.tld format check
    isUuid(value) UUID v1–v5
    isNumericString(value) String that represents a valid number
    isAlpha(value) Only a–z / A–Z characters
    isAlphaNumeric(value) Only a–z, A–Z, 0–9 characters
    hasMinLength(value, min) String length ≥ min
    hasMaxLength(value, max) String length ≤ max
    isHexColor(value) #rgb or #rrggbb CSS hex color
    isIpv4(value) Valid IPv4 address
    isIpv6(value) Valid IPv6 address
    isEmail("user@example.com")                          // => true
    isUuid("550e8400-e29b-41d4-a716-446655440000") // => true
    isHexColor("#1A2B3C") // => true
    isIpv4("192.168.1.1") // => true
    isIpv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") // => true

    Generate the full HTML API docs with:

    pnpm docs
    

    Output is written to ./docs/index.html.

    pnpm install      # install dependencies
    pnpm test # run tests
    pnpm test:watch # run tests in watch mode
    pnpm lint # lint source
    pnpm lint:fix # lint and auto-fix
    pnpm format # format with Prettier
    pnpm check-types # TypeScript type check
    pnpm build # build ESM + CJS bundles
    pnpm ci # lint + type-check + test + build

    MIT