validno 0.2.6 → 0.3.1

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.
Files changed (50) hide show
  1. package/README.md +517 -1
  2. package/dist/Schema.js +7 -5
  3. package/dist/ValidnoResult.js +12 -7
  4. package/dist/checkType.js +1 -0
  5. package/dist/constants/details.js +12 -11
  6. package/dist/constants/schema.js +10 -10
  7. package/dist/dev.js +19 -26
  8. package/dist/engine/ValidateEngine.js +44 -0
  9. package/dist/engine/ValidnoResult.js +102 -0
  10. package/dist/engine/index.js +2 -0
  11. package/dist/engine/methods/finishValidation.js +15 -0
  12. package/dist/engine/methods/handleKey.js +41 -0
  13. package/dist/engine/methods/handleMissingKey.js +19 -0
  14. package/dist/engine/methods/handleMissingKeyValidation.js +9 -0
  15. package/dist/engine/methods/handleNestedKey.js +19 -0
  16. package/dist/engine/methods/validate.js +14 -0
  17. package/dist/engine/methods/validateRules.js +187 -0
  18. package/dist/engine/methods/validateType.js +135 -0
  19. package/dist/types/common.js +1 -0
  20. package/dist/utils/errors.js +30 -21
  21. package/dist/utils/helpers.js +53 -57
  22. package/dist/utils/validateType.js +9 -8
  23. package/dist/utils/validations.js +157 -153
  24. package/dist/validate/index.js +1 -0
  25. package/dist/validate/validate.js +151 -0
  26. package/dist/validate.js +136 -127
  27. package/dist/validateEngine/ValidateEngine.js +44 -0
  28. package/dist/validateEngine/index.js +2 -0
  29. package/dist/validateEngine/methods/ValidateEngine.js +139 -0
  30. package/dist/validateEngine/methods/checkRulesForKey.js +15 -0
  31. package/dist/validateEngine/methods/checkValueType.js +134 -0
  32. package/dist/validateEngine/methods/finalizeValidation.js +15 -0
  33. package/dist/validateEngine/methods/finishValidation.js +15 -0
  34. package/dist/validateEngine/methods/handleKey.js +43 -0
  35. package/dist/validateEngine/methods/handleMissingKey.js +19 -0
  36. package/dist/validateEngine/methods/handleMissingKeyValidation.js +9 -0
  37. package/dist/validateEngine/methods/handleNestedKey.js +19 -0
  38. package/dist/validateEngine/methods/validate.js +14 -0
  39. package/dist/validateEngine/methods/validateKey.js +31 -0
  40. package/dist/validateEngine/methods/validateKeyDetails.js +13 -0
  41. package/dist/validateEngine/methods/validateKeyValue.js +13 -0
  42. package/dist/validateEngine/methods/validateNestedKey.js +19 -0
  43. package/dist/validateEngine/methods/validateType.js +134 -0
  44. package/dist/validateEngine/validate.js +14 -0
  45. package/dist/validateSchema/ValidateEngine.js +147 -0
  46. package/dist/validateSchema/index.js +6 -0
  47. package/dist/validateSchema/validate.js +151 -0
  48. package/dist/validateSchema.js +6 -0
  49. package/dist/validateType.js +4 -4
  50. package/package.json +1 -1
package/README.md CHANGED
@@ -1 +1,517 @@
1
- #TODO
1
+ # Validno
2
+
3
+ A lightweight and flexible TypeScript validation library for Node.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm i validno
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```javascript
14
+ import Schema from 'validno';
15
+
16
+ // Define your schema
17
+ const userSchema = new Schema({
18
+ name: {
19
+ type: String,
20
+ required: true,
21
+ rules: {
22
+ lengthMin: 2,
23
+ lengthMax: 50
24
+ }
25
+ },
26
+ email: {
27
+ type: String,
28
+ required: true,
29
+ rules: {
30
+ isEmail: true
31
+ }
32
+ },
33
+ age: {
34
+ type: Number,
35
+ required: false,
36
+ rules: {
37
+ min: 18,
38
+ max: 120
39
+ }
40
+ }
41
+ });
42
+
43
+ // Validate data
44
+ const userData = {
45
+ name: "Barney Stinson",
46
+ email: "barney@himym.com",
47
+ age: 35
48
+ };
49
+
50
+ const result = userSchema.validate(userData);
51
+
52
+ if (result.ok) {
53
+ console.log('Validation passed!');
54
+ } else {
55
+ console.log('Errors:', result.errors);
56
+ }
57
+ ```
58
+
59
+ ## Features
60
+
61
+ - **Type Validation**: Support for built-in types (String, Number, Boolean, Array, Object, Date, RegExp) and custom types
62
+ - **Flexible Rules**: Comprehensive set of validation rules for strings, numbers, arrays, and custom logic
63
+ - **Nested Objects**: Full support for nested object validation
64
+ - **Custom Messages**: Define custom error messages for validation failures
65
+ - **Partial Validation**: Validate only specific keys when needed
66
+ - **TypeScript Support**: Written in TypeScript with full type definitions
67
+
68
+ ## Schema Definition
69
+
70
+ ### Basic Schema Structure
71
+
72
+ ```javascript
73
+ const schema = new Schema({
74
+ fieldName: {
75
+ type: String, // Required: field type
76
+ required: true, // Optional: whether field is required (default: true)
77
+ rules: {}, // Optional: validation rules
78
+ title: "Field Name", // Optional: human-readable field name (only used in custom messages for now)
79
+ customMessage: (details) => "Custom error" // Optional: custom error function
80
+ }
81
+ });
82
+ ```
83
+
84
+ ### Supported Types
85
+
86
+ ```javascript
87
+ const schema = new Schema({
88
+ stringField: { type: String },
89
+ numberField: { type: Number },
90
+ booleanField: { type: Boolean },
91
+ arrayField: { type: Array },
92
+ objectField: { type: Object },
93
+ dateField: { type: Date },
94
+ regexField: { type: RegExp },
95
+ nullField: { type: null },
96
+
97
+ // Union types (multiple allowed types)
98
+ mixedField: { type: [String, Number] },
99
+
100
+ // Custom classes
101
+ customField: { type: MyCustomClass },
102
+
103
+ // Any type
104
+ anyField: { type: 'any' }
105
+ });
106
+ ```
107
+
108
+ ### Array Type Validation
109
+
110
+ ```javascript
111
+ const schema = new Schema({
112
+ // Array of any items
113
+ items: { type: Array },
114
+
115
+ // Array where each item must be a specific type
116
+ numbers: {
117
+ type: Array,
118
+ eachType: Number,
119
+ }
120
+ });
121
+ ```
122
+
123
+ ## Validation Rules
124
+
125
+ ### String Rules
126
+
127
+ ```javascript
128
+ const schema = new Schema({
129
+ text: {
130
+ type: String,
131
+ rules: {
132
+ // Length validations
133
+ length: 10, // Exact length
134
+ lengthMin: 5, // Minimum length
135
+ lengthMax: 100, // Maximum length
136
+ lengthMinMax: [5, 100], // Length range
137
+ lengthNot: 0, // Not this length
138
+
139
+ // Format validations
140
+ isEmail: true, // Valid email format
141
+ regex: /^[A-Z]+$/, // Custom regex pattern
142
+
143
+ // Value comparisons
144
+ is: "exact value", // Must equal this value
145
+ isNot: "forbidden" // Must not equal this value
146
+ }
147
+ }
148
+ });
149
+ ```
150
+
151
+ ### Number Rules
152
+
153
+ ```javascript
154
+ const schema = new Schema({
155
+ count: {
156
+ type: Number,
157
+ rules: {
158
+ min: 0, // Minimum value (>=)
159
+ max: 100, // Maximum value (<=)
160
+ minMax: [0, 100], // Value range
161
+ is: 42, // Must equal this value
162
+ isNot: 0 // Must not equal this value
163
+ }
164
+ }
165
+ });
166
+ ```
167
+
168
+ ### Array Rules
169
+
170
+ ```javascript
171
+ const schema = new Schema({
172
+ items: {
173
+ type: Array,
174
+ rules: {
175
+ length: 5, // Exact length
176
+ lengthMin: 1, // Minimum length
177
+ lengthMax: 10, // Maximum length
178
+ lengthMinMax: [1, 10], // Length range
179
+ enum: ['a', 'b', 'c'] // All items must be from this list
180
+ }
181
+ }
182
+ });
183
+ ```
184
+
185
+ ### Custom Rules
186
+
187
+ ```javascript
188
+ const schema = new Schema({
189
+ password: {
190
+ type: String,
191
+ rules: {
192
+ custom: (value, { schema, input }) => {
193
+ // Custom validation logic
194
+ const hasUpperCase = /[A-Z]/.test(value);
195
+ const hasLowerCase = /[a-z]/.test(value);
196
+ const hasNumbers = /\d/.test(value);
197
+
198
+ const isPassed = hasUpperCase && hasLowerCase && hasNumbers
199
+
200
+ // Return true or false value...
201
+ return isPassed
202
+
203
+ // ...or provide details for the validation
204
+ return {
205
+ result: isPassed,
206
+ details: isPassed ? '' : "Password must contain an uppercase letter, a lowercase letter, and a number"
207
+ }
208
+ }
209
+ }
210
+ }
211
+ });
212
+ ```
213
+
214
+ ### Enum Validation
215
+
216
+ ```javascript
217
+ const schema = new Schema({
218
+ status: {
219
+ type: String,
220
+ rules: {
221
+ enum: ['active', 'inactive', 'pending']
222
+ }
223
+ }
224
+ });
225
+ ```
226
+
227
+ ## Nested Objects
228
+
229
+ ```javascript
230
+ const schema = new Schema({
231
+ user: {
232
+ name: {
233
+ type: String,
234
+ required: true
235
+ },
236
+ address: {
237
+ street: {
238
+ type: String,
239
+ required: true
240
+ },
241
+ city: {
242
+ type: String,
243
+ required: true
244
+ },
245
+ zipCode: {
246
+ type: String,
247
+ required: false,
248
+ rules: {
249
+ regex: /^\d{5}$/
250
+ }
251
+ }
252
+ }
253
+ }
254
+ });
255
+
256
+ const data = {
257
+ user: {
258
+ name: "Barney Stinson",
259
+ address: {
260
+ street: "123 Main St",
261
+ city: "New York",
262
+ zipCode: "10001"
263
+ }
264
+ }
265
+ };
266
+
267
+ const result = schema.validate(data);
268
+ ```
269
+
270
+ ## Custom Error Messages
271
+
272
+ ### Field-Level Custom Messages
273
+
274
+ ```javascript
275
+ const schema = new Schema({
276
+ email: {
277
+ type: String,
278
+ required: true,
279
+ rules: {
280
+ isEmail: true
281
+ },
282
+ customMessage: ({ keyword, value, key }) => {
283
+ if (keyword === 'isEmail') {
284
+ return `Please enter a valid email address for ${key}`;
285
+ }
286
+ return `Invalid value for ${key}`;
287
+ }
288
+ }
289
+ });
290
+ ```
291
+
292
+ ### Custom Message Parameters
293
+
294
+ The `customMessage` function receives an object with:
295
+ - `keyword`: The validation rule that failed
296
+ - `value`: The actual value being validated
297
+ - `key`: The field name
298
+ - `title`: The field title (if specified)
299
+ - `reqs`: The field requirements object
300
+ - `schema`: The full schema object
301
+ - `rules`: The rules object for this field
302
+
303
+ ## Partial Validation
304
+
305
+ Validate only specific fields:
306
+
307
+ ```javascript
308
+ const schema = new Schema({
309
+ name: { type: String },
310
+ email: { type: String },
311
+ phone: { type: String }
312
+ });
313
+
314
+ const data = { name: "Barney", email: "barney@himym.com" };
315
+
316
+ // Validate only the name field
317
+ const nameResult = schema.validate(data, 'name');
318
+
319
+ // Validate only name and email fields
320
+ const partialResult = schema.validate(data, ['name', 'email']);
321
+ ```
322
+
323
+ ## Validation Result
324
+
325
+ The `validate` method returns a result object with:
326
+
327
+ ```javascript
328
+ {
329
+ ok: boolean, // Overall validation success
330
+ passed: string[], // Array of field names that passed
331
+ failed: string[], // Array of field names that failed
332
+ missed: string[], // Array of required fields that were missing
333
+ errors: string[], // Array of all error messages
334
+ byKeys: { // Validation status by field name
335
+ fieldName: boolean
336
+ },
337
+ errorsByKeys: { // Error messages by field name
338
+ fieldName: string[]
339
+ },
340
+ joinErrors: (separator?) => string // Method to join all errors
341
+ }
342
+ ```
343
+
344
+ ### Using the Result
345
+
346
+ ```javascript
347
+ const result = schema.validate(data);
348
+
349
+ if (result.ok) {
350
+ console.log('All validations passed!');
351
+ } else {
352
+ console.log('Failed fields:', result.failed);
353
+ console.log('Missing fields:', result.missed);
354
+ console.log('All errors:', result.joinErrors(', '));
355
+
356
+ // Check specific field
357
+ if (!result.byKeys.email) {
358
+ console.log('Email errors:', result.errorsByKeys.email);
359
+ }
360
+ }
361
+ ```
362
+
363
+ ## Built-in Validation Utilities
364
+
365
+ Validno exports validation utilities that you can use independently:
366
+
367
+ ```javascript
368
+ import { validations } from 'validno';
369
+
370
+ // Type checks
371
+ validations.isString(value);
372
+ validations.isNumber(value);
373
+ validations.isArray(value);
374
+ validations.isObject(value);
375
+ validations.isDate(value);
376
+ validations.isBoolean(value);
377
+
378
+ // String format validation
379
+ validations.isEmail(email);
380
+ validations.isDateYYYYMMDD(dateString);
381
+ validations.isHex(colorCode);
382
+
383
+ // Length validation
384
+ validations.lengthIs(value, 5);
385
+ validations.lengthMin(value, 2);
386
+ validations.lengthMax(value, 10);
387
+
388
+ // Number comparisons
389
+ validations.isNumberGte(value, 10);
390
+ validations.isNumberLt(value, 100);
391
+
392
+ // Date comparisons
393
+ validations.isDateLte(date1, date2);
394
+ validations.isDateGt(date1, date2);
395
+
396
+ // Deep equality
397
+ validations.is(obj1, obj2);
398
+ validations.not(value1, value2);
399
+
400
+ // Regular expressions
401
+ validations.regexTested(string, /pattern/);
402
+ ```
403
+
404
+ ## Advanced Examples
405
+
406
+ ### Complex Nested Validation
407
+
408
+ ```javascript
409
+ const orderSchema = new Schema({
410
+ orderId: {
411
+ type: String,
412
+ required: true,
413
+ rules: {
414
+ regex: /^ORD-\d{6}$/
415
+ }
416
+ },
417
+ customer: {
418
+ id: {
419
+ type: Number,
420
+ required: true,
421
+ rules: {
422
+ min: 1
423
+ }
424
+ },
425
+ profile: {
426
+ firstName: {
427
+ type: String,
428
+ required: true,
429
+ rules: {
430
+ lengthMin: 2,
431
+ lengthMax: 50
432
+ }
433
+ },
434
+ lastName: {
435
+ type: String,
436
+ required: true,
437
+ rules: {
438
+ lengthMin: 2,
439
+ lengthMax: 50
440
+ }
441
+ },
442
+ email: {
443
+ type: String,
444
+ required: true,
445
+ rules: {
446
+ isEmail: true
447
+ }
448
+ }
449
+ }
450
+ },
451
+ items: {
452
+ type: Array,
453
+ eachType: Object,
454
+ required: true,
455
+ rules: {
456
+ lengthMin: 1
457
+ }
458
+ },
459
+ totalAmount: {
460
+ type: Number,
461
+ required: true,
462
+ rules: {
463
+ min: 0.01
464
+ }
465
+ },
466
+ status: {
467
+ type: String,
468
+ required: true,
469
+ rules: {
470
+ enum: ['pending', 'processing', 'shipped', 'delivered', 'cancelled']
471
+ }
472
+ }
473
+ });
474
+ ```
475
+
476
+ ## TypeScript Support
477
+
478
+ Validno is written in TypeScript and provides full type definitions:
479
+
480
+ ```typescript
481
+ import Schema from 'validno';
482
+ import { SchemaDefinition, FieldSchema } from 'validno';
483
+
484
+ interface User {
485
+ name: string;
486
+ email: string;
487
+ age?: number;
488
+ }
489
+
490
+ const userSchema = new Schema({
491
+ name: {
492
+ type: String,
493
+ required: true
494
+ } as FieldSchema,
495
+ email: {
496
+ type: String,
497
+ required: true,
498
+ rules: {
499
+ isEmail: true
500
+ }
501
+ } as FieldSchema,
502
+ age: {
503
+ type: Number,
504
+ required: false
505
+ } as FieldSchema
506
+ } as SchemaDefinition);
507
+
508
+ const newUser = {
509
+ name: "Barney",
510
+ email: "barney@himym.com",
511
+ }
512
+
513
+ // Use generic type...
514
+ const result = userSchema.validate<User>(newUser, ['name', 'email'])
515
+
516
+ // ...or use automatic type inference based on the object
517
+ const result = userSchema.validate(newUser, ['name', 'email'])
package/dist/Schema.js CHANGED
@@ -1,14 +1,16 @@
1
- import { ESchemaFields } from "./constants/schema.js";
2
- import validate from "./validate.js";
3
- export const defaultSchemaKeys = Object.values(ESchemaFields);
1
+ import { SchemaFields } from "./constants/schema.js";
2
+ import ValidateEngine from "./engine/ValidateEngine.js";
3
+ export const defaultSchemaKeys = Object.values(SchemaFields);
4
4
  export class Schema {
5
5
  constructor(inputSchemaDefinition) {
6
6
  if (!inputSchemaDefinition || typeof inputSchemaDefinition !== 'object') {
7
7
  throw new Error("Invalid schema input");
8
8
  }
9
- this.schema = inputSchemaDefinition;
9
+ this.definition = inputSchemaDefinition;
10
10
  }
11
11
  validate(inputData, validationKeys) {
12
- return validate.call(this, inputData, validationKeys);
12
+ const engine = new ValidateEngine(this.definition);
13
+ const result = engine.validate(inputData, validationKeys);
14
+ return result;
13
15
  }
14
16
  }
@@ -17,12 +17,14 @@ class ValidnoResult {
17
17
  this.byKeys[key] = result;
18
18
  }
19
19
  fixParentByChilds(parentKey, childChecks = []) {
20
- const isEveryOk = childChecks.every(c => c === true);
20
+ const isEveryOk = childChecks.every(check => check === true);
21
21
  this.setKeyStatus(parentKey, isEveryOk);
22
- if (isEveryOk === true)
22
+ if (isEveryOk) {
23
23
  this.setPassed(parentKey);
24
- else
24
+ }
25
+ else {
25
26
  this.setFailed(parentKey);
27
+ }
26
28
  }
27
29
  setMissing(key, errMsg) {
28
30
  const error = errMsg || _errors.getMissingError(key);
@@ -35,7 +37,7 @@ class ValidnoResult {
35
37
  this.setKeyStatus(key, true);
36
38
  }
37
39
  setFailed(key, msg) {
38
- if (key in this.errorsByKeys === false) {
40
+ if (!(key in this.errorsByKeys)) {
39
41
  this.errorsByKeys[key] = [];
40
42
  }
41
43
  this.failed.push(key);
@@ -55,8 +57,9 @@ class ValidnoResult {
55
57
  this.passed = [...this.passed, ...resultsNew.passed];
56
58
  this.byKeys = Object.assign(Object.assign({}, this.byKeys), resultsNew.byKeys);
57
59
  for (const key in resultsNew.errorsByKeys) {
58
- if (key in this.errorsByKeys === false)
60
+ if (!(key in this.errorsByKeys)) {
59
61
  this.errorsByKeys[key] = [];
62
+ }
60
63
  this.errorsByKeys[key] = [
61
64
  ...this.errorsByKeys[key],
62
65
  ...resultsNew.errorsByKeys[key]
@@ -72,10 +75,12 @@ class ValidnoResult {
72
75
  }
73
76
  }
74
77
  finish() {
75
- if (this.failed.length || this.errors.length)
78
+ if (this.failed.length || this.errors.length) {
76
79
  this.ok = false;
77
- else
80
+ }
81
+ else {
78
82
  this.ok = true;
83
+ }
79
84
  this.clearEmptyErrorsByKeys();
80
85
  return this;
81
86
  }
@@ -0,0 +1 @@
1
+ "use strict";
@@ -1,11 +1,12 @@
1
- export var EValidationId;
2
- (function (EValidationId) {
3
- EValidationId["Missing"] = "missing";
4
- EValidationId["Type"] = "type";
5
- EValidationId["Rule"] = "rule";
6
- })(EValidationId || (EValidationId = {}));
7
- export var EValidationDetails;
8
- (function (EValidationDetails) {
9
- EValidationDetails["OK"] = "ok";
10
- EValidationDetails["INVALID_DATE"] = "\u0414\u0430\u0442\u0430 \u043D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u0430";
11
- })(EValidationDetails || (EValidationDetails = {}));
1
+ export var ValidationIds;
2
+ (function (ValidationIds) {
3
+ ValidationIds["Missing"] = "missing";
4
+ ValidationIds["Type"] = "type";
5
+ ValidationIds["Rule"] = "rule";
6
+ })(ValidationIds || (ValidationIds = {}));
7
+ export var ValidationDetails;
8
+ (function (ValidationDetails) {
9
+ ValidationDetails["OK"] = "OK";
10
+ ValidationDetails["INVALID_DATE"] = "Invalid date";
11
+ ValidationDetails["CustomRuleFailed"] = "Custom rule failed";
12
+ })(ValidationDetails || (ValidationDetails = {}));
@@ -1,10 +1,10 @@
1
- export var ESchemaFields;
2
- (function (ESchemaFields) {
3
- ESchemaFields["Required"] = "required";
4
- ESchemaFields["Type"] = "type";
5
- ESchemaFields["EachType"] = "eachType";
6
- ESchemaFields["Rules"] = "rules";
7
- ESchemaFields["Title"] = "title";
8
- ESchemaFields["CustomMessage"] = "customMessage";
9
- ESchemaFields["JoinErrors"] = "joinErrors";
10
- })(ESchemaFields || (ESchemaFields = {}));
1
+ export var SchemaFields;
2
+ (function (SchemaFields) {
3
+ SchemaFields["Required"] = "required";
4
+ SchemaFields["Type"] = "type";
5
+ SchemaFields["EachType"] = "eachType";
6
+ SchemaFields["Rules"] = "rules";
7
+ SchemaFields["Title"] = "title";
8
+ SchemaFields["CustomMessage"] = "customMessage";
9
+ SchemaFields["JoinErrors"] = "joinErrors";
10
+ })(SchemaFields || (SchemaFields = {}));
package/dist/dev.js CHANGED
@@ -1,30 +1,23 @@
1
- import { Schema } from "./Schema.js";
2
- const testSchema = new Schema({
3
- parent: {
4
- collection: {
5
- type: [String, Array],
6
- required: true
1
+ import Schema from './index.js';
2
+ const userSchema = new Schema({
3
+ name: {
4
+ type: String,
5
+ required: true
6
+ },
7
+ email: {
8
+ type: String,
9
+ required: true,
10
+ rules: {
11
+ isEmail: true
7
12
  }
13
+ },
14
+ age: {
15
+ type: Number,
16
+ required: false
8
17
  }
9
18
  });
10
- const testObj = {
11
- parent: {
12
- collection: ['xxxx']
13
- }
14
- };
15
- const testObj2 = {
16
- parent: {
17
- collection: false
18
- }
19
- };
20
- const testObj3 = {
21
- parent: {
22
- collection: 'str'
23
- }
19
+ const newUser = {
20
+ name: "Barney",
21
+ email: "barney@himym.com",
24
22
  };
25
- const res = testSchema.validate(testObj);
26
- console.log(res.ok === true ? '#1 ✅' : '#1 ❌');
27
- const res2 = testSchema.validate(testObj2);
28
- console.log(res2.ok === false ? '#2 ✅' : '#2 ❌');
29
- const res3 = testSchema.validate(testObj3);
30
- console.log(res3.ok === true ? '#3 ✅' : '#3 ❌');
23
+ const result = userSchema.validate(newUser, 'age');