@reldens/utils 0.53.0 → 0.55.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/CLAUDE.md ADDED
@@ -0,0 +1,97 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Package Overview
6
+
7
+ **@reldens/utils** is a core utility package used throughout the Reldens ecosystem. It provides essential utilities for:
8
+ - Object manipulation (Shortcuts class)
9
+ - Event management (EventsManager)
10
+ - Logging (Logger)
11
+ - Schema validation (SchemaValidator)
12
+ - Environment variables (EnvVar)
13
+ - Error handling (ErrorManager)
14
+ - Interaction area validation (InteractionArea)
15
+ - Pagination utilities (PageRangeProvider)
16
+ - Validator base interface (ValidatorInterface)
17
+
18
+ ## Key Commands
19
+
20
+ ```bash
21
+ # Run tests
22
+ npm test
23
+ ```
24
+
25
+ ## Architecture
26
+
27
+ ### Core Classes
28
+
29
+ **Shortcuts** (`lib/shortcuts.js`):
30
+ - Provides utility methods for object manipulation, property access, and validation
31
+ - Imported as `sc` throughout the Reldens codebase
32
+ - Used instead of direct property access or Object.prototype methods
33
+ - Type checks: `isObject`, `isArray`, `isFunction`, `isObjectFunction`, `isString`, `isNumber`, `isInt`, `isFloat`, `isBoolean`, `isSymbol`, `isPromise`, `isTrue`
34
+ - Ownership/existence: `hasOwn`, `get`, `getByPath`, `getByPriority`, `length`
35
+ - Array utilities: `inArray`, `isNotEmptyArray`, `removeFromArray`, `randomValueFromArray`, `arraySort`, `sortObjectKeysBy`, `convertObjectsArrayToObjectByKeys`, `chunk`, `flatten`, `unique`
36
+ - Object utilities: `deepMergeProperties`, `propsAssign`, `pickProps`, `omitProps`, `hasDangerousKeys`
37
+ - Search: `fetchByProperty`, `fetchAllByProperty`, `fetchByPropertyOnObject`, `fetchAllByPropertyOnObject`
38
+ - JSON: `toJson`, `parseJson`, `deepJsonClone`, `toJsonString`
39
+ - String utilities: `startsWith`, `contains`, `cleanMessage`, `slugify`, `sanitize`, `sanitizeUrl`, `camelCase`, `capitalizedCamelCase`, `kebabCase`, `capitalize`, `truncate`, `splitToArray`
40
+ - Number utilities: `roundToPrecision`, `randomInteger`, `randomChars`, `randomCharsWithSymbols`, `randomString`, `clamp`, `parseNumber`, `isValidInteger`
41
+ - Date/time: `getCurrentDate`, `getDateForFileName`, `formatDate`, `getTime`
42
+ - Validation: `isValidUrl`, `isValidIsoCode`, `isSecurePath`, `validateInput`
43
+ - Functional: `debounce`, `throttle`, `serializeFormData`
44
+
45
+ **EventsManager** (`lib/events-manager.js`):
46
+ - Singleton event system used across Reldens
47
+ - Supports both sync (`emitSync`) and async (`emit`) events
48
+ - Allows event listeners to modify data via reference
49
+ - Debug mode available via `debug` property
50
+
51
+ **Logger** (`lib/logger.js`):
52
+ - Centralized logging utility with multiple log levels
53
+ - Levels: emergency, alert, critical, error, warning, notice, info, debug
54
+ - Configurable log level and trace options
55
+ - Used instead of console.log
56
+
57
+ **SchemaValidator** (`lib/schema-validator.js`):
58
+ - JSON schema validation utility
59
+ - Validates data structures against defined schemas
60
+ - Returns validation results with errors
61
+
62
+ **EnvVar** (`lib/env-var.js`):
63
+ - Environment variable utilities
64
+ - Type conversion and validation
65
+ - Safe environment variable access
66
+
67
+ **ErrorManager** (`lib/error-manager.js`):
68
+ - Singleton error handling utility
69
+ - Configurable error tracing via `enableTrace` property
70
+ - Supports custom error callbacks via `callback` property
71
+ - Default behavior throws Error with a message
72
+
73
+ **InteractionArea** (`lib/interaction-area.js`):
74
+ - Validates if positions are within interaction range
75
+ - Used for player-to-player, player-to-object interactions
76
+ - Configurable interaction area/margin
77
+ - Key methods: `setupInteractionArea`, `isValidInteraction`, `getPosition`
78
+
79
+ **PageRangeProvider** (`lib/page-range-provider.js`):
80
+ - Singleton pagination utility
81
+ - Generates page ranges for UI pagination
82
+ - Key method: `fetch(page, totalPages, totalDisplayedPages, firstLabel, lastLabel)`
83
+ - Returns array of page objects with label and value
84
+
85
+ **ValidatorInterface** (`lib/validator-interface.js`):
86
+ - Base interface for validator implementations
87
+ - Provides default `validate()` method that returns true
88
+ - Extend this class to create custom validators
89
+
90
+ ## Important Notes
91
+
92
+ - This package has NO external dependencies
93
+ - All code must be pure JavaScript with no dependencies
94
+ - The Shortcuts class is used extensively across Reldens - any changes affect the entire ecosystem
95
+ - Singleton pattern is used for: ErrorManager, PageRangeProvider, and EventsManagerSingleton (exported alongside the EventsManager class)
96
+ - Logger methods should be used instead of console.log everywhere in Reldens
97
+ - InteractionArea is instantiable (not a singleton) - create instances for each interactive object
package/README.md CHANGED
@@ -78,14 +78,13 @@ Pagination helper that calculates page ranges for UI components, handling first/
78
78
 
79
79
  ```javascript
80
80
  const { PageRangeProvider } = require('@reldens/utils');
81
- let pageProvider = new PageRangeProvider();
82
81
 
83
- // Generate page range
84
- let range = pageProvider.fetch(5, 20, 7, 'First', 'Last');
82
+ // Generate page range (it's a singleton, use directly)
83
+ let range = PageRangeProvider.fetch(5, 20, 7, 'First', 'Last');
85
84
  // Returns: [{label: 'First', value: 1}, {label: 2, value: 2}, ...]
86
85
 
87
86
  // Simple range
88
- let simpleRange = pageProvider.fetch(3, 10, 5);
87
+ let simpleRange = PageRangeProvider.fetch(3, 10, 5);
89
88
  // Returns pages around current page 3
90
89
  ```
91
90
 
@@ -98,8 +97,8 @@ const { SchemaValidator } = require('@reldens/utils');
98
97
  let schema = {
99
98
  username: { type: 'string', min: 3, max: 20 },
100
99
  age: { type: 'int', min: 18 },
101
- profile: {
102
- type: 'object',
100
+ profile: {
101
+ type: 'object',
103
102
  nested: {
104
103
  email: { type: 'string', required: true }
105
104
  }
@@ -111,6 +110,94 @@ let userData = { username: 'player1', age: 25, profile: { email: 'test@example.c
111
110
  let isValid = validator.validate(userData);
112
111
  ```
113
112
 
113
+ ### Shortcuts (sc)
114
+ Core utility class providing essential object manipulation, property access, and validation methods used throughout Reldens.
115
+
116
+ ```javascript
117
+ const { sc } = require('@reldens/utils');
118
+
119
+ // Safe property access
120
+ let value = sc.get(obj, 'nested.property.path', 'defaultValue');
121
+
122
+ // Check property ownership
123
+ if (sc.hasOwn(obj, 'propertyName')) {
124
+ // Property exists
125
+ }
126
+
127
+ // Type checking
128
+ sc.isObject(value); // true/false
129
+ sc.isArray(value); // true/false
130
+ sc.isFunction(value); // true/false
131
+
132
+ // Deep operations
133
+ let cloned = sc.deepClone(originalObject);
134
+ let merged = sc.deepMerge(obj1, obj2);
135
+
136
+ // Property manipulation
137
+ sc.getDef(obj, 'key', 'default'); // Get with default
138
+ sc.getOneDef(obj, ['key1', 'key2'], 'default'); // Get first found key
139
+ ```
140
+
141
+ ### ErrorManager
142
+ Singleton error handling utility with configurable tracing and custom error callbacks.
143
+
144
+ ```javascript
145
+ const { ErrorManager } = require('@reldens/utils');
146
+
147
+ // Enable stack traces
148
+ ErrorManager.enableTrace = true;
149
+
150
+ // Custom error handling
151
+ ErrorManager.callback = (message) => {
152
+ // Custom error handling logic
153
+ console.error('Custom error:', message);
154
+ // Return false to prevent default throw
155
+ return false;
156
+ };
157
+
158
+ // Trigger error
159
+ ErrorManager.error('Something went wrong');
160
+ ```
161
+
162
+ ### EnvVar
163
+ Environment variable utilities with type conversion and validation for safe environment variable access.
164
+
165
+ ```javascript
166
+ const { EnvVar } = require('@reldens/utils');
167
+
168
+ // Get environment variables with type conversion
169
+ let port = EnvVar.integer(process.env, 'PORT', 3000); // Returns integer or default
170
+ let enabled = EnvVar.boolean(process.env, 'FEATURE_ENABLED', false); // Returns boolean
171
+ let apiUrl = EnvVar.string(process.env, 'API_URL', 'http://localhost'); // Returns string
172
+
173
+ // Other type helpers
174
+ let portNumber = EnvVar.port(process.env, 'PORT', 8080); // Validates port range 1-65535
175
+ let config = EnvVar.json(process.env, 'CONFIG', {}); // Parse JSON string
176
+ let items = EnvVar.array(process.env, 'ITEMS', [], ','); // Split string to array
177
+ let url = EnvVar.url(process.env, 'API_URL', 'http://localhost'); // Validate URL
178
+ ```
179
+
180
+ ### ValidatorInterface
181
+ Base interface for creating custom validators with a standard validate method.
182
+
183
+ ```javascript
184
+ const { ValidatorInterface } = require('@reldens/utils');
185
+
186
+ // Extend to create custom validators
187
+ class CustomValidator extends ValidatorInterface {
188
+ validate(data) {
189
+ // Custom validation logic
190
+ if (!data.requiredField) {
191
+ return false;
192
+ }
193
+ return true;
194
+ }
195
+ }
196
+
197
+ let validator = new CustomValidator();
198
+ let isValid = validator.validate({ requiredField: 'value' });
199
+ ```
200
+
114
201
  ---
115
202
 
116
203
  Need something specific?
package/lib/shortcuts.js CHANGED
@@ -229,9 +229,9 @@ class Shortcuts
229
229
  return JSON.parse(this.toJsonString(obj));
230
230
  }
231
231
 
232
- toJsonString(obj)
232
+ toJsonString(obj, ...args)
233
233
  {
234
- return JSON.stringify(obj);
234
+ return JSON.stringify(obj, ...args);
235
235
  }
236
236
 
237
237
  get(obj, prop, defaultReturn)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/utils",
3
3
  "scope": "@reldens",
4
- "version": "0.53.0",
4
+ "version": "0.55.0",
5
5
  "description": "Reldens - Utils",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -57,7 +57,8 @@ class TestEventsManager
57
57
  console.error('Test method failed:', methodName, error.message);
58
58
  }
59
59
  }
60
- this.printSummary();
60
+ let failed = this.printSummary();
61
+ return {total: this.testCount, passed: this.passedCount, failed};
61
62
  }
62
63
 
63
64
  testConstructorCreatesInstance()
@@ -410,14 +411,14 @@ class TestEventsManager
410
411
  username: 'test',
411
412
  password: 'secret123',
412
413
  authToken: 'token123',
413
- apiKey: 'key123',
414
+ secretKey: 'key123',
414
415
  safe: 'data'
415
416
  };
416
417
  let result = emitter.filterSensitiveData(obj);
417
418
  this.assert('test' === result.username, 'Should keep safe fields');
418
419
  this.assert('[FILTERED]' === result.password, 'Should filter password');
419
420
  this.assert('[FILTERED]' === result.authToken, 'Should filter token');
420
- this.assert('[FILTERED]' === result.apiKey, 'Should filter key');
421
+ this.assert('[FILTERED]' === result.secretKey, 'Should filter key');
421
422
  this.assert('data' === result.safe, 'Should keep safe data');
422
423
  });
423
424
  }
@@ -450,14 +451,14 @@ class TestEventsManager
450
451
  password: 'secret'
451
452
  },
452
453
  config: {
453
- apiKey: 'key123',
454
+ secretKey: 'key123',
454
455
  safe: 'value'
455
456
  }
456
457
  };
457
458
  let result = emitter.filterSensitiveData(obj);
458
459
  this.assert('test' === result.user.name, 'Should keep nested safe fields');
459
460
  this.assert('[FILTERED]' === result.user.password, 'Should filter nested password');
460
- this.assert('[FILTERED]' === result.config.apiKey, 'Should filter nested key');
461
+ this.assert('[FILTERED]' === result.config.secretKey, 'Should filter nested key');
461
462
  this.assert('value' === result.config.safe, 'Should keep nested safe value');
462
463
  });
463
464
  }
@@ -530,6 +531,7 @@ class TestEventsManager
530
531
  {
531
532
  this.test('emit uses sanitized arguments', async () => {
532
533
  let emitter = new EventsManager();
534
+ emitter.enableSensitiveDataFiltering = true;
533
535
  let receivedArgs = null;
534
536
 
535
537
  emitter.on('sanitize-test', (...args) => {
@@ -549,6 +551,7 @@ class TestEventsManager
549
551
  {
550
552
  this.test('emitSync uses sanitized arguments', () => {
551
553
  let emitter = new EventsManager();
554
+ emitter.enableSensitiveDataFiltering = true;
552
555
  let receivedArgs = null;
553
556
 
554
557
  emitter.on('sanitize-sync-test', (...args) => {
@@ -1084,26 +1087,20 @@ class TestEventsManager
1084
1087
 
1085
1088
  printSummary()
1086
1089
  {
1087
- console.log('\n'+'='.repeat(50));
1088
- console.log('TEST SUMMARY');
1089
- console.log('='.repeat(50));
1090
- console.log('Total tests:', this.testCount);
1091
- console.log('Passed:', this.passedCount);
1092
- console.log('Failed:', this.testCount - this.passedCount);
1093
- console.log('Success rate:', Math.round((this.passedCount / this.testCount) * 100)+'%');
1094
- if(this.testCount - this.passedCount > 0){
1090
+ let failedCount = this.testCount - this.passedCount;
1091
+ console.log('\n'+'='.repeat(60));
1092
+ console.log('EVENTS MANAGER TEST SUMMARY');
1093
+ console.log('='.repeat(60));
1094
+ console.log('Total: '+this.testCount+' | Passed: '+this.passedCount+' | Failed: '+failedCount);
1095
+ if(0 < failedCount){
1095
1096
  console.log('\nFailed tests:');
1096
1097
  for(let result of this.testResults){
1097
1098
  if('FAIL' === result.status){
1098
- console.log('-', result.name, ':', result.error);
1099
+ console.log(''+result.name+': '+result.error);
1099
1100
  }
1100
1101
  }
1101
1102
  }
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));
1103
+ return failedCount;
1107
1104
  }
1108
1105
 
1109
1106
  }
package/tests/run.js CHANGED
@@ -4,9 +4,22 @@ async function runTests(){
4
4
  console.log('='.repeat(60));
5
5
  console.log('TESTING IMPLEMENTATION');
6
6
  console.log('='.repeat(60));
7
+ let suites = [];
8
+ const { TestShortcuts } = require('./shortcuts-test.js');
9
+ suites.push(await (new TestShortcuts()).runAllTests());
7
10
  const { TestEventsManager } = require('./events-manager-test.js');
8
- let testInstance = new TestEventsManager();
9
- await testInstance.runAllTests();
11
+ suites.push(await (new TestEventsManager()).runAllTests());
12
+ let totalTests = suites.reduce((sum, s) => sum + s.total, 0);
13
+ let totalPassed = suites.reduce((sum, s) => sum + s.passed, 0);
14
+ let totalFailed = suites.reduce((sum, s) => sum + s.failed, 0);
15
+ console.log('\n'+'='.repeat(60));
16
+ console.log('FINAL TOTAL');
17
+ console.log('='.repeat(60));
18
+ console.log('Total: '+totalTests+' | Passed: '+totalPassed+' | Failed: '+totalFailed);
19
+ console.log('='.repeat(60));
20
+ if(0 < totalFailed){
21
+ process.exit(1);
22
+ }
10
23
  }
11
24
 
12
25
  runTests().catch(error => {