UNPKG

nhb-toolbox

Version:

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

159 lines (158 loc) 5.46 kB
import { isDateLike } from '../date/guards.js'; import { isCustomFile, isFileList, isFileOrBlob, isFileUpload } from '../form/guards.js'; import { isArrayOfType, isNotEmptyObject, isObject } from '../guards/non-primitives.js'; import { isString } from '../guards/primitives.js'; import { trimString } from '../string/basics.js'; export function sanitizeData(input, options, _return) { const { keysToIgnore = [], requiredKeys = [], trimStrings = true, ignoreNullish = false, ignoreFalsy = false, ignoreEmpty = false, } = options || {}; const ignoreKeySet = new Set(keysToIgnore); const _isRequiredKey = (key) => { return Array.isArray(requiredKeys) ? requiredKeys?.some((path) => key === path || key.startsWith(`${path}.`)) : requiredKeys === '*'; }; const _skipObject = (obj) => { return ignoreEmpty && isObject(obj) && !isNotEmptyObject(obj); }; const _shouldNotProcess = (value) => { return (isCustomFile(value) || isFileList(value) || isFileOrBlob(value) || isFileUpload(value) || isDateLike(value)); }; const _processArray = (arr, path) => { return arr ?.map((item) => { if (isString(item) && trimStrings) { return trimString(item); } if (Array.isArray(item)) { return _processArray(item, path); } if (isObject(item)) { return _processObject(item, path); } return item; }) ?.filter((v) => { if (ignoreNullish && v == null) return false; if (ignoreFalsy && !v) return false; if (_skipObject(v) && !_isRequiredKey(path)) return false; return true; }); }; const _processObject = (object, parentPath = '') => Object.entries(object).reduce((acc, [key, value]) => { const fullKeyPath = parentPath ? `${parentPath}.${key}` : key; if (ignoreKeySet.has(fullKeyPath)) { return acc; } if (ignoreNullish && !_isRequiredKey(fullKeyPath) && value == null) { return acc; } if (ignoreFalsy && !value && !_isRequiredKey(fullKeyPath)) { return acc; } if (isString(value) && trimStrings) { acc[key] = trimString(value); } else if (_shouldNotProcess(value)) { acc[key] = value; } else if (value && isObject(value)) { if (_shouldNotProcess(value)) { acc[key] = value; } else { const processedValue = _processObject(value, fullKeyPath); if (!ignoreEmpty || _isRequiredKey(fullKeyPath) || isNotEmptyObject(processedValue)) { acc[key] = processedValue; } } } else if (value && Array.isArray(value)) { const processedArray = _processArray(value, fullKeyPath); if (!ignoreEmpty || _isRequiredKey(fullKeyPath) || processedArray?.length > 0) { acc[key] = processedArray; } } else { acc[key] = value; } return acc; }, {}); if (isString(input)) { return trimString(input); } if (Array.isArray(input)) { if (isArrayOfType(input, isString)) { return trimString(input); } return input ?.map((item) => sanitizeData(item, options, _return)) ?.filter((val) => { if (ignoreNullish && val == null) return false; if (ignoreFalsy && !val) return false; if (_skipObject(val)) return false; return true; }); } if (isObject(input)) { return _processObject(input); } return input; } export function parseObjectValues(object, parseNested = true) { function _deepParseValues(data) { if (Array.isArray(data)) { return data?.map(_deepParseValues); } else if (isNotEmptyObject(data)) { const result = {}; for (const [key, value] of Object.entries(data)) { result[key] = parseNested ? _deepParseValues(value) : value; } return result; } else if (isString(data)) { try { const parsed = JSON.parse(data); return _deepParseValues(parsed); } catch { if (data === 'true') return true; else if (data === 'false') { return false; } else if (data === 'null') { return null; } else if (data === 'undefined') { return undefined; } else if (!Number.isNaN(Number(data))) { return Number(data); } else return data; } } return data; } const parsedBody = {}; if (isNotEmptyObject(object)) { Object.entries(object)?.forEach(([key, value]) => { parsedBody[key] = _deepParseValues(value); }); } return parsedBody; }