@reldens/utils 0.51.0 → 0.53.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Reldens - Utils
4
4
 
5
- ### Features
5
+ ## Features
6
6
 
7
7
  - Interaction area calculation helper.
8
8
  - Page range provider helper.
@@ -13,6 +13,106 @@
13
13
  - Error Manager.
14
14
  - Events Manager and Events Manager Singleton.
15
15
 
16
+ ---
17
+
18
+ ## Usage examples
19
+
20
+ ### EventsManager
21
+ Advanced event emitter with features like event sanitization, memory leak detection, key-based event management, and support for async/sync execution.
22
+
23
+ ```javascript
24
+ const { EventsManager } = require('@reldens/utils');
25
+ let eventsManager = new EventsManager();
26
+
27
+ // Listen to events
28
+ eventsManager.on('userLogin', (userData) => {
29
+ console.log('User logged in:', userData.username);
30
+ });
31
+
32
+ // Emit events
33
+ eventsManager.emit('userLogin', { username: 'player1', id: 123 });
34
+
35
+ // Listen with unique keys
36
+ eventsManager.onWithKey('gameStart', callback, 'uniqueKey');
37
+ eventsManager.offWithKey('uniqueKey'); // Remove by key
38
+ ```
39
+
40
+ ### InteractionArea
41
+ Spatial interaction validation helper that determines if objects are within interaction range based on coordinates and area margins.
42
+
43
+ ```javascript
44
+ const { InteractionArea } = require('@reldens/utils');
45
+ let interactionArea = new InteractionArea();
46
+
47
+ // Setup interaction area
48
+ interactionArea.setupInteractionArea(50, 100, 200); // margin=50, x=100, y=200
49
+
50
+ // Validate interaction
51
+ let isValid = interactionArea.isValidInteraction(120, 210); // true
52
+ let isInvalid = interactionArea.isValidInteraction(200, 300); // false
53
+ ```
54
+
55
+ ### Logger
56
+ Advanced logging utility with multiple log levels, custom levels support, active level filtering, and environment-based configuration.
57
+
58
+ ```javascript
59
+ const { Logger } = require('@reldens/utils');
60
+
61
+ // Standard logging
62
+ Logger.info('Application started');
63
+ Logger.error('Database connection failed', errorDetails);
64
+ Logger.debug('Processing user request', requestData);
65
+
66
+ // Custom levels
67
+ Logger.activeLogLevels = [3, 4, 10]; // Only critical, error, and custom level 10
68
+ Logger.customLevels = [10, 11, 12];
69
+ Logger.log(10, 'custom', 'Custom log message');
70
+
71
+ // Configuration
72
+ Logger.setForcedDisabled(false);
73
+ Logger.setAddTimeStamp(true);
74
+ ```
75
+
76
+ ### PageRangeProvider
77
+ Pagination helper that calculates page ranges for UI components, handling first/last page navigation and customizable display options.
78
+
79
+ ```javascript
80
+ const { PageRangeProvider } = require('@reldens/utils');
81
+ let pageProvider = new PageRangeProvider();
82
+
83
+ // Generate page range
84
+ let range = pageProvider.fetch(5, 20, 7, 'First', 'Last');
85
+ // Returns: [{label: 'First', value: 1}, {label: 2, value: 2}, ...]
86
+
87
+ // Simple range
88
+ let simpleRange = pageProvider.fetch(3, 10, 5);
89
+ // Returns pages around current page 3
90
+ ```
91
+
92
+ ### SchemaValidator
93
+ Object validation utility that validates object properties against defined schemas with support for nested objects and custom validation rules.
94
+
95
+ ```javascript
96
+ const { SchemaValidator } = require('@reldens/utils');
97
+
98
+ let schema = {
99
+ username: { type: 'string', min: 3, max: 20 },
100
+ age: { type: 'int', min: 18 },
101
+ profile: {
102
+ type: 'object',
103
+ nested: {
104
+ email: { type: 'string', required: true }
105
+ }
106
+ }
107
+ };
108
+
109
+ let validator = new SchemaValidator(schema);
110
+ let userData = { username: 'player1', age: 25, profile: { email: 'test@example.com' } };
111
+ let isValid = validator.validate(userData);
112
+ ```
113
+
114
+ ---
115
+
16
116
  Need something specific?
17
117
 
18
118
  [Request a feature here: https://www.reldens.com/features-request](https://www.reldens.com/features-request)
@@ -21,10 +21,11 @@ class EventsManager
21
21
  this.maxEventKeyLength = 1000;
22
22
  this.maxListeners = 10000;
23
23
  this.maxEventArgs = 50;
24
- this.sensitiveFields = ['password', 'token', 'secret', 'key', 'auth', 'credential'];
24
+ this.sensitiveFields = ['password', 'token', 'secret', 'auth', 'credential'];
25
25
  this.hasLoggedMaxListeners = false;
26
26
  this.symbolString = '--[[await-event-emitter]]--';
27
27
  this.typeKeyName = sc.isFunction(Symbol) ? Symbol.for(this.symbolString) : this.symbolString;
28
+ this.enableSensitiveDataFiltering = false;
28
29
  }
29
30
 
30
31
  assertType(type)
@@ -81,6 +82,9 @@ class EventsManager
81
82
  Logger.warning('Event arguments exceed maximum: '+args.length);
82
83
  return [];
83
84
  }
85
+ if(!this.enableSensitiveDataFiltering){
86
+ return args;
87
+ }
84
88
  return args.map(arg => {
85
89
  if(sc.isObject(arg)){
86
90
  return this.filterSensitiveData(arg);
@@ -89,11 +93,21 @@ class EventsManager
89
93
  });
90
94
  }
91
95
 
92
- filterSensitiveData(obj)
96
+ filterSensitiveData(obj, visited = new WeakSet())
93
97
  {
94
98
  if(!sc.isObject(obj)){
95
99
  return obj;
96
100
  }
101
+ if(visited.has(obj)){
102
+ return '[CIRCULAR]';
103
+ }
104
+ if(obj instanceof Map || obj instanceof Set || obj instanceof Date || obj instanceof Array){
105
+ return obj;
106
+ }
107
+ if(Object.getPrototypeOf(obj) !== Object.prototype){
108
+ return obj;
109
+ }
110
+ visited.add(obj);
97
111
  let filtered = {};
98
112
  for(let key of Object.keys(obj)){
99
113
  if(sc.hasDangerousKeys(null, key)){
@@ -107,11 +121,16 @@ class EventsManager
107
121
  continue;
108
122
  }
109
123
  if(sc.isObject(obj[key])){
110
- filtered[key] = this.filterSensitiveData(obj[key]);
124
+ filtered[key] = this.filterSensitiveData(obj[key], visited);
125
+ continue;
126
+ }
127
+ if(sc.isFunction(obj[key])) {
128
+ filtered[key] = obj[key];
111
129
  continue;
112
130
  }
113
131
  filtered[key] = obj[key];
114
132
  }
133
+ visited.delete(obj);
115
134
  return filtered;
116
135
  }
117
136
 
@@ -442,7 +461,9 @@ class EventsManager
442
461
  }
443
462
  let logMessage = type+' Event: '+key;
444
463
  if(args && 0 < args.length){
445
- let filteredArgs = args.map(arg => this.filterSensitiveData(arg));
464
+ let filteredArgs = this.enableSensitiveDataFiltering
465
+ ? args.map(arg => this.filterSensitiveData(arg))
466
+ : args;
446
467
  logMessage += ' with '+filteredArgs.length+' arguments';
447
468
  }
448
469
  Logger.debug(logMessage);
package/lib/logger.js CHANGED
@@ -32,6 +32,8 @@ class Logger
32
32
  this.maxLogArgLength = sc.get(context, 'RELDENS_LOGS_MAX_ARGUMENT_LENGTH', 0);
33
33
  this.maxStackTraceLength = sc.get(context, 'RELDENS_LOGS_MAX_STACK_TRACE_LENGTH', 0);
34
34
  this.applySanitizer = Boolean(sc.get(context, 'RELDENS_LOGS_APPLY_SANITIZER', false));
35
+ this.activeLogLevels = [];
36
+ this.customLevels = [];
35
37
  }
36
38
 
37
39
  context()
@@ -91,8 +93,21 @@ class Logger
91
93
  return (this.context().RELDENS_ENABLE_TRACE_FOR || '').split(',');
92
94
  }
93
95
 
94
- log(levelLabel, ...args)
96
+ log(levelNumber, levelLabel, ...args)
95
97
  {
98
+ if(this.forcedDisabled){
99
+ return this;
100
+ }
101
+ if(0 < this.activeLogLevels.length){
102
+ if(-1 === this.activeLogLevels.indexOf(levelNumber)){
103
+ return this;
104
+ }
105
+ }
106
+ if(0 === this.activeLogLevels.length){
107
+ if(levelNumber > this.logLevel()){
108
+ return this;
109
+ }
110
+ }
96
111
  let date = !this.addTimeStamp ? '' : (new Date()).toISOString().slice(0, 19).replace('T', ' ')+' - ';
97
112
  let processedArgs = args;
98
113
  if(this.applySanitizer){
@@ -125,90 +140,42 @@ class Logger
125
140
 
126
141
  debug(...args)
127
142
  {
128
- if(this.forcedDisabled){
129
- return this;
130
- }
131
- if(8 > this.logLevel()){
132
- return this;
133
- }
134
- return this.log('debug', ...args);
143
+ return this.log(8, 'debug', ...args);
135
144
  }
136
145
 
137
146
  info(...args)
138
147
  {
139
- if(this.forcedDisabled){
140
- return this;
141
- }
142
- if(7 > this.logLevel()){
143
- return this;
144
- }
145
- return this.log('info', ...args);
148
+ return this.log(7, 'info', ...args);
146
149
  }
147
150
 
148
151
  notice(...args)
149
152
  {
150
- if(this.forcedDisabled){
151
- return this;
152
- }
153
- if(6 > this.logLevel()){
154
- return this;
155
- }
156
- return this.log('notice', ...args);
153
+ return this.log(6, 'notice', ...args);
157
154
  }
158
155
 
159
156
  warning(...args)
160
157
  {
161
- if(this.forcedDisabled){
162
- return this;
163
- }
164
- if(5 > this.logLevel()){
165
- return this;
166
- }
167
- return this.log('warning', ...args);
158
+ return this.log(5, 'warning', ...args);
168
159
  }
169
160
 
170
161
  error(...args)
171
162
  {
172
- if(this.forcedDisabled){
173
- return this;
174
- }
175
- if(4 > this.logLevel()){
176
- return this;
177
- }
178
- return this.log('error', ...args);
163
+ return this.log(4, 'error', ...args);
179
164
  }
180
165
 
181
166
  critical(...args)
182
167
  {
183
- if(this.forcedDisabled){
184
- return this;
185
- }
186
- if(3 > this.logLevel()){
187
- return this;
188
- }
189
- return this.log('critical', ...args);
168
+ return this.log(3, 'critical', ...args);
190
169
  }
191
170
 
192
171
  alert(...args)
193
172
  {
194
- if(this.forcedDisabled){
195
- return this;
196
- }
197
- if(2 > this.logLevel()){
198
- return this;
199
- }
200
- return this.log('alert', ...args);
173
+ return this.log(2, 'alert', ...args);
201
174
  }
202
175
 
203
176
  emergency(...args)
204
177
  {
205
- if(this.forcedDisabled){
206
- return this;
207
- }
208
- if(1 > this.logLevel()){
209
- return this;
210
- }
211
- return this.log('emergency', ...args);
178
+ return this.log(1, 'emergency', ...args);
212
179
  }
213
180
 
214
181
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/utils",
3
3
  "scope": "@reldens",
4
- "version": "0.51.0",
4
+ "version": "0.53.0",
5
5
  "description": "Reldens - Utils",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -1,3 +1,9 @@
1
+ /**
2
+ *
3
+ * Reldens - TestEventsManager
4
+ *
5
+ */
6
+
1
7
  const EventsManager = require('../lib/events-manager');
2
8
 
3
9
  class TestEventsManager
@@ -8,6 +14,7 @@ class TestEventsManager
8
14
  this.testResults = [];
9
15
  this.testCount = 0;
10
16
  this.passedCount = 0;
17
+ this.currentTestMethod = '';
11
18
  }
12
19
 
13
20
  test(name, testFn)
@@ -15,11 +22,13 @@ class TestEventsManager
15
22
  this.testCount++;
16
23
  try{
17
24
  testFn();
18
- console.log('✓ PASS:', name);
25
+ let logMessage = '✓ PASS: '+this.currentTestMethod+' - '+name;
26
+ console.log(logMessage);
19
27
  this.passedCount++;
20
28
  this.testResults.push({name, status: 'PASS'});
21
29
  } catch(error){
22
- console.log('✗ FAIL:', name, '-', error.message);
30
+ let logMessage = '✗ FAIL: '+this.currentTestMethod+' - '+name+' - '+error.message;
31
+ console.log(logMessage);
23
32
  this.testResults.push({name, status: 'FAIL', error: error.message});
24
33
  }
25
34
  }
@@ -31,21 +40,23 @@ class TestEventsManager
31
40
  }
32
41
  }
33
42
 
34
- runAllTests()
43
+ async runAllTests()
35
44
  {
36
45
  console.log('Running tests for EventsManager...\n');
37
-
38
46
  let methodNames = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
39
47
  let testMethods = methodNames.filter(name =>
40
48
  name.startsWith('test') &&
41
49
  'function' === typeof this[name] &&
42
50
  name !== 'test'
43
51
  );
44
-
45
52
  for(let methodName of testMethods){
46
- this[methodName]();
53
+ this.currentTestMethod = methodName;
54
+ try{
55
+ await this[methodName]();
56
+ } catch(error){
57
+ console.error('Test method failed:', methodName, error.message);
58
+ }
47
59
  }
48
-
49
60
  this.printSummary();
50
61
  }
51
62
 
@@ -289,16 +300,17 @@ class TestEventsManager
289
300
  let loggedMessages = [];
290
301
  let originalDebug = console.log;
291
302
 
292
- console.log = (...args) => loggedMessages.push(args.join(' '));
293
- process.env.RELDENS_LOG_LEVEL = 8;
294
-
295
- for(let i = 0; i < 10; i++){
296
- emitter.on('test-max-'+i, () => {});
303
+ try{
304
+ console.log = (...args) => loggedMessages.push(args.join(' '));
305
+ process.env.RELDENS_LOG_LEVEL = 8;
306
+ for(let i = 0; i < 10; i++){
307
+ emitter.on('test-max-'+i, () => {});
308
+ }
309
+ } finally{
310
+ console.log = originalDebug;
311
+ delete process.env.RELDENS_LOG_LEVEL;
297
312
  }
298
313
 
299
- console.log = originalDebug;
300
- delete process.env.RELDENS_LOG_LEVEL;
301
-
302
314
  let debugLogs = loggedMessages.filter(log => log.includes('High listener count detected'));
303
315
  this.assert(1 === debugLogs.length, 'Should log high listener count only once');
304
316
  this.assert(true === emitter.hasLoggedMaxListeners, 'Should set flag after logging');
@@ -498,14 +510,15 @@ class TestEventsManager
498
510
  let loggedMessages = [];
499
511
  let originalLog = console.log;
500
512
 
501
- console.log = (...args) => loggedMessages.push(args.join(' '));
502
- process.env.RELDENS_LOG_LEVEL = 8;
503
-
504
- let sensitiveArgs = [{password: 'secret', safe: 'data'}];
505
- emitter.logDebugEvent('test', 'Fire', sensitiveArgs);
506
-
507
- console.log = originalLog;
508
- delete process.env.RELDENS_LOG_LEVEL;
513
+ try{
514
+ console.log = (...args) => loggedMessages.push(args.join(' '));
515
+ process.env.RELDENS_LOG_LEVEL = 8;
516
+ let sensitiveArgs = [{password: 'secret', safe: 'data'}];
517
+ emitter.logDebugEvent('test', 'Fire', sensitiveArgs);
518
+ } finally{
519
+ console.log = originalLog;
520
+ delete process.env.RELDENS_LOG_LEVEL;
521
+ }
509
522
 
510
523
  let debugLog = loggedMessages.find(log => log.includes('Fire Event: test'));
511
524
  this.assert(debugLog, 'Should log debug event');
@@ -580,13 +593,14 @@ class TestEventsManager
580
593
 
581
594
  let loggedMessages = [];
582
595
  let originalLog = console.log;
583
- console.log = (...args) => loggedMessages.push(args.join(' '));
584
- process.env.RELDENS_LOG_LEVEL = 8;
585
-
586
- emitter.logDebugEvent('non-matching-key', 'Listen');
587
-
588
- console.log = originalLog;
589
- delete process.env.RELDENS_LOG_LEVEL;
596
+ try{
597
+ console.log = (...args) => loggedMessages.push(args.join(' '));
598
+ process.env.RELDENS_LOG_LEVEL = 8;
599
+ emitter.logDebugEvent('non-matching-key', 'Listen');
600
+ } finally{
601
+ console.log = originalLog;
602
+ delete process.env.RELDENS_LOG_LEVEL;
603
+ }
590
604
 
591
605
  let debugLogs = loggedMessages.filter(log => log.includes('Listen Event:'));
592
606
  this.assert(0 === debugLogs.length, 'Should not log when no pattern matches');
@@ -894,17 +908,16 @@ class TestEventsManager
894
908
  let emitter = new EventsManager();
895
909
  let loggedEvents = [];
896
910
  let originalLog = console.log;
897
-
898
- console.log = (...args) => loggedEvents.push(args.join(' '));
899
- process.env.RELDENS_LOG_LEVEL = 8;
900
- emitter.debug = 'all';
901
-
902
- emitter.on('debug-test', () => {});
903
- await emitter.emit('debug-test');
904
-
905
- console.log = originalLog;
906
- delete process.env.RELDENS_LOG_LEVEL;
907
-
911
+ try{
912
+ console.log = (...args) => loggedEvents.push(args.join(' '));
913
+ process.env.RELDENS_LOG_LEVEL = 8;
914
+ emitter.debug = 'all';
915
+ emitter.on('debug-test', () => {});
916
+ await emitter.emit('debug-test');
917
+ } finally{
918
+ console.log = originalLog;
919
+ delete process.env.RELDENS_LOG_LEVEL;
920
+ }
908
921
  let hasListenLog = loggedEvents.some(log => log.includes('Listen Event:'));
909
922
  let hasFireLog = loggedEvents.some(log => log.includes('Fire Event:'));
910
923
  this.assert(hasListenLog, 'Should log listen events');
@@ -1058,6 +1071,17 @@ class TestEventsManager
1058
1071
  });
1059
1072
  }
1060
1073
 
1074
+ testFilterSensitiveDataWithCircularReferences()
1075
+ {
1076
+ this.test('filterSensitiveData handles circular references', () => {
1077
+ let emitter = new EventsManager();
1078
+ let obj = {safe: 'data'};
1079
+ obj.circular = obj;
1080
+ let result = emitter.filterSensitiveData(obj);
1081
+ this.assert('[CIRCULAR]' === result.circular, 'Should handle circular refs');
1082
+ });
1083
+ }
1084
+
1061
1085
  printSummary()
1062
1086
  {
1063
1087
  console.log('\n'+'='.repeat(50));
@@ -1067,7 +1091,6 @@ class TestEventsManager
1067
1091
  console.log('Passed:', this.passedCount);
1068
1092
  console.log('Failed:', this.testCount - this.passedCount);
1069
1093
  console.log('Success rate:', Math.round((this.passedCount / this.testCount) * 100)+'%');
1070
-
1071
1094
  if(this.testCount - this.passedCount > 0){
1072
1095
  console.log('\nFailed tests:');
1073
1096
  for(let result of this.testResults){
@@ -1076,9 +1099,13 @@ class TestEventsManager
1076
1099
  }
1077
1100
  }
1078
1101
  }
1102
+ console.log('\n'+'='.repeat(60));
1103
+ if(this.testCount - this.passedCount === 0){
1104
+ console.log('All tests completed successfully!');
1105
+ }
1106
+ console.log('='.repeat(60));
1079
1107
  }
1080
1108
 
1081
1109
  }
1082
1110
 
1083
- let testRunner = new TestEventsManager();
1084
- testRunner.runAllTests();
1111
+ module.exports.TestEventsManager = TestEventsManager;
package/tests/run.js CHANGED
@@ -1,13 +1,15 @@
1
1
  console.log('Running all tests...\n');
2
2
 
3
- try{
3
+ async function runTests(){
4
4
  console.log('='.repeat(60));
5
- console.log('TESTING NEW IMPLEMENTATION');
5
+ console.log('TESTING IMPLEMENTATION');
6
6
  console.log('='.repeat(60));
7
- require('./events-manager-test.js');
7
+ const { TestEventsManager } = require('./events-manager-test.js');
8
+ let testInstance = new TestEventsManager();
9
+ await testInstance.runAllTests();
10
+ }
8
11
 
9
- console.log('\n\nAll tests completed successfully!');
10
- } catch(error){
12
+ runTests().catch(error => {
11
13
  console.error('\nTest execution failed:', error.message);
12
14
  process.exit(1);
13
- }
15
+ });