snap-validate 0.2.0 โ†’ 0.3.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Mini Validator Contributors
3
+ Copyright (c) 2025 Snap Validate Contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Snap Validate โšก
2
2
 
3
- [![npm version](https://badge.fury.io/js/snap-validate.svg)](https://badge.fury.io/js/snap-validate)
3
+ [![npm version](https://img.shields.io/npm/v/snap-validate.svg?style=flat-square)](https://www.npmjs.com/package/snap-validate)
4
4
  [![Build Status](https://github.com/aniru-dh21/snap-validate/workflows/CI%2FCD%20Pipeline/badge.svg)](https://github.com/aniru-dh21/snap-validate/actions)
5
- [![Coverage Status](https://codecov.io/gh/aniru-dh21/snap-validate/branch/main/graph/badge.svg)](https://codecov.io/gh/aniru-dh21/snap-validate)
6
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![npm downloads](https://img.shields.io/npm/dm/snap-validate.svg?style=flat-square)](https://npm-stat.com/charts.html?package=snap-validate)
7
7
 
8
8
  A lightning-fast, lightweight validation library for common patterns without heavy dependencies. Perfect for client-side and server-side validation with zero external dependencies.
9
9
 
@@ -14,6 +14,9 @@ A lightning-fast, lightweight validation library for common patterns without hea
14
14
  - ๐Ÿ”ง **Flexible**: Chainable validation rules and custom validators
15
15
  - ๐Ÿ“ง **Common Patterns**: Email, phone, credit card, URL, password validation
16
16
  - ๐ŸŒ **International**: Support for different formats (US/International phone, postal codes)
17
+ - ๐Ÿ”„ **Async Support**: Full async validation support for database checks and API calls
18
+ - ๐ŸŽฏ **Conditional**: Advanced conditional validation with `when()` and `optional()`
19
+ - ๐Ÿ› ๏ธ **Custom Validators**: Add your own sync and async validation logic
17
20
  - ๐Ÿงช **Well Tested**: Comprehensive test suite with high coverage
18
21
  - ๐Ÿ“ฆ **Easy Integration**: Works in Node.js and browsers
19
22
  - ๐Ÿ”— **Chainable API**: Intuitive fluent interface
@@ -126,6 +129,89 @@ validators.zipCode('K1A 0A6', 'ca').validate();
126
129
  validators.zipCode('SW1A 1AA', 'uk').validate();
127
130
  ```
128
131
 
132
+ ## Advanced Validation Features
133
+
134
+ ### Conditional Validation
135
+
136
+ ```javascript
137
+ const { BaseValidator } = require('snap-validate');
138
+
139
+ // Validate only when condition is met
140
+ const validator = new BaseValidator(value)
141
+ .when(user.isAdmin, validators.required('Admin field required'))
142
+ .min(5, 'Must be at least 5 characters');
143
+
144
+ // Optional validation - skip if empty/null/undefined
145
+ const optionalValidator = new BaseValidator(value)
146
+ .optional()
147
+ .email('Must be a valid email if provided');
148
+
149
+ // Function-based conditions
150
+ const conditionalValidator = new BaseValidator(value)
151
+ .when(() => user.role === 'admin', validators.required())
152
+ .max(100);
153
+ ```
154
+
155
+ ### Custom Validators
156
+
157
+ ```javascript
158
+ const { BaseValidator } = require('snap-validate');
159
+
160
+ // Synchronous custom validation
161
+ const customValidator = new BaseValidator(value)
162
+ .custom((val) => val !== 'forbidden', 'Value cannot be forbidden')
163
+ .custom((val) => {
164
+ if (val.includes('admin') && !user.isAdmin) {
165
+ return 'Only admins can use this value';
166
+ }
167
+ return true;
168
+ });
169
+
170
+ // Asynchronous custom validation
171
+ const asyncValidator = new BaseValidator(email)
172
+ .email()
173
+ .customAsync(async (email) => {
174
+ const exists = await checkEmailExists(email);
175
+ return !exists || 'Email already exists';
176
+ }, 'Email validation failed');
177
+
178
+ // Use async validation
179
+ const result = await asyncValidator.validateAsync();
180
+ ```
181
+
182
+ ### Async Validation
183
+
184
+ ```javascript
185
+ // Async validation for single field
186
+ const validator = new BaseValidator(username)
187
+ .required()
188
+ .min(3)
189
+ .customAsync(async (username) => {
190
+ const available = await checkUsernameAvailable(username);
191
+ return available || 'Username is already taken';
192
+ });
193
+
194
+ const result = await validator.validateAsync();
195
+
196
+ // Async schema validation
197
+ const asyncSchema = {
198
+ username: (value) => new BaseValidator(value)
199
+ .required()
200
+ .customAsync(async (val) => {
201
+ const available = await checkUsernameAvailable(val);
202
+ return available || 'Username taken';
203
+ }),
204
+
205
+ email: (value) => validators.email(value)
206
+ .customAsync(async (val) => {
207
+ const exists = await checkEmailExists(val);
208
+ return !exists || 'Email already registered';
209
+ })
210
+ };
211
+
212
+ const asyncResult = await validate.async(asyncSchema, userData);
213
+ ```
214
+
129
215
  ## Custom Validation
130
216
 
131
217
  ### Using BaseValidator
@@ -157,6 +243,7 @@ const schema = {
157
243
  age: (value) => new BaseValidator(value)
158
244
  .required()
159
245
  .pattern(/^\d+$/, 'Age must be a number')
246
+ .custom((val) => parseInt(val) >= 18, 'Must be 18 or older')
160
247
  };
161
248
 
162
249
  const userData = {
@@ -185,6 +272,16 @@ if (!schemaResult.isValid) {
185
272
  console.log('Field errors:', errors);
186
273
  // Output: { email: ['Invalid email format'], password: ['Password too weak'] }
187
274
  }
275
+
276
+ // Async error handling
277
+ try {
278
+ const asyncResult = await validator.validateAsync();
279
+ if (!asyncResult.isValid) {
280
+ console.log('Async validation errors:', asyncResult.errors);
281
+ }
282
+ } catch (error) {
283
+ console.log('Validation exception:', error.message);
284
+ }
188
285
  ```
189
286
 
190
287
  ## Browser Usage
@@ -212,7 +309,12 @@ if (!schemaResult.isValid) {
212
309
  - `min(length, message?)` - Minimum length validation
213
310
  - `max(length, message?)` - Maximum length validation
214
311
  - `pattern(regex, message?)` - Pattern matching validation
215
- - `validate()` - Execute validation and return result
312
+ - `when(condition, validator)` - Conditional validation
313
+ - `optional()` - Skip validation if empty/null/undefined
314
+ - `custom(fn, message?)` - Custom synchronous validation
315
+ - `customAsync(fn, message?)` - Custom asynchronous validation
316
+ - `validate()` - Execute synchronous validation
317
+ - `validateAsync()` - Execute asynchronous validation
216
318
 
217
319
  ### Available Validators
218
320
 
@@ -225,6 +327,10 @@ if (!schemaResult.isValid) {
225
327
  - `validators.numeric(value)`
226
328
  - `validators.zipCode(value, country?)`
227
329
 
330
+ ### Validation Functions
331
+
332
+ - `validate(schema, data)` - Synchronous schema validation
333
+ - `validate.async(schema, data)` - Asynchronous schema validation
228
334
 
229
335
  ## Contributing
230
336
 
@@ -264,14 +370,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
264
370
 
265
371
  ## Changelog
266
372
 
267
- ### v1.0.0
268
-
269
- - Initial release
270
- - Basic validation patterns (email, phone, credit card, URL, password)
271
- - Schema validation support
272
- - Comprehensive test suite
273
- - CI/CD pipeline setup
274
- - Lightning-fast performance optimizations
373
+ See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes.
275
374
 
276
375
  ---
277
376
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snap-validate",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Lightweight validation library for common patterns without heavy dependencies",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Snap Validate - Lightweight validator library
3
- * @version 0.0.1
2
+ * Snap Validate - Enhanced Lightweight validator library
3
+ * @version 0.3.0
4
4
  */
5
5
 
6
6
  // Core validation class
@@ -22,10 +22,17 @@ class BaseValidator {
22
22
  constructor(value) {
23
23
  this.value = value;
24
24
  this.rules = [];
25
+ this.asyncRules = [];
26
+ this.isOptional = false;
25
27
  }
26
28
 
27
29
  required(message = 'This field is required') {
28
30
  this.rules.push(() => {
31
+ // Skip validation if optional and empty
32
+ if (this.isOptional && (this.value === null || this.value === undefined || this.value === '')) {
33
+ return new ValidationResult(true);
34
+ }
35
+
29
36
  if (this.value === null || this.value === undefined || this.value === '') {
30
37
  return new ValidationResult(false, [message]);
31
38
  }
@@ -34,10 +41,33 @@ class BaseValidator {
34
41
  return this;
35
42
  }
36
43
 
44
+ optional() {
45
+ this.isOptional = true;
46
+ return this;
47
+ }
48
+
37
49
  min(length, message = `Minimum length is ${length}`) {
38
50
  this.rules.push(() => {
39
- if (this.value && this.value.length < length) {
40
- return new ValidationResult(false, [message]);
51
+ // Skip validation if optional and empty
52
+ if (this.isOptional && (this.value === null || this.value === undefined || this.value === '')) {
53
+ return new ValidationResult(true);
54
+ }
55
+
56
+ // Only validate if value exists and a length property
57
+ if (this.value != null && this.value !== '') {
58
+ // Check if value has length property (string, array)
59
+ if (typeof this.value === 'string' || Array.isArray(this.value)) {
60
+ if (this.value.length < length) {
61
+ return new ValidationResult(false, [message]);
62
+ }
63
+ } else if (typeof this.value === 'number') {
64
+ // For numbers, compare the value itself
65
+ if (this.value < length) {
66
+ return new ValidationResult(false, [message]);
67
+ }
68
+ } else {
69
+ return new ValidationResult(false, ['Value must be a string, array, or number']);
70
+ }
41
71
  }
42
72
  return new ValidationResult(true);
43
73
  });
@@ -46,8 +76,26 @@ class BaseValidator {
46
76
 
47
77
  max(length, message = `Maximum length is ${length}`) {
48
78
  this.rules.push(() => {
49
- if (this.value && this.value.length > length) {
50
- return new ValidationResult(false, [message]);
79
+ // Skip validation if optional and empty
80
+ if (this.isOptional && (this.value === null || this.value === undefined || this.value === '')) {
81
+ return new ValidationResult(true);
82
+ }
83
+
84
+ // Only validate if value exists and has a length property
85
+ if (this.value != null && this.value !== '') {
86
+ // Check if value has length property (string, array)
87
+ if (typeof this.value === 'string' || Array.isArray(this.value)) {
88
+ if (this.value.length > length) {
89
+ return new ValidationResult(false, [message]);
90
+ }
91
+ } else if (typeof this.value === 'number') {
92
+ // For numbers, compare the value itself
93
+ if (this.value > length) {
94
+ return new ValidationResult(false, [message]);
95
+ }
96
+ } else {
97
+ return new ValidationResult(false, ['Value must be a string, array or number']);
98
+ }
51
99
  }
52
100
  return new ValidationResult(true);
53
101
  });
@@ -56,22 +104,155 @@ class BaseValidator {
56
104
 
57
105
  pattern(regex, message = 'Invalid format') {
58
106
  this.rules.push(() => {
59
- if (this.value && !regex.test(this.value)) {
60
- return new ValidationResult(false, [message]);
107
+ // Skip validation if optional and empty
108
+ if (this.isOptional && (this.value === null || this.value === undefined || this.value === '')) {
109
+ return new ValidationResult(true);
61
110
  }
111
+
112
+ // Only test pattern if value exists and is not empty
113
+ if (this.value != null && this.value !== '') {
114
+ // Ensure value is a string before testing regex
115
+ const stringValue = String(this.value);
116
+ if (!regex.test(stringValue)) {
117
+ return new ValidationResult(false, [message]);
118
+ }
119
+ }
120
+ return new ValidationResult(true);
121
+ });
122
+ return this;
123
+ }
124
+
125
+ when(condition, validator) {
126
+ this.rules.push(() => {
127
+ // Evaluate condition
128
+ const shouldValidate = typeof condition === 'function' ? condition(this.value) : condition;
129
+
130
+ if (shouldValidate) {
131
+ // Apply the conditional validator
132
+ if (typeof validator === 'function') {
133
+ const conditionalValidator = validator(this.value);
134
+ return conditionalValidator.validate();
135
+ } else {
136
+ // If validator is already a BaseValidator instance
137
+ return validator.validate();
138
+ }
139
+ }
140
+
62
141
  return new ValidationResult(true);
63
142
  });
64
143
  return this;
65
144
  }
66
145
 
146
+ custom(validatorFn, message = 'Custom validation failed') {
147
+ this.rules.push(() => {
148
+ // Skip validation if optional and empty
149
+ if (this.isOptional && (this.value === null || this.value === undefined || this.value === '')) {
150
+ return new ValidationResult(true);
151
+ }
152
+
153
+ try {
154
+ const result = validatorFn(this.value);
155
+
156
+ // Handle boolean result
157
+ if (typeof result === 'boolean') {
158
+ return result ? new ValidationResult(true) : new ValidationResult(false, [message]);
159
+ }
160
+
161
+ // Handle ValidationResult object
162
+ if (result && typeof result === 'object' && 'isValid' in result) {
163
+ return result;
164
+ }
165
+
166
+ // Handle string result (error message)
167
+ if (typeof result === 'string') {
168
+ return new ValidationResult(false, [result]);
169
+ }
170
+
171
+ // Default to true if no clear result
172
+ return new ValidationResult(true);
173
+ } catch (error) {
174
+ return new ValidationResult(false, [`Custom validation error: ${error.message}`]);
175
+ }
176
+ });
177
+ return this;
178
+ }
179
+
180
+ customAsync(validatorFn, message = 'Async validation failed') {
181
+ this.asyncRules.push(async () => {
182
+ // Skip validation if optional and empty
183
+ if (this.isOptional && (this.value === null || this.value === undefined || this.value === '')) {
184
+ return new ValidationResult(true);
185
+ }
186
+
187
+ try {
188
+ const result = await validatorFn(this.value);
189
+
190
+ // Handle boolean result
191
+ if (typeof result === 'boolean') {
192
+ return result ? new ValidationResult(true) : new ValidationResult(false, [message]);
193
+ }
194
+
195
+ // Handle ValidationResult object
196
+ if (result && typeof result === 'object' && 'isValid' in result) {
197
+ return result;
198
+ }
199
+
200
+ // Handle string result (error message)
201
+ if (typeof result === 'string') {
202
+ return new ValidationResult(false, [result]);
203
+ }
204
+
205
+ // Default to true if no clear result
206
+ return new ValidationResult(true);
207
+ } catch (error) {
208
+ return new ValidationResult(false, [`Async validation error: ${error.message}`]);
209
+ }
210
+ });
211
+ return this;
212
+ }
213
+
67
214
  validate() {
68
215
  const result = new ValidationResult(true);
69
216
 
70
217
  for (const rule of this.rules) {
71
- const ruleResult = rule();
72
- if (!ruleResult.isValid) {
218
+ try {
219
+ const ruleResult = rule();
220
+ if (!ruleResult.isValid) {
221
+ result.isValid = false;
222
+ result.errors.push(...ruleResult.errors);
223
+ }
224
+ } catch (error) {
225
+ // Handle any unexpected errors during validation
226
+ result.isValid = false;
227
+ result.errors.push(`Validation error: ${error.message}`);
228
+ }
229
+ }
230
+
231
+ return result;
232
+ }
233
+
234
+ async validateAsync() {
235
+ // First run synchronous validations
236
+ const syncResult = this.validate();
237
+
238
+ if (!syncResult.isValid) {
239
+ return syncResult;
240
+ }
241
+
242
+ // Then run asynchronous validations
243
+ const result = new ValidationResult(true, [...syncResult.errors]);
244
+
245
+ for (const asyncRule of this.asyncRules) {
246
+ try {
247
+ const ruleResult = await asyncRule();
248
+ if (!ruleResult.isValid) {
249
+ result.isValid = false;
250
+ result.errors.push(...ruleResult.errors);
251
+ }
252
+ } catch (error) {
253
+ // Handle any unexpected errors during async validation
73
254
  result.isValid = false;
74
- result.errors.push(...ruleResult.errors);
255
+ result.errors.push(`Async validation error: ${error.message}`);
75
256
  }
76
257
  }
77
258
 
@@ -106,8 +287,11 @@ const validators = {
106
287
  let sum = 0;
107
288
  let isEven = false;
108
289
 
109
- for (let i = num.length - 1; i >= 0; i--) {
110
- let digit = parseInt(num[i]);
290
+ // Remove spaces and ensure we have a string
291
+ const cleanNum = String(num).replace(/\s/g, '');
292
+
293
+ for (let i = cleanNum.length - 1; i >= 0; i--) {
294
+ let digit = parseInt(cleanNum[i]);
111
295
 
112
296
  if (isEven) {
113
297
  digit *= 2;
@@ -122,12 +306,27 @@ const validators = {
122
306
  };
123
307
 
124
308
  const validator = new BaseValidator(value)
125
- .required('Credit card number is required')
126
- .pattern(/^\d{13,19}$/, 'Credit card must be 13-19 digits');
309
+ .required('Credit card number is required');
127
310
 
311
+ // Add custom validation for credit card format and Luhn check
128
312
  validator.rules.push(() => {
129
- if (value && !luhnCheck(value.replace(/\s/g, ''))) {
130
- return new ValidationResult(false, ['Invalid credit card number']);
313
+ // Skip validation if optional and empty
314
+ if (validator.isOptional && (validator.value === null || validator.value === undefined || validator.value === '')) {
315
+ return new ValidationResult(true);
316
+ }
317
+
318
+ if (validator.value) {
319
+ const cleanValue = String(validator.value).replace(/\s/g, '');
320
+
321
+ // Check length (13-19 digits)
322
+ if (!/^\d{13,19}$/.test(cleanValue)) {
323
+ return new ValidationResult(false, ['Credit card must be 13-19 digits']);
324
+ }
325
+
326
+ // Check Luhn algorithm
327
+ if (!luhnCheck(cleanValue)) {
328
+ return new ValidationResult(false, ['Invalid credit card number']);
329
+ }
131
330
  }
132
331
  return new ValidationResult(true);
133
332
  });
@@ -174,7 +373,7 @@ const validators = {
174
373
  alphanumeric: (value) => {
175
374
  return new BaseValidator(value)
176
375
  .required('This field is required')
177
- .pattern(/^[a-zA-Z0-9]+$/, 'Only letters and number are allowed');
376
+ .pattern(/^[a-zA-Z0-9]+$/, 'Only letters and numbers are allowed');
178
377
  },
179
378
 
180
379
  numeric: (value) => {
@@ -198,17 +397,88 @@ const validators = {
198
397
 
199
398
  // Main validation function
200
399
  const validate = (schema, data) => {
400
+ // Input validation
401
+ if (!schema || typeof schema !== 'object') {
402
+ throw new Error('Schema must be a valid object');
403
+ }
404
+
405
+ if (!data || typeof data !== 'object') {
406
+ throw new Error('Data must be a valid object');
407
+ }
408
+
409
+ const results = {};
410
+ let isValid = true;
411
+
412
+ for (const [field, validator] of Object.entries(schema)) {
413
+ try {
414
+ const fieldValue = data[field];
415
+ const result = typeof validator === 'function'
416
+ ? validator(fieldValue).validate()
417
+ : validator.validate();
418
+
419
+ results[field] = result;
420
+ if (!result.isValid) {
421
+ isValid = false;
422
+ }
423
+ } catch (error) {
424
+ // Handle validation setup errors
425
+ results[field] = new ValidationResult(false, [`Validation setup error: ${error.message}`]);
426
+ isValid = false;
427
+ }
428
+ }
429
+
430
+ return {
431
+ isValid,
432
+ errors: results,
433
+ getErrors: () => {
434
+ const errors = {};
435
+ for (const [field, result] of Object.entries(results)) {
436
+ if (!result.isValid) {
437
+ errors[field] = result.errors;
438
+ }
439
+ }
440
+ return errors;
441
+ }
442
+ };
443
+ };
444
+
445
+ // Async validation function
446
+ const validateAsync = async (schema, data) => {
447
+ // Input validation
448
+ if (!schema || typeof schema !== 'object') {
449
+ throw new Error('Schema must be a valid object');
450
+ }
451
+
452
+ if (!data || typeof data !== 'object') {
453
+ throw new Error('Data must be a valid object');
454
+ }
455
+
201
456
  const results = {};
202
457
  let isValid = true;
203
458
 
204
459
  for (const [field, validator] of Object.entries(schema)) {
205
- const fieldValue = data[field];
206
- const result = typeof validator === 'function'
207
- ? validator(fieldValue).validate()
208
- : validator.validate();
460
+ try {
461
+ const fieldValue = data[field];
462
+
463
+ let result;
464
+ if (typeof validator === 'function') {
465
+ const validatorInstance = validator(fieldValue);
466
+ result = validatorInstance.asyncRules.length > 0
467
+ ? await validatorInstance.validateAsync()
468
+ : validatorInstance.validate();
469
+ } else {
470
+ result = validator.asyncRules && validator.asyncRules.length > 0
471
+ ? await validator.validateAsync()
472
+ : validator.validate();
473
+ }
209
474
 
210
- results[field] = result;
211
- if (!result.isValid) {
475
+ results[field] = result;
476
+ if (!result.isValid) {
477
+ isValid = false;
478
+ }
479
+ } catch (error) {
480
+ // Handle validation setup errors
481
+ results[field] = new ValidationResult(false, [`Validation setup error: ${error.message}`]);
212
482
  isValid = false;
213
483
  }
214
484
  }
@@ -232,5 +502,6 @@ module.exports = {
232
502
  BaseValidator,
233
503
  ValidationResult,
234
504
  validators,
235
- validate
505
+ validate,
506
+ validateAsync
236
507
  };
package/types/index.d.ts CHANGED
@@ -1,10 +1,17 @@
1
- declare module 'mini-validator' {
2
- export interface ValidationResult {
1
+ declare module 'snap-validate' {
2
+ /**
3
+ * Result of a validation operation
4
+ */
5
+ export class ValidationResult {
6
+ constructor(isValid: boolean, errors?: string[]);
3
7
  isValid: boolean;
4
8
  errors: string[];
5
9
  addError(message: string): ValidationResult;
6
10
  }
7
11
 
12
+ /**
13
+ * Password validation options
14
+ */
8
15
  export interface PasswordOptions {
9
16
  minLength?: number;
10
17
  requireUppercase?: boolean;
@@ -13,25 +20,113 @@ declare module 'mini-validator' {
13
20
  requireSpecialChars?: boolean;
14
21
  }
15
22
 
23
+ /**
24
+ * Phone number format types
25
+ */
16
26
  export type PhoneFormat = 'us' | 'international' | 'simple';
27
+
28
+ /**
29
+ * Country codes for zip code validation
30
+ */
17
31
  export type CountryCode = 'us' | 'ca' | 'uk';
18
32
 
33
+ /**
34
+ * Custom validation function that returns boolean
35
+ */
36
+ export type CustomValidatorFunction = (
37
+ value: any
38
+ ) => boolean | string | ValidationResult;
39
+
40
+ /**
41
+ * Async custom validation function
42
+ */
43
+ export type AsyncValidatorFunction = (
44
+ value: any
45
+ ) => Promise<boolean | string | ValidationResult>;
46
+
47
+ /**
48
+ * Conditional validation condition
49
+ */
50
+ export type ConditionalFunction = (value: any) => boolean;
51
+
52
+ /**
53
+ * Conditional validator function
54
+ */
55
+ export type ConditionalValidatorFunction = (value: any) => BaseValidator;
56
+
57
+ /**
58
+ * Base validator class with chainable validation methods
59
+ */
19
60
  export class BaseValidator {
20
61
  constructor(value: any);
62
+ value: any;
63
+ rules: Array<() => ValidationResult>;
64
+ asyncRules: Array<() => Promise<ValidationResult>>;
65
+ isOptional: boolean;
66
+
67
+ /**
68
+ * Make field required
69
+ */
21
70
  required(message?: string): BaseValidator;
71
+
72
+ /**
73
+ * Make field optional (skips validation if empty)
74
+ */
75
+ optional(): BaseValidator;
76
+
77
+ /**
78
+ * Set minimum length/value
79
+ */
22
80
  min(length: number, message?: string): BaseValidator;
81
+
82
+ /**
83
+ * Set maximum length/value
84
+ */
23
85
  max(length: number, message?: string): BaseValidator;
86
+
87
+ /**
88
+ * Validate against regex pattern
89
+ */
24
90
  pattern(regex: RegExp, message?: string): BaseValidator;
91
+
92
+ /**
93
+ * Conditional validation
94
+ */
95
+ when(
96
+ condition: boolean | ConditionalFunction,
97
+ validator: BaseValidator | ConditionalValidatorFunction
98
+ ): BaseValidator;
99
+
100
+ /**
101
+ * Custom synchronous validation
102
+ */
103
+ custom(
104
+ validatorFn: CustomValidatorFunction,
105
+ message?: string
106
+ ): BaseValidator;
107
+
108
+ /**
109
+ * Custom asynchronous validation
110
+ */
111
+ customAsync(
112
+ validatorFn: AsyncValidatorFunction,
113
+ message?: string
114
+ ): BaseValidator;
115
+
116
+ /**
117
+ * Execute synchronous validation
118
+ */
25
119
  validate(): ValidationResult;
26
- }
27
120
 
28
- export class ValidationResult {
29
- constructor(isValid: boolean, errors?: string[]);
30
- isValid: boolean;
31
- errors: string[];
32
- addError(message: string): ValidationResult;
121
+ /**
122
+ * Execute asynchronous validation (includes sync rules)
123
+ */
124
+ validateAsync(): Promise<ValidationResult>;
33
125
  }
34
126
 
127
+ /**
128
+ * Predefined validators
129
+ */
35
130
  export interface Validators {
36
131
  email(value: string): BaseValidator;
37
132
  phone(value: string, format?: PhoneFormat): BaseValidator;
@@ -43,18 +138,43 @@ declare module 'mini-validator' {
43
138
  zipCode(value: string, country?: CountryCode): BaseValidator;
44
139
  }
45
140
 
141
+ /**
142
+ * Result of schema validation
143
+ */
46
144
  export interface SchemaValidationResult {
47
145
  isValid: boolean;
48
146
  errors: { [field: string]: ValidationResult };
49
147
  getErrors(): { [field: string]: string[] };
50
148
  }
51
149
 
150
+ /**
151
+ * Validation function type for schema
152
+ */
52
153
  export type ValidationFunction = (value: any) => BaseValidator;
53
- export type Schema = { [field: string]: ValidationFunction };
54
154
 
155
+ /**
156
+ * Schema definition type
157
+ */
158
+ export type Schema = { [field: string]: ValidationFunction | BaseValidator };
159
+
160
+ /**
161
+ * Predefined validator instances
162
+ */
55
163
  export const validators: Validators;
164
+
165
+ /**
166
+ * Validate data against schema synchronously
167
+ */
56
168
  export function validate(
57
169
  schema: Schema,
58
170
  data: { [key: string]: any }
59
171
  ): SchemaValidationResult;
172
+
173
+ /**
174
+ * Validate data against schema asynchronously
175
+ */
176
+ export function validateAsync(
177
+ schema: Schema,
178
+ data: { [key: string]: any }
179
+ ): Promise<SchemaValidationResult>;
60
180
  }