UNPKG

config-srv

Version:

API and REST interface for editing a structured set of parameters

276 lines (250 loc) 8.05 kB
/* eslint-disable max-len */ function isObject (v) { return v != null && typeof v === 'object' && !Array.isArray(v) && !(v instanceof Date) && !(v instanceof Set) && !(v instanceof Map); } function pad (num, numberOfSymbols) { const str = `00000${num}`; return str.slice(-numberOfSymbols); } function isNonEmptyObject (value) { if (!isObject(value)) { return false; } return !!Object.keys(value).length; } function hasProp (obj, propNAme) { return Object.prototype.hasOwnProperty.call(obj, propNAme); } const INFINITY = 1 / 0; function toKey (value) { if (typeof value === 'string') { return value; } const result = (`${value}`); return (result === '0' && (1 / value) === -INFINITY) ? '-0' : result; } function baseGet (object, path) { if (!Array.isArray(path)) { path = String(path); path = path.split('.'); } let index = 0; const { length } = path; while (object != null && index < length) { object = object[toKey(path[index++])]; } return (index && index === length) ? object : undefined; } function get (object, path, defaultValue) { if (typeof path === 'string') { path = path.trim(); if (!path) { return object; } } else if (Array.isArray(path) && !path.length) { return object; } const result = object == null ? undefined : baseGet(object, path); return result === undefined ? defaultValue : result; } function each (obj, iteratee) { Object.entries(obj).forEach(([key, value]) => { iteratee(value, key); }); } function filterObj (obj, thruly) { each(obj, (value, key) => { if (!thruly(value, key)) { delete obj[key]; } }); } function cloneDeep (obj, options = {}) { if (!options.removeProps) { options.removeProps = []; // Array of property names to be removed } if (options.pureObj === undefined) { options.pureObj = false; // If true, cloned objects will not contain a constructor } if (options.removeSymbols === undefined) { options.removeSymbols = false; // If true, cloned objects will not contain Symbol properties } if (!options.hash) { options.hash = new WeakMap(); } const { removeProps, pureObj, removeSymbols, hash } = options; // https://stackoverflow.com/a/40294058/5239731 if (Object(obj) !== obj) return obj; // primitives if (hash.has(obj)) return hash.get(obj); // cyclic reference let result; if (obj instanceof Set) { result = new Set(obj); // See note about this! } else if (obj instanceof Map) { result = new Map(Array.from(obj, ([key, val]) => [key, cloneDeep(val, options)])); } else if (obj instanceof Date) { result = new Date(obj); } else if (obj instanceof RegExp) { result = new RegExp(obj.source, obj.flags); } else if (obj instanceof Function) { result = obj; } else if (obj.constructor) { // If there is a constructor. Except when pureObj === true and it is a real {}-object if (pureObj) { if (obj.constructor === Object) { result = Object.create(null); } else { result = new obj.constructor(); } } else { result = new obj.constructor(); } } else { result = Object.create(null); } hash.set(obj, result); const keys = (removeSymbols ? Object.keys(obj) : [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]) .filter((propName) => !removeProps.includes(propName)); return Object.assign(result, ...keys.map( (key) => ({ [key]: cloneDeep(obj[key], options) }), )); } function canDeepDive (v) { return typeof v === 'object' && v !== null && Object.keys(v).length > 0; } function cloneDeepWithoutUndefined (obj) { if (isObject(obj)) { const keys = [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]; const entries = keys.map((key) => [key, obj[key]]); return entries.filter(([, v]) => v !== undefined).reduce((r, [k, v]) => { r[k] = canDeepDive(obj) ? cloneDeepWithoutUndefined(v) : v; return r; }, {}); } if (Array.isArray(obj)) { return obj.filter((v) => v !== undefined).reduce((r, v) => { r.push(canDeepDive(obj) ? cloneDeepWithoutUndefined(v) : v); return r; }, []); } return obj; } /** * Deep equality function for non-primitive arguments * * @param value1 * @param value2 * @returns {boolean} */ function deepEqual (value1, value2) { if (value1 === value2) return true; // Сравнение по значению для примитивов if (typeof value1 !== 'object' || value1 === null || typeof value2 !== 'object' || value2 === null) { return false; // Один из них не объект или null и они не равны } const keys1 = Object.keys(value1); const keys2 = Object.keys(value2); if (keys1.length !== keys2.length) return false; // Разное количество ключей for (let key of keys1) { if (!keys2.includes(key) || !deepEqual(value1[key], value2[key])) { return false; } } return true; } /** * Checks if the passed object is a Schema * * @param {Object} obj * @return {Boolean} */ function isSchemaItem (obj) { return isNonEmptyObject(obj) && hasProp(obj, 'id') && hasProp(obj, 'type') && ( hasProp(obj, 'value') || hasProp(obj, 't') || hasProp(obj, 'title') ); } function defineFinalHiddenProperty (obj, propertyName, value) { Object.defineProperty(obj, propertyName, { value, writable: false, configurable: false, enumerable: false, }); } const throttle = (func, limit) => { let lastFunc; let lastRan; return function t (...args) { const context = this; if (!lastRan) { func.apply(context, args); lastRan = Date.now(); } else { clearTimeout(lastFunc); lastFunc = setTimeout(() => { if ((Date.now() - lastRan) >= limit) { func.apply(context, args); lastRan = Date.now(); } }, limit - (Date.now() - lastRan)); } }; }; module.exports = { cloneDeepWithoutUndefined, isObject, getCmdOrEnv (paramName, defaultValue) { const cmdLineArgs = process.argv.slice(2, process.argv.length); const argName = `--${paramName}=`; const cmdLineArgValue = (cmdLineArgs.find((el) => el.startsWith(argName)) || '').substr(argName.length); return cmdLineArgValue || process.env[paramName] || defaultValue; }, parseAndValidateDate (str, reasonContainer = {}) { if (typeof str !== 'string') { reasonContainer.reason = `A string representation of the date is expected ( pattern "YYYY-MM-DD[THH:mm:ss[.SSS]]" ). Received type: ${typeof str}`; return null; } const reDateTime = /^(\d{4})-([01]\d)-([0-3]\d)(?:[ T]([0-2]\d?):([0-5]\d?):([0-5]\d?)(?:\.(\d{3}))?)?$/; let match = reDateTime.exec(str); if (!match) { reasonContainer.reason = `The string representation of the date does not match the pattern "YYYY-MM-DD[THH:mm:ss[.SSS]]": "${str}"`; return null; } match = [...match]; match.shift(); const [YYYY, MM, DD, HH = 0, mm = 0, ss = 0, SSS = 0] = match.map((v) => Number(v) || 0); const d = new Date(YYYY, MM - 1, DD, HH, mm, ss, SSS); const dNum = d.getTime(); if (!dNum && dNum !== 0) { // NaN value, Invalid date reasonContainer.reason = `String representation of date cannot be converted to date: "${str}". Expected format is YYYY-MM-DDTHH.mm.ss.SSS or YYYY-MM-DD or HH.mm.ss.SSS or HH.mm.ss`; return null; } if (d.getMonth() !== MM - 1 || d.getDate() !== DD || d.getHours() !== HH || d.getMinutes() !== mm || d.getSeconds() !== ss || d.getMilliseconds() !== SSS) { reasonContainer.reason = `String representation of date is invalid: "${str}"`; return null; } return `${YYYY}-${pad(MM, 2)}-${pad(DD, 2)}T${pad(HH, 2)}:${pad(mm, 2)}:${pad(ss, 2)}.${(`${SSS}000`).substr(0, 3)}`; }, isNonEmptyObject, hasProp, each, filterObj, get, cloneDeep, deepEqual, isSchemaItem, canDeepDive, defineFinalHiddenProperty, throttle, };