@react-hive/honey-utils
v3.8.0
Published
A lightweight TypeScript utility library providing a collection of helper functions for common programming tasks
Maintainers
Readme
@react-hive/honey-utils
A lightweight TypeScript utility library providing a collection of helper functions for common programming tasks.
Features
- 🔍 Type Guards - Functions for runtime type checking
- 🧵 String Utilities - String manipulation and transformation
- 🔢 Array Utilities - Array filtering and manipulation
- 🧮 Math Utilities - Common mathematical calculations
- 🎯 Function Utilities - Function handling helpers
- 🖥️ DOM Utilities - Browser DOM manipulation helpers
- 📦 Zero Dependencies - Lightweight and dependency-free
- 📝 TypeScript Support - Full TypeScript type definitions
Installation
# Using npm
npm install @react-hive/honey-utils
# Using yarn
yarn add @react-hive/honey-utils
# Using pnpm
pnpm add @react-hive/honey-utilsUsage
Importing
// Import specific utilities
import { toKebabCase, isString, boolFilter } from '@react-hive/honey-utils';
// Or import everything
import * as HoneyUtils from '@react-hive/honey-utils';String Utilities
import {
isString,
isNilOrEmptyString,
toKebabCase,
camelToDashCase,
splitStringIntoWords,
hashString
} from '@react-hive/honey-utils';
/**
* Check if value is a string
*/
isString('hello');
// ➜ true
isString(123);
// ➜ false
/**
* Check if value is null, undefined, or empty string
*/
isNilOrEmptyString('');
// ➜ true
isNilOrEmptyString(null);
// ➜ true
isNilOrEmptyString('hello');
// ➜ false
/**
* Convert string to kebab-case
*/
toKebabCase('helloWorld');
// ➜ 'hello-world'
/**
* Convert camelCase to dash-case
*/
camelToDashCase('helloWorld');
// ➜ 'hello-world'
/**
* Split string into words
*/
splitStringIntoWords('hello world');
// ➜ ['hello', 'world']
/**
* Generate a hash from a string
*/
const hash = hashString('background-color: red;');
// ➜ 'e4k1z0x'Array Utilities
import {
isArray,
isEmptyArray,
compact,
unique,
chunk,
intersection,
difference,
pipe,
compose,
} from '@react-hive/honey-utils';
/**
* Check if value is an array
*/
isArray([1, 2, 3]);
// ➜ true
isArray({});
// ➜ false
/**
* Check if value is an empty array
*/
isEmptyArray([]);
// ➜ true
isEmptyArray([1, 2, 3]);
// ➜ false
/**
* Filter out falsy values from an array
*/
compact([0, 1, false, 2, '', 3, null, undefined, true]);
// ➜ [1, 2, 3, true]
/**
* Remove duplicate values from an array
*/
unique([1, 2, 2, 3, 1, 4]);
// ➜ [1, 2, 3, 4]
/**
* Split an array into chunks of specified size
*/
chunk([1, 2, 3, 4, 5], 2);
// ➜ [[1, 2], [3, 4], [5]]
/**
* Find common elements between arrays
*/
intersection([1, 2, 3], [2, 3, 4]);
// ➜ [2, 3]
/**
* Find elements in one array not in another
*/
difference([1, 2, 3, 4], [2, 4]);
// ➜ [1, 3]
/**
* Compose functions from left to right
*/
const double = (n: number) => n * 2;
const increment = (n: number) => n + 1;
pipe(double, increment)(3);
// ➜ 7 → increment(double(3)) → increment(6)
/**
* Compose functions from right to left
*/
compose(increment, double)(3);
// ➜ 7 → increment(double(3)) → increment(6)Asynchronous Utilities
import {
isPromise,
runParallel,
runSequential,
reduceAsync,
filterParallel,
someAsync,
everyAsync,
findAsync,
} from '@react-hive/honey-utils';
/**
* Check if value is a Promise
*/
isPromise(Promise.resolve());
// ➜ true
isPromise({});
// ➜ false
/**
* Run async operations in parallel and collect results
*/
await runParallel([1, 2, 3], async (n) => {
await delay(100);
return n * 2;
});
// ➜ [2, 4, 6]
/**
* Run async operations sequentially and collect results
*/
await runSequential([1, 2, 3], async (n, i) => {
await delay(100);
return n * i;
});
// ➜ [0, 2, 6]
/**
* Reduce array asynchronously
*/
await reduceAsync([1, 2, 3], async (acc, n) => {
await delay(50);
return acc + n;
}, 0);
// ➜ 6
/**
* Filter array asynchronously
*/
await filterParallel([1, 2, 3, 4], async (n) => {
await delay(30);
return n % 2 === 0;
});
// ➜ [2, 4]
/**
* Check if some items match condition asynchronously
*/
await someAsync([1, 3, 5], async (n) => {
await delay(10);
return n % 2 === 0;
});
// ➜ false
/**
* Check if all items match condition asynchronously
*/
await everyAsync([2, 4, 6], async (n) => {
await delay(10);
return n % 2 === 0;
});
// ➜ true
/**
* Find first matching item asynchronously
*/
await findAsync([1, 3, 4, 5], async (n) => {
await delay(20);
return n % 2 === 0;
});
// ➜ 4Function Utilities
import {
noop,
isFunction,
invokeIfFunction,
delay,
retry
} from '@react-hive/honey-utils';
/**
* No-operation function. Does nothing
*/
noop();
/**
* Check if value is a function
*/
isFunction(() => {});
// ➜ true
isFunction({});
// ➜ false
/**
* Invoke if function, otherwise return value
*/
const fn = (x: number) => x * 2;
invokeIfFunction(fn, 5); // 10
invokeIfFunction('not a function', 5);
// ➜ 'not a function'
/**
* Waits for 1 second before continuing
*/
await delay(1000);
/**
* Retry an async function with configurable options
*/
async function fetchData() {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('Network error');
}
return await response.json();
}
const fetchWithRetry = retry(fetchData, {
maxAttempts: 5,
delayMs: 500,
backoff: true,
onRetry: (attempt, error) => {
console.warn(`Attempt ${attempt} failed:`, error);
}
});
fetchWithRetry()
.then(data => console.log('Success:', data))
.catch(error => console.error('Failed after retries:', error));Type Guards
import {
isNumber,
isBool,
isObject,
isNil,
isEmptyObject,
isNull,
isUndefined,
isDate,
isValidDate,
isRegExp,
isMap,
isSet
} from '@react-hive/honey-utils';
/**
* Check if value is a number
*/
isNumber(123);
// ➜ true
isNumber('123');
// ➜ false
/**
* Check if value is a boolean
*/
isBool(true);
// ➜ true
isBool('true');
// ➜ false
/**
* Check if value is an object
*/
isObject({});
// ➜ true
isObject('object');
// ➜ false
/**
* Check if value is null or undefined
*/
isNil(null);
// ➜ true
isNil(undefined);
// ➜ true
isNil('');
// ➜ false
/**
* Check if value is an empty object
*/
isEmptyObject({});
// ➜ true
isEmptyObject({ key: 'value' });
// ➜ false
/**
* Check if value is a Date object
*/
isDate(new Date());
// ➜ true
isDate('2023-01-01');
// ➜ false
/**
* Check if value is a valid Date object
*/
isValidDate(new Date());
// ➜ true
isValidDate(new Date('invalid'));
// ➜ false
/**
* Check if value is a RegExp
*/
isRegExp(/test/);
// ➜ true
isRegExp('test');
// ➜ false
/**
* Check if value is a Map or Set
*/
isMap(new Map());
// ➜ true
isSet(new Set());
// ➜ trueMath Utilities
import {
calculateEuclideanDistance,
calculateMovingSpeed,
calculatePercentage
} from '@react-hive/honey-utils';
/**
* Calculate Euclidean distance between two points
*/
calculateEuclideanDistance(0, 0, 3, 4);
// ➜ 5
/**
* Calculate moving speed
*/
calculateMovingSpeed(100, 5);
// ➜ 20
/**
* Calculate percentage of a value
*/
calculatePercentage(200, 25);
// ➜ 50DOM Utilities
import { parse2DMatrix, cloneBlob } from '@react-hive/honey-utils';
/**
* Extract transformation values from an HTML element's 2D matrix
*/
const element = document.getElementById('my-element');
if (element) {
const { translateX, translateY, scaleX, scaleY, skewX, skewY } = parse2DMatrix(element);
console.log(`Element is translated by ${translateX}px horizontally and ${translateY}px vertically`);
console.log(`Element is scaled by ${scaleX} horizontally and ${scaleY} vertically`);
console.log(`Element is skewed by ${skewX} horizontally and ${skewY} vertically`);
}
/**
* Clone a Blob object
*/
const originalBlob = new Blob(['Hello World'], { type: 'text/plain' });
const clonedBlob = cloneBlob(originalBlob);
console.log(clonedBlob.type);
// ➜ 'text/plain'File Utilities
import { parseFileName, fileListToFile, blobToFile } from '@react-hive/honey-utils';
/**
* Parse a file name into base name + extension
*/
const [base, ext] = parseFileName('archive.tar.gz');
console.log(base);
// ➜ 'archive.tar'
console.log(ext);
// ➜ 'gz'
/**
* Hidden file (no name)
*/
parseFileName('.gitignore');
// ➜ ['.gitignore', '']
/**
* No file extension
*/
parseFileName('README');
// ➜ ['README', '']
/**
* Convert a FileList to an array
*/
const input = document.querySelector('input[type="file"]')!;
input.addEventListener('change', () => {
const files = fileListToFiles(input.files);
console.log(Array.isArray(files));
// ➜ true
console.log(files[0] instanceof File);
// ➜ true
});
/**
* Convert a Blob to a File
*/
const blob = new Blob(['Hello world'], { type: 'text/plain' });
const file = blobToFile(blob, 'hello.txt');
console.log(file instanceof File);
// ➜ true
console.log(file.name);
// ➜ 'hello.txt'
console.log(file.type);
// ➜ 'text/plain'Assert Function
import { assert } from '@react-hive/honey-utils';
// Assert a condition
function divide(a: number, b: number): number {
assert(b !== 0, 'Cannot divide by zero');
return a / b;
}API Documentation
String Utilities
isString(value: unknown): value is string- Checks if a value is astring.isNilOrEmptyString(value: unknown): value is null | undefined- Checks if a value isnull,undefined, or an empty string.toKebabCase(input: string): string- Converts a string to kebab-case.camelToDashCase(input: string): string- Converts camelCase to dash-case.splitStringIntoWords(input: string): string[]- Splits a string into an array of words.hashString(input: string): string- Generates a short hash from a string.
Object Utilities
definedProps<T extends object>(obj: T): DefinedProps<T>- Creates a new object by removing all properties whose values areundefined.
Array Utilities
isArray(value: unknown): value is unknown[]- Checks if a value is an array.isEmptyArray(value: unknown): value is []- Checks if a value is an empty array.compact<T>(array: (T | Falsy)[]): T[]– Returns a new array with all falsy values (false, null, undefined, 0, '', NaN) removed, preserving only truthy items of typeT.unique<T>(array: T[]): T[]- Returns a new array with all duplicate elements removed. Keeps only the first occurrence of each value.chunk<T>(array: T[], size: number): T[][]- Splits an array into smaller arrays ("chunks") of the specified size.intersection<T>(...arrays: T[][]): T[]- Returns an array of elements that exist in all provided arrays.difference<T>(array: T[], exclude: T[]): T[]- Returns a new array that contains items fromarraythat are not present inexclude.pipe(...fns: Function[]): Function- Composes unary functions left-to-right. Returns a new function that applies all given functions in a sequence.compose(...fns: Function[]): Function- Composes unary functions right-to-left. Same aspipe, but applies functions in reverse order.
Function Utilities
isFunction(value: unknown): value is Function- Checks if a value is afunction.noop(): void- A no-operation function.not<Args extends any[]>(fn: (...args: Args) => any): (...args: Args) => boolean- Creates a new function that negates the result of the given predicate function. Useful for logical inversions, e.g., turningisEvenintoisOdd.invokeIfFunction<Args extends any[], Result>(input: ((...args: Args) => Result) | Result, ...args: Args): Result- Invokes the input if it's a function, otherwise returns it as-is.delay(delayMs: number): Promise<void>- Creates a promise that resolves after the specified delay in milliseconds.timeout<T>(promise: Promise<T>, timeoutMs: number, message?: string): Promise<T>- Wraps a promise with a timeout. If the promise does not settle within the given duration, it rejects with a timeout error.retry<Task, TaskResult>(task: Task, options?: RetryOptions): Function- Wraps an asynchronous function with retry logic, with configurable max attempts, delay between retries, exponential backoff, and retry callbacks.once<T extends (...args: any[]) => any>(fn: T): T- Wraps a function so it can only be executed once. The result of the first invocation is cached and returned for all subsequent calls. Preserves both the original function’s parameter types andthisbinding.
Type Guards
assert(condition: any, message: string): asserts condition- Asserts that a condition is truthy, throwing an error with the provided message if it's not.isNumber(value: unknown): value is number- Checks if a value is anumber.isBool(value: unknown): value is boolean- Checks if a value is aboolean.isObject(value: unknown): value is object- Checks if a value is anobject.isNil(value: unknown): value is null | undefined- Checks if a value isnullorundefined.isEmptyObject(value: unknown): value is Record<string, never>- Checks if a value is an empty object.isNull(value: unknown): value is null- Checks if a value isnull.isUndefined(value: unknown): value is undefined- Checks if a value isundefined.isDefined<T>(value: T): value is NonNullable<T>- Checks if a value is neithernullnorundefined.isFiniteNumber(value: unknown): value is number- Checks if a value is a finite number.isInteger(value: unknown): value is number- Checks if a value is an integer.isDecimal(value: unknown): value is number- Checks if a value is a decimal number (finite and non-integer).isDate(value: unknown): value is Date- Checks if a value is aDateobject.isValidDate(value: unknown): value is Date- Checks if a value is a validDateobject.isRegExp(value: unknown): value is RegExp- Checks if a value is aRegExpobject.isMap(value: unknown): value is Map<unknown, unknown>- Checks if a value is aMap.isSet(value: unknown): value is Set<unknown>- Checks if a value is aSet.isSymbol(value: unknown): value is symbol- Checks if a value is aSymbol.isBlob(value: unknown): value is Blob— Checks if a value is aBlob.isError(value: unknown): value is Error— Checks if a value is anErrorobject.
Math Utilities
calculateEuclideanDistance(startX: number, startY: number, endX: number, endY: number): number- Calculates the Euclidean distance between two points.calculateMovingSpeed(distance: number, elapsedTime: number): number- Calculates moving speed.calculatePercentage(value: number, percentage: number): number- Calculates the specified percentage of a value.
DOM Utilities
parse2DMatrix(element: HTMLElement): { translateX: number, translateY: number, scaleX: number, scaleY: number, skewX: number, skewY: number }- Extracts transformation values (translate, scale, skew) from the 2D transformation matrix of a given HTML element.cloneBlob(blob: Blob): Blob- Creates a clone of a Blob object with the same content and type as the original.getDOMRectIntersectionRatio(sourceRect: DOMRect, targetRect: DOMRect): number- Calculates the ratio of thetargetRectthat is overlapped by thesourceRect. Returns a number between0(no overlap) and1(fully covered).getElementOffsetRect(element: HTMLElement): DOMRect- Returns aDOMRectrepresenting the element's layout position usingoffsetLeft,offsetTop, andclientWidth/clientHeight.isAnchorHtmlElement(element: HTMLElement): element is HTMLAnchorElement- Determines whether the provided element is an<a>tag. Acts as a type guard that narrows the element toHTMLAnchorElement.isContentEditableHtmlElement(element: HTMLElement): boolean- Returnstrueif the element hascontenteditable="true", making it user-editable and implicitly focusable.isHtmlElementFocusable(element: Nullable<HTMLElement>): boolean- Checks whether an element is considered focusable according to browser rules. Factors include: visibility,display,disabled,tabindex, native focusable tags,contenteditable, and presence of a non-nulltabindex.getFocusableHtmlElements(container: HTMLElement): HTMLElement[]- Returns all focusable descendant elements within a container, usingisHtmlElementFocusableto filter them.isLocalStorageReadable(): boolean- Determines whetherlocalStoragecan be safely read from. This check works even when writes fail (e.g., due toQuotaExceededError) and ensures that callinggetItem()does not throw in restricted environments.getLocalStorageCapabilities(): LocalStorageCapabilities- Detects the browser's read and write capabilities forlocalStorage. Readability is determined by safe execution ofgetItem(), while writability requires successfulsetItem()andremoveItem().
File Utilities
isFile(value: unknown): value is File- Checks if a value is aFile.parseFileName(fileName: string): [baseName: string, extension: string]- Splits a file name into its base name and extension using the last.as the separator. Handles edge cases such as hidden files (.gitignore), multi-dot names (archive.tar.gz), and names ending with a dot ("file."). The extension is returned in lowercase.fileListToFiles(fileList: FileList | null): File[]- Converts aFileListobject (such as the one returned from an<input type="file">) into a standard array ofFileobjects. Returns an empty array when the input isnull.blobToFile(blob: Blob, fileName: string): File- Converts a Blob object into a File object with the specified name.traverseFileSystemDirectory(directoryEntry: FileSystemDirectoryEntry, options?: TraverseDirectoryOptions): Promise<File[]>— Recursively scans a directory using the File System API and returns all nested files asFileobjects. Supports skipping system files.readFilesFromDataTransfer(dataTransfer: DataTransfer | null, options?: TraverseDirectoryOptions): Promise<File[]>— Extracts files from aDataTransferobject (e.g., drag-and-drop or paste events). Supports both regular files and entire directories via the non-standardwebkitGetAsEntryAPI. Directories are traversed recursively usingtraverseFileSystemDirectory, producing a fully flattened list of all discoveredFileobjects.
Asynchronous Utilities
isPromise<T = unknown>(value: unknown): value is Promise<T>- Checks if a value is aPromise.runSequential<Item, Result>(array: Item[], fn: (item, index, array) => Promise<Result>): Promise<Result[]>- Runs asynchronous operations on each array item sequentially and returns the results in the original order.runParallel<Item, Result>(array: Item[], fn: (item, index, array) => Promise<Result>): Promise<Result[]>- Executes an asynchronous function for each array item in parallel and returns a promise of all results.reduceAsync<Item, Accumulator>(array: Item[], fn, initialValue): Promise<Accumulator>- Asynchronously reduces an array to a single accumulated value. Each step waits for the previous promise to resolve.filterSequential<Item>(array: Item[], predicate): Promise<Item[]>– Filters an array using an asynchronous predicate sequentially. Each item is processed one after another, and only those for which predicate(item) returns true are included in the result.filterParallel<Item>(array: Item[], predicate): Promise<Item[]>– Filters an array using an asynchronous predicate in parallel. Returns a promise that resolves with only the items for which predicate(item) returnstrue.someAsync<Item>(array: Item[], predicate): Promise<boolean>- Returnstrueif any item in the array passes the async predicate.everyAsync<Item>(array: Item[], predicate): Promise<boolean>- Returnstrueif all items in the array pass the async predicate.findAsync<Item>(array: Item[], predicate): Promise<Nullable<Item>>- Returns the first array item that passes the async predicate, ornullif no match is found.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Author
Mykhailo Aliinyk [email protected]
