UNPKG

nhb-toolbox

Version:

A versatile collection of smart, efficient, and reusable utility functions and classes for everyday development needs.

285 lines (284 loc) 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseJSON = exports.isDeepEqual = void 0; exports.convertArrayToString = convertArrayToString; exports.debounceAction = debounceAction; exports.throttleAction = throttleAction; exports.getInstanceMethodNames = getInstanceMethodNames; exports.getStaticMethodNames = getStaticMethodNames; exports.countInstanceMethods = countInstanceMethods; exports.countStaticMethods = countStaticMethods; exports.getInstanceGetterNames = getInstanceGetterNames; exports.getStaticGetterNames = getStaticGetterNames; exports.getClassDetails = getClassDetails; exports.deepParsePrimitives = deepParsePrimitives; const helpers_1 = require("../array/helpers"); const sort_1 = require("../array/sort"); const non_primitives_1 = require("../guards/non-primitives"); const primitives_1 = require("../guards/primitives"); const specials_1 = require("../guards/specials"); /** * * Deeply compare two values (arrays, objects, or primitive values). * * @param a First value to compare. * @param b Second value to compare. * @returns Whether the values are deeply equal. */ const isDeepEqual = (a, b) => { // If both values are strictly equal (handles primitive types and same references) if (a === b) return true; // If the types of the two values are different if (typeof a !== typeof b) return false; // If either is null or undefined, they must both be null or undefined if (a === null || b === null) return a === b; // Check for array equality if (Array.isArray(a) && Array.isArray(b)) { if (a?.length !== b?.length) return false; return a?.every((element, index) => (0, exports.isDeepEqual)(element, b[index])); } // Check for object equality if (typeof a === 'object' && typeof b === 'object') { const aKeys = Object.keys(a); const bKeys = Object.keys(b); if (aKeys?.length !== bKeys?.length) return false; return aKeys?.every((key) => (0, exports.isDeepEqual)(a[key], b[key])); } return false; }; exports.isDeepEqual = isDeepEqual; /** * * Converts an array of primitive values or objects to a string using a separator or target key. * * @param array Array to convert. * @param options Options for separator or key extraction from objects. * @returns String representation of array values. */ function convertArrayToString(array, options) { if (!(0, non_primitives_1.isValidArray)(array)) return ''; const { separator = ', ' } = options ?? {}; if ((0, non_primitives_1.isArrayOfType)(array, primitives_1.isPrimitive)) { return array?.join(separator); } else if ((0, non_primitives_1.isArrayOfType)(array, non_primitives_1.isNotEmptyObject)) { if (options && 'target' in options) { return array ?.map((el) => (0, helpers_1._resolveNestedKey)(el, options?.target)) ?.join(separator); } else { return ''; } } return ''; } /** * * A generic debounce function that delays the execution of a callback. * * @param callback - The function to debounce. * @param delay - The delay in milliseconds. Default is `300ms`. * @returns A debounced version of the callback function. * * @example * const debouncedSearch = debounceAction((query: string) => { * console.log(`Searching for: ${query}`); * }, 300); * * debouncedSearch('laptop'); // Executes after 300ms of inactivity. */ function debounceAction(callback, delay = 300) { let timeoutId = undefined; return (...args) => { // Clear the previous timeout clearTimeout(timeoutId); // Set a new timeout timeoutId = setTimeout(() => { callback(...args); }, delay); }; } /** * * A generic throttle function that ensures a callback is executed at most once per specified interval. * * @param callback - The function to throttle. * @param delay - The delay in milliseconds. Default is `150ms`. * @returns A throttled version of the callback function. * * @example * const throttledResize = throttleAction(() => { * console.log('Resized'); * }, 300); * * window.addEventListener('resize', throttledResize); */ function throttleAction(callback, delay = 150) { let lastCall = 0; return (...args) => { const now = Date.now(); if (now - lastCall >= delay) { lastCall = now; callback(...args); } }; } /** * * Retrieves the names of all instance methods defined directly on a class prototype. * * @param cls - The class constructor (not an instance). * @returns A sorted array of instance method names. */ function getInstanceMethodNames(cls) { const prototype = cls.prototype; const methods = Object.getOwnPropertyNames(prototype).filter((method) => { if (method === 'constructor') { return false; } const descriptor = Object.getOwnPropertyDescriptor(prototype, method); return (0, non_primitives_1.isMethodDescriptor)(descriptor); }); return (0, sort_1.sortAnArray)(methods); } /** * * Retrieves the names of all static methods defined directly on a class constructor. * * @param cls - The class constructor (not an instance). * @returns A sorted array of static method names. */ function getStaticMethodNames(cls) { const methods = Object.getOwnPropertyNames(cls).filter((method) => { return (method !== 'prototype' && method !== 'name' && method !== 'length'); }); return (0, sort_1.sortAnArray)(methods); } /** * * Counts the number of instance methods defined directly on a class prototype. * * @param cls - The class constructor (not an instance). * @returns The number of instance methods defined on the class prototype. */ function countInstanceMethods(cls) { return getInstanceMethodNames(cls)?.length; } /** * * Counts the number of static methods defined directly on a class constructor. * * @param cls - The class constructor (not an instance). * @returns The number of static methods defined on the class constructor. */ function countStaticMethods(cls) { return getStaticMethodNames(cls)?.length; } /** * * Retrieves the names of all instance getters defined directly on a class prototype. * * @param cls - The class constructor (not an instance). * @returns A sorted array of instance getter names. */ function getInstanceGetterNames(cls) { const descriptors = Object.getOwnPropertyDescriptors(cls.prototype); const result = Object.entries(descriptors) .filter(([key, desc]) => typeof desc.get === 'function' && key !== 'constructor') .map(([key]) => key); return (0, sort_1.sortAnArray)(result); } /** * * Retrieves the names of all static getters defined directly on a class constructor. * * @param cls - The class constructor (not an instance). * @returns A sorted array of static getter names. */ function getStaticGetterNames(cls) { const descriptors = Object.getOwnPropertyDescriptors(cls); const result = Object.entries(descriptors) .filter(([key, desc]) => typeof desc.get === 'function' && key !== 'prototype') .map(([key]) => key); return (0, sort_1.sortAnArray)(result); } /** * * Gathers detailed information about the instance and static methods of a class. * * @param cls - The class constructor (not an instance). * @returns An object containing names and counts of instance and static methods. */ function getClassDetails(cls) { const instanceNames = getInstanceMethodNames(cls); const staticNames = getStaticMethodNames(cls); const instanceGetters = getInstanceGetterNames(cls); const staticGetters = getStaticGetterNames(cls); return { instanceMethods: instanceNames, staticMethods: staticNames, instanceGetters, staticGetters, instanceCount: instanceNames?.length, staticCount: staticNames?.length, totalGetters: instanceGetters?.length + staticGetters?.length, totalMethods: instanceNames?.length + staticNames?.length, }; } /** * * Parses any valid JSON string, optionally converting stringified primitives inside (nested) arrays or objects. * * @template T - Expected return type (default is unknown). * @param value - The JSON string to parse. * @param parsePrimitives - Whether to convert stringified primitives (default: `true`). * @returns The parsed JSON value typed as `T`, or the original parsed value with optional primitive conversion. * - Returns `{}` if parsing fails, such as when the input is malformed or invalid JSON or passing single quoted string. * * - *Unlike `parseJsonToObject`, which ensures the root value is an object, * this function returns any valid JSON structure such as arrays, strings, numbers, or objects.* * * This is useful when you're not sure of the root structure of the JSON, or when you expect something other than an object. * * @see `parseJsonToObject` for strict object-only parsing. */ const parseJSON = (value, parsePrimitives = true) => { try { const parsed = JSON.parse(value); return (parsePrimitives ? deepParsePrimitives(parsed) : parsed); } catch { return {}; } }; exports.parseJSON = parseJSON; /** * * Recursively parses primitive values inside objects and arrays. * * @template T - Expected return type after parsing (default is unknown). * @param input - Any input value to parse recursively. * @returns Input with primitives (strings like "true", "123") converted, typed as `T`. */ function deepParsePrimitives(input) { if (Array.isArray(input)) { return input?.map(deepParsePrimitives); } if ((0, non_primitives_1.isObject)(input)) { const result = {}; for (const [key, value] of Object.entries(input)) { result[key] = deepParsePrimitives(value); } return result; } if ((0, primitives_1.isString)(input)) { if (/^(true|false)$/i.test(input)) { return (input?.toLowerCase() === 'true'); } if ((0, specials_1.isNumericString)(input)) { return Number(input); } if (input === 'null') { return null; } if (input === 'undefined') { return undefined; } return input; } return input; }