@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 +97 -0
- package/README.md +93 -6
- package/lib/shortcuts.js +2 -2
- package/package.json +1 -1
- package/tests/events-manager-test.js +16 -19
- package/tests/run.js +15 -2
- package/tests/shortcuts-test.js +1107 -0
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 =
|
|
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 =
|
|
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
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
1088
|
-
console.log('
|
|
1089
|
-
console.log('
|
|
1090
|
-
console.log('
|
|
1091
|
-
console.log('Passed:'
|
|
1092
|
-
|
|
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('
|
|
1099
|
+
console.log(' ✗ '+result.name+': '+result.error);
|
|
1099
1100
|
}
|
|
1100
1101
|
}
|
|
1101
1102
|
}
|
|
1102
|
-
|
|
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
|
-
|
|
9
|
-
|
|
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 => {
|