roks-jsh
v0.1.4
Published
A comprehensive collection of JavaScript helper utilities for everyday development tasks
Maintainers
Readme
roks-jsh
A comprehensive, tree-shakeable collection of JavaScript/TypeScript helper utilities for everyday development tasks. Features 683+ tests across 33 modules covering dates, type guards, async operations, arrays, strings, numbers, events, storage, environment, password validation, object manipulation, file paths, colors, URL processing, and more.
Installation
npm install roks-jsh
pnpm install roks-jsh
yarn add roks-jsh
bun add roks-jshUsage
Named imports (recommended)
import { isEmpty, clamp, fnDebounce, capitalize, isString, formatDate } from 'roks-jsh';
isEmpty(''); // true
clamp(10, 0, 5); // 5
capitalize('hello'); // 'Hello'
isString('test'); // true
formatDate(new Date(), 'YYYY-MM-DD'); // '2024-01-15'Direct subpath imports
For maximum control or older bundlers, import directly from a specific module:
import { isEmpty } from 'roks-jsh/isEmpty';
import { capitalize, camelCase } from 'roks-jsh/stringUtils';
import { formatDate, relativeTime } from 'roks-jsh/dateUtils';
import { isString, isNumber } from 'roks-jsh/typeGuards';
import { range, zip, compact } from 'roks-jsh/arrayUtils';
import { deepFreeze } from 'roks-jsh/deepFreeze';
import { EventEmitter } from 'roks-jsh/eventEmitter';
import { retryWithBackoff } from 'roks-jsh/retryWithBackoff';
import { getEnv, requireEnv } from 'roks-jsh/envUtils';
import { storageSet, storageGet } from 'roks-jsh/storageUtils';CommonJS
const { isEmpty, validatePassword } = require('roks-jsh');Tree-Shaking
This library is designed for optimal tree-shaking. When you import only what you need, your bundler (webpack, Vite, esbuild, Rollup) will exclude everything else from your final bundle.
How it works:
"sideEffects": falseinpackage.jsontells bundlers every module is safe to drop if unused- The build outputs individual module files (not a single monolithic bundle), so bundlers can trace exactly which files are needed
- The barrel
index.jsuses pureexport { } fromre-exports that bundlers can statically analyze - Subpath exports (
roks-jsh/stringUtils,roks-jsh/dateUtils, etc.) bypass the barrel entirely for guaranteed zero overhead
Result: If you only use isEmpty and capitalize, your bundle includes ~300 bytes from this library — not the full package.
Available subpath imports
| Subpath | Contents |
|---------|----------|
| roks-jsh/isEmpty | isEmpty |
| roks-jsh/clamp | clamp |
| roks-jsh/chunkArray | chunkArray |
| roks-jsh/arrayAdvancedSearcher | arrayAdvancedSearcher |
| roks-jsh/arrayUtils | range, zip, unzip, flatten, compact, first, last, uniqueBy, bifurcate, frequency |
| roks-jsh/timercounter | TimeCounter, CounterObject |
| roks-jsh/asyncDelay | asyncDelay |
| roks-jsh/randomize | randomInt, randomHex, randomString, randomFloat |
| roks-jsh/canceldt | CancellationTokenSource, CancellationToken |
| roks-jsh/asyncSetTimeout | AsyncSetInterval |
| roks-jsh/stringMasker | maskString |
| roks-jsh/sortObjects | sortObjects |
| roks-jsh/passwordValidator | validatePassword, calculatePasswordStrength, etc. |
| roks-jsh/numberUtils | isEven, isOdd, isPrime, isPerfectSquare |
| roks-jsh/objectUtils | deepClone, deepMerge, pick, omit, isEqual, get, set, has, defaults |
| roks-jsh/urlUtils | parseUrl, isValidUrl, buildUrl, getQueryParams, etc. |
| roks-jsh/colorUtils | hexToRgb, rgbToHex, lighten, darken, getContrastRatio, etc. |
| roks-jsh/mathUtils | roundTo, lerp, mapRange, average, median, sum, factorial, etc. |
| roks-jsh/validationUtils | isValidEmail, isValidPhone, isValidCreditCard, isValidUUID, etc. |
| roks-jsh/cryptoUtils | hash, generateSalt, base64Encode, base64Decode, generateUUID, etc. |
| roks-jsh/promiseUtils | retry, timeout, parallel, sequence, delay, etc. |
| roks-jsh/filePathUtils | getFileExtension, joinPath, normalizePath, formatFileSize, etc. |
| roks-jsh/functionUtils | fnDebounce, fnThrottle, fnMemoize, fnOnce, fnRetry, etc. |
| roks-jsh/errorUtils | CustomError, ValidationError, parseError, withErrorHandling, etc. |
| roks-jsh/collectionUtils | groupBy, sortBy, unique, intersection, difference, partition, etc. |
| roks-jsh/stringUtils | capitalize, camelCase, kebabCase, snakeCase, slugify, truncate, etc. |
| roks-jsh/dateUtils | formatDate, relativeTime, dateDiff, isToday, startOf, endOf, addTime, etc. |
| roks-jsh/typeGuards | isString, isNumber, isArray, isPlainObject, isPromise, isNullish, etc. |
| roks-jsh/storageUtils | storageSet, storageGet, storageRemove, storageHas, storageClear, etc. |
| roks-jsh/eventEmitter | EventEmitter |
| roks-jsh/retryWithBackoff | retryWithBackoff |
| roks-jsh/deepFreeze | deepFreeze, isDeeplyFrozen, freezeClone |
| roks-jsh/envUtils | getEnv, requireEnv, getEnvNumber, getEnvBoolean, isProduction, isDevelopment, etc. |
Features
- ✅ 683+ tests with comprehensive coverage across 33 modules
- ✅ Full TypeScript support with JSDoc-annotated type definitions
- ✅ Fully tree-shakeable — preserved modules with subpath exports
- ✅ Zero dependencies — pure JavaScript utilities
- ✅ ESM & CommonJS dual output
- ✅ IDE-friendly — autocomplete with descriptions, examples, and parameter docs
API Reference
Date Utilities
import { formatDate, relativeTime, dateDiff, isToday, startOf, endOf, addTime } from 'roks-jsh';
formatDate(new Date(), 'YYYY-MM-DD HH:mm'); // '2024-06-15 14:30'
relativeTime(new Date(Date.now() - 3600000)); // '1 hour ago'
dateDiff(new Date(2024, 5, 15), new Date(2024, 5, 10), 'days'); // 5
isToday(new Date()); // true
startOf(new Date(), 'month'); // first day of month, 00:00:00
endOf(new Date(), 'day'); // today 23:59:59.999
addTime(new Date(), 7, 'days'); // one week from nowAlso: isYesterday, isTomorrow, isDateInRange, isLeapYear, daysInMonth.
Type Guards
Runtime type checking with TypeScript type narrowing:
import { isString, isNumber, isPlainObject, isNullish, isPromise, isArray } from 'roks-jsh';
isString('hello'); // true (narrows to string)
isNumber(42); // true (excludes NaN)
isPlainObject({}); // true (excludes arrays, class instances)
isNullish(null); // true (null | undefined)
isPromise(fetch('/')); // true
isArray([1, 2]); // trueAlso: isBoolean, isFunction, isObject, isNull, isUndefined, isDate, isRegExp, isSymbol, isMap, isSet, isError, isFiniteNumber, isInteger.
Storage Utilities
Safe localStorage/sessionStorage wrappers with JSON serialization and TTL:
import { storageSet, storageGet, storageRemove, storageHas } from 'roks-jsh';
storageSet('user', { name: 'Alice', age: 30 });
storageSet('token', 'abc123', { ttl: 3600000 }); // expires in 1 hour
storageSet('temp', 'data', { type: 'session' }); // sessionStorage
storageGet<string>('token'); // 'abc123' or null if expired
storageGet('missing', { defaultValue: 'fallback' }); // 'fallback'
storageHas('user'); // true
storageRemove('token');Also: isStorageAvailable, storageClear, storageKeys, storageSize.
Event Emitter
A tiny, typed pub/sub emitter:
import { EventEmitter } from 'roks-jsh';
interface Events {
login: { userId: string };
logout: undefined;
}
const emitter = new EventEmitter<Events>();
const dispose = emitter.on('login', (data) => {
console.log(`User ${data.userId} logged in`);
});
emitter.once('logout', () => console.log('Logged out'));
emitter.emit('login', { userId: '123' });
dispose(); // remove listener
emitter.removeAll(); // remove allRetry with Backoff
Exponential backoff with jitter, max delay, and abort signal:
import { retryWithBackoff } from 'roks-jsh';
const result = await retryWithBackoff(() => fetch('/api/data'), {
maxAttempts: 5,
initialDelay: 500,
maxDelay: 10000,
multiplier: 2,
jitter: true,
onRetry: (attempt, error, nextDelay) => {
console.log(`Attempt ${attempt} failed, retrying in ${nextDelay}ms`);
},
retryIf: (error) => (error as any).status !== 404, // don't retry 404s
});
console.log(result.data, result.attempts);
// With abort signal
const controller = new AbortController();
retryWithBackoff(fn, { signal: controller.signal });
controller.abort(); // cancels retriesDeep Freeze
Recursively freeze objects for immutability:
import { deepFreeze, isDeeplyFrozen, freezeClone } from 'roks-jsh';
const config = deepFreeze({
db: { host: 'localhost', port: 5432 },
features: ['auth', 'logging'],
});
// config.db.port = 3000; // throws in strict mode
isDeeplyFrozen(config); // true
// Clone and freeze without mutating original
const original = { a: { b: 1 } };
const frozen = freezeClone(original);
// original is still mutable, frozen is notEnvironment Utilities
Safe access to environment variables with type coercion:
import { getEnv, requireEnv, getEnvNumber, getEnvBoolean, isProduction, validateEnv } from 'roks-jsh';
const port = getEnvNumber('PORT', 3000);
const debug = getEnvBoolean('DEBUG', false);
const dbUrl = requireEnv('DATABASE_URL'); // throws if not set
if (isProduction()) { /* ... */ }
const { valid, missing } = validateEnv(['DATABASE_URL', 'API_KEY', 'SECRET']);
if (!valid) console.error('Missing env vars:', missing);Also: getEnvArray, isDevelopment, isTest.
Array Utilities (Extended)
import { range, zip, unzip, flatten, compact, first, last, uniqueBy, bifurcate, frequency } from 'roks-jsh';
range(5); // [0, 1, 2, 3, 4]
range(1, 10, 2); // [1, 3, 5, 7, 9]
range(5, 0); // [5, 4, 3, 2, 1]
zip([1, 2, 3], ['a', 'b', 'c']); // [[1,'a'], [2,'b'], [3,'c']]
unzip([[1,'a'], [2,'b']]); // [[1, 2], ['a', 'b']]
flatten([1, [2, [3, [4]]]], Infinity); // [1, 2, 3, 4]
compact([0, 1, false, 2, '', null]); // [1, 2]
first([1, 2, 3]); // 1
last([1, 2, 3]); // 3
uniqueBy(users, u => u.id);
bifurcate([1,2,3,4,5], n => n % 2 === 0); // [[2,4], [1,3,5]]
frequency(['a','b','a','c','a']); // Map { a=>3, b=>1, c=>1 }Basic Utilities
import { isEmpty, clamp } from 'roks-jsh';
isEmpty(''); // true
isEmpty([]); // true
isEmpty({}); // true
clamp(10, 0, 5); // 5
clamp(-3, 0, 10); // 0String Utilities
import { capitalize, camelCase, kebabCase, slugify, truncate, maskString } from 'roks-jsh';
capitalize('hello'); // 'Hello'
camelCase('hello world'); // 'helloWorld'
kebabCase('helloWorld'); // 'hello-world'
slugify('Hello, World!'); // 'hello-world'
truncate('long text', 7); // 'long...'
maskString('1234567890', { keepEnd: 4 }); // '******7890'Also: snakeCase, pascalCase, titleCase, isBlank, removeWhitespace, countWords, startsWithIgnoreCase, endsWithIgnoreCase, includesIgnoreCase, numberToWords, wordsToNumber.
Object Utilities
import { deepClone, deepMerge, pick, omit, isEqual, get, set, has, defaults } from 'roks-jsh';
const clone = deepClone({ a: { b: 1 } });
pick({ a: 1, b: 2, c: 3 }, ['a', 'b']); // { a: 1, b: 2 }
get({ a: { b: 1 } }, 'a.b'); // 1
set({}, 'a.b.c', 42); // { a: { b: { c: 42 } } }
isEqual({ a: 1 }, { a: 1 }); // trueNumber & Math Utilities
import { isPrime, isEven, roundTo, lerp, mapRange, factorial, range } from 'roks-jsh';
isPrime(7); // true
isEven(4); // true
roundTo(3.14159, 2); // 3.14
lerp(0, 100, 0.5); // 50
mapRange(5, 0, 10, 0, 100); // 50
factorial(5); // 120Also: isOdd, isPerfectSquare, clampPercentage, clampRange, percentage, isBetween, average, median, sum, min, max, gcd, lcm, degreesToRadians, radiansToDegrees.
Function Utilities
import { fnDebounce, fnThrottle, fnMemoize, fnOnce, fnRetry } from 'roks-jsh';
const debounced = fnDebounce(() => save(), 300);
const throttled = fnThrottle(() => scroll(), 100);
const memoized = fnMemoize((x) => expensiveCalc(x));
const once = fnOnce(() => init());
const withRetry = fnRetry(() => fetch('/api'), 3, 1000);Also: fnDelay, fnAfter, fnBefore, fnNegate, fnConstant, fnIdentity.
Async Utilities
import { asyncDelay, AsyncSetInterval, CancellationTokenSource } from 'roks-jsh';
await asyncDelay(100);
const interval = AsyncSetInterval(async () => await fetchData(), 5000);
interval.cancel();
const cts = new CancellationTokenSource();
cts.token.register(() => console.log('cancelled'));
cts.cancelAfter(5000, 'timeout');Promise Utilities
import { retry, timeout, parallel, sequence, delay } from 'roks-jsh';
const result = await retry(() => fetch('/api'), { attempts: 3 });
const timed = await timeout(fetch('/slow'), 5000);
const results = await parallel([task1, task2, task3], 2); // concurrency limit
await sequence([step1, step2, step3]);Also: raceWithTimeout, delayReject, withProgress.
Validation Utilities
import { isValidEmail, isValidPhone, isValidUUID, isValidIPv4 } from 'roks-jsh';
isValidEmail('[email protected]'); // true
isValidPhone('+1-555-123-4567'); // true
isValidUUID('550e8400-e29b-41d4-a716-446655440000'); // trueAlso: isValidCreditCard, isValidJSON, isValidBase64, isValidIPv6, isValidPostalCode, isValidPassword.
Password Validation
import { validatePassword, calculatePasswordStrength, getStrengthCategory } from 'roks-jsh';
const result = validatePassword('MySecure123!');
result.isValid; // true
const score = calculatePasswordStrength('MySecure123!');
getStrengthCategory(score); // 'strong'Also: hasMinLength, hasUppercase, hasLowercase, hasNumbers, hasSpecialChars, isNotCommon, hasNoSequential, hasNoRepeated.
Color Utilities
import { hexToRgb, lighten, darken, getContrastRatio, randomColor } from 'roks-jsh';
hexToRgb('#ff0000'); // { r: 255, g: 0, b: 0 }
lighten('#3366cc', 20); // lighter shade
getContrastRatio('#000', '#fff'); // 21
randomColor(); // e.g. '#a4f2b8'Also: rgbToHex, rgbToHexFromObject, isValidHex, meetsContrastStandard, rgbToHsl, hslToRgb, mixColors, invertColor, grayscale.
URL Utilities
import { parseUrl, buildUrl, getQueryParams, isValidUrl } from 'roks-jsh';
parseUrl('https://example.com/path?q=1')?.hostname; // 'example.com'
buildUrl('https://api.example.com/search', { q: 'js', limit: 10 });
getQueryParams('https://example.com?a=1&b=2'); // { a: '1', b: '2' }Also: isSecureUrl, getDomain, getPathname, getPort, setQueryParams, removeQueryParams, getQueryParam, hasQueryParam, cleanUrl, getOrigin, isSameOrigin, getUrlExtension, isImageUrl.
File Path Utilities
import { getFileExtension, joinPath, normalizePath, formatFileSize } from 'roks-jsh';
getFileExtension('document.pdf'); // 'pdf'
joinPath('folder', 'sub', 'file.txt'); // 'folder/sub/file.txt'
normalizePath('./folder/../file.txt'); // 'file.txt'
formatFileSize(1048576); // '1 MB'Also: getFilename, getDirectory, isAbsolutePath, resolvePath, getRelativePath, hasExtension, changeExtension, isDirectoryPath, sanitizeFilename, getMimeType.
Crypto Utilities
import { hash, generateSalt, base64Encode, generateUUID, randomPick } from 'roks-jsh';
hash('password', 'sha256');
generateSalt(16);
base64Encode('hello world');
generateUUID(); // '550e8400-e29b-41d4-a716-446655440000'
randomPick([1, 2, 3]); // random elementAlso: hashWithSalt, xorCipher, base64Decode, randomBool.
Collection Utilities
import { groupBy, unique, sortBy, partition, take, colShuffle } from 'roks-jsh';
groupBy([{ type: 'A', v: 1 }, { type: 'B', v: 2 }], i => i.type);
unique([1, 2, 2, 3]); // [1, 2, 3]
partition([1, 2, 3, 4], n => n % 2 === 0); // [[2, 4], [1, 3]]
take([1, 2, 3, 4, 5], 3); // [1, 2, 3]Also: intersection, difference, union, countBy, minBy, maxBy, drop, takeRight, dropRight.
Error Utilities
import { CustomError, ValidationError, isRetryableError, withErrorHandling } from 'roks-jsh';
throw new ValidationError('Invalid input');
throw new CustomError('Oops', 'CUSTOM_CODE', 400);
const safe = withErrorHandling(
() => riskyOperation(),
(err) => console.error(err)
);Also: AuthenticationError, AuthorizationError, NotFoundError, ConflictError, RateLimitError, NetworkError, TimeoutError, parseError, createRetryableError, extractStackTrace, parseStackFrame, getCallStack, withErrorBoundary, aggregateErrors, formatError.
Random Utilities
import { randomInt, randomString, randomHex, randomFloat } from 'roks-jsh';
randomInt(0, 100); // e.g. 42
randomString(8); // e.g. 'a4f2k8q1'
randomHex(6); // e.g. 'ff3a2b'
randomFloat(0, 1); // e.g. 0.7321Time Utilities
import { TimeCounter } from 'roks-jsh';
const t = TimeCounter.fromTime(1, 30, 15);
t.hours; // 1
t.minutes; // 30
t.seconds; // 15
t.toString(); // '01:30:15'Advanced Array Search
import { arrayAdvancedSearcher } from 'roks-jsh';
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
];
const results = arrayAdvancedSearcher(people, [
{ anyOf: [
{ key: 'name', op: 'includes', value: 'li' },
{ key: 'age', op: 'gte', value: 30 }
]}
]);Sort Objects
import { sortObjects } from 'roks-jsh';
const sorted = sortObjects(people, [
{ key: 'age', direction: 'desc' },
{ key: 'name', direction: 'asc' },
]);Development
npm install # Install dependencies
npm test # Run tests (vitest)
npm run build # Build for production
npm run lint # Lint codeContributing
PRs welcome! Please:
- Run
npm testand ensure all tests pass - Add tests for new features
- Update documentation
- Follow existing code style
License
MIT — see LICENSE for details.
Built with TypeScript, Vitest, and Rollup. All utilities are pure functions with no side effects.
