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.

320 lines 10.9 kB
var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; /** * Checks whether the object has no own enumerable properties. * * @param {Record<any, any>} obj - The object to check. * @returns {boolean} True if the object is empty, false otherwise. */ export function isObjEmpty(obj) { return !!obj && typeof obj === "object" && Object.keys(obj).length === 0; } /** * Returns a new object with only the specified keys picked. * * @template T * @template K * @param {T} obj - The source object. * @param {K[]} keys - Keys to pick from the object. * @returns {Pick<T, K>} New object with picked keys. */ export function pick(obj, keys) { return Object.fromEntries(keys.map(function (key) { return [key, obj[key]]; })); } /** * Returns a new object with the specified keys omitted. * * @template T * @template K * @param {T} obj - The source object. * @param {K[]} keys - Keys to omit from the object. * @returns {Omit<T, K>} New object without omitted keys. */ export function omit(obj, keys) { return Object.fromEntries(Object.entries(obj).filter(function (_a) { var _b = __read(_a, 1), key = _b[0]; return !keys.includes(key); })); } /** * Deeply merges two objects (mutates and returns the target object, not pure). * * @template T * @param {T} target - The object to merge into (will be mutated). * @param {Partial<T>} source - The object to merge from. * @returns {T} The merged object (same as target). */ export function deepObjMerge(target, source) { for (var key in source) { var sourceVal = source[key]; var targetVal = target[key]; if (sourceVal && typeof sourceVal === "object" && !Array.isArray(sourceVal)) { if (!targetVal || typeof targetVal !== "object" || Array.isArray(targetVal)) { target[key] = {}; } deepObjMerge(target[key], sourceVal); } else { target[key] = sourceVal; } } return target; } /** * Flattens a nested object using dot notation for keys (e.g., `{a: {b: 1}}` → `{ "a.b": 1 }`). * * @template T * @param {T} obj - The object to flatten. * @param {string} [prefix=""] - Optional prefix for nested keys (used internally). * @returns {Record<string, any>} A new object with flattened keys. */ export function flattenObject(obj, prefix) { if (prefix === void 0) { prefix = ""; } return Object.entries(obj).reduce(function (acc, _a) { var _b = __read(_a, 2), key = _b[0], value = _b[1]; var newKey = prefix ? "".concat(prefix, ".").concat(key) : key; if (value && typeof value === "object" && !Array.isArray(value)) { Object.assign(acc, flattenObject(value, newKey)); } else { acc[newKey] = value; } return acc; }, {}); } /** * Safely gets the value of a deeply nested key in an object using dot/bracket notation path. * * @template T * @param {T} obj - The object to extract from. * @param {string} path - String path using dot and/or bracket notation (e.g., 'user.friends[0].name'). * @returns {any} The value at the given path, or undefined if not found. */ export function getValueByPath(obj, path) { if (!obj || typeof obj !== "object") return undefined; // Convert path like "a.b[0].c" into ["a", "b", "0", "c"] var parts = path .replace(/\[(\d+)\]/g, ".$1") // convert [0] to .0 .split(".") .filter(Boolean); // remove empty strings return parts.reduce(function (acc, key) { return acc === null || acc === void 0 ? void 0 : acc[key]; }, obj); } /** * Sets a value at a deeply nested key in an object using dot/bracket notation path. * Creates intermediate objects and arrays as needed. * * @template T * @param {T} obj - The object to modify. * @param {string} path - String path using dot and/or bracket notation (e.g., 'user.friends[0].name'). * @param {any} value - The value to set at the path. * @returns {T} The modified object. */ export function setValueByPath(obj, path, value) { if (!obj || typeof obj !== "object") return obj; // Convert path like "a.b[0].c" into ["a", "b", "0", "c"] var parts = path .replace(/\[(\d+)\]/g, ".$1") // convert [0] to .0 .split(".") .filter(Boolean); // remove empty strings // Handle empty path if (parts.length === 0) return obj; var current = obj; // Navigate to the parent of the target property for (var i = 0; i < parts.length - 1; i++) { var key = parts[i]; var isArrayIndex = !isNaN(Number(parts[i + 1])); if (current[key] === undefined) { // Create intermediate object or array based on next key type current[key] = isArrayIndex ? [] : {}; } current = current[key]; } // Set the value at the final key var finalKey = parts[parts.length - 1]; current[finalKey] = value; return obj; } /** * Performs a deep equality check between two objects. * * @param {any} a - First value to compare. * @param {any} b - Second value to compare. * @returns {boolean} True if values are deeply equal. */ export function isEqual(a, b) { // Check if primitives or references to the same object if (a === b) return true; // Check if either is null/undefined or not an object if (a == null || b == null || typeof a !== "object" || typeof b !== "object") { return false; } // Handle Date objects if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime(); } // If one is Date but the other isn't if (a instanceof Date || b instanceof Date) { return false; } // Handle arrays if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; // Check each element for (var i = 0; i < a.length; i++) { if (!isEqual(a[i], b[i])) return false; } return true; } // If one is Array but the other isn't if (Array.isArray(a) || Array.isArray(b)) { return false; } // Compare object keys var keysA = Object.keys(a); var keysB = Object.keys(b); if (keysA.length !== keysB.length) return false; // Check if every key in A exists in B and has the same value return keysA.every(function (key) { return Object.prototype.hasOwnProperty.call(b, key) && isEqual(a[key], b[key]); }); } /** * Filters object properties based on a predicate function. * * @template T * @param {T} obj - The source object. * @param {(value: any, key: string, obj: T) => boolean} predicate - Filter function. * @returns {Partial<T>} New object with filtered properties. */ export function filterObject(obj, predicate) { return Object.fromEntries(Object.entries(obj).filter(function (_a) { var _b = __read(_a, 2), key = _b[0], value = _b[1]; return predicate(value, key, obj); })); } /** * Maps object values to new values using a mapping function. * * @template T * @template U * @param {T} obj - The source object. * @param {(value: any, key: string, obj: T) => U} mapFn - Mapping function. * @returns {Record<keyof T, U>} New object with mapped values. */ export function mapObject(obj, mapFn) { return Object.fromEntries(Object.entries(obj).map(function (_a) { var _b = __read(_a, 2), key = _b[0], value = _b[1]; return [key, mapFn(value, key, obj)]; })); } /** * Recursively freezes an object and all its properties. * Makes an object immutable. * * @template T * @param {T} obj - The object to freeze. * @returns {Readonly<T>} The frozen object. */ export function deepFreeze(obj) { var e_1, _a; // Freeze primitive properties Object.freeze(obj); try { // Recursively freeze nested objects for (var _b = __values(Object.getOwnPropertyNames(obj)), _c = _b.next(); !_c.done; _c = _b.next()) { var prop = _c.value; var value = obj[prop]; if (value !== null && typeof value === "object" && !Object.isFrozen(value)) { deepFreeze(value); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } return obj; } /** * Safely checks if a value is an object (not null, not array). * * @param {unknown} value - Value to check. * @returns {boolean} True if value is a non-null, non-array object. */ export function isObject(value) { return value !== null && typeof value === "object" && !Array.isArray(value); } /** * Gets all paths in an object using dot notation. * * @param {Record<string, any>} obj - Object to analyze. * @param {string} [parentPath=""] - Internal param for recursion. * @returns {string[]} Array of all paths in dot notation. */ export function getAllPaths(obj, parentPath) { if (parentPath === void 0) { parentPath = ""; } if (!isObject(obj)) return []; return Object.entries(obj).flatMap(function (_a) { var _b = __read(_a, 2), key = _b[0], value = _b[1]; var currentPath = parentPath ? "".concat(parentPath, ".").concat(key) : key; if (isObject(value)) { return __spreadArray([currentPath], __read(getAllPaths(value, currentPath)), false); } return [currentPath]; }); } //# sourceMappingURL=obj.utils.js.map