UNPKG

lemon-core

Version:
851 lines 32.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Utilities = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const js_yaml_1 = __importDefault(require("js-yaml")); const crypto_1 = __importDefault(require("crypto")); const query_string_1 = __importDefault(require("query-string")); const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const uuid = __importStar(require("uuid")); const NS = 'util'; const COLORS = { red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m', white: '\x1b[37m', }; /** * class: Utilities * - various functions */ class Utilities { constructor(_$) { /** * parse float by decimal point 2 */ this.F2 = (x, mode = 'round') => this.FN(x, 2, mode); /** * parse float by decimal point 3 */ this.F3 = (x, mode = 'round') => this.FN(x, 3, mode); /** * convert and cut string like `abcd....z` */ this.S = (_, h, t = 32, delim = '...') => [typeof _ == 'string' ? _ : `${this.json(_) || ''}`] .map(s => h && s.length > h + t ? s.substring(0, h) + delim + (s.length > h + t ? s.substring(s.length - t) : '') : s) .join(''); /** * group as qs */ this.qs = { /** * parse qs string */ parse: (q) => this.qs_parse(q), /** * stringify qs object */ stringify: (q) => this.qs_stringify(q), }; /** * get crypto object. */ this.crypto = (passwd, algorithm) => { algorithm = algorithm || 'aes-256-ctr'; const MAGIC = 'LM!#'; return new (class { constructor() { this.encrypt = (val) => { val = val === undefined ? null : val; // msg = msg && typeof msg == 'object' ? JSON_TAG+JSON.stringify(msg) : msg; //! 어느 데이터 타입이든 저장하기 위해서, object로 만든다음, 암호화 시킨다. const msg = JSON.stringify({ alg: algorithm, val: val }); const buffer = Buffer.from(`${MAGIC}${msg || ''}`, 'utf8'); // const key = Buffer.from(`${passwd || ''}`, 'utf8'); const cipher = crypto_1.default.createCipher(algorithm, passwd); // const cipher = crypto.createCipheriv(algorithm, key, iv); const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]); return crypted.toString(1 ? 'base64' : 'utf8'); }; this.decrypt = (msg) => { const buffer = Buffer.from(`${msg || ''}`, 'base64'); // const key = Buffer.from(`${passwd || ''}`, 'utf8'); const decipher = crypto_1.default.createDecipher(algorithm, passwd); // const decipher = crypto.createDecipheriv(algorithm, key, iv); const dec = Buffer.concat([decipher.update(buffer), decipher.final()]).toString('utf8'); if (!dec.startsWith(MAGIC)) throw new Error('400 INVALID PASSWD - invalid magic string!'); const data = dec.substr(MAGIC.length); if (data && !data.startsWith('{') && !data.endsWith('}')) throw new Error('400 INVALID PASSWD - invalid json string!'); const $msg = JSON.parse(data) || {}; return $msg.val; }; } })(); }; /** * get crypto2 object (w/ Cipheriv). * - to avoid `(node:66818) Warning: Use Cipheriv for counter mode of aes-256-ctr` * * @param passwd password to crypt * @param algorithm (default as `aes-256-ctr`) * @param ivNumb iv number to populate. (default as 0, or -1 use random) * @param magic magic string to verify (default `LM!#`) */ this.crypto2 = (passwd, algorithm, ivNumb, magic) => { algorithm = algorithm || 'aes-256-ctr'; const MAGIC = magic === undefined ? 'LM!#' : `${magic || ''}`; const iv = Buffer.from(Array.prototype.map.call(Buffer.alloc(16), () => { return ivNumb === undefined ? 0 : ivNumb === -1 ? Math.floor(Math.random() * 256) : ivNumb; })); return new (class { constructor() { this.encrypt = (val) => { val = val === undefined ? null : val; //! use json string to support all data-type const msg = JSON.stringify({ alg: algorithm, val: val }); const buffer = Buffer.from(`${MAGIC}${msg || ''}`, 'utf8'); const key = Buffer.concat([Buffer.from(passwd)], Buffer.alloc(32).length); const cipher = crypto_1.default.createCipheriv(algorithm, key, iv); const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]); return crypted.toString('base64'); }; this.decrypt = (msg) => { const buffer = Buffer.from(`${msg || ''}`, 'base64'); const key = Buffer.concat([Buffer.from(passwd)], Buffer.alloc(32).length); const decipher = crypto_1.default.createDecipheriv(algorithm, key, iv); const dec = Buffer.concat([decipher.update(buffer), decipher.final()]).toString('utf8'); if (!dec.startsWith(MAGIC)) throw new Error(`400 INVALID PASSWD - invalid magic string!`); const data = dec.substr(MAGIC.length); if (data && !data.startsWith('{') && !data.endsWith('}')) throw new Error('400 INVALID PASSWD - invalid json string!'); const $msg = JSON.parse(data) || {}; return $msg.val; }; } })(); }; /** * builder for `JWTHelper` * @param passcode string for verification. * @param current_ms (optional) current time in millisecond (required to verify `exp`) */ this.jwt = (passcode, current_ms) => { const $U = this; /** * main class. */ return new (class JWTHelper { constructor() { /** * use `jsonwebtoken` directly. */ this.$ = jsonwebtoken_1.default; /** * encode object to token string * - Synchronous Sign with default (HS256: HMAC SHA256) * * @param data object * @param algorithm algorithm to use */ this.encode = (data, algorithm = 'HS256') => { data = current_ms ? Object.assign(Object.assign({}, data), { iat: Math.floor(current_ms / 1000) }) : data; const token = jsonwebtoken_1.default.sign(data, passcode, { algorithm }); return token; }; /** * decode token string * * @param token string */ this.decode = (token, options) => { const N = jsonwebtoken_1.default.decode(token, options); return N; }; /** * verify token * - Synchronous Verify with default (HS256: HMAC SHA256) * * @param token * @param algorithm * @throws `jwt expired` if exp has expired!. */ this.verify = (token, algorithm = 'HS256') => { const verified = jsonwebtoken_1.default.verify(token, passcode, { algorithms: [algorithm] }); const cur = $U.N(current_ms, 0); const exp = $U.N(verified === null || verified === void 0 ? void 0 : verified.exp, 0) * 1000; if (cur > 0 && exp > 0 && exp < current_ms) throw new Error(`jwt expired at ${$U.ts(exp)}`); return verified; }; } })(); }; this._$ = _$; this.log = _$.log; this.err = _$.err; this.name = `${NS}-utils`; } //! some helper function.s get_env(name, def_val) { if (typeof this._$.environ === 'function') return this._$.environ(name, def_val); // as default, load from proces.env. const val = (process && process.env[name]) || undefined; return val === undefined ? def_val : val; } env(name, def_val) { return this.get_env(name, def_val); } is_dev() { const env = this.get_env('ENV') || this.get_env('NODE_ENV') || this.get_env('STAGE'); return env === 'production' || env === 'op' ? false : true; } load_data_yaml(name, folder) { if (!name) throw new Error('param:name is required!'); folder = folder || 'data'; //! calculate the target data file. const fname = path_1.default.resolve(__dirname, `../${folder}/` + name + (name.endsWith('.yml') ? '' : '.yml')); this.log(NS, 'load file =', fname); //! prepare promised. const chain = new Promise(function (resolve, reject) { // Get document, or throw exception on error try { const doc = js_yaml_1.default.load(fs_1.default.readFileSync(fname, 'utf8')); resolve(doc); } catch (e) { reject(e); } }); return chain; } load_sync_yaml(name, folder) { if (!name) throw new Error('param:name is required!'); folder = folder || 'data'; //! calculate the target data file. const fname = path_1.default.resolve(__dirname, `../${folder}/` + name + (name.endsWith('.yml') ? '' : '.yml')); // Get document, or throw exception on error try { this.log(NS, 'load-sync-file =', fname); const doc = js_yaml_1.default.load(fs_1.default.readFileSync(fname, 'utf8')); return doc; } catch (e) { this.err(NS, `error:load-sync-yaml(${name})=`, e); } return {}; } extend(a, b) { for (const x in b) a[x] = b[x]; return a; } isset(x) { return x === undefined ? false : true; } empty(x) { return x ? false : true; } min(a, b) { return a < b ? a : b; } max(a, b) { return a > b ? a : b; } round(a) { return Math.round(a); } json(o, isSorted) { if (isSorted) { const output = {}; Object.keys(o) .sort() .forEach(key => { output[key] = o[key]; }); o = output; } return o ? JSON.stringify(o) : typeof o == 'number' ? `${o}` : `${o || ''}`; } /** * timestamp string like `2020-02-22` */ static timestamp(date, timeZone) { const dt = date && typeof date === 'object' ? date : date ? new Date(date) : new Date(); const now = new Date(); const tzo = now.getTimezoneOffset(); // Asia/Seoul => -540 const diff = timeZone * 60 + tzo; if (diff) dt.setSeconds(dt.getSeconds() + 1 * diff * 60); const y = dt.getFullYear(); const m = dt.getMonth() + 1; //Months are zero based const d = dt.getDate(); const h = dt.getHours(); const i = dt.getMinutes(); const s = dt.getSeconds(); const d2 = (x) => `${x < 10 ? '0' : ''}${x}`; const ret = d2(y) + '-' + d2(m) + '-' + d2(d) + ' ' + d2(h) + ':' + d2(i) + ':' + d2(s); return ret; } /** * parse timestamp to date. */ static datetime(dt, timeZone) { let ret = null; if (typeof dt == 'string') { const now = new Date(); const tzo = now.getTimezoneOffset(); const diff = timeZone * 60 + tzo; let tstr = ''; if (/^[12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/.test(dt)) { // like 1978-12-01 tstr = dt + ' 12:00:00'; } else if (/^[4-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/.test(dt)) { // like 79-12-01 tstr = '19' + dt + ' 12:00:00'; } else if (/^[0-3][0-9]-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/.test(dt)) { // like 19-12-01 tstr = '20' + dt + ' 12:00:00'; } else if (/^[12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]) ([01]?[0-9]|2[0-3]):[0-5][0-9]$/.test(dt)) { // like 1978-12-01 12:34 tstr = dt + ':00'; } else if (/^[12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]) ([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/.test(dt)) { // like 1978-12-01 12:34:20 tstr = dt + ''; } else if (/^[12]\d{3}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])$/.test(dt)) { // like 19781201 tstr = dt.substr(0, 4) + '-' + dt.substr(4, 2) + '-' + dt.substr(6, 2) + ' 12:00:00'; } else if (/^[12]\d{3}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01]) ([01]?[0-9]|2[0-3])[0-5][0-9]$/.test(dt)) { // like 19781201 1234 tstr = dt.substr(0, 4) + '-' + dt.substr(4, 2) + '-' + dt.substr(6, 2) + ' ' + dt.substr(9, 2) + ':' + dt.substr(11, 2) + ':00'; } ret = ((ts) => { if (!ts) return null; const aa = ts.split(' '); const dd = aa[0].split('-'); const hh = aa[1].split(':'); const y = parseInt(dd[0]); const m = parseInt(dd[1]) - 1; const d = parseInt(dd[2]); const h = parseInt(hh[0]); const i = parseInt(hh[1]); const s = parseInt(hh[2]); return new Date(y, m, d, h, i, s, 0); })(tstr); if (ret && diff) ret.setSeconds(ret.getSeconds() + -1 * diff * 60); return ret; } else if (typeof dt == 'number') { ret = new Date(dt); } else if (typeof dt == 'object' && dt instanceof Date) { ret = dt; } else if (dt === undefined) { ret = new Date(); } else { throw new Error('Invalid type of dt: ' + typeof dt); } return ret; } ts(d, timeZone) { return Utilities.timestamp(d, timeZone); } dt(dt, timeZone) { return Utilities.datetime(dt, timeZone); } now() { return this.dt(); } /** * 현재 시간값 (number of milliseconds since midnight of January 1, 1970.) * * @returns {number} */ current_time_ms(shift) { const time_shift = this.N(shift, 0); let ret = new Date().getTime(); ret += time_shift; return ret; } /** * NameSpace Maker. */ NS(ns, color, len, delim) { if (!ns) return ns; len = len || 4; len = len - ns.length; len = len < 0 ? 0 : len; const LC = this.env('LC', '0') === '1'; // LINE COLORING const SPACE = ' '; ns = SPACE.substr(0, len) + ns + (delim === undefined ? ':' : `${delim || ''}`); if (LC && COLORS[color]) ns = `${COLORS[color]}${ns}\x1b[0m`; return ns; } /** * escape string for mysql. */ escape(str, urldecode) { if (str === undefined) return 'NULL'; if (this.isInteger(str)) return str; str = str || ''; if (typeof str == 'object') { str = JSON.stringify(str); } str = str.replace(/\\/g, '\\\\').replace(/\$/g, '\\$').replace(/'/g, "\\'").replace(/"/g, '\\"'); if (urldecode) { // url-decode str = decodeURI(str); } return "'" + str + "'"; } /** * check if integer * @param x any number or string */ isInteger(x) { return typeof x === 'number' && x % 1 === 0; } /** * convert as integer number. * @param x any number or string * @param def default value. */ N(x, def) { try { if (x === '' || x === undefined || x === null) return def; if (typeof x === 'number' && x % 1 === 0) return x; if (typeof x == 'number') return parseInt('' + x); x = '0' + x; x = x.startsWith('0-') || x.startsWith('0+') ? x.substr(1) : x; // minus return parseInt(x.replace(/,/gi, '').trim()); } catch (e) { this.err('err at _N: x=' + x + ';' + typeof x + ';' + (e.message || ''), e); return def; } } /** * parse as float number (like 1.01) * @param x any number or string * @param def default value. */ F(x, def) { try { if (x === '' || x === undefined || x === null) return def; if (typeof x === 'number' && x % 1 === 0) return x; if (typeof x == 'number') return parseFloat('' + x); x = '0' + x; x = x.startsWith('0-') || x.startsWith('0+') ? x.substr(1) : x; // minus return parseFloat(x.replace(/,/gi, '').trim()); } catch (e) { this.err('err at _N: x=' + x + ';' + typeof x + ';' + (e.message || ''), e); return def; } } /** * parse float by len * ``` * FN(0.333333, 2) = 0.33 * ``` * @param x any numbe or string * @param len decimal length * @param mode 'round' | 'floor' */ FN(x, len, mode) { mode = mode === undefined ? 'round' : mode; const DIV = [0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0]; if (len < 0 || len >= DIV.length) throw new Error(`@len[${len}] is out of range!`); const div = DIV[len]; if (div <= 0) return this.N(x, 0); // as integer const val = this.F(x, 0) * div; const val2 = mode == 'round' ? Math.round(val) : Math.floor(val); const val3 = val2 / div; return val3; } /** * remove internal properties which starts with _ or $ */ cleanup(node) { return Object.keys(node).reduce(function (N, key) { if (key.startsWith('_') || key.startsWith('$')) delete N[key]; return N; }, node); } //! remove underscore variables. updated(that, that2) { const updated = Object.keys(that2).reduce((self, key) => { if (that[key] !== that2[key]) { if (that[key] === null && that2[key] === '') { // both same. return self; } self[key] = that2[key]; } return self; }, {}); return updated; } copy($N) { return Object.keys($N).reduce(function ($n, key) { $n[key] = $N[key]; return $n; }, {}); } copy_node(node, isClear) { isClear = isClear === undefined ? false : isClear; return Object.keys(node).reduce(function (N, key) { if (key.startsWith('_') || key.startsWith('$')) return N; N[key] = isClear ? null : node[key]; return N; }, {}); } /** * clean up all member without only KEY member. */ bare_node($N, opts) { let $n = {}; $n._id = $N._id; $n._current_time = $N._current_time; if (opts) $n = this.extend($n, opts); return $n; } /** * get keys in difference. */ diff(obj1, obj2) { obj1 = obj1 || {}; obj2 = obj2 || {}; const diff = Object.keys(obj1) .reduce((result, key) => { if (!obj2.hasOwnProperty(key)) { result.push(key); } else if (this.isEqual(obj1[key], obj2[key])) { const resultKeyIndex = result.indexOf(key); result.splice(resultKeyIndex, 1); } return result; }, Object.keys(obj2)) .sort(); return diff; } /** * check if equal between 2 object. * - inspired from `underscore` module originally, and optimized for compartibility. */ isEqual(obj1, obj2) { const keys = Object.keys; function tagTester(name) { return function (obj) { return toString.call(obj) === '[object ' + name + ']'; }; } const isFunction = tagTester('Function'); function _has(obj, path) { return obj != null && Object.hasOwnProperty.call(obj, path); } // Internal recursive comparison function for `isEqual`. function eq(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](https://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a === 1 / b; // `null` or `undefined` only equal to itself (strict comparison). if (a == null || b == null) return false; // `NaN`s are equivalent, but non-reflexive. if (a !== a) return b !== b; // Exhaust primitive checks const type = typeof a; if (type !== 'function' && type !== 'object' && typeof b != 'object') return false; return deepEq(a, b, aStack, bStack); } // Internal recursive comparison function for `isEqual`. function deepEq(a, b, aStack, bStack) { // Compare `[[Class]]` names. const className = toString.call(a); if (className !== toString.call(b)) return false; switch (className) { // Strings, numbers, regular expressions, dates, and booleans are compared by value. case '[object RegExp]': // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return '' + a === '' + b; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. // Object(NaN) is equivalent to NaN. if (+a !== +a) return +b !== +b; // An `egal` comparison is performed for other numeric values. return +a === 0 ? 1 / +a === 1 / b : +a === +b; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a === +b; } const areArrays = className === '[object Array]'; if (!areArrays) { if (typeof a != 'object' || typeof b != 'object') return false; // Objects with different constructors are not equivalent, but `Object`s or `Array`s // from different frames are. const aCtor = a.constructor, bCtor = b.constructor; if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor && isFunction(bCtor) && bCtor instanceof bCtor) && 'constructor' in a && 'constructor' in b) { return false; } } // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. // Initializing stack of traversed objects. // It's done here since we only need them for objects and arrays comparison. aStack = aStack || []; bStack = bStack || []; let length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] === a) return bStack[length] === b; } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); // Recursively compare objects and arrays. if (areArrays) { // Compare array lengths to determine if a deep comparison is necessary. length = a.length; if (length !== b.length) return false; // Deep compare the contents, ignoring non-numeric properties. while (length--) { if (!eq(a[length], b[length], aStack, bStack)) return false; } } else { // Deep compare objects. const _keys = keys(a); let key; length = _keys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. if (keys(b).length !== length) return false; while (length--) { // Deep compare each member key = _keys[length]; if (!(_has(b, key) && eq(a[key], b[key], aStack, bStack))) return false; } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return true; } return eq(obj1, obj2); } /** * calcualte node differences */ diff_node(obj1, obj2) { obj1 = obj1 || {}; obj2 = obj2 || {}; const keys1 = Object.keys(obj1).filter(key => (key.startsWith('_') || key.startsWith('$') ? false : true)); const keys2 = Object.keys(obj2).filter(key => (key.startsWith('_') || key.startsWith('$') ? false : true)); const diff = keys1.reduce((list, key) => { if (!obj2.hasOwnProperty(key)) { list.push(key); } else if (this.isEqual(obj1[key], obj2[key])) { const resultKeyIndex = list.indexOf(key); list.splice(resultKeyIndex, 1); } return list; }, keys2); return diff; } /** * get 32-bits hash value. * * @param data */ hash(data) { data = data || ''; data = typeof data === 'object' ? this.json(data, true) : data; //WARN! it must be sorted json. data = typeof data !== 'string' ? String(data) : data; /** * Calculate a 32 bit FNV-1a hash * Found here: https://gist.github.com/vaiorabbit/5657561 * Ref.: http://isthe.com/chongo/tech/comp/fnv/ * * @param {string} str the input value * @param {boolean} [asString=false] set to true to return the hash value as * 8-digit hex string instead of an integer * @param {integer} [seed] optionally pass the hash of the previous chunk * @returns {integer | string} */ const hashFnv32a = function (str, asString, seed) { /*jshint bitwise:false */ let i, l; let hval = seed === undefined ? 0x811c9dc5 : seed; for (i = 0, l = str.length; i < l; i++) { hval ^= str.charCodeAt(i); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } // Convert to 8 digit hex string return ('0000000' + (hval >>> 0).toString(16)).substr(-8); }; return hashFnv32a(data, true); } //! start promise chain. promise(param) { return new Promise(function (resolve) { resolve(param); }); } //! promise in sequence. // example) promise_sequence([1,2,3], item => item+1); promise_sequence(array, func) { let chain = this.promise(array.shift()); chain = array.reduce((chain, item) => { return chain.then(() => func(item)); }, chain.then(item => func(item))); return chain; } /** * get md5 hash */ md5(data, digest) { digest = digest === undefined ? 'hex' : digest; return crypto_1.default.createHash('md5').update(data).digest(digest); } /** * get hmac hash */ hmac(data, KEY, algorithm, encoding) { KEY = KEY || 'XENI'; encoding = encoding || 'base64'; algorithm = algorithm || 'sha256'; return crypto_1.default.createHmac(algorithm, KEY).update(data).digest(encoding); } /** * parse query-string. * * @param query */ qs_parse(query) { const param = query_string_1.default.parse(query); Object.keys(param).forEach(key => { if (false) { } //! 빈 파라미터의 값을 빈 문자열로 치환 else if (param[key] === null) { param[key] = ''; } //! 숫자로 된 문자열이 오면 숫자로 변환 else if (/^[1-9][0-9]*$/.test(param[key])) { param[key] = this.N(param[key]); } }); return param; } /** * stringify as querystring. * @param query */ qs_stringify(query) { const param = query_string_1.default.stringify(query); return param; } /** * get UUID as `uuid.v4()` */ uuid() { return uuid.v4(); } } exports.Utilities = Utilities; //# sourceMappingURL=utilities.js.map