UNPKG

@heliomarpm/helpers

Version:

A library with many useful features

576 lines (575 loc) 23.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Utils = void 0; exports.Utils = { /** * Generates a random valid CPF (Brazilian National Register of Individuals) * @returns a string containing the generated CPF */ gerarCPF() { const calcularDigito = (cpf) => { let soma = 0; let peso = cpf.length + 1; for (let i = 0; i < cpf.length; i++) { soma += parseInt(cpf[i]) * peso; peso--; } const resto = soma % 11; return resto < 2 ? '0' : (11 - resto).toString(); }; let cpf = ''; for (let i = 0; i < 9; i++) { cpf += Math.floor(Math.random() * 10).toString(); } const primeiroDigito = calcularDigito(cpf); const segundoDigito = calcularDigito(cpf + primeiroDigito); return `${cpf}${primeiroDigito}${segundoDigito}`; }, /** * Generates a random valid CNPJ (Brazilian National Corporate Registration Number) * @returns a string containing the generated CNPJ */ gerarCNPJ() { const calcPrimeiroDigito = (cnpj) => { const pesos = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; let soma = 0; for (let i = 0; i < 12; i++) { soma += parseInt(cnpj[i]) * pesos[i]; } const resto = soma % 11; return resto < 2 ? '0' : (11 - resto).toString(); }; const calcSegundoDigito = (cnpj) => { const pesos = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; let soma = 0; for (let i = 0; i < 13; i++) { soma += parseInt(cnpj[i]) * pesos[i]; } const resto = soma % 11; return resto < 2 ? '0' : (11 - resto).toString(); }; let cnpj = ''; for (let i = 0; i < 12; i++) { cnpj += Math.floor(Math.random() * 10).toString(); } const primeiroDigito = calcPrimeiroDigito(cnpj); const segundoDigito = calcSegundoDigito(cnpj + primeiroDigito); return `${cnpj}${primeiroDigito}${segundoDigito}`; }, /** * Returns a function that sorts an array of objects by the given properties. * * If a property name starts with a '-', the sort order is reversed. * * The function returned is a compare function, compliant with the API of * `Array.prototype.sort()`. * * When sorting, all values are converted to strings. If a value is an object, * it is converted to a string using `JSON.stringify()`. * * @param {string | string[]} properties - Properties to sort by. * @returns {function} a compare function * * @example * const validations = [ * { level: "WARNING", type: "TypeA", message: "This is a warning." }, * { level: "ERROR", type: "TypeE", message: "Something went wrong." }, * { level: "ERROR", type: "TypeE", message: "Another error." }, * ]; * * const sortedValidations = validations.sort(sortByProps(['level', 'type', '-message'])); * console.log(sortedValidations); // [{ level: "ERROR", type: "TypeE", message: "Something went wrong." }, ...] */ sortByProps(properties) { const propertyList = Array.isArray(properties) ? properties : [properties]; const stringifyValue = (value) => { return String(typeof value === 'object' ? JSON.stringify(value || '') : value || ''); }; return (objA, objB) => { for (const property of propertyList) { let sortOrder = 1; let propName = property; if (property.startsWith('-')) { sortOrder = -1; propName = property.substring(1); } const comparisonResult = stringifyValue(objA[propName]).localeCompare(stringifyValue(objB[propName])); if (comparisonResult !== 0) { return comparisonResult * sortOrder; } } return 0; }; }, /** * Sorts an array of objects by the specified key in ascending or descending order. * * @template T - The type of objects within the array. * @template K - The key of the object by which the array should be sorted. * @param list - The array of objects to be sorted. * @param key - The key of the object to sort by. * @param orderBy - The order of sorting, either 'asc' for ascending or 'desc' for descending. Defaults to 'asc'. * @returns A new array sorted by the specified key in the specified order. * * @example * const people = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]; * const sortedByName = orderBy(people, 'name'); // Sorts by name in ascending order. * const sortedByAgeDesc = orderBy(people, 'age', 'desc'); // Sorts by age in descending order. */ orderBy(list, key, orderBy = 'asc') { return list.sort((a, b) => { const aValue = a[key]; const bValue = b[key]; return (orderBy === 'asc' ? aValue > bValue ? 1 : -1 : aValue < bValue ? 1 : -1); }); }, /** * Retrieves a nested value from an object using a dot-separated path. * * @param obj The target object from which to retrieve the nested value. * @param path A dot-separated string indicating the path to the nested value. * @returns The nested value or `undefined` if the path is invalid. * * @example * const data = { * user: { * name: { * first: "John", * last: "Doe" * } * } * }; * const firstName = getNestedValue(data, "user.name.first"); // "John" */ getNestedValue(obj, path) { try { return path.split('.').reduce((acc, key) => acc[key], obj); } catch (_a) { return undefined; } }, /** * Sets a nested value within an object given a path. * * @param target The object to set the nested value on. * @param path The path to the nested value. Uses dot notation. * @param value The value to set the nested value to. * * @example * interface User { * user: { * name: { * first: string; * last: string; * } * } * } * const data: User = { * user: { * name: { * first: "John", * last: "Doe" * } * } * }; * setNestedValue<User>(data, "user.name.first", "Jane"); // Sets data.user.name.first to "Jane" */ setNestedValue(target, path, value) { if (!target) { throw new Error('Target object is required.'); } if (path.trim() === '') { throw new Error('Path is required.'); } // Divide a string da chave, lidando com arrays corretamente const keys = path.match(/[^.[\]]+/g); keys.reduce((acc, key, index) => { const isLastKey = index === keys.length - 1; const nextKey = keys[index + 1]; const isNextKeyArrayIndex = nextKey !== undefined && /^\d+$/.test(nextKey); // Converte índices para número, se for um array const parsedKey = /^\d+$/.test(key) ? Number(key) : key; if (isLastKey) { acc[parsedKey] = value; } else { if (!(parsedKey in acc)) { acc[parsedKey] = isNextKeyArrayIndex ? [] : {}; } } return acc[parsedKey]; }, target); }, /** * Returns the first argument if it is not null or undefined, otherwise returns the second argument. * @param value The value to check. * @param fallback The value to return if `value` is null or undefined. * @returns The first argument if it is not null or undefined, otherwise the second argument. * * @example * const foo = null; * const bar = ifNull(foo, "baz"); // "baz" */ ifNull(value, fallback) { return value !== null && value !== void 0 ? value : fallback; }, /** * Returns the first non-null, non-undefined, and non-empty value from the given arguments. * * @param values - A list of values to check. * @returns The first valid value or `undefined` if all values are null, undefined, or empty. * * @example * const result = ifNullOrEmpty(null, '', undefined, 'Hello', 'World'); // "Hello" * const result2 = ifNullOrEmpty(null, '', 0); // 0 * const result3 = ifNullOrEmpty([], {}); // undefined */ ifNullOrEmpty(...values) { return values.find((value) => value !== null && value !== undefined && (typeof value !== 'string' || value.trim() !== '') && (!Array.isArray(value) || value.length !== 0) && (typeof value !== 'object' || Object.keys(value).length !== 0)); }, /** * Generates a random GUID in the format `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`. * @returns A random GUID. * * @example * * const guid = generateGuid(); * console.log(guid); // "f47ac10b-58cc-4372-a567-0e02b2c3d479" */ generateGuid() { if (typeof crypto === 'undefined' || !crypto.getRandomValues) { throw new Error('Crypto API not available in this environment.'); } const randomBytes = new Uint8Array(16); crypto.getRandomValues(randomBytes); // Convert the bytes to a string return [...randomBytes] .map((byte, index) => { const hex = byte.toString(16).padStart(2, '0'); // Insert dashes at the appropriate positions return index === 4 || index === 6 || index === 8 || index === 10 ? `-${hex}` : hex; }) .join(''); }, crypto: { /** * Generates a new 256-bit AES-GCM key. * * @returns A Promise that resolves to a CryptoKey that can be used for encryption and decryption. * * @example * const key = await generateKey(); */ generateKey() { return __awaiter(this, void 0, void 0, function* () { return crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']); }); }, /** * Encrypts a string using AES-GCM algorithm and returns the result as a Base64 string. * @param text The plaintext string to encrypt. * @param key The CryptoKey used for encryption. * @returns A Promise that resolves to the encrypted Base64 string. */ encrypt(text, key) { return __awaiter(this, void 0, void 0, function* () { const iv = crypto.getRandomValues(new Uint8Array(12)); // Initialization Vector (IV) const encodedText = new TextEncoder().encode(text); const encryptedData = yield crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encodedText); // Concatenate IV + encrypted data and convert to Base64 const combined = new Uint8Array(iv.length + encryptedData.byteLength); combined.set(iv, 0); combined.set(new Uint8Array(encryptedData), iv.length); return Buffer.from(combined).toString('base64'); }); }, /** * Decodes a Base64 string and decrypts the contents using the AES-GCM algorithm. * @param encryptedText The Base64 string to decrypt. * @param key The CryptoKey used for decryption. * @returns The decrypted string. */ decrypt(encryptedText, key) { return __awaiter(this, void 0, void 0, function* () { const rawData = Buffer.from(encryptedText, 'base64'); // Decodifica Base64 para Uint8Array const iv = rawData.slice(0, 12); // Extrai o IV const encryptedData = rawData.slice(12); // Extrai os dados criptografados const decryptedData = (yield crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, encryptedData)); return new TextDecoder().decode(decryptedData); }); } }, /** * Returns an array of month names for a given locale. * * @param options - An object with options for formatting the month names. * @param options.locale - The locale string to use for formatting the month names. * @param options.month - The type of month name to return. Can be 'long', 'short', or 'numeric'. * @returns An array of month names (e.g., "January", "February", ...) for the specified locale. * * @example * const months = monthNames('en-US'); // ["January", "February", ..., "December"] * * @see https://www.w3schools.com/jsref/jsref_tolocalestring.asp */ months({ locale = 'default', month = 'long' } = {}) { return Array.from({ length: 12 }, (_, i) => { const monthName = new Date(2000, i).toLocaleString(locale, { month }); return monthName.charAt(0).toUpperCase() + monthName.slice(1); }); }, /** * Returns an array of full weekday names for a given locale. * * @param {Object} [options] * @param {string} [options.locale='default'] - The locale string to use for formatting the weekday names. * @param {string} [options.weekday='long'] - The type of weekday name to return. Can be 'narrow', 'short', or 'long'. * @returns {string[]} An array of full weekday names (e.g., "Sunday", "Monday", ...) for the specified locale. * * @example * const days = dayNames({ locale: 'en-US', weekday: 'short' }); * // Output: ["Sun", "Mon", ..., "Sat"] * * @see https://www.w3schools.com/jsref/jsref_tolocalestring.asp */ weekdays({ locale = 'default', weekday = 'long' } = {}) { const firstSunday = new Date(2000, 0, 1); firstSunday.setDate(firstSunday.getDate() - firstSunday.getDay()); return Array.from({ length: 7 }, (_, i) => { const dayName = new Date(firstSunday.getFullYear(), firstSunday.getMonth(), firstSunday.getDate() + i).toLocaleString(locale, { weekday }); return dayName.charAt(0).toUpperCase() + dayName.slice(1); }); }, /** * Pauses execution for a specified duration. * @param ms - The number of milliseconds to wait. * @returns A promise that resolves after the specified duration. * * @example * await sleep(1000); // Pauses for 1 second */ sleep(ms) { return __awaiter(this, void 0, void 0, function* () { if (ms < 0 || isNaN(ms)) { throw new Error("The 'ms' parameter must be greater than 0."); } return new Promise(resolve => setTimeout(resolve, ms)); }); }, /** * Executes a function and retries it if it fails, up to a specified number of attempts. * * @param fn - The function to execute and retry. * @param options - Options for retrying the function. * @param options.retries - The maximum number of attempts to make before giving up. * @param options.delay - The duration to wait between attempts. * @param options.onRetry - A callback that is called when an attempt fails. * @returns A promise that resolves to the result of the function, or rejects with the last error. * * @example * await retry(() => fetch('https://example.com/api/data'), { * retries: 5, * delay: 500, * onRetry: (error, attempt) => console.log(`Attempt ${attempt} failed with error ${error.message}`) * }); */ retry(fn_1) { return __awaiter(this, arguments, void 0, function* (fn, options = {}) { const { retries = 3, delay = 1000, onRetry } = options; if (delay < 1 || isNaN(delay)) { throw new Error("The 'delay' parameter must be greater than 0."); } for (let attempt = 1; attempt <= retries; attempt++) { try { return yield fn(); } catch (error) { if (attempt === retries) throw error; onRetry === null || onRetry === void 0 ? void 0 : onRetry(error, attempt); yield this.sleep(delay); } } // If we reach this point, all attempts have failed throw new Error('Retry attempts exhausted'); }); }, /** * Creates a memoized version of the given function. Memoization is a technique for * optimizing performance by storing the results of expensive function calls and * reusing them when the same inputs occur again. * @param fn - The function to memoize. * @returns A memoized version of the function. * * @example * const add = (a: number, b: number) => a + b; * const memoizedAdd = memoize(add); * * memoizedAdd(1, 2); // Calculates the result and stores it in the cache * memoizedAdd(1, 2); // Returns the cached result */ memoize(fn) { const cache = new Map(); const memoizedFn = (...args) => { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } // Call the original function if the result is not cached const result = fn(...args); cache.set(key, result); return result; }; memoizedFn.clear = () => cache.clear(); return memoizedFn; }, /** * Creates a debounced version of the given function. Debouncing is a technique for * optimizing performance by delaying the execution of a function until a specified * delay has passed since the last time it was called. * @param fn - The function to debounce. * @param wait - The delay in milliseconds. * @returns A debounced version of the function. * * @example * const add = (a: number, b: number) => a + b; * const debouncedAdd = debounce(add, 500); * * debouncedAdd(1, 2); // Calls the function immediately * debouncedAdd(3, 4); // Calls the function after 500ms * * const debouncedSearch = debounce(searchFunction, 300); * input.addEventListener('input', debouncedSearch); */ debounce(fn, wait) { let timeoutId; return function (...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), wait); }; }, /** * Creates a throttled version of the given function. Throttling is a technique for * optimizing performance by limiting the number of times a function can be called * within a specified time window. * @param fn - The function to throttle. * @param wait - The time window in milliseconds. * @returns A throttled version of the function. * * @example * const add = (a: number, b: number) => a + b; * const throttledAdd = throttle(add, 500); * * throttledAdd(1, 2); // Calls the function immediately * throttledAdd(3, 4); // Calls the function after 500ms if the previous call was outside the time window * * const throttledScroll = throttle(handleScroll, 200); * window.addEventListener('scroll', throttledScroll); */ throttle(fn, wait) { let lastRun = 0; let timeoutId; let lastResult; return (...args) => { const now = Date.now(); const timeLastRun = now - lastRun; if (timeLastRun >= wait) { lastResult = fn(...args); lastRun = now; return lastResult; } if (timeoutId) clearTimeout(timeoutId); const remainingTime = wait - timeLastRun; timeoutId = setTimeout(() => { lastRun = Date.now(); lastResult = fn(...args); }, remainingTime); // Return the last result return lastResult; }; }, /** * Creates a version of the given function that can only be called once. The * first call to the function is executed normally, but any subsequent calls * return the result of the first call. * @param fn - The function to wrap. * @returns A version of the function that can only be called once. * * @example * const add = (a: number, b: number) => a + b; * const onceAdd = once(add); * * const result1 = onceAdd(1, 2); // Calls the function and returns 3 * const result2 = onceAdd(3, 4); // Returns 3 * * const init = once(() => console.log('Inicializado')); * init(); // 'Inicializado' * init(); // nada acontece */ once(fn) { let called = false; let result; return ((...args) => { if (!called) { called = true; result = fn(...args); } return result; }); }, /** * Composes a sequence of functions that process a value from left to right. * Each function in the sequence takes a single argument and returns a value * of the same type, which is then passed to the next function in the sequence. * @param fns - An array of functions to be composed. * @returns A function that takes a single argument and processes it through * the composed sequence of functions. * * @example * const addOne = (x: number) => x + 1; * const subTwo = (x: number) => x - 2; * const process = pipe(addOne, subTwo); * console.log(process(3)); // Outputs 2 (3+1-2) */ pipe(...fns) { return (arg) => fns.reduce((prev, fn) => fn(prev), arg); }, /** * Composes a sequence of functions that process a value from right to left. * Each function in the sequence takes a single argument and returns a value * of the same type, which is then passed to the next function in the sequence. * @param fns - An array of functions to be composed. * @returns A function that takes a single argument and processes it through * the composed sequence of functions. * * @example * const addOne = (x: number) => x + 1; * const subTwo = (x: number) => x - 2; * const process = compose(addOne, subTwo); * console.log(process(-1)); // Outputs -2 (-1-2+1) */ compose(...fns) { // return pipe(...fns.reverse()); return (arg) => fns.reduceRight((acc, fn) => fn(acc), arg); } };