@cpmech/basic
Advanced tools
Comparing version 4.6.0 to 4.7.0
@@ -1,1 +0,417 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=e=>""!==e&&"null"!==e,t=(e,o="__EMPTY__",r=!1)=>Object.keys(e).reduce((s,i)=>({...s,[i]:null===e[i]||void 0===e[i]?r?e[i]:o:Array.isArray(e[i])?e[i].map(e=>""===e?o:e):"object"==typeof e[i]?t(e[i],o):""===e[i]?o:e[i]}),{}),o=e=>Object.keys(e).reduce((t,r)=>({...t,[r]:Array.isArray(e[r])?e[r].slice(0):"object"==typeof e[r]?o(e[r]):e[r]}),{}),r=(e,t)=>{const o=Object.keys(e);for(const s of o){const o=t[s];Array.isArray(o)?e[s]=o.slice(0):"object"==typeof o?r(e[s],o):e[s]=o}},s=(e,t)=>{if(e.length!==t.length)return!1;for(let o=0;o<e.length;o++)if(e[o]!==t[o])return!1;return!0},i=(e,t)=>{const o=Object.keys(e);for(const r of o){const o=t[r];if(Array.isArray(o)){if(!s(e[r],o))return!0}else if("object"==typeof e[r]&&null!==e[r]){if(i(e[r],o))return!0}else if(e[r]!==o)return!0}return!1},n=(e,t="__EMPTY__",o=!1)=>Object.keys(e).reduce((r,s)=>({...r,[s]:null===e[s]||void 0===e[s]?o?e[s]:"":Array.isArray(e[s])?e[s].map(e=>e===t?"":e):"object"==typeof e[s]?n(e[s],t):e[s]===t?"":e[s]}),{}),l=(e,t=!0)=>{const o=[];for(const r of Object.keys(e)){const s=e[r];o.push(r),"object"!=typeof s||Array.isArray(s)||(t&&o.push("{"),l(s,t).forEach(e=>o.push(e)),t&&o.push("}"))}return o},c=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);let p=!0,a=!0,y="[ERROR]";const u=(e,t)=>{for(const o of Object.keys(e))if(c(t,o)){const r=t[o];null!=r&&(Array.isArray(r)?e[o]=r.slice(0):"object"==typeof r?u(e[o],r):e[o]=r)}};exports.Locales=class{constructor(e,t="en",o="us"){this.getLocale=()=>this.locale,this.setLocale=e=>this.locale=e,this.getCountry=()=>this.country,this.setCountry=e=>this.country=e,this.getIndexDay=()=>"us"===this.country?1:0,this.getIndexMonth=()=>"us"===this.country?0:1,this.getFirstFieldMax=()=>"us"===this.country?12:31,this.getSecondFieldMax=()=>"us"===this.country?31:12,this.getDateFormat=()=>"us"===this.country?"MM/DD/YYYY":"DD/MM/YYYY",this.translate=(e,t="")=>{if(!c(this.resources,this.locale))return`INTERNAL_ERROR: cannot find locale ${this.locale} in resources object`;const o=this.resources[this.locale];if(!e)return"INTERNAL_ERROR: path variable must not be empty";const r=e.split(".").reduce((e,t)=>c(e,t)?e[t]:"",o);return r||(t||`INTERNAL_ERROR: cannot find message with path = ${e}`)},this.resources=e,this.locale=t,this.country=o}},exports.allFilled=(t,o)=>{let r;r=o?Object.keys(t).filter(e=>!o.includes(e)):Object.keys(t);for(const o of r)if(!e(t[o]))return!1;return!0},exports.blank2empty=t,exports.camelize=(e,t=!1,o="_")=>t?e.split(o).map(e=>e.toLowerCase().replace(/./,e=>e.toUpperCase())).join(""):e.split(o).map((e,t)=>t>0?e.toLowerCase().replace(/./,e=>e.toUpperCase()):e.toLowerCase()).join(""),exports.cloneSimple=o,exports.copySimple=r,exports.diffSimple=i,exports.elog=e=>{let t="";return e.message&&"string"==typeof e.message?(e.message.replace(`${y} `,""),t=`${y} ${e.message}`):t="string"==typeof e||"number"==typeof e||"boolean"==typeof e?`${y} ${e}`:`${y}\n${JSON.stringify(e,void 0,2)}`,t=t.replace(`${y} ${y}`,y),a&&console.log(t),t},exports.email2key=e=>e.toLowerCase().replace(/[@\.]/g,"_"),exports.empty2blank=n,exports.filled=e,exports.getObjectKeys=l,exports.hasProp=c,exports.limitNumDecimals=(e,t=2)=>Number(Number.parseFloat(String(e)).toFixed(t)),exports.makeGetField=(e,t)=>o=>{if(!Object.prototype.hasOwnProperty.call(e,o))throw new Error(`cannot find ${o}`);const r=e[o];if(typeof r!==t)throw new Error(`type of ${o} is incorrect`);return r},exports.makeSetField=(e,t,o)=>(r,s)=>{if(!Object.prototype.hasOwnProperty.call(e,r))throw new Error(`cannot find ${r}`);const i=e[r];if(typeof i!==o)throw new Error(`type of ${r} is incorrect`);i!==s&&(e[r]=s,t())},exports.maybeCopySimple=u,exports.mlog=e=>{p&&("string"==typeof e||"number"==typeof e||"boolean"==typeof e?console.log(e):console.log(JSON.stringify(e,void 0,2)))},exports.numOnly=e=>e.replace(/[\D\s\._\-]+/g,""),exports.setElog=e=>a=e,exports.setElogPrefix=e=>y=e,exports.setMlog=e=>p=e,exports.shallowCompareArrays=s,exports.sleep=e=>new Promise(t=>setTimeout(t,e)),exports.unixTimeNow=()=>Math.floor(Date.now()/1e3); | ||
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
const filled = (entry) => { | ||
if (entry === '' || entry === 'null') { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
// allFilled checks if all 'root-level' properties of object are filled (not '', not 'null') | ||
const allFilled = (obj, ignoredKeys) => { | ||
let keys; | ||
if (ignoredKeys) { | ||
keys = Object.keys(obj).filter(key => !ignoredKeys.includes(key)); | ||
} | ||
else { | ||
keys = Object.keys(obj); | ||
} | ||
for (const key of keys) { | ||
if (!filled(obj[key])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
// blank2empty will replace '' with empty recursively on SIMPLE objects | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
// (5) null or undefined will be converted to EMPTY | ||
const blank2empty = (obj, empty = '__EMPTY__', keepUndefined = false) => Object.keys(obj).reduce((acc, curr) => ({ | ||
...acc, | ||
[curr]: obj[curr] === null || obj[curr] === undefined | ||
? keepUndefined | ||
? obj[curr] | ||
: empty | ||
: Array.isArray(obj[curr]) | ||
? obj[curr].map((x) => (x === '' ? empty : x)) | ||
: typeof obj[curr] === 'object' | ||
? blank2empty(obj[curr], empty) | ||
: obj[curr] === '' | ||
? empty | ||
: obj[curr], | ||
}), {}); | ||
const camelize = (name, firstUpper = false, separator = '_') => { | ||
if (firstUpper) { | ||
return name | ||
.split(separator) | ||
.map(w => w.toLowerCase().replace(/./, m => m.toUpperCase())) | ||
.join(''); | ||
} | ||
return name | ||
.split(separator) | ||
.map((w, i) => { | ||
if (i > 0) { | ||
return w.toLowerCase().replace(/./, m => m.toUpperCase()); | ||
} | ||
return w.toLowerCase(); | ||
}) | ||
.join(''); | ||
}; | ||
// cloneSimple (hierarchically) creates a copy of "simple" entries of object | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
const cloneSimple = (obj) => Object.keys(obj).reduce((acc, curr) => ({ | ||
...acc, | ||
[curr]: Array.isArray(obj[curr]) | ||
? obj[curr].slice(0) | ||
: typeof obj[curr] === 'object' | ||
? cloneSimple(obj[curr]) | ||
: obj[curr], | ||
}), {}); | ||
// copySimple (hierarchically) copies all entries of object into destination | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
const copySimple = (destination, origin) => { | ||
const keys = Object.keys(destination); | ||
for (const key of keys) { | ||
const value = origin[key]; | ||
if (Array.isArray(value)) { | ||
destination[key] = value.slice(0); | ||
} | ||
else if (typeof value === 'object') { | ||
copySimple(destination[key], value); | ||
} | ||
else { | ||
destination[key] = value; | ||
} | ||
} | ||
}; | ||
// shallowCompareArrays returns true if two arrays are equal to each other | ||
// NOTE: the type of the array entries must be "fundamental" | ||
const shallowCompareArrays = (left, right) => { | ||
if (left.length !== right.length) { | ||
return false; | ||
} | ||
for (let i = 0; i < left.length; i++) { | ||
if (left[i] !== right[i]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
// diffSimple (hierarchically) compares two objects and return true if they are different | ||
// | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
// | ||
// IMPORTANT: | ||
// (5) 'right' may have ore fields than 'left' | ||
// | ||
// example: | ||
// | ||
// diffSimple({a:'a'}, {a:'a', b:'b'}) === false | ||
// | ||
const diffSimple = (left, right) => { | ||
const keys = Object.keys(left); | ||
for (const key of keys) { | ||
const value = right[key]; | ||
if (Array.isArray(value)) { | ||
if (!shallowCompareArrays(left[key], value)) { | ||
return true; | ||
} | ||
} | ||
else if (typeof left[key] === 'object' && left[key] !== null) { | ||
if (diffSimple(left[key], value)) { | ||
return true; | ||
} | ||
} | ||
else { | ||
if (left[key] !== value) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; // all equal | ||
}; | ||
// email2key converts email address to key by removing the @ mark and dots | ||
const email2key = (email) => { | ||
return email.toLowerCase().replace(/[@\.]/g, '_'); | ||
}; | ||
// empty2blank will replace empty with '' recursively on SIMPLE objects | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
// (5) null or undefined will be converted to '' | ||
const empty2blank = (obj, empty = '__EMPTY__', keepUndefined = false) => Object.keys(obj).reduce((acc, curr) => ({ | ||
...acc, | ||
[curr]: obj[curr] === null || obj[curr] === undefined | ||
? keepUndefined | ||
? obj[curr] | ||
: '' | ||
: Array.isArray(obj[curr]) | ||
? obj[curr].map((x) => (x === empty ? '' : x)) | ||
: typeof obj[curr] === 'object' | ||
? empty2blank(obj[curr], empty) | ||
: obj[curr] === empty | ||
? '' | ||
: obj[curr], | ||
}), {}); | ||
// getObjectKeys (hierarchically) returns all keys (and subkeys) of an object | ||
// NOTE: will not recurse into array | ||
const getObjectKeys = (obj, useBracesToIndicateNesting = true) => { | ||
const results = []; | ||
for (const key of Object.keys(obj)) { | ||
const value = obj[key]; | ||
results.push(key); | ||
if (typeof value === 'object' && !Array.isArray(value)) { | ||
if (useBracesToIndicateNesting) { | ||
results.push('{'); | ||
} | ||
getObjectKeys(value, useBracesToIndicateNesting).forEach(v => results.push(v)); | ||
if (useBracesToIndicateNesting) { | ||
results.push('}'); | ||
} | ||
} | ||
} | ||
return results; | ||
}; | ||
const hasProp = (object, propertyName) => { | ||
return Object.prototype.hasOwnProperty.call(object, propertyName); | ||
}; | ||
// this function will remove excess decimal digits by truncating the value | ||
// NOTE: this function will round up or down | ||
const limitNumDecimals = (val, maxDigits = 2) => { | ||
return Number(Number.parseFloat(String(val)).toFixed(maxDigits)); | ||
}; | ||
// Locales helps to extract localized messages an a resources object such as: | ||
// const resources = { | ||
// en: { | ||
// hello: 'Hello World', | ||
// home: { | ||
// title: 'Main page', | ||
// }, | ||
// err: { | ||
// internal: 'INTERNAL_ERROR: Some problem happened, please contact us', | ||
// }, | ||
// }, | ||
// pt: { | ||
// hello: 'Alô Mundo', | ||
// home: { | ||
// title: 'Página principal', | ||
// }, | ||
// err: { | ||
// internal: 'ERRO_INTERNO: Aconteceu algum problema, por favor nos contactar', | ||
// }, | ||
// }, | ||
// }; | ||
class Locales { | ||
// NOTE: resources is stored "byRef" | ||
constructor(resources, locale = 'en', country = 'us') { | ||
this.getLocale = () => this.locale; | ||
this.setLocale = (locale) => (this.locale = locale); | ||
this.getCountry = () => this.country; | ||
this.setCountry = (country) => (this.country = country); | ||
this.getIndexDay = () => (this.country === 'us' ? 1 : 0); | ||
this.getIndexMonth = () => (this.country === 'us' ? 0 : 1); | ||
this.getFirstFieldMax = () => (this.country === 'us' ? 12 : 31); | ||
this.getSecondFieldMax = () => (this.country === 'us' ? 31 : 12); | ||
this.getDateFormat = () => (this.country === 'us' ? 'MM/DD/YYYY' : 'DD/MM/YYYY'); | ||
this.translate = (path, defaultMessage = '') => { | ||
// check for locale in resources | ||
if (!hasProp(this.resources, this.locale)) { | ||
return `INTERNAL_ERROR: cannot find locale ${this.locale} in resources object`; | ||
} | ||
// resources | ||
const res = this.resources[this.locale]; | ||
// check for path | ||
if (!path) { | ||
return 'INTERNAL_ERROR: path variable must not be empty'; | ||
} | ||
// extract localized message | ||
const list = path.split('.'); | ||
const msg = list.reduce((acc, curr) => { | ||
if (hasProp(acc, curr)) { | ||
return acc[curr]; | ||
} | ||
return ''; | ||
}, res); | ||
// default message? | ||
if (!msg) { | ||
return defaultMessage || `INTERNAL_ERROR: cannot find message with path = ${path}`; | ||
} | ||
// results | ||
return msg; | ||
}; | ||
this.resources = resources; | ||
this.locale = locale; | ||
this.country = country; | ||
} | ||
} | ||
let MLOG_VERBOSE = true; | ||
let ELOG_VERBOSE = true; | ||
let ELOG_PREFIX = '[ERROR]'; | ||
// setMlog sets message logging verbose mode | ||
const setMlog = (verbose) => (MLOG_VERBOSE = verbose); | ||
// setElog sets error message logging verbose mode | ||
const setElog = (verbose) => (ELOG_VERBOSE = verbose); | ||
// setElogPrefix sets error log prefix | ||
const setElogPrefix = (prefix) => (ELOG_PREFIX = prefix); | ||
// mlog logs message, if verbose mode is on | ||
const mlog = (message) => { | ||
if (MLOG_VERBOSE) { | ||
if (typeof message === 'string' || | ||
typeof message === 'number' || | ||
typeof message === 'boolean') { | ||
console.log(message); | ||
} | ||
else { | ||
console.log(JSON.stringify(message, undefined, 2)); | ||
} | ||
} | ||
}; | ||
// elog logs error, if vermobse mode is on, and returns the formatted error message | ||
const elog = (error) => { | ||
let msg = ''; | ||
if (error.message && typeof error.message === 'string') { | ||
error.message.replace(`${ELOG_PREFIX} `, ''); | ||
msg = `${ELOG_PREFIX} ${error.message}`; | ||
} | ||
else { | ||
if (typeof error === 'string' || typeof error === 'number' || typeof error === 'boolean') { | ||
msg = `${ELOG_PREFIX} ${error}`; | ||
} | ||
else { | ||
msg = `${ELOG_PREFIX}\n${JSON.stringify(error, undefined, 2)}`; | ||
} | ||
} | ||
msg = msg.replace(`${ELOG_PREFIX} ${ELOG_PREFIX}`, ELOG_PREFIX); | ||
if (ELOG_VERBOSE) { | ||
console.log(msg); | ||
} | ||
return msg; | ||
}; | ||
// makeGetField makes a getField function | ||
const makeGetField = (obj, TT) => (fieldName) => { | ||
// check if field exists | ||
if (!Object.prototype.hasOwnProperty.call(obj, fieldName)) { | ||
throw new Error(`cannot find ${fieldName}`); | ||
} | ||
// check if type is correct | ||
const value = obj[fieldName]; | ||
if (typeof value !== TT) { | ||
throw new Error(`type of ${fieldName} is incorrect`); | ||
} | ||
// results | ||
return value; | ||
}; | ||
// makeSetFiled makes a setField function | ||
const makeSetField = (obj, onChange, TT) => (fieldName, value) => { | ||
// check if field exists | ||
if (!Object.prototype.hasOwnProperty.call(obj, fieldName)) { | ||
throw new Error(`cannot find ${fieldName}`); | ||
} | ||
// previous value | ||
const oldValue = obj[fieldName]; | ||
// check if type is correct | ||
if (typeof oldValue !== TT) { | ||
throw new Error(`type of ${fieldName} is incorrect`); | ||
} | ||
// check if value has been changed | ||
if (oldValue !== value) { | ||
obj[fieldName] = value; | ||
onChange(); | ||
} | ||
}; | ||
// maybeCopySimple (hierarchically) copies some data from origin | ||
// into destination, iff the origin data exists (not undefined / not null) | ||
// but it does copy empty strings and numbers | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
const maybeCopySimple = (destination, origin) => { | ||
for (const key of Object.keys(destination)) { | ||
if (hasProp(origin, key)) { | ||
const value = origin[key]; | ||
if (value !== undefined && value !== null) { | ||
if (Array.isArray(value)) { | ||
destination[key] = value.slice(0); | ||
} | ||
else if (typeof value === 'object') { | ||
maybeCopySimple(destination[key], value); | ||
} | ||
else { | ||
destination[key] = value; | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
const numOnly = (value) => { | ||
return value.replace(/[\D\s\._\-]+/g, ''); | ||
}; | ||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | ||
const unixTimeNow = () => Math.floor(Date.now() / 1000); | ||
exports.Locales = Locales; | ||
exports.allFilled = allFilled; | ||
exports.blank2empty = blank2empty; | ||
exports.camelize = camelize; | ||
exports.cloneSimple = cloneSimple; | ||
exports.copySimple = copySimple; | ||
exports.diffSimple = diffSimple; | ||
exports.elog = elog; | ||
exports.email2key = email2key; | ||
exports.empty2blank = empty2blank; | ||
exports.filled = filled; | ||
exports.getObjectKeys = getObjectKeys; | ||
exports.hasProp = hasProp; | ||
exports.limitNumDecimals = limitNumDecimals; | ||
exports.makeGetField = makeGetField; | ||
exports.makeSetField = makeSetField; | ||
exports.maybeCopySimple = maybeCopySimple; | ||
exports.mlog = mlog; | ||
exports.numOnly = numOnly; | ||
exports.setElog = setElog; | ||
exports.setElogPrefix = setElogPrefix; | ||
exports.setMlog = setMlog; | ||
exports.shallowCompareArrays = shallowCompareArrays; | ||
exports.sleep = sleep; | ||
exports.unixTimeNow = unixTimeNow; |
@@ -1,1 +0,389 @@ | ||
const e=e=>""!==e&&"null"!==e,t=(t,r)=>{let o;o=r?Object.keys(t).filter(e=>!r.includes(e)):Object.keys(t);for(const r of o)if(!e(t[r]))return!1;return!0},r=(e,t="__EMPTY__",o=!1)=>Object.keys(e).reduce((s,n)=>({...s,[n]:null===e[n]||void 0===e[n]?o?e[n]:t:Array.isArray(e[n])?e[n].map(e=>""===e?t:e):"object"==typeof e[n]?r(e[n],t):""===e[n]?t:e[n]}),{}),o=(e,t=!1,r="_")=>t?e.split(r).map(e=>e.toLowerCase().replace(/./,e=>e.toUpperCase())).join(""):e.split(r).map((e,t)=>t>0?e.toLowerCase().replace(/./,e=>e.toUpperCase()):e.toLowerCase()).join(""),s=e=>Object.keys(e).reduce((t,r)=>({...t,[r]:Array.isArray(e[r])?e[r].slice(0):"object"==typeof e[r]?s(e[r]):e[r]}),{}),n=(e,t)=>{const r=Object.keys(e);for(const o of r){const r=t[o];Array.isArray(r)?e[o]=r.slice(0):"object"==typeof r?n(e[o],r):e[o]=r}},c=(e,t)=>{if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return!1;return!0},i=(e,t)=>{const r=Object.keys(e);for(const o of r){const r=t[o];if(Array.isArray(r)){if(!c(e[o],r))return!0}else if("object"==typeof e[o]&&null!==e[o]){if(i(e[o],r))return!0}else if(e[o]!==r)return!0}return!1},a=e=>e.toLowerCase().replace(/[@\.]/g,"_"),l=(e,t="__EMPTY__",r=!1)=>Object.keys(e).reduce((o,s)=>({...o,[s]:null===e[s]||void 0===e[s]?r?e[s]:"":Array.isArray(e[s])?e[s].map(e=>e===t?"":e):"object"==typeof e[s]?l(e[s],t):e[s]===t?"":e[s]}),{}),y=(e,t=!0)=>{const r=[];for(const o of Object.keys(e)){const s=e[o];r.push(o),"object"!=typeof s||Array.isArray(s)||(t&&r.push("{"),y(s,t).forEach(e=>r.push(e)),t&&r.push("}"))}return r},u=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),f=(e,t=2)=>Number(Number.parseFloat(String(e)).toFixed(t));class p{constructor(e,t="en",r="us"){this.getLocale=()=>this.locale,this.setLocale=e=>this.locale=e,this.getCountry=()=>this.country,this.setCountry=e=>this.country=e,this.getIndexDay=()=>"us"===this.country?1:0,this.getIndexMonth=()=>"us"===this.country?0:1,this.getFirstFieldMax=()=>"us"===this.country?12:31,this.getSecondFieldMax=()=>"us"===this.country?31:12,this.getDateFormat=()=>"us"===this.country?"MM/DD/YYYY":"DD/MM/YYYY",this.translate=(e,t="")=>{if(!u(this.resources,this.locale))return`INTERNAL_ERROR: cannot find locale ${this.locale} in resources object`;const r=this.resources[this.locale];if(!e)return"INTERNAL_ERROR: path variable must not be empty";const o=e.split(".").reduce((e,t)=>u(e,t)?e[t]:"",r);return o||(t||`INTERNAL_ERROR: cannot find message with path = ${e}`)},this.resources=e,this.locale=t,this.country=r}}let h=!0,b=!0,g="[ERROR]";const j=e=>h=e,O=e=>b=e,d=e=>g=e,m=e=>{h&&("string"==typeof e||"number"==typeof e||"boolean"==typeof e?console.log(e):console.log(JSON.stringify(e,void 0,2)))},w=e=>{let t="";return e.message&&"string"==typeof e.message?(e.message.replace(`${g} `,""),t=`${g} ${e.message}`):t="string"==typeof e||"number"==typeof e||"boolean"==typeof e?`${g} ${e}`:`${g}\n${JSON.stringify(e,void 0,2)}`,t=t.replace(`${g} ${g}`,g),b&&console.log(t),t},A=(e,t)=>r=>{if(!Object.prototype.hasOwnProperty.call(e,r))throw new Error(`cannot find ${r}`);const o=e[r];if(typeof o!==t)throw new Error(`type of ${r} is incorrect`);return o},R=(e,t,r)=>(o,s)=>{if(!Object.prototype.hasOwnProperty.call(e,o))throw new Error(`cannot find ${o}`);const n=e[o];if(typeof n!==r)throw new Error(`type of ${o} is incorrect`);n!==s&&(e[o]=s,t())},$=(e,t)=>{for(const r of Object.keys(e))if(u(t,r)){const o=t[r];null!=o&&(Array.isArray(o)?e[r]=o.slice(0):"object"==typeof o?$(e[r],o):e[r]=o)}},E=e=>e.replace(/[\D\s\._\-]+/g,""),_=e=>new Promise(t=>setTimeout(t,e)),M=()=>Math.floor(Date.now()/1e3);export{p as Locales,t as allFilled,r as blank2empty,o as camelize,s as cloneSimple,n as copySimple,i as diffSimple,w as elog,a as email2key,l as empty2blank,e as filled,y as getObjectKeys,u as hasProp,f as limitNumDecimals,A as makeGetField,R as makeSetField,$ as maybeCopySimple,m as mlog,E as numOnly,O as setElog,d as setElogPrefix,j as setMlog,c as shallowCompareArrays,_ as sleep,M as unixTimeNow}; | ||
const filled = (entry) => { | ||
if (entry === '' || entry === 'null') { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
// allFilled checks if all 'root-level' properties of object are filled (not '', not 'null') | ||
const allFilled = (obj, ignoredKeys) => { | ||
let keys; | ||
if (ignoredKeys) { | ||
keys = Object.keys(obj).filter(key => !ignoredKeys.includes(key)); | ||
} | ||
else { | ||
keys = Object.keys(obj); | ||
} | ||
for (const key of keys) { | ||
if (!filled(obj[key])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
// blank2empty will replace '' with empty recursively on SIMPLE objects | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
// (5) null or undefined will be converted to EMPTY | ||
const blank2empty = (obj, empty = '__EMPTY__', keepUndefined = false) => Object.keys(obj).reduce((acc, curr) => ({ | ||
...acc, | ||
[curr]: obj[curr] === null || obj[curr] === undefined | ||
? keepUndefined | ||
? obj[curr] | ||
: empty | ||
: Array.isArray(obj[curr]) | ||
? obj[curr].map((x) => (x === '' ? empty : x)) | ||
: typeof obj[curr] === 'object' | ||
? blank2empty(obj[curr], empty) | ||
: obj[curr] === '' | ||
? empty | ||
: obj[curr], | ||
}), {}); | ||
const camelize = (name, firstUpper = false, separator = '_') => { | ||
if (firstUpper) { | ||
return name | ||
.split(separator) | ||
.map(w => w.toLowerCase().replace(/./, m => m.toUpperCase())) | ||
.join(''); | ||
} | ||
return name | ||
.split(separator) | ||
.map((w, i) => { | ||
if (i > 0) { | ||
return w.toLowerCase().replace(/./, m => m.toUpperCase()); | ||
} | ||
return w.toLowerCase(); | ||
}) | ||
.join(''); | ||
}; | ||
// cloneSimple (hierarchically) creates a copy of "simple" entries of object | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
const cloneSimple = (obj) => Object.keys(obj).reduce((acc, curr) => ({ | ||
...acc, | ||
[curr]: Array.isArray(obj[curr]) | ||
? obj[curr].slice(0) | ||
: typeof obj[curr] === 'object' | ||
? cloneSimple(obj[curr]) | ||
: obj[curr], | ||
}), {}); | ||
// copySimple (hierarchically) copies all entries of object into destination | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
const copySimple = (destination, origin) => { | ||
const keys = Object.keys(destination); | ||
for (const key of keys) { | ||
const value = origin[key]; | ||
if (Array.isArray(value)) { | ||
destination[key] = value.slice(0); | ||
} | ||
else if (typeof value === 'object') { | ||
copySimple(destination[key], value); | ||
} | ||
else { | ||
destination[key] = value; | ||
} | ||
} | ||
}; | ||
// shallowCompareArrays returns true if two arrays are equal to each other | ||
// NOTE: the type of the array entries must be "fundamental" | ||
const shallowCompareArrays = (left, right) => { | ||
if (left.length !== right.length) { | ||
return false; | ||
} | ||
for (let i = 0; i < left.length; i++) { | ||
if (left[i] !== right[i]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
// diffSimple (hierarchically) compares two objects and return true if they are different | ||
// | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
// | ||
// IMPORTANT: | ||
// (5) 'right' may have ore fields than 'left' | ||
// | ||
// example: | ||
// | ||
// diffSimple({a:'a'}, {a:'a', b:'b'}) === false | ||
// | ||
const diffSimple = (left, right) => { | ||
const keys = Object.keys(left); | ||
for (const key of keys) { | ||
const value = right[key]; | ||
if (Array.isArray(value)) { | ||
if (!shallowCompareArrays(left[key], value)) { | ||
return true; | ||
} | ||
} | ||
else if (typeof left[key] === 'object' && left[key] !== null) { | ||
if (diffSimple(left[key], value)) { | ||
return true; | ||
} | ||
} | ||
else { | ||
if (left[key] !== value) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; // all equal | ||
}; | ||
// email2key converts email address to key by removing the @ mark and dots | ||
const email2key = (email) => { | ||
return email.toLowerCase().replace(/[@\.]/g, '_'); | ||
}; | ||
// empty2blank will replace empty with '' recursively on SIMPLE objects | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
// (5) null or undefined will be converted to '' | ||
const empty2blank = (obj, empty = '__EMPTY__', keepUndefined = false) => Object.keys(obj).reduce((acc, curr) => ({ | ||
...acc, | ||
[curr]: obj[curr] === null || obj[curr] === undefined | ||
? keepUndefined | ||
? obj[curr] | ||
: '' | ||
: Array.isArray(obj[curr]) | ||
? obj[curr].map((x) => (x === empty ? '' : x)) | ||
: typeof obj[curr] === 'object' | ||
? empty2blank(obj[curr], empty) | ||
: obj[curr] === empty | ||
? '' | ||
: obj[curr], | ||
}), {}); | ||
// getObjectKeys (hierarchically) returns all keys (and subkeys) of an object | ||
// NOTE: will not recurse into array | ||
const getObjectKeys = (obj, useBracesToIndicateNesting = true) => { | ||
const results = []; | ||
for (const key of Object.keys(obj)) { | ||
const value = obj[key]; | ||
results.push(key); | ||
if (typeof value === 'object' && !Array.isArray(value)) { | ||
if (useBracesToIndicateNesting) { | ||
results.push('{'); | ||
} | ||
getObjectKeys(value, useBracesToIndicateNesting).forEach(v => results.push(v)); | ||
if (useBracesToIndicateNesting) { | ||
results.push('}'); | ||
} | ||
} | ||
} | ||
return results; | ||
}; | ||
const hasProp = (object, propertyName) => { | ||
return Object.prototype.hasOwnProperty.call(object, propertyName); | ||
}; | ||
// this function will remove excess decimal digits by truncating the value | ||
// NOTE: this function will round up or down | ||
const limitNumDecimals = (val, maxDigits = 2) => { | ||
return Number(Number.parseFloat(String(val)).toFixed(maxDigits)); | ||
}; | ||
// Locales helps to extract localized messages an a resources object such as: | ||
// const resources = { | ||
// en: { | ||
// hello: 'Hello World', | ||
// home: { | ||
// title: 'Main page', | ||
// }, | ||
// err: { | ||
// internal: 'INTERNAL_ERROR: Some problem happened, please contact us', | ||
// }, | ||
// }, | ||
// pt: { | ||
// hello: 'Alô Mundo', | ||
// home: { | ||
// title: 'Página principal', | ||
// }, | ||
// err: { | ||
// internal: 'ERRO_INTERNO: Aconteceu algum problema, por favor nos contactar', | ||
// }, | ||
// }, | ||
// }; | ||
class Locales { | ||
// NOTE: resources is stored "byRef" | ||
constructor(resources, locale = 'en', country = 'us') { | ||
this.getLocale = () => this.locale; | ||
this.setLocale = (locale) => (this.locale = locale); | ||
this.getCountry = () => this.country; | ||
this.setCountry = (country) => (this.country = country); | ||
this.getIndexDay = () => (this.country === 'us' ? 1 : 0); | ||
this.getIndexMonth = () => (this.country === 'us' ? 0 : 1); | ||
this.getFirstFieldMax = () => (this.country === 'us' ? 12 : 31); | ||
this.getSecondFieldMax = () => (this.country === 'us' ? 31 : 12); | ||
this.getDateFormat = () => (this.country === 'us' ? 'MM/DD/YYYY' : 'DD/MM/YYYY'); | ||
this.translate = (path, defaultMessage = '') => { | ||
// check for locale in resources | ||
if (!hasProp(this.resources, this.locale)) { | ||
return `INTERNAL_ERROR: cannot find locale ${this.locale} in resources object`; | ||
} | ||
// resources | ||
const res = this.resources[this.locale]; | ||
// check for path | ||
if (!path) { | ||
return 'INTERNAL_ERROR: path variable must not be empty'; | ||
} | ||
// extract localized message | ||
const list = path.split('.'); | ||
const msg = list.reduce((acc, curr) => { | ||
if (hasProp(acc, curr)) { | ||
return acc[curr]; | ||
} | ||
return ''; | ||
}, res); | ||
// default message? | ||
if (!msg) { | ||
return defaultMessage || `INTERNAL_ERROR: cannot find message with path = ${path}`; | ||
} | ||
// results | ||
return msg; | ||
}; | ||
this.resources = resources; | ||
this.locale = locale; | ||
this.country = country; | ||
} | ||
} | ||
let MLOG_VERBOSE = true; | ||
let ELOG_VERBOSE = true; | ||
let ELOG_PREFIX = '[ERROR]'; | ||
// setMlog sets message logging verbose mode | ||
const setMlog = (verbose) => (MLOG_VERBOSE = verbose); | ||
// setElog sets error message logging verbose mode | ||
const setElog = (verbose) => (ELOG_VERBOSE = verbose); | ||
// setElogPrefix sets error log prefix | ||
const setElogPrefix = (prefix) => (ELOG_PREFIX = prefix); | ||
// mlog logs message, if verbose mode is on | ||
const mlog = (message) => { | ||
if (MLOG_VERBOSE) { | ||
if (typeof message === 'string' || | ||
typeof message === 'number' || | ||
typeof message === 'boolean') { | ||
console.log(message); | ||
} | ||
else { | ||
console.log(JSON.stringify(message, undefined, 2)); | ||
} | ||
} | ||
}; | ||
// elog logs error, if vermobse mode is on, and returns the formatted error message | ||
const elog = (error) => { | ||
let msg = ''; | ||
if (error.message && typeof error.message === 'string') { | ||
error.message.replace(`${ELOG_PREFIX} `, ''); | ||
msg = `${ELOG_PREFIX} ${error.message}`; | ||
} | ||
else { | ||
if (typeof error === 'string' || typeof error === 'number' || typeof error === 'boolean') { | ||
msg = `${ELOG_PREFIX} ${error}`; | ||
} | ||
else { | ||
msg = `${ELOG_PREFIX}\n${JSON.stringify(error, undefined, 2)}`; | ||
} | ||
} | ||
msg = msg.replace(`${ELOG_PREFIX} ${ELOG_PREFIX}`, ELOG_PREFIX); | ||
if (ELOG_VERBOSE) { | ||
console.log(msg); | ||
} | ||
return msg; | ||
}; | ||
// makeGetField makes a getField function | ||
const makeGetField = (obj, TT) => (fieldName) => { | ||
// check if field exists | ||
if (!Object.prototype.hasOwnProperty.call(obj, fieldName)) { | ||
throw new Error(`cannot find ${fieldName}`); | ||
} | ||
// check if type is correct | ||
const value = obj[fieldName]; | ||
if (typeof value !== TT) { | ||
throw new Error(`type of ${fieldName} is incorrect`); | ||
} | ||
// results | ||
return value; | ||
}; | ||
// makeSetFiled makes a setField function | ||
const makeSetField = (obj, onChange, TT) => (fieldName, value) => { | ||
// check if field exists | ||
if (!Object.prototype.hasOwnProperty.call(obj, fieldName)) { | ||
throw new Error(`cannot find ${fieldName}`); | ||
} | ||
// previous value | ||
const oldValue = obj[fieldName]; | ||
// check if type is correct | ||
if (typeof oldValue !== TT) { | ||
throw new Error(`type of ${fieldName} is incorrect`); | ||
} | ||
// check if value has been changed | ||
if (oldValue !== value) { | ||
obj[fieldName] = value; | ||
onChange(); | ||
} | ||
}; | ||
// maybeCopySimple (hierarchically) copies some data from origin | ||
// into destination, iff the origin data exists (not undefined / not null) | ||
// but it does copy empty strings and numbers | ||
// NOTE: (1) will NOT recurse into array | ||
// (2) arrays must NOT have mixed types or objects; | ||
// (3) arrays must only contain "simple" types | ||
// (4) simple means: string, number, boolean | ||
// i.e arrays must be "simple" as string[], number[], boolean[] | ||
const maybeCopySimple = (destination, origin) => { | ||
for (const key of Object.keys(destination)) { | ||
if (hasProp(origin, key)) { | ||
const value = origin[key]; | ||
if (value !== undefined && value !== null) { | ||
if (Array.isArray(value)) { | ||
destination[key] = value.slice(0); | ||
} | ||
else if (typeof value === 'object') { | ||
maybeCopySimple(destination[key], value); | ||
} | ||
else { | ||
destination[key] = value; | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
const numOnly = (value) => { | ||
return value.replace(/[\D\s\._\-]+/g, ''); | ||
}; | ||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | ||
const unixTimeNow = () => Math.floor(Date.now() / 1000); | ||
export { Locales, allFilled, blank2empty, camelize, cloneSimple, copySimple, diffSimple, elog, email2key, empty2blank, filled, getObjectKeys, hasProp, limitNumDecimals, makeGetField, makeSetField, maybeCopySimple, mlog, numOnly, setElog, setElogPrefix, setMlog, shallowCompareArrays, sleep, unixTimeNow }; |
{ | ||
"name": "@cpmech/basic", | ||
"version": "4.6.0", | ||
"version": "4.7.0", | ||
"license": "MIT", | ||
@@ -16,3 +16,3 @@ "author": { | ||
"cov": "jest --coverage", | ||
"build": "rm -rf dist && rollup --config ../rollup.config.js" | ||
"build": "rm -rf dist && rollup --config rollup.config.js" | ||
}, | ||
@@ -35,5 +35,2 @@ "files": [ | ||
"rollup": "^1.31.1", | ||
"rollup-plugin-auto-external": "^2.0.0", | ||
"rollup-plugin-node-resolve": "^5.2.0", | ||
"rollup-plugin-terser": "^5.2.0", | ||
"rollup-plugin-typescript2": "^0.26.0", | ||
@@ -46,3 +43,3 @@ "ts-jest": "^25.2.1", | ||
}, | ||
"gitHead": "f26d8e9d996a5bd8e2f0721864418bba344e120b" | ||
"gitHead": "cb53cdd8d83c7ece9d9ff4ae5e7f1d151bee2036" | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
34232
11
838
1