UNPKG

@catbee/utils

Version:

A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.

356 lines (352 loc) 11.4 kB
/* * The MIT License * * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import { getValueByPath } from '@catbee/utils/object'; import { randomBytes } from 'crypto'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); function chunk(array, size) { if (!Array.isArray(array)) throw new TypeError("Expected an array"); if (array.length === 0) return []; if (!Number.isInteger(size) || size <= 0) throw new Error("Chunk size must be a positive integer"); const result = []; for (let i = 0; i < array.length; i += size) { result.push(array.slice(i, i + size)); } return result; } __name(chunk, "chunk"); function unique(array, keyFn) { if (!Array.isArray(array) || array.length === 0) return []; if (!keyFn) return Array.from(new Set(array)); const seen = /* @__PURE__ */ new Set(); const result = []; for (const item of array) { const key = keyFn(item); if (!seen.has(key)) { seen.add(key); result.push(item); } } return result; } __name(unique, "unique"); function flattenDeep(array) { if (!Array.isArray(array)) return []; const result = []; const stack = [ ...array ]; while (stack.length) { const val = stack.pop(); if (Array.isArray(val)) { stack.push(...val); } else { result.push(val); } } return result.reverse(); } __name(flattenDeep, "flattenDeep"); function random(array) { if (!Array.isArray(array) || array.length === 0) return void 0; return array[secureIndex(array.length)]; } __name(random, "random"); function groupBy(array, keyOrFn) { if (!Array.isArray(array) || array.length === 0) return {}; const keyFn = typeof keyOrFn === "function" ? keyOrFn : (item) => String(getValueByPath(item, keyOrFn)); const result = {}; for (const item of array) { const key = String(keyFn(item)); if (Object.hasOwn(result, key)) { result[key].push(item); } else { result[key] = [ item ]; } } return Object.fromEntries(Object.entries(result).map(([k, v]) => [ k, v ])); } __name(groupBy, "groupBy"); function shuffle(array) { if (!Array.isArray(array)) throw new TypeError("Expected an array"); const copy = array.slice(); for (let i = copy.length - 1; i > 0; i--) { const j = secureIndex(i + 1); [copy[i], copy[j]] = [ copy[j], copy[i] ]; } return copy; } __name(shuffle, "shuffle"); function pluck(array, key) { if (!Array.isArray(array)) return []; return array.map((item) => item?.[key]); } __name(pluck, "pluck"); function difference(a, b) { if (!Array.isArray(a) || !Array.isArray(b)) return []; const setB = new Set(b); return a.filter((item) => !setB.has(item)); } __name(difference, "difference"); function intersect(a, b) { if (!Array.isArray(a) || !Array.isArray(b)) return []; const setB = new Set(b); return a.filter((item) => setB.has(item)); } __name(intersect, "intersect"); function mergeSort(array, key, direction = "asc", compareFn) { if (!Array.isArray(array)) throw new TypeError("Expected array"); const arr = array.slice(); if (arr.length <= 1) return arr; const keyFn = typeof key === "function" ? key : (item) => getValueByPath(item, key); const collator = new Intl.Collator("en", { numeric: true }); const compare = compareFn ?? function(a, b) { const aVal = keyFn(a); const bVal = keyFn(b); if (aVal === bVal) return 0; if (aVal == null) return direction === "asc" ? 1 : -1; if (bVal == null) return direction === "asc" ? -1 : 1; return direction === "asc" ? collator.compare(String(aVal), String(bVal)) : collator.compare(String(bVal), String(aVal)); }; const merge = /* @__PURE__ */ __name((left, right) => { const result = []; let i = 0, j = 0; while (i < left.length && j < right.length) { result.push(compare(left[i], right[j]) <= 0 ? left[i++] : right[j++]); } while (i < left.length) result.push(left[i++]); while (j < right.length) result.push(right[j++]); return result; }, "merge"); const sort = /* @__PURE__ */ __name((input) => { const len = input.length; if (len <= 1) return input; const mid = len >> 1; const left = sort(input.slice(0, mid)); const right = sort(input.slice(mid)); return merge(left, right); }, "sort"); return sort(arr); } __name(mergeSort, "mergeSort"); function zip(...arrays) { if (arrays.length === 0) return []; if (arrays.some((arr) => !Array.isArray(arr))) { throw new TypeError("All arguments must be arrays"); } const minLength = Math.min(...arrays.map((arr) => arr.length)); const result = []; for (let i = 0; i < minLength; i++) { result.push(arrays.map((arr) => arr[i])); } return result; } __name(zip, "zip"); function partition(array, predicate) { const pass = []; const fail = []; if (!Array.isArray(array)) return [ pass, fail ]; for (let i = 0; i < array.length; i++) { const item = array[i]; (predicate(item, i, array) ? pass : fail).push(item); } return [ pass, fail ]; } __name(partition, "partition"); function range(start, end, step = 1) { if (!Number.isFinite(start) || !Number.isFinite(end) || !Number.isFinite(step)) { throw new TypeError("Arguments must be finite numbers"); } if (step === 0) throw new Error("Step cannot be zero"); const result = []; if (step > 0) { for (let i = start; i < end; i += step) result.push(i); } else { for (let i = start; i > end; i += step) result.push(i); } return result; } __name(range, "range"); function take(array, n = 1) { if (!Array.isArray(array) || n <= 0) return []; return n >= array.length ? array.slice() : array.slice(0, n); } __name(take, "take"); function takeWhile(array, predicate) { const result = []; if (!Array.isArray(array)) return result; const len = array.length; for (let i = 0; i < len; i++) { const item = array[i]; if (!predicate(item, i)) break; result.push(item); } return result; } __name(takeWhile, "takeWhile"); function compact(array) { if (!Array.isArray(array)) return []; const result = []; for (const v of array) { if (v) result.push(v); } return result; } __name(compact, "compact"); function countBy(array, keyFn) { if (!Array.isArray(array)) return {}; const result = {}; for (const item of array) { const key = String(keyFn(item)); result[key] = (result[key] ?? 0) + 1; } return result; } __name(countBy, "countBy"); function toggle(array, item) { if (!Array.isArray(array)) return [ item ]; const exists = array.includes(item); if (exists) return array.filter((x) => x !== item); return [ ...array, item ]; } __name(toggle, "toggle"); function secureIndex(max) { if (!Number.isInteger(max) || max <= 0) throw new RangeError("Max must be a positive integer"); const limit = 4294967295 - 4294967295 % max; let rand; do { rand = randomBytes(4).readUInt32BE(0); } while (rand >= limit); return rand % max; } __name(secureIndex, "secureIndex"); var secureRandom = /* @__PURE__ */ __name((array) => { if (!Array.isArray(array) || array.length === 0) return void 0; const idx = secureIndex(array.length); return array[idx]; }, "secureRandom"); function findLast(array, predicate) { if (!Array.isArray(array)) return void 0; for (let i = array.length - 1; i >= 0; i--) { if (predicate(array[i], i, array)) return array[i]; } return void 0; } __name(findLast, "findLast"); function findLastIndex(array, predicate) { if (!Array.isArray(array)) return -1; for (let i = array.length - 1; i >= 0; i--) { if (predicate(array[i], i, array)) return i; } return -1; } __name(findLastIndex, "findLastIndex"); function chunkBy(array, predicate) { if (!Array.isArray(array) || array.length === 0) return []; const result = []; let chunk2 = []; for (let i = 0; i < array.length; i++) { if (predicate(array[i], i, array) && chunk2.length) { result.push(chunk2); chunk2 = []; } chunk2.push(array[i]); } if (chunk2.length) result.push(chunk2); return result; } __name(chunkBy, "chunkBy"); function remove(array, value) { if (!Array.isArray(array)) return []; return array.filter((item) => item !== value); } __name(remove, "remove"); function isSorted(array, direction = "asc", compareFn) { if (!Array.isArray(array) || array.length <= 1) return true; const cmp = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0); for (let i = 1; i < array.length; i++) { const res = cmp(array[i - 1], array[i]); if (direction === "asc" && res > 0 || direction === "desc" && res < 0) return false; } return true; } __name(isSorted, "isSorted"); function headOfArr(array) { return Array.isArray(array) && array.length > 0 ? array[0] : void 0; } __name(headOfArr, "headOfArr"); function lastOfArr(array) { return Array.isArray(array) && array.length > 0 ? array.at(-1) : void 0; } __name(lastOfArr, "lastOfArr"); function drop(array, n) { if (!Array.isArray(array) || n <= 0) return [ ...array ]; return array.slice(n); } __name(drop, "drop"); function dropWhile(array, predicate) { if (!Array.isArray(array)) return []; let i = 0; while (i < array.length && predicate(array[i], i)) { i++; } return array.slice(i); } __name(dropWhile, "dropWhile"); function maxBy(array, keyOrFn) { if (!Array.isArray(array) || array.length === 0) return void 0; const fn = typeof keyOrFn === "function" ? keyOrFn : (item) => item[keyOrFn]; return array.reduce((max, item) => fn(item) > fn(max) ? item : max, array[0]); } __name(maxBy, "maxBy"); function minBy(array, keyOrFn) { if (!Array.isArray(array) || array.length === 0) return void 0; const fn = typeof keyOrFn === "function" ? keyOrFn : (item) => item[keyOrFn]; return array.reduce((min, item) => fn(item) < fn(min) ? item : min, array[0]); } __name(minBy, "minBy"); export { chunk, chunkBy, compact, countBy, difference, drop, dropWhile, findLast, findLastIndex, flattenDeep, groupBy, headOfArr, intersect, isSorted, lastOfArr, maxBy, mergeSort, minBy, partition, pluck, random, range, remove, secureIndex, secureRandom, shuffle, take, takeWhile, toggle, unique, zip };