@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
JavaScript
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