@reldens/utils 0.47.0 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/env-var.js CHANGED
@@ -4,12 +4,14 @@
4
4
  *
5
5
  */
6
6
 
7
+ const sc = require('./shortcuts');
8
+
7
9
  class EnvVar
8
10
  {
9
11
 
10
12
  string(obj, key, defaultValue)
11
13
  {
12
- const val = obj[key];
14
+ let val = obj[key];
13
15
  if('string' === typeof val){
14
16
  return val;
15
17
  }
@@ -18,7 +20,7 @@ class EnvVar
18
20
 
19
21
  nonEmptyString(obj, key, defaultValue)
20
22
  {
21
- const val = obj[key];
23
+ let val = obj[key];
22
24
  if('string' === typeof val && '' !== val.trim()){
23
25
  return val;
24
26
  }
@@ -27,16 +29,17 @@ class EnvVar
27
29
 
28
30
  number(obj, key, defaultValue)
29
31
  {
30
- const val = obj[key];
31
- if(!isNaN(Number(val)) && '' !== val && null !== val){
32
- return Number(val);
32
+ let val = obj[key];
33
+ let parsed = sc.parseNumber(val);
34
+ if(parsed){
35
+ return parsed;
33
36
  }
34
37
  return defaultValue;
35
38
  }
36
39
 
37
40
  boolean(obj, key, defaultValue)
38
41
  {
39
- const val = obj[key];
42
+ let val = obj[key];
40
43
  if('true' === val || '1' === val){
41
44
  return true;
42
45
  }
@@ -46,6 +49,51 @@ class EnvVar
46
49
  return defaultValue;
47
50
  }
48
51
 
52
+ array(obj, key, defaultValue, separator = ',')
53
+ {
54
+ let val = obj[key];
55
+ let parsed = sc.splitToArray(val, separator);
56
+ if(parsed){
57
+ return parsed;
58
+ }
59
+ return defaultValue;
60
+ }
61
+
62
+ url(obj, key, defaultValue)
63
+ {
64
+ let val = obj[key];
65
+ if(!sc.isString(val)){
66
+ return defaultValue;
67
+ }
68
+ if(sc.isValidUrl(val)){
69
+ return val;
70
+ }
71
+ return defaultValue;
72
+ }
73
+
74
+ json(obj, key, defaultValue)
75
+ {
76
+ let val = obj[key];
77
+ if(!sc.isString(val)){
78
+ return defaultValue;
79
+ }
80
+ return sc.parseJson(val, defaultValue);
81
+ }
82
+
83
+ integer(obj, key, defaultValue, min, max)
84
+ {
85
+ let val = this.number(obj, key, defaultValue);
86
+ if(val !== defaultValue && sc.isValidInteger(val, min, max)){
87
+ return val;
88
+ }
89
+ return defaultValue;
90
+ }
91
+
92
+ port(obj, key, defaultValue)
93
+ {
94
+ return this.integer(obj, key, defaultValue, 1, 65535);
95
+ }
96
+
49
97
  }
50
98
 
51
99
  module.exports = new EnvVar();
@@ -28,8 +28,21 @@ class AwaitEventEmitterExtended extends AwaitEventEmitter
28
28
  this.debug = false;
29
29
  }
30
30
 
31
+ validateEventKey(eventKey)
32
+ {
33
+ return !sc.hasDangerousKeys(null, eventKey);
34
+ }
35
+
31
36
  onWithKey(eventName, callback, uniqueRemoveKey, masterKey)
32
37
  {
38
+ if(!this.validateEventKey(eventName) || !this.validateEventKey(uniqueRemoveKey)){
39
+ Logger.critical('Invalid event key detected: '+eventName+' or '+uniqueRemoveKey);
40
+ return false;
41
+ }
42
+ if(masterKey && !this.validateEventKey(masterKey)){
43
+ Logger.critical('Invalid master key detected: '+masterKey);
44
+ return false;
45
+ }
33
46
  if(
34
47
  sc.hasOwn(this.eventsByRemoveKeys, uniqueRemoveKey)
35
48
  || (
@@ -62,6 +75,14 @@ class AwaitEventEmitterExtended extends AwaitEventEmitter
62
75
  */
63
76
  offWithKey(uniqueRemoveKey, masterKey)
64
77
  {
78
+ if(!this.validateEventKey(uniqueRemoveKey)){
79
+ Logger.critical('Invalid remove key detected: '+uniqueRemoveKey);
80
+ return false;
81
+ }
82
+ if(masterKey && !this.validateEventKey(masterKey)){
83
+ Logger.critical('Invalid master key detected: '+masterKey);
84
+ return false;
85
+ }
65
86
  if(masterKey && !sc.hasOwn(this.eventsByRemoveKeys, masterKey)){
66
87
  Logger.debug('Event not found by masterKey "'+masterKey+'".');
67
88
  return false;
@@ -74,8 +95,16 @@ class AwaitEventEmitterExtended extends AwaitEventEmitter
74
95
  ? this.eventsByRemoveKeys[masterKey][uniqueRemoveKey]
75
96
  : this.eventsByRemoveKeys[uniqueRemoveKey];
76
97
 
98
+ if(!this.validateEventKey(eventToRemove.eventName)){
99
+ Logger.critical('Invalid event name in stored event: '+eventToRemove.eventName);
100
+ return false;
101
+ }
77
102
  let dataArr = this.listeners(eventToRemove.eventName);
78
103
  let currentListenerIndex = dataArr.indexOf(eventToRemove.callback);
104
+ if(-1 === currentListenerIndex){
105
+ Logger.debug('Event listener not found in _events array.');
106
+ return false;
107
+ }
79
108
  this._events[eventToRemove.eventName].splice(currentListenerIndex, 1);
80
109
  if(0 === this._events[eventToRemove.eventName].length){
81
110
  delete this._events[eventToRemove.eventName];
@@ -95,6 +124,10 @@ class AwaitEventEmitterExtended extends AwaitEventEmitter
95
124
  */
96
125
  offByMasterKey(masterKey)
97
126
  {
127
+ if(!this.validateEventKey(masterKey)){
128
+ Logger.critical('Invalid master key detected: '+masterKey);
129
+ return false;
130
+ }
98
131
  if(!sc.hasOwn(this.eventsByRemoveKeys, masterKey)){
99
132
  Logger.debug('Events not found by masterKey "'+masterKey+'".');
100
133
  return false;
@@ -104,8 +137,14 @@ class AwaitEventEmitterExtended extends AwaitEventEmitter
104
137
  Logger.debug('Removing events by masterKey: '+masterKey, Object.keys(this.eventsByRemoveKeys[masterKey]));
105
138
  for(let uniqueRemoveKey of Object.keys(this.eventsByRemoveKeys[masterKey])){
106
139
  let eventToRemove = this.eventsByRemoveKeys[masterKey][uniqueRemoveKey];
140
+ if(!this.validateEventKey(eventToRemove.eventName)){
141
+ continue;
142
+ }
107
143
  let dataArr = this.listeners(eventToRemove.eventName);
108
144
  let currentListenerIndex = dataArr.indexOf(eventToRemove.callback);
145
+ if(-1 === currentListenerIndex){
146
+ continue;
147
+ }
109
148
  this._events[eventToRemove.eventName].splice(currentListenerIndex, 1);
110
149
  if(0 === this._events[eventToRemove.eventName].length){
111
150
  delete this._events[eventToRemove.eventName];
@@ -116,7 +155,11 @@ class AwaitEventEmitterExtended extends AwaitEventEmitter
116
155
 
117
156
  on(key, callback)
118
157
  {
119
- if(this.debug !== false){
158
+ if(!this.validateEventKey(key)){
159
+ Logger.critical('Invalid event key detected: '+key);
160
+ return false;
161
+ }
162
+ if(false !== this.debug){
120
163
  this.logDebugEvent(key, 'Listen');
121
164
  }
122
165
  super.on(key, callback);
@@ -124,7 +167,11 @@ class AwaitEventEmitterExtended extends AwaitEventEmitter
124
167
 
125
168
  async emit(key, ...args)
126
169
  {
127
- if(this.debug !== false){
170
+ if(!this.validateEventKey(key)){
171
+ Logger.critical('Invalid event key detected: '+key);
172
+ return false;
173
+ }
174
+ if(false !== this.debug){
128
175
  this.logDebugEvent(key, 'Fire');
129
176
  }
130
177
  await super.emit(key, ...args);
package/lib/logger.js CHANGED
@@ -4,6 +4,8 @@
4
4
  *
5
5
  */
6
6
 
7
+ const sc = require('./shortcuts');
8
+
7
9
  class Logger
8
10
  {
9
11
 
@@ -21,15 +23,14 @@ class Logger
21
23
 
22
24
  constructor()
23
25
  {
24
- // @TODO - BETA
25
- // - Implement different log systems (console.log, files logs, db log?).
26
- // - Implement notifications system (email?), and make it configurable for the different log levels.
27
26
  let context = this.context();
28
- this.enableTraceBack = '';
29
- this.logLevelBack = 3;
30
- this.forcedDisabled = Boolean(context.RELDENS_FORCED_DISABLED_LOGS || false);
31
- this.addTimeStamp = Boolean(context.RELDENS_INCLUDE_LOGS_TIMESTAMP || true);
27
+ this.enableTraceBack = sc.get(context, 'RELDENS_LOGS_ENABLE_TRACE_BACK', '');
28
+ this.logLevelBack = sc.get(context, 'RELDENS_LOGS_LEVEL_BACK', 3);
32
29
  this.callback = false;
30
+ this.forcedDisabled = Boolean(sc.get(context, 'RELDENS_LOGS_FORCED_DISABLED', false));
31
+ this.addTimeStamp = Boolean(sc.get(context, 'RELDENS_LOGS_INCLUDE_TIMESTAMP', true));
32
+ this.maxLogArgLength = sc.get(context, 'RELDENS_LOGS_MAX_ARGUMENT_LENGTH', 1000);
33
+ this.maxStackTraceLength = sc.get(context, 'RELDENS_LOGS_MAX_STACK_TRACE_LENGTH', 2000);
33
34
  }
34
35
 
35
36
  context()
@@ -94,7 +95,8 @@ class Logger
94
95
  log(levelLabel, ...args)
95
96
  {
96
97
  let date = !this.addTimeStamp ? '' : (new Date()).toISOString().slice(0, 19).replace('T', ' ')+' - ';
97
- this.logWithCallback(date+levelLabel.toUpperCase()+' -', ...args);
98
+ let sanitizedArgs = args.map(arg => sc.isString(arg) ? sc.cleanMessage(arg, this.maxLogArgLength) : arg);
99
+ this.logWithCallback(date+levelLabel.toUpperCase()+' -', ...sanitizedArgs);
98
100
  if(-1 !== this.enableTraceFor().indexOf('all') || -1 !== this.enableTraceFor().indexOf(levelLabel)){
99
101
  if('function' !== typeof Error?.captureStackTrace){
100
102
  this.logWithCallback('Error.captureStackTrace is not available.', typeof Error?.captureStackTrace);
@@ -102,7 +104,8 @@ class Logger
102
104
  }
103
105
  let stackHolder = {};
104
106
  Error.captureStackTrace(stackHolder, levelLabel);
105
- this.logWithCallback(stackHolder.stack);
107
+ let sanitizedStack = sc.cleanMessage(stackHolder.stack, this.maxStackTraceLength);
108
+ this.logWithCallback(sanitizedStack);
106
109
  }
107
110
  return this;
108
111
  }
@@ -43,6 +43,13 @@ class SchemaValidator extends ValidatorInterface
43
43
  }
44
44
  for(let i of Object.keys(schema)){
45
45
  let validate = schema[i];
46
+ if(validate.required && !sc.hasOwn(obj, i)){
47
+ Logger.debug('Required property missing.', i);
48
+ return false;
49
+ }
50
+ if(!sc.hasOwn(obj, i) && !validate.required){
51
+ continue;
52
+ }
46
53
  if(!this.isValidSchema(obj[i], validate, i)){
47
54
  return false;
48
55
  }
@@ -61,12 +68,36 @@ class SchemaValidator extends ValidatorInterface
61
68
  Logger.debug('No schema type provided.', schema, obj, objectKey);
62
69
  return false;
63
70
  }
71
+ if(schema.custom && sc.isFunction(schema.custom)){
72
+ if(!schema.custom(obj)){
73
+ Logger.debug('Custom validation failed.', objectKey, obj, schema);
74
+ return false;
75
+ }
76
+ }
77
+ if(schema.enum && sc.isArray(schema.enum)){
78
+ if(-1 === schema.enum.indexOf(obj)){
79
+ Logger.debug('Value not in enum.', objectKey, obj, schema.enum);
80
+ return false;
81
+ }
82
+ }
64
83
  switch(schema.type){
65
84
  case 'string':
66
85
  if(!sc.isString(obj)){
67
86
  Logger.debug('Object is not a string.', objectKey, obj, schema);
68
87
  return false;
69
88
  }
89
+ if(schema.min && obj.length < schema.min){
90
+ Logger.debug('String too short.', objectKey, obj.length, schema.min);
91
+ return false;
92
+ }
93
+ if(schema.max && obj.length > schema.max){
94
+ Logger.debug('String too long.', objectKey, obj.length, schema.max);
95
+ return false;
96
+ }
97
+ if(schema.pattern && !schema.pattern.test(obj)){
98
+ Logger.debug('String pattern mismatch.', objectKey, obj, schema.pattern);
99
+ return false;
100
+ }
70
101
  break;
71
102
  case 'number':
72
103
  if(!sc.isInt(obj)){
@@ -75,18 +106,42 @@ class SchemaValidator extends ValidatorInterface
75
106
  return false;
76
107
  }
77
108
  }
109
+ if(schema.min && obj < schema.min){
110
+ Logger.debug('Number too small.', objectKey, obj, schema.min);
111
+ return false;
112
+ }
113
+ if(schema.max && obj > schema.max){
114
+ Logger.debug('Number too large.', objectKey, obj, schema.max);
115
+ return false;
116
+ }
78
117
  break;
79
118
  case 'int':
80
119
  if(!sc.isInt(obj)){
81
120
  Logger.debug('Object is not an integer.', objectKey, obj, schema);
82
121
  return false;
83
122
  }
123
+ if(schema.min && obj < schema.min){
124
+ Logger.debug('Integer too small.', objectKey, obj, schema.min);
125
+ return false;
126
+ }
127
+ if(schema.max && obj > schema.max){
128
+ Logger.debug('Integer too large.', objectKey, obj, schema.max);
129
+ return false;
130
+ }
84
131
  break;
85
132
  case 'float':
86
133
  if(!sc.isFloat(obj)){
87
134
  Logger.debug('Object is not a float.', objectKey, obj, schema);
88
135
  return false;
89
136
  }
137
+ if(schema.min && obj < schema.min){
138
+ Logger.debug('Float too small.', objectKey, obj, schema.min);
139
+ return false;
140
+ }
141
+ if(schema.max && obj > schema.max){
142
+ Logger.debug('Float too large.', objectKey, obj, schema.max);
143
+ return false;
144
+ }
90
145
  break;
91
146
  case 'boolean':
92
147
  if(!sc.isBoolean(obj)){
@@ -105,6 +160,14 @@ class SchemaValidator extends ValidatorInterface
105
160
  Logger.debug('Object is not an array.', objectKey, obj, schema);
106
161
  return false;
107
162
  }
163
+ if(schema.min && obj.length < schema.min){
164
+ Logger.debug('Array too short.', objectKey, obj.length, schema.min);
165
+ return false;
166
+ }
167
+ if(schema.max && obj.length > schema.max){
168
+ Logger.debug('Array too long.', objectKey, obj.length, schema.max);
169
+ return false;
170
+ }
108
171
  if(schema.valuesType){
109
172
  for(let i of obj){
110
173
  if(!this.isValidSchema(i, {type: schema.valuesType}, objectKey)){
package/lib/shortcuts.js CHANGED
@@ -93,12 +93,32 @@ class Shortcuts
93
93
  return 'boolean' === typeof value;
94
94
  }
95
95
 
96
+ hasDangerousKeys(obj, key = null)
97
+ {
98
+ let dangerousKeys = ['__proto__', 'constructor', 'prototype'];
99
+ if(key){
100
+ return -1 !== dangerousKeys.indexOf(key);
101
+ }
102
+ if(!this.isObject(obj)){
103
+ return false;
104
+ }
105
+ for(let dangerousKey of dangerousKeys){
106
+ if(this.hasOwn(obj, dangerousKey)){
107
+ return true;
108
+ }
109
+ }
110
+ return false;
111
+ }
112
+
96
113
  deepMergeProperties(target, source)
97
114
  {
98
115
  if(!this.isObject(target) || !this.isObject(source)){
99
116
  return false;
100
117
  }
101
118
  for(let key of Object.keys(source)){
119
+ if(this.hasDangerousKeys(null, key)){
120
+ continue;
121
+ }
102
122
  if(this.isObject(source[key])){
103
123
  if(!this.hasOwn(target, key)){
104
124
  target[key] = source[key];
@@ -157,8 +177,8 @@ class Shortcuts
157
177
  if(!sortField){
158
178
  return collection;
159
179
  }
160
- let directionValue = 'act' === direction ? 1 : -1;
161
- let directionOpposite = 'act' === direction ? -1 : 1;
180
+ let directionValue = 'asc' === direction ? 1 : -1;
181
+ let directionOpposite = 'asc' === direction ? -1 : 1;
162
182
  return collection.sort((a, b) => {
163
183
  return a[sortField] > b[sortField] ? directionValue : directionOpposite;
164
184
  });
@@ -182,11 +202,16 @@ class Shortcuts
182
202
 
183
203
  parseJson(jsonString, defaultReturn = false)
184
204
  {
205
+ let parsed;
185
206
  try {
186
- return JSON.parse(jsonString);
207
+ parsed = JSON.parse(jsonString);
187
208
  } catch (e) {
188
209
  return defaultReturn;
189
210
  }
211
+ if(this.hasDangerousKeys(parsed)){
212
+ return defaultReturn;
213
+ }
214
+ return parsed;
190
215
  }
191
216
 
192
217
  deepJsonClone(obj)
@@ -321,7 +346,7 @@ class Shortcuts
321
346
  removeFromArray(valuesArray, removeValues)
322
347
  {
323
348
  return valuesArray.filter((value) => {
324
- return removeValues.indexOf(value) === -1;
349
+ return -1 === removeValues.indexOf(value);
325
350
  });
326
351
  }
327
352
 
@@ -409,7 +434,7 @@ class Shortcuts
409
434
  if(!message){
410
435
  return '';
411
436
  }
412
- let text = message.toString().replace(/\\/g, '');
437
+ let text = message.replace(/\\/g, '').replace(/[\r\n\t]/g, ' ');
413
438
  if(0 < characterLimit){
414
439
  return text.substring(0, characterLimit);
415
440
  }
@@ -418,8 +443,7 @@ class Shortcuts
418
443
 
419
444
  slugify(text)
420
445
  {
421
- return (text || '')
422
- .replace(/&/g, ' and ')
446
+ return (text || '').replace(/&/g, ' and ')
423
447
  .replace(/[^a-zA-Z0-9]/g, ' ')
424
448
  .trim()
425
449
  .replace(/\s+/g, '-')
@@ -428,8 +452,7 @@ class Shortcuts
428
452
 
429
453
  isValidIsoCode(isoCode)
430
454
  {
431
- let isoLanguageCodeRegex = /^[a-zA-Z]{2}$/;
432
- return isoLanguageCodeRegex.test(isoCode);
455
+ return (/^[a-zA-Z]{2}$/).test(isoCode);
433
456
  }
434
457
 
435
458
  sanitize(input)
@@ -437,39 +460,45 @@ class Shortcuts
437
460
  if(!input){
438
461
  return '';
439
462
  }
440
- return input
441
- .replace(/&(?![a-zA-Z0-9#]+;)/g, '&amp;')
463
+ return input.replace(/&/g, '&amp;')
442
464
  .replace(/</g, '&lt;')
443
465
  .replace(/>/g, '&gt;')
444
466
  .replace(/"/g, '&quot;')
445
467
  .replace(/'/g, '&#x27;')
446
468
  .replace(/\//g, '&#x2F;')
447
- .replace(/`/g, '&#x60;');
469
+ .replace(/`/g, '&#x60;')
470
+ .replace(/\(/g, '&#x28;')
471
+ .replace(/\)/g, '&#x29;');
448
472
  }
449
473
 
450
474
  sanitizeUrl(url)
451
475
  {
452
- if(!url){
476
+ if(!url || !this.isString(url) || 2048 < url.length){
453
477
  return '';
454
478
  }
455
479
  let sanitized = url.trim();
456
- let urlPattern = /^(?:https?:\/\/|mailto:|tel:|#|\/|\.\/|\.\.\/)/i;
480
+ let urlPattern = /^https?:\/\/(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)(?::[1-9][0-9]{0,4})?(?:\/(?:[\w\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*(?:\?(?:[\w\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?(?:#(?:[\w\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?$/;
457
481
  if(!urlPattern.test(sanitized)){
458
482
  return '';
459
483
  }
460
- return sanitized
461
- .replace(/javascript:/gi, '')
484
+ return sanitized.replace(/javascript:/gi, '')
462
485
  .replace(/data:/gi, '')
463
486
  .replace(/vbscript:/gi, '');
464
487
  }
465
488
 
466
489
  camelCase(str)
467
490
  {
491
+ if(!this.isString(str) || 0 === str.length){
492
+ return str;
493
+ }
468
494
  return str.replace(/[_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '');
469
495
  }
470
496
 
471
497
  capitalizedCamelCase(str)
472
498
  {
499
+ if(!this.isString(str) || 0 === str.length){
500
+ return str;
501
+ }
473
502
  let camelStr = this.camelCase(str);
474
503
  return camelStr.charAt(0).toUpperCase() + camelStr.slice(1);
475
504
  }
@@ -479,6 +508,159 @@ class Shortcuts
479
508
  return str.replace(/[_\s]+/g, '-');
480
509
  }
481
510
 
511
+ capitalize(str)
512
+ {
513
+ if(!this.isString(str) || 0 === str.length){
514
+ return str;
515
+ }
516
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
517
+ }
518
+
519
+ chunk(array, size)
520
+ {
521
+ if(!this.isArray(array) || 0 >= size){
522
+ return [];
523
+ }
524
+ let result = [];
525
+ for(let i = 0; i < array.length; i += size){
526
+ result.push(array.slice(i, i + size));
527
+ }
528
+ return result;
529
+ }
530
+
531
+ flatten(array, depth = 1)
532
+ {
533
+ if(!this.isArray(array)){
534
+ return [];
535
+ }
536
+ return array.flat(depth);
537
+ }
538
+
539
+ unique(array)
540
+ {
541
+ if(!this.isArray(array)){
542
+ return [];
543
+ }
544
+ return [...new Set(array)];
545
+ }
546
+
547
+ clamp(value, min, max)
548
+ {
549
+ if(!this.isNumber(value) || !this.isNumber(min) || !this.isNumber(max)){
550
+ return value;
551
+ }
552
+ return Math.min(Math.max(value, min), max);
553
+ }
554
+
555
+ truncate(str, length, suffix = '...')
556
+ {
557
+ if(!this.isString(str) || str.length <= length){
558
+ return str;
559
+ }
560
+ return str.substring(0, length) + suffix;
561
+ }
562
+
563
+ pickProps(obj, props)
564
+ {
565
+ if(!this.isObject(obj) || !this.isArray(props)){
566
+ return {};
567
+ }
568
+ let result = {};
569
+ for(let prop of props){
570
+ if(this.hasDangerousKeys(null, prop)){
571
+ continue;
572
+ }
573
+ if(this.hasOwn(obj, prop)){
574
+ result[prop] = obj[prop];
575
+ }
576
+ }
577
+ return result;
578
+ }
579
+
580
+ omitProps(obj, props)
581
+ {
582
+ if(!this.isObject(obj) || !this.isArray(props)){
583
+ return obj;
584
+ }
585
+ let result = {};
586
+ for(let key of Object.keys(obj)){
587
+ if(-1 === props.indexOf(key)){
588
+ result[key] = obj[key];
589
+ }
590
+ }
591
+ return result;
592
+ }
593
+
594
+ debounce(func, wait)
595
+ {
596
+ if(!this.isFunction(func) || !this.isNumber(wait)){
597
+ return func;
598
+ }
599
+ let timeout;
600
+ return function executedFunction(...args) {
601
+ let later = () => {
602
+ clearTimeout(timeout);
603
+ func.apply(this, args);
604
+ };
605
+ clearTimeout(timeout);
606
+ timeout = setTimeout(later, wait);
607
+ };
608
+ }
609
+
610
+ throttle(func, limit)
611
+ {
612
+ if(!this.isFunction(func) || !this.isNumber(limit)){
613
+ return func;
614
+ }
615
+ let inThrottle;
616
+ return function executedFunction(...args) {
617
+ if(!inThrottle){
618
+ func.apply(this, args);
619
+ inThrottle = true;
620
+ setTimeout(() => inThrottle = false, limit);
621
+ }
622
+ };
623
+ }
624
+
625
+ isValidUrl(url)
626
+ {
627
+ if(!this.isString(url) || 2048 < url.length){
628
+ return false;
629
+ }
630
+ let urlPattern = /^https?:\/\/(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)(?::[1-9][0-9]{0,4})?(?:\/(?:[\w\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*(?:\?(?:[\w\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?(?:#(?:[\w\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*)?$/;
631
+ return urlPattern.test(url);
632
+ }
633
+
634
+ isValidInteger(value, min, max)
635
+ {
636
+ if(!this.isNumber(value) || !Number.isInteger(value)){
637
+ return false;
638
+ }
639
+ if('number' === typeof min && value < min){
640
+ return false;
641
+ }
642
+ if('number' === typeof max && value > max){
643
+ return false;
644
+ }
645
+ return true;
646
+ }
647
+
648
+ parseNumber(value)
649
+ {
650
+ if(!isNaN(Number(value)) && '' !== value && null !== value){
651
+ return Number(value);
652
+ }
653
+ return null;
654
+ }
655
+
656
+ splitToArray(str, separator = ',')
657
+ {
658
+ if(!this.isString(str) || '' === str.trim()){
659
+ return null;
660
+ }
661
+ return str.split(separator).map(item => item.trim()).filter(item => '' !== item);
662
+ }
663
+
482
664
  }
483
665
 
484
666
  module.exports = new Shortcuts();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/utils",
3
3
  "scope": "@reldens",
4
- "version": "0.47.0",
4
+ "version": "0.48.0",
5
5
  "description": "Reldens - Utils",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",