sehawq.db 4.0.3 → 4.0.5

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.
@@ -1,325 +1,325 @@
1
- /**
2
- * Data Validator - Keeps your data clean and proper 🧼
3
- *
4
- * Because garbage in, garbage out is a real thing
5
- * Validates everything from emails to custom business rules
6
- */
7
-
8
- class Validator {
9
- constructor() {
10
- this.rules = new Map();
11
- this.customValidators = new Map();
12
-
13
- this._setupBuiltinRules();
14
- }
15
-
16
- /**
17
- * Setup built-in validation rules
18
- */
19
- _setupBuiltinRules() {
20
- // Type validators
21
- this.rules.set('string', value => typeof value === 'string');
22
- this.rules.set('number', value => typeof value === 'number' && !isNaN(value));
23
- this.rules.set('boolean', value => typeof value === 'boolean');
24
- this.rules.set('array', value => Array.isArray(value));
25
- this.rules.set('object', value => value && typeof value === 'object' && !Array.isArray(value));
26
- this.rules.set('function', value => typeof value === 'function');
27
- this.rules.set('null', value => value === null);
28
- this.rules.set('undefined', value => value === undefined);
29
-
30
- // Common format validators
31
- this.rules.set('email', value =>
32
- typeof value === 'string' &&
33
- /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
34
- );
35
-
36
- this.rules.set('url', value => {
37
- if (typeof value !== 'string') return false;
38
- try {
39
- new URL(value);
40
- return true;
41
- } catch {
42
- return false;
43
- }
44
- });
45
-
46
- this.rules.set('uuid', value =>
47
- typeof value === 'string' &&
48
- /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)
49
- );
50
-
51
- this.rules.set('date', value =>
52
- value instanceof Date && !isNaN(value.getTime())
53
- );
54
-
55
- this.rules.set('hexColor', value =>
56
- typeof value === 'string' &&
57
- /^#?([0-9A-F]{3}|[0-9A-F]{6})$/i.test(value)
58
- );
59
-
60
- // Comparison validators
61
- this.rules.set('min', (value, min) => {
62
- if (typeof value === 'number') return value >= min;
63
- if (typeof value === 'string' || Array.isArray(value)) return value.length >= min;
64
- return false;
65
- });
66
-
67
- this.rules.set('max', (value, max) => {
68
- if (typeof value === 'number') return value <= max;
69
- if (typeof value === 'string' || Array.isArray(value)) return value.length <= max;
70
- return false;
71
- });
72
-
73
- this.rules.set('range', (value, [min, max]) => {
74
- if (typeof value !== 'number') return false;
75
- return value >= min && value <= max;
76
- });
77
-
78
- this.rules.set('minLength', (value, min) =>
79
- (typeof value === 'string' || Array.isArray(value)) && value.length >= min
80
- );
81
-
82
- this.rules.set('maxLength', (value, max) =>
83
- (typeof value === 'string' || Array.isArray(value)) && value.length <= max
84
- );
85
-
86
- this.rules.set('length', (value, length) =>
87
- (typeof value === 'string' || Array.isArray(value)) && value.length === length
88
- );
89
-
90
- // Pattern validators
91
- this.rules.set('pattern', (value, pattern) =>
92
- typeof value === 'string' && pattern.test(value)
93
- );
94
-
95
- this.rules.set('alphanumeric', value =>
96
- typeof value === 'string' && /^[a-zA-Z0-9]+$/.test(value)
97
- );
98
-
99
- this.rules.set('numeric', value =>
100
- typeof value === 'string' && /^\d+$/.test(value)
101
- );
102
-
103
- // Collection validators
104
- this.rules.set('in', (value, allowed) =>
105
- Array.isArray(allowed) && allowed.includes(value)
106
- );
107
-
108
- this.rules.set('notIn', (value, disallowed) =>
109
- Array.isArray(disallowed) && !disallowed.includes(value)
110
- );
111
-
112
- // Special validators
113
- this.rules.set('required', value =>
114
- value !== null && value !== undefined && value !== ''
115
- );
116
-
117
- this.rules.set('optional', () => true);
118
- }
119
-
120
- /**
121
- * Add custom validator
122
- */
123
- addRule(name, validatorFn) {
124
- if (this.rules.has(name) || this.customValidators.has(name)) {
125
- throw new Error(`Validator '${name}' already exists`);
126
- }
127
-
128
- this.customValidators.set(name, validatorFn);
129
- return this;
130
- }
131
-
132
- /**
133
- * Remove validator
134
- */
135
- removeRule(name) {
136
- this.rules.delete(name);
137
- this.customValidators.delete(name);
138
- return this;
139
- }
140
-
141
- /**
142
- * Validate single value against rules
143
- */
144
- validateValue(value, rules) {
145
- const errors = [];
146
-
147
- for (const rule of rules) {
148
- const [ruleName, ...ruleArgs] = Array.isArray(rule) ? rule : [rule];
149
-
150
- let isValid = false;
151
-
152
- // Check built-in rules first
153
- if (this.rules.has(ruleName)) {
154
- const validator = this.rules.get(ruleName);
155
- isValid = validator(value, ...ruleArgs);
156
- }
157
- // Check custom rules
158
- else if (this.customValidators.has(ruleName)) {
159
- const validator = this.customValidators.get(ruleName);
160
- isValid = validator(value, ...ruleArgs);
161
- }
162
- else {
163
- errors.push(`Unknown validation rule: ${ruleName}`);
164
- continue;
165
- }
166
-
167
- if (!isValid) {
168
- errors.push(this._formatError(ruleName, ruleArgs, value));
169
- }
170
- }
171
-
172
- return {
173
- isValid: errors.length === 0,
174
- errors,
175
- value
176
- };
177
- }
178
-
179
- /**
180
- * Validate object against schema
181
- */
182
- validateObject(obj, schema) {
183
- const errors = {};
184
- let isValid = true;
185
-
186
- for (const [field, fieldRules] of Object.entries(schema)) {
187
- const value = obj[field];
188
- const result = this.validateValue(value, fieldRules);
189
-
190
- if (!result.isValid) {
191
- errors[field] = result.errors;
192
- isValid = false;
193
- }
194
- }
195
-
196
- return {
197
- isValid,
198
- errors,
199
- data: obj
200
- };
201
- }
202
-
203
- /**
204
- * Create schema validator
205
- */
206
- schema(schemaDef) {
207
- return (data) => this.validateObject(data, schemaDef);
208
- }
209
-
210
- /**
211
- * Format validation error
212
- */
213
- _formatError(rule, args, value) {
214
- const valueStr = typeof value === 'string' ? `"${value}"` : JSON.stringify(value);
215
-
216
- switch (rule) {
217
- case 'required':
218
- return 'Field is required';
219
- case 'min':
220
- return `Value must be at least ${args[0]}`;
221
- case 'max':
222
- return `Value must be at most ${args[0]}`;
223
- case 'minLength':
224
- return `Length must be at least ${args[0]} characters`;
225
- case 'maxLength':
226
- return `Length must be at most ${args[0]} characters`;
227
- case 'length':
228
- return `Length must be exactly ${args[0]} characters`;
229
- case 'range':
230
- return `Value must be between ${args[0][0]} and ${args[0][1]}`;
231
- case 'email':
232
- return 'Must be a valid email address';
233
- case 'url':
234
- return 'Must be a valid URL';
235
- case 'uuid':
236
- return 'Must be a valid UUID';
237
- case 'pattern':
238
- return 'Value does not match required pattern';
239
- case 'in':
240
- return `Value must be one of: ${args[0].join(', ')}`;
241
- case 'notIn':
242
- return `Value must not be one of: ${args[0].join(', ')}`;
243
- default:
244
- return `Failed validation: ${rule}`;
245
- }
246
- }
247
-
248
- /**
249
- * Quick validators (static methods)
250
- */
251
- static isEmail(value) {
252
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
253
- }
254
-
255
- static isURL(value) {
256
- try {
257
- new URL(value);
258
- return true;
259
- } catch {
260
- return false;
261
- }
262
- }
263
-
264
- static isDate(value) {
265
- return value instanceof Date && !isNaN(value.getTime());
266
- }
267
-
268
- static isNumber(value) {
269
- return typeof value === 'number' && !isNaN(value);
270
- }
271
-
272
- static isString(value) {
273
- return typeof value === 'string';
274
- }
275
-
276
- static isArray(value) {
277
- return Array.isArray(value);
278
- }
279
-
280
- static isObject(value) {
281
- return value && typeof value === 'object' && !Array.isArray(value);
282
- }
283
-
284
- static isEmpty(value) {
285
- if (value === null || value === undefined) return true;
286
- if (typeof value === 'string') return value.trim().length === 0;
287
- if (Array.isArray(value)) return value.length === 0;
288
- if (typeof value === 'object') return Object.keys(value).length === 0;
289
- return false;
290
- }
291
-
292
- /**
293
- * Sanitize functions
294
- */
295
- static trim(value) {
296
- return typeof value === 'string' ? value.trim() : value;
297
- }
298
-
299
- static toLowerCase(value) {
300
- return typeof value === 'string' ? value.toLowerCase() : value;
301
- }
302
-
303
- static toUpperCase(value) {
304
- return typeof value === 'string' ? value.toUpperCase() : value;
305
- }
306
-
307
- static toNumber(value) {
308
- if (typeof value === 'number') return value;
309
- const num = parseFloat(value);
310
- return isNaN(num) ? value : num;
311
- }
312
-
313
- static toBoolean(value) {
314
- if (typeof value === 'boolean') return value;
315
- if (typeof value === 'string') {
316
- return value.toLowerCase() === 'true' || value === '1';
317
- }
318
- return !!value;
319
- }
320
- }
321
-
322
- // Export singleton instance
323
- const validator = new Validator();
324
- module.exports = validator;
1
+ /**
2
+ * Data Validator - Keeps your data clean and proper 🧼
3
+ *
4
+ * Because garbage in, garbage out is a real thing
5
+ * Validates everything from emails to custom business rules
6
+ */
7
+
8
+ class Validator {
9
+ constructor() {
10
+ this.rules = new Map();
11
+ this.customValidators = new Map();
12
+
13
+ this._setupBuiltinRules();
14
+ }
15
+
16
+ /**
17
+ * Setup built-in validation rules
18
+ */
19
+ _setupBuiltinRules() {
20
+ // Type validators
21
+ this.rules.set('string', value => typeof value === 'string');
22
+ this.rules.set('number', value => typeof value === 'number' && !isNaN(value));
23
+ this.rules.set('boolean', value => typeof value === 'boolean');
24
+ this.rules.set('array', value => Array.isArray(value));
25
+ this.rules.set('object', value => value && typeof value === 'object' && !Array.isArray(value));
26
+ this.rules.set('function', value => typeof value === 'function');
27
+ this.rules.set('null', value => value === null);
28
+ this.rules.set('undefined', value => value === undefined);
29
+
30
+ // Common format validators
31
+ this.rules.set('email', value =>
32
+ typeof value === 'string' &&
33
+ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
34
+ );
35
+
36
+ this.rules.set('url', value => {
37
+ if (typeof value !== 'string') return false;
38
+ try {
39
+ new URL(value);
40
+ return true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ });
45
+
46
+ this.rules.set('uuid', value =>
47
+ typeof value === 'string' &&
48
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)
49
+ );
50
+
51
+ this.rules.set('date', value =>
52
+ value instanceof Date && !isNaN(value.getTime())
53
+ );
54
+
55
+ this.rules.set('hexColor', value =>
56
+ typeof value === 'string' &&
57
+ /^#?([0-9A-F]{3}|[0-9A-F]{6})$/i.test(value)
58
+ );
59
+
60
+ // Comparison validators
61
+ this.rules.set('min', (value, min) => {
62
+ if (typeof value === 'number') return value >= min;
63
+ if (typeof value === 'string' || Array.isArray(value)) return value.length >= min;
64
+ return false;
65
+ });
66
+
67
+ this.rules.set('max', (value, max) => {
68
+ if (typeof value === 'number') return value <= max;
69
+ if (typeof value === 'string' || Array.isArray(value)) return value.length <= max;
70
+ return false;
71
+ });
72
+
73
+ this.rules.set('range', (value, [min, max]) => {
74
+ if (typeof value !== 'number') return false;
75
+ return value >= min && value <= max;
76
+ });
77
+
78
+ this.rules.set('minLength', (value, min) =>
79
+ (typeof value === 'string' || Array.isArray(value)) && value.length >= min
80
+ );
81
+
82
+ this.rules.set('maxLength', (value, max) =>
83
+ (typeof value === 'string' || Array.isArray(value)) && value.length <= max
84
+ );
85
+
86
+ this.rules.set('length', (value, length) =>
87
+ (typeof value === 'string' || Array.isArray(value)) && value.length === length
88
+ );
89
+
90
+ // Pattern validators
91
+ this.rules.set('pattern', (value, pattern) =>
92
+ typeof value === 'string' && pattern.test(value)
93
+ );
94
+
95
+ this.rules.set('alphanumeric', value =>
96
+ typeof value === 'string' && /^[a-zA-Z0-9]+$/.test(value)
97
+ );
98
+
99
+ this.rules.set('numeric', value =>
100
+ typeof value === 'string' && /^\d+$/.test(value)
101
+ );
102
+
103
+ // Collection validators
104
+ this.rules.set('in', (value, allowed) =>
105
+ Array.isArray(allowed) && allowed.includes(value)
106
+ );
107
+
108
+ this.rules.set('notIn', (value, disallowed) =>
109
+ Array.isArray(disallowed) && !disallowed.includes(value)
110
+ );
111
+
112
+ // Special validators
113
+ this.rules.set('required', value =>
114
+ value !== null && value !== undefined && value !== ''
115
+ );
116
+
117
+ this.rules.set('optional', () => true);
118
+ }
119
+
120
+ /**
121
+ * Add custom validator
122
+ */
123
+ addRule(name, validatorFn) {
124
+ if (this.rules.has(name) || this.customValidators.has(name)) {
125
+ throw new Error(`Validator '${name}' already exists`);
126
+ }
127
+
128
+ this.customValidators.set(name, validatorFn);
129
+ return this;
130
+ }
131
+
132
+ /**
133
+ * Remove validator
134
+ */
135
+ removeRule(name) {
136
+ this.rules.delete(name);
137
+ this.customValidators.delete(name);
138
+ return this;
139
+ }
140
+
141
+ /**
142
+ * Validate single value against rules
143
+ */
144
+ validateValue(value, rules) {
145
+ const errors = [];
146
+
147
+ for (const rule of rules) {
148
+ const [ruleName, ...ruleArgs] = Array.isArray(rule) ? rule : [rule];
149
+
150
+ let isValid = false;
151
+
152
+ // Check built-in rules first
153
+ if (this.rules.has(ruleName)) {
154
+ const validator = this.rules.get(ruleName);
155
+ isValid = validator(value, ...ruleArgs);
156
+ }
157
+ // Check custom rules
158
+ else if (this.customValidators.has(ruleName)) {
159
+ const validator = this.customValidators.get(ruleName);
160
+ isValid = validator(value, ...ruleArgs);
161
+ }
162
+ else {
163
+ errors.push(`Unknown validation rule: ${ruleName}`);
164
+ continue;
165
+ }
166
+
167
+ if (!isValid) {
168
+ errors.push(this._formatError(ruleName, ruleArgs, value));
169
+ }
170
+ }
171
+
172
+ return {
173
+ isValid: errors.length === 0,
174
+ errors,
175
+ value
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Validate object against schema
181
+ */
182
+ validateObject(obj, schema) {
183
+ const errors = {};
184
+ let isValid = true;
185
+
186
+ for (const [field, fieldRules] of Object.entries(schema)) {
187
+ const value = obj[field];
188
+ const result = this.validateValue(value, fieldRules);
189
+
190
+ if (!result.isValid) {
191
+ errors[field] = result.errors;
192
+ isValid = false;
193
+ }
194
+ }
195
+
196
+ return {
197
+ isValid,
198
+ errors,
199
+ data: obj
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Create schema validator
205
+ */
206
+ schema(schemaDef) {
207
+ return (data) => this.validateObject(data, schemaDef);
208
+ }
209
+
210
+ /**
211
+ * Format validation error
212
+ */
213
+ _formatError(rule, args, value) {
214
+ const valueStr = typeof value === 'string' ? `"${value}"` : JSON.stringify(value);
215
+
216
+ switch (rule) {
217
+ case 'required':
218
+ return 'Field is required';
219
+ case 'min':
220
+ return `Value must be at least ${args[0]}`;
221
+ case 'max':
222
+ return `Value must be at most ${args[0]}`;
223
+ case 'minLength':
224
+ return `Length must be at least ${args[0]} characters`;
225
+ case 'maxLength':
226
+ return `Length must be at most ${args[0]} characters`;
227
+ case 'length':
228
+ return `Length must be exactly ${args[0]} characters`;
229
+ case 'range':
230
+ return `Value must be between ${args[0][0]} and ${args[0][1]}`;
231
+ case 'email':
232
+ return 'Must be a valid email address';
233
+ case 'url':
234
+ return 'Must be a valid URL';
235
+ case 'uuid':
236
+ return 'Must be a valid UUID';
237
+ case 'pattern':
238
+ return 'Value does not match required pattern';
239
+ case 'in':
240
+ return `Value must be one of: ${args[0].join(', ')}`;
241
+ case 'notIn':
242
+ return `Value must not be one of: ${args[0].join(', ')}`;
243
+ default:
244
+ return `Failed validation: ${rule}`;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Quick validators (static methods)
250
+ */
251
+ static isEmail(value) {
252
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
253
+ }
254
+
255
+ static isURL(value) {
256
+ try {
257
+ new URL(value);
258
+ return true;
259
+ } catch {
260
+ return false;
261
+ }
262
+ }
263
+
264
+ static isDate(value) {
265
+ return value instanceof Date && !isNaN(value.getTime());
266
+ }
267
+
268
+ static isNumber(value) {
269
+ return typeof value === 'number' && !isNaN(value);
270
+ }
271
+
272
+ static isString(value) {
273
+ return typeof value === 'string';
274
+ }
275
+
276
+ static isArray(value) {
277
+ return Array.isArray(value);
278
+ }
279
+
280
+ static isObject(value) {
281
+ return value && typeof value === 'object' && !Array.isArray(value);
282
+ }
283
+
284
+ static isEmpty(value) {
285
+ if (value === null || value === undefined) return true;
286
+ if (typeof value === 'string') return value.trim().length === 0;
287
+ if (Array.isArray(value)) return value.length === 0;
288
+ if (typeof value === 'object') return Object.keys(value).length === 0;
289
+ return false;
290
+ }
291
+
292
+ /**
293
+ * Sanitize functions
294
+ */
295
+ static trim(value) {
296
+ return typeof value === 'string' ? value.trim() : value;
297
+ }
298
+
299
+ static toLowerCase(value) {
300
+ return typeof value === 'string' ? value.toLowerCase() : value;
301
+ }
302
+
303
+ static toUpperCase(value) {
304
+ return typeof value === 'string' ? value.toUpperCase() : value;
305
+ }
306
+
307
+ static toNumber(value) {
308
+ if (typeof value === 'number') return value;
309
+ const num = parseFloat(value);
310
+ return isNaN(num) ? value : num;
311
+ }
312
+
313
+ static toBoolean(value) {
314
+ if (typeof value === 'boolean') return value;
315
+ if (typeof value === 'string') {
316
+ return value.toLowerCase() === 'true' || value === '1';
317
+ }
318
+ return !!value;
319
+ }
320
+ }
321
+
322
+ // Export singleton instance
323
+ const validator = new Validator();
324
+ module.exports = validator;
325
325
  module.exports.Validator = Validator;