check-rule-mate 0.4.3 → 0.5.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.
- package/README.md +226 -84
- package/dist/main.cjs.js +1 -1
- package/package.json +15 -3
- package/src/index.d.ts +128 -0
- package/src/js/dataValidate.js +79 -24
- package/src/main.js +2 -2
- package/src/types.js +27 -8
package/README.md
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
# check-rule-mate
|
|
2
|
-
|
|
2
|
+
Rule-based, extensible and async-friendly data validation engine for complex forms and business rules.
|
|
3
3
|
|
|
4
4
|
## Overview
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
**check-rule-mate** is a lightweight **rule-driven validation engine** for JavaScript.
|
|
7
|
+
Instead of coupling validation logic directly to schemas or fields, check-rule-mate separates concerns into **rules**, **schemas**, **validators**, and **error messages**, allowing you to build **highly reusable**, **composable**, and **context-aware validations**.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
It is designed for scenarios where traditional schema-based validators start to feel limiting, especially when you need:
|
|
10
|
+
|
|
11
|
+
- Cross-field validation
|
|
12
|
+
- Contextual rules
|
|
13
|
+
- Async checks
|
|
14
|
+
- Reusable validation logic across multiple forms
|
|
15
|
+
- Full control over execution flow and error handling
|
|
9
16
|
|
|
10
17
|
|
|
11
18
|
**Github repository:** [check-rule-mate repository](https://github.com/johnrock16/check-rule-mate)
|
|
@@ -15,49 +22,76 @@ The core goal is to provide a **reusable and easy-to-extend** for handling vario
|
|
|
15
22
|
**Test the core functionalities here:** [check-rule-mate demo](https://johnrock16.github.io/check-rule-mate/)
|
|
16
23
|
(Note: Creating or modifying custom validators is not supported in the demo, as it requires JavaScript implementation.)
|
|
17
24
|
|
|
25
|
+
## Why check-rule-mate?
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
Use **check-rule-mate** if you:
|
|
28
|
+
- Need reusable validation logic across different forms and contexts
|
|
29
|
+
- Have complex or conditional validation rules
|
|
30
|
+
- Want full control over how validation runs
|
|
31
|
+
- Need async validations (API calls, database checks, etc.)
|
|
32
|
+
- Prefer rule-driven validation instead of tightly coupled schemas
|
|
33
|
+
- Want clean separation between rules, data, and messages
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Core Concepts
|
|
20
37
|
|
|
21
|
-
-
|
|
22
|
-
- **Modular Design**: Separation of rule definitions and error messages for easy management.
|
|
23
|
-
- **Easy Integration**: Can be used in any JavaScript environment.
|
|
24
|
-
- **Clear Error Handling**: Handles errors and displays messages.
|
|
25
|
-
- **Extendable**: Create your custom validators and rules and extend as you want.
|
|
38
|
+
check-rule-mate is built around four main concepts:
|
|
26
39
|
|
|
27
|
-
|
|
40
|
+
1. **Rules**: How data is validated
|
|
41
|
+
2. **Schema**: What should be validated
|
|
42
|
+
3. **Validation Helpers**: How rules are executed
|
|
43
|
+
4. **Error Messages**: How errors are communicated
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
45
|
+
This separation makes the system flexible, scalable, and easy to maintain.
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- Rule-based validation engine
|
|
50
|
+
- Reusable validation rules
|
|
51
|
+
- Modifiers for contextual rule extensions
|
|
52
|
+
- Cross-field validation using dynamic parameters
|
|
53
|
+
- Async validation support
|
|
54
|
+
- Abort early or collect all errors
|
|
55
|
+
- Strict or loose schema matching
|
|
56
|
+
- i18n-ready error messages
|
|
57
|
+
- Framework-agnostic (frontend or backend)
|
|
33
58
|
|
|
34
59
|
## Table of Contents
|
|
35
60
|
|
|
36
61
|
- [Getting Started](#Getting-Started)
|
|
37
|
-
- [Installation](#Installation)
|
|
38
|
-
- [
|
|
62
|
+
- [NPM - Installation](#Installation)
|
|
63
|
+
- [Repository - Installation](#Repository---Installation)
|
|
64
|
+
- [Running Tests](#Repository---Running-Tests)
|
|
39
65
|
- [How It Works](#How-It-Works)
|
|
40
|
-
- [Basic
|
|
41
|
-
- [Defining Validation
|
|
42
|
-
- [1.
|
|
43
|
-
- [2.
|
|
44
|
-
- [3. Validation Helpers](#Validation-Helpers)
|
|
45
|
-
- [4. Error Messages](#Error-Messages)
|
|
66
|
+
- [Basic Usage](#Basic-Usage)
|
|
67
|
+
- [Defining Validation](#Defining-Validation)
|
|
68
|
+
- [1. Schema](#Defining-a-Schema-What-to-validate)
|
|
69
|
+
- [2. Rules](#Defining-Rules-How-to-validate)
|
|
70
|
+
- [3. Validation Helpers](#Validation-Helpers-Execution-Layer)
|
|
71
|
+
- [4. Error Messages](#Error-Messages-i18n-ready)
|
|
46
72
|
- [5. Example Usage](#Example-Usage)
|
|
47
73
|
- [Vanilla](#vanilla)
|
|
48
74
|
- [Express](#express)
|
|
49
75
|
- [Frontend](#frontend)
|
|
76
|
+
- [When NOT to use check-rule-mate](#When-NOT-to-use-check-rule-mate)
|
|
77
|
+
- [License](#License);
|
|
50
78
|
|
|
51
79
|
## Getting Started
|
|
80
|
+
|
|
52
81
|
### Installation
|
|
82
|
+
If you are in NPM use this:
|
|
83
|
+
```bash
|
|
84
|
+
npm install check-rule-mate
|
|
85
|
+
```
|
|
53
86
|
|
|
54
|
-
|
|
87
|
+
### Repository - Installation
|
|
88
|
+
If you downloaded the repository you can install using:
|
|
55
89
|
```bash
|
|
56
90
|
npm install
|
|
57
91
|
npm start
|
|
58
92
|
```
|
|
59
93
|
|
|
60
|
-
### Running Tests
|
|
94
|
+
### Repository - Running Tests
|
|
61
95
|
|
|
62
96
|
Execute the test suite with:
|
|
63
97
|
```bash
|
|
@@ -65,69 +99,108 @@ npm test
|
|
|
65
99
|
```
|
|
66
100
|
|
|
67
101
|
## How It Works
|
|
68
|
-
### Basic
|
|
102
|
+
### Basic Usage
|
|
69
103
|
|
|
70
|
-
Here’s an example of validating a set of fields:
|
|
71
104
|
```javascript
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
105
|
+
import MY_RULES from './rules/myValidatorRules.json' with { type: 'json' };
|
|
106
|
+
import CONTACT_US from './schemas/contactUs.json' with { type: 'json' };
|
|
107
|
+
import ERROR_MESSAGES from './i18n/en_US/errors.json' with { type: 'json' };
|
|
108
|
+
|
|
109
|
+
import { createValidator } from 'check-rule-mate';
|
|
110
|
+
import { myValidator } from './validators/myValidator.js';
|
|
111
|
+
|
|
112
|
+
const fields = {
|
|
113
|
+
name: 'John',
|
|
114
|
+
lastName: 'Doe',
|
|
115
|
+
email: 'email@email.com',
|
|
116
|
+
emailConfirm: 'email@email.com',
|
|
117
|
+
phone: '',
|
|
118
|
+
subject: 'I need a coffee',
|
|
119
|
+
message: 'Give me coffee'
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
async function runFormValidate() {
|
|
123
|
+
const validator = createValidator(fields, {
|
|
124
|
+
validationHelpers: myValidator,
|
|
125
|
+
rules: MY_RULES,
|
|
126
|
+
schema: CONTACT_US,
|
|
127
|
+
errorMessages: ERROR_MESSAGES,
|
|
128
|
+
options: {
|
|
129
|
+
cache: true,
|
|
130
|
+
abortEarly: false,
|
|
131
|
+
propertiesMustMatch: true,
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const result = await validator.validate();
|
|
99
136
|
|
|
100
|
-
|
|
137
|
+
// This should return { ok: true }
|
|
138
|
+
console.log(result);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
runFormValidate();
|
|
101
142
|
```
|
|
102
143
|
|
|
103
|
-
|
|
144
|
+
### Validation Result
|
|
145
|
+
A validation result follows this structure:
|
|
146
|
+
|
|
147
|
+
When is **valid**:
|
|
148
|
+
```javascript
|
|
149
|
+
{ ok: true }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
When is **invalid** and **has errors**:
|
|
153
|
+
```typescript
|
|
154
|
+
{
|
|
155
|
+
error: true,
|
|
156
|
+
errors: {
|
|
157
|
+
[field: string]: {
|
|
158
|
+
name: string,
|
|
159
|
+
type: string,
|
|
160
|
+
message: string,
|
|
161
|
+
code: string,
|
|
162
|
+
}[];
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
```
|
|
104
166
|
|
|
105
|
-
- **fields**: The object containing data to be validated.
|
|
106
|
-
- **validationHelpers**: Functions to validate field data (see /dataValidator/validators).
|
|
107
|
-
- **rules**: General validation rules for your application.
|
|
108
|
-
- **dataRule**: Specific rules linking fields to validation logic.
|
|
109
|
-
- **dataErrorMessages**: Custom error messages returned upon validation failure.
|
|
110
167
|
|
|
111
|
-
## Defining Validation
|
|
168
|
+
## Defining Validation
|
|
112
169
|
|
|
113
|
-
###
|
|
170
|
+
### Defining a Schema (What to validate)
|
|
114
171
|
|
|
115
|
-
|
|
172
|
+
Schemas map **data fields** to **rules**.
|
|
116
173
|
```json
|
|
117
174
|
{
|
|
118
|
-
"name": {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
|
|
175
|
+
"name": {
|
|
176
|
+
"rule": "name",
|
|
177
|
+
"required": true
|
|
178
|
+
},
|
|
179
|
+
"email": {
|
|
180
|
+
"rule": "email",
|
|
181
|
+
"required": true,
|
|
182
|
+
"cache": false,
|
|
183
|
+
},
|
|
184
|
+
"emailConfirm": {
|
|
185
|
+
"rule": "email--confirm",
|
|
186
|
+
"required": true,
|
|
187
|
+
"cache": false,
|
|
188
|
+
},
|
|
189
|
+
"phone": {
|
|
190
|
+
"rule": "phone",
|
|
191
|
+
"required": false
|
|
192
|
+
}
|
|
125
193
|
}
|
|
126
194
|
```
|
|
127
195
|
|
|
128
|
-
|
|
196
|
+
#### Schema Properties
|
|
197
|
+
- **rule**: Rule name (supports modifiers via `rule--modifier`)
|
|
198
|
+
- **required**: Whether the field must exist and not be empty
|
|
199
|
+
- **cache**: if this field will have cache or not
|
|
200
|
+
|
|
201
|
+
### Defining Rules (How to validate)
|
|
129
202
|
|
|
130
|
-
|
|
203
|
+
Rules define validation logic, independent of any specific form.
|
|
131
204
|
|
|
132
205
|
```json
|
|
133
206
|
{
|
|
@@ -169,17 +242,53 @@ Define reusable validation logic. Example:
|
|
|
169
242
|
}
|
|
170
243
|
}
|
|
171
244
|
```
|
|
172
|
-
#### Key Components:
|
|
173
245
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
- **
|
|
177
|
-
- **
|
|
178
|
-
- **
|
|
246
|
+
#### Rule Properties
|
|
247
|
+
|
|
248
|
+
- **validate**: Ordered list of validation functions
|
|
249
|
+
- **regex**: Optional regex used by the regex helper
|
|
250
|
+
- **error**: Error keys mapped to validation functions
|
|
251
|
+
- **modifier**: Contextual rule extensions
|
|
252
|
+
- **params**: Parameters passed to validation helpers
|
|
253
|
+
|
|
254
|
+
#### Modifiers (Contextual Rules)
|
|
255
|
+
Modifiers allow extending a rule **without duplicating logic**.
|
|
256
|
+
|
|
257
|
+
Example:
|
|
258
|
+
```json
|
|
259
|
+
"email--confirm"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Internally:
|
|
263
|
+
|
|
264
|
+
- Base rule: email
|
|
265
|
+
- Modifier: confirm
|
|
266
|
+
|
|
267
|
+
Modifiers can override:
|
|
268
|
+
|
|
269
|
+
- validate
|
|
270
|
+
- params
|
|
271
|
+
- regex
|
|
272
|
+
- error
|
|
273
|
+
|
|
274
|
+
This makes rules highly reusable and expressive.
|
|
275
|
+
|
|
276
|
+
#### Dynamic Parameters ($field)
|
|
277
|
+
Rules can reference other fields dynamically:
|
|
278
|
+
|
|
279
|
+
```json
|
|
280
|
+
"params": {
|
|
281
|
+
"equals": ["$email"]
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
At runtime:
|
|
286
|
+
- `$email` resolves to `data.email`
|
|
287
|
+
- Enables **cross-field validation**
|
|
179
288
|
|
|
180
289
|
|
|
181
|
-
### Validation Helpers
|
|
182
|
-
|
|
290
|
+
### Validation Helpers (Execution Layer)
|
|
291
|
+
Validation helpers are the **runtime implementation** of rules.
|
|
183
292
|
|
|
184
293
|
```javascript
|
|
185
294
|
const myValidator = function (value, rule, modifier = null, data = null) {
|
|
@@ -219,13 +328,37 @@ const myValidator = function (value, rule, modifier = null, data = null) {
|
|
|
219
328
|
};
|
|
220
329
|
```
|
|
221
330
|
|
|
222
|
-
|
|
331
|
+
#### Helper Signature
|
|
332
|
+
```typescript
|
|
333
|
+
(value, rule, modifier?, data?) => Record<string, Function>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Helpers:
|
|
337
|
+
- Can be **sync or async**
|
|
338
|
+
- Are **stateless**
|
|
339
|
+
- Do not know about schemas or error messages
|
|
223
340
|
|
|
224
|
-
|
|
341
|
+
### Error Messages (i18n-ready)
|
|
342
|
+
Errors are resolved via keys, not hardcoded strings.
|
|
225
343
|
``` json
|
|
226
344
|
{
|
|
227
|
-
"common": {
|
|
228
|
-
|
|
345
|
+
"common": {
|
|
346
|
+
"hasText": "Please fill the field"
|
|
347
|
+
},
|
|
348
|
+
"email": {
|
|
349
|
+
"regex": "Please enter a valid email",
|
|
350
|
+
"equals": "Emails do not match"
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
This makes localization and message customization straightforward.
|
|
355
|
+
|
|
356
|
+
### Validation Options
|
|
357
|
+
```typescript
|
|
358
|
+
options: {
|
|
359
|
+
cache?: boolean, // If cache is enabled or not
|
|
360
|
+
abortEarly?: boolean, // Stop on first error
|
|
361
|
+
propertiesMustMatch?: boolean, // Schema vs data strictness
|
|
229
362
|
}
|
|
230
363
|
```
|
|
231
364
|
|
|
@@ -270,3 +403,12 @@ npm run example:express
|
|
|
270
403
|
Here you can found the DEMO page and it's a type of "playground" to test how RULES works and validations works. (Here you can't create customized javascript so custom validatorHelpers are disabled by default)
|
|
271
404
|
|
|
272
405
|
**Frontend example:** [check-rule-mate demo.](https://johnrock16.github.io/check-rule-mate/)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
## When NOT to use check-rule-mate
|
|
409
|
+
- Simple one-off forms
|
|
410
|
+
- Basic required-only validation
|
|
411
|
+
- When schema-based validation is enough
|
|
412
|
+
|
|
413
|
+
## License
|
|
414
|
+
- ISC
|
package/dist/main.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var V=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var U=(r,n)=>{for(var u in n)V(r,u,{get:n[u],enumerable:!0})},F=(r,n,u,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let y of D(n))!T.call(r,y)&&y!==u&&V(r,y,{get:()=>n[y],enumerable:!(i=j(n,y))||i.enumerable});return r};var L=r=>F(V({},"__esModule",{value:!0}),r);var x={};U(x,{createValidator:()=>E});module.exports=L(x);var m={propertiesMustMatch:!0,abortEarly:!1,cache:!0};function E(r,{validationHelpers:n={},rules:u,schema:i,errorMessages:y={},options:f=m}){f={...m,...f},r=r;let k={},a={},p={all:async e=>Promise.all([...e].map(async s=>await v(s,r))),first:async e=>{let s=[];for(let l of e){let c=await v(l,r);if(s.push(c),!c)return s}return s}};function g(e,s){if(!e||typeof s!="string")return;let l=s.split("."),c=e;for(let d of l){if(c[d]===void 0)return;c=c[d]}return c}async function v(e,s=null){var l,c;if(i[e.key]){let{rule:d,required:t}=i[e.key];if((((l=i[e.key])==null?void 0:l.cache)!==void 0?i[e.key].cache:f.cache)&&s[e.key]===((c=a[e.key])==null?void 0:c.value))return a[e.key].isValid;if((d&&t||!t&&e.value!="")&&d){let O=d.split("--")[0],q=d.split("--").length>1?d.split("--")[1]:"",P=_(e.value,u[O],q,n,s),{isValid:w,errorMessage:R,errorType:I}=await P.validate();return w||(k[e.key]={name:e.key,code:R,type:I,message:g(y,R)||""}),a[e.key]={isValid:w,value:s[e.key]},w}}else if(f.propertiesMustMatch)return k[e.key]={name:e.key,message:"Invalid property"},!1;return!0}async function M(e){return await v({key:e,value:r[e]},r)?{ok:!0}:{error:!0,errors:k[e]}}async function o(){k={};let e=Object.keys(r).map(s=>({key:s,value:r[s]}));if(e&&e.length>0){if(!Object.keys(i).every(t=>r.hasOwnProperty(t)))return{error:!0,errorMessage:"Missing properties"};if((f!=null&&f.abortEarly?await p.first(e):await p.all(e)).some(t=>!t))return{error:!0,errors:k};let l=Object.keys(i).map(t=>({key:t,required:i[t].required})),c=e.map(t=>t.key);if(!l.filter(t=>t.required).map(t=>t.key).every(t=>c.includes(t)))return{error:!0}}else if(!e||e.length===0)return{error:!0,errorMessage:"Missing fields for schema"};return{ok:!0}}function h(e){r=e}return{validate:o,validateField:M,setData:h}}function _(r,n,u=null,i=null,y=null){async function f(a){let p,g;return{isValid:!(await Promise.all(a.validate.map(async o=>{let h=!0;if(a.params&&a.params[o]&&a.params[o].length>0){let e=a.params[o].map(l=>typeof l=="string"&&l[0]==="$"?l.substring(1,l.length):l);h=await this[o](...e)}else h=await this[o]();return!h&&!p&&(a!=null&&a.error[o])&&(p=a.error[o],g=o),h}))).some(o=>!o),errorMessage:p,errorType:g}}async function k(){if(i&&typeof i=="function"){let p=i(r,n,u,y);Object.keys(p).forEach(g=>{this[g]=p[g]})}return u?await f.call(this,n.modifier[u]):await f.call(this,n)}return{validate:k}}0&&(module.exports={createValidator});
|
package/package.json
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "check-rule-mate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/main.cjs.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
|
-
"
|
|
7
|
+
"types": "index.d.ts",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"data validation",
|
|
10
|
+
"data validation js",
|
|
11
|
+
"validator",
|
|
12
|
+
"validate",
|
|
13
|
+
"form validator",
|
|
14
|
+
"form validator js",
|
|
15
|
+
"form validate",
|
|
16
|
+
"check-rule-mate",
|
|
17
|
+
"check rule",
|
|
18
|
+
"check rule mate"
|
|
19
|
+
],
|
|
8
20
|
"scripts": {
|
|
9
21
|
"start": "node ./examples/vanilla/src/index.js",
|
|
10
22
|
"build": "node build",
|
|
@@ -15,7 +27,7 @@
|
|
|
15
27
|
"author": "João Rocha",
|
|
16
28
|
"license": "ISC",
|
|
17
29
|
"devDependencies": {
|
|
18
|
-
"esbuild": "^0.
|
|
30
|
+
"esbuild": "^0.27.2",
|
|
19
31
|
"express": "^4.21.2",
|
|
20
32
|
"jest": "^29.7.0"
|
|
21
33
|
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file contains all type definitions used in the project.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a single data field.
|
|
7
|
+
*/
|
|
8
|
+
export interface DataField {
|
|
9
|
+
/** The name and key of the field */
|
|
10
|
+
name: string
|
|
11
|
+
|
|
12
|
+
/** The value of the field */
|
|
13
|
+
value: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Options to control validator behavior.
|
|
18
|
+
*/
|
|
19
|
+
export interface ValidatorOptions {
|
|
20
|
+
/** If the form fields don't match the expected structure, triggers an error */
|
|
21
|
+
propertiesMustMatch: boolean
|
|
22
|
+
|
|
23
|
+
/** Stops validation when the first error is caught */
|
|
24
|
+
abortEarly: boolean
|
|
25
|
+
|
|
26
|
+
/** Defines if the schema will use cache by default */
|
|
27
|
+
cache: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A single validation helper function.
|
|
32
|
+
*/
|
|
33
|
+
export type ValidatorHelper = (
|
|
34
|
+
value: any,
|
|
35
|
+
rule: string,
|
|
36
|
+
modifier: string,
|
|
37
|
+
data: DataField[]
|
|
38
|
+
) => boolean
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A map of validation helper functions.
|
|
42
|
+
*/
|
|
43
|
+
export type ValidationHelpers = Record<string, ValidatorHelper>
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Schema rule for a single field.
|
|
47
|
+
*/
|
|
48
|
+
export interface SchemaRuleField {
|
|
49
|
+
/** The validation rule for the field (e.g., "name", "email", "phone", "hasText") */
|
|
50
|
+
rule: string
|
|
51
|
+
|
|
52
|
+
/** Indicates whether the field is required */
|
|
53
|
+
required: boolean
|
|
54
|
+
|
|
55
|
+
/** Indicates if the field requires cache or not */
|
|
56
|
+
cache: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Schema rule object.
|
|
61
|
+
*/
|
|
62
|
+
export interface SchemaRule {
|
|
63
|
+
field: SchemaRuleField
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Validation schema mapping field names to rules.
|
|
68
|
+
*/
|
|
69
|
+
export type SchemaRules = Record<string, SchemaRule>
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validator configuration object.
|
|
73
|
+
*/
|
|
74
|
+
export interface DataValidatorConfigs {
|
|
75
|
+
/** The validator functions to help your validations */
|
|
76
|
+
validationHelpers: ValidationHelpers
|
|
77
|
+
|
|
78
|
+
/** The rules you want to use through validation */
|
|
79
|
+
rules: object
|
|
80
|
+
|
|
81
|
+
/** The rules you want to use per field */
|
|
82
|
+
schema: SchemaRules
|
|
83
|
+
|
|
84
|
+
/** The error messages you want to show during errors */
|
|
85
|
+
errorMessages: object
|
|
86
|
+
|
|
87
|
+
/** Validator options */
|
|
88
|
+
options: ValidatorOptions
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Represents a successful response.
|
|
93
|
+
*/
|
|
94
|
+
export interface DataValidatorSuccessResponse {
|
|
95
|
+
/** Indicates the operation was successful */
|
|
96
|
+
ok: boolean
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Error object.
|
|
101
|
+
*/
|
|
102
|
+
export interface CheckError {
|
|
103
|
+
/** Field name */
|
|
104
|
+
name: string
|
|
105
|
+
|
|
106
|
+
/** Error path */
|
|
107
|
+
code?: string
|
|
108
|
+
|
|
109
|
+
/** Error type */
|
|
110
|
+
type?: string
|
|
111
|
+
|
|
112
|
+
/** Error message */
|
|
113
|
+
message?: string
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Represents an error response.
|
|
118
|
+
*/
|
|
119
|
+
export interface DataValidatorErrorResponse {
|
|
120
|
+
/** Indicates an error occurred */
|
|
121
|
+
error: boolean
|
|
122
|
+
|
|
123
|
+
/** A message describing the error */
|
|
124
|
+
errorMessage?: string
|
|
125
|
+
|
|
126
|
+
/** Additional error details */
|
|
127
|
+
errors?: Record<string, CheckError>
|
|
128
|
+
}
|
package/src/js/dataValidate.js
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
|
+
const DEFAUL_OPTIONS = { propertiesMustMatch: true, abortEarly: false, cache: true };
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Validate your data fields using your rules, data rules and validators.
|
|
3
5
|
* @param {[DataField]} data - All the data fields to be validate
|
|
4
6
|
* @param {DataValidatorConfigs} config - The configs which will be followed during validation
|
|
5
|
-
* @returns {DataValidatorSuccessResponse | DataValidatorErrorResponse} - The response of your validation
|
|
6
7
|
*/
|
|
7
|
-
export function
|
|
8
|
-
|
|
8
|
+
export function createValidator(data, {validationHelpers = {}, rules, schema, errorMessages = {}, options = DEFAUL_OPTIONS}) {
|
|
9
|
+
options = { ...DEFAUL_OPTIONS, ...options};
|
|
10
|
+
data = data;
|
|
11
|
+
let errors = {};
|
|
12
|
+
let oldData = {};
|
|
13
|
+
|
|
14
|
+
const validateByStrategies = {
|
|
15
|
+
all: async (dataArr) => Promise.all([...dataArr].map(async (input) => await inputValidation(input, data))),
|
|
16
|
+
first: async (dataArr) => {
|
|
17
|
+
const results = []
|
|
18
|
+
for (const input of dataArr) {
|
|
19
|
+
const result = await inputValidation(input, data);
|
|
20
|
+
results.push(result);
|
|
21
|
+
if (!result) {
|
|
22
|
+
return results
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return results;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
9
28
|
|
|
10
29
|
function getObjectValueByPath(obj, path) {
|
|
11
30
|
if (!obj || typeof path !== 'string') return undefined;
|
|
@@ -22,41 +41,73 @@ export function dataValidate(data, {validationHelpers = {}, rules, dataRule, dat
|
|
|
22
41
|
}
|
|
23
42
|
|
|
24
43
|
async function inputValidation(dataAttribute, data = null) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
if (schema[dataAttribute.key]) {
|
|
45
|
+
const { rule, required } = schema[dataAttribute.key];
|
|
46
|
+
const cacheEnabled = schema[dataAttribute.key]?.cache !== undefined ? schema[dataAttribute.key].cache : options.cache;
|
|
47
|
+
|
|
48
|
+
if (cacheEnabled && data[dataAttribute.key] === oldData[dataAttribute.key]?.value) {
|
|
49
|
+
return oldData[dataAttribute.key].isValid;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if ((rule && required) || (!required && dataAttribute.value != '')) {
|
|
53
|
+
if (rule) {
|
|
54
|
+
const INPUT_RULE = rule.split('--')[0];
|
|
55
|
+
const RULE_MODIFIER = rule.split('--').length > 1 ? rule.split('--')[1] : '';
|
|
56
|
+
const dataAttributeValidation = dataAttributeValidator(dataAttribute.value, rules[INPUT_RULE], RULE_MODIFIER, validationHelpers, data);
|
|
57
|
+
const { isValid, errorMessage, errorType } = await dataAttributeValidation.validate();
|
|
58
|
+
if (!isValid) {
|
|
59
|
+
errors[dataAttribute.key] = {
|
|
60
|
+
name: dataAttribute.key,
|
|
61
|
+
code: errorMessage,
|
|
62
|
+
type: errorType,
|
|
63
|
+
message: getObjectValueByPath(errorMessages, errorMessage) || ''
|
|
64
|
+
}
|
|
39
65
|
}
|
|
66
|
+
oldData[dataAttribute.key] = {isValid: isValid, value: data[dataAttribute.key]};
|
|
67
|
+
return isValid;
|
|
40
68
|
}
|
|
41
|
-
return isValid;
|
|
42
69
|
}
|
|
70
|
+
} else if (options.propertiesMustMatch) {
|
|
71
|
+
errors[dataAttribute.key] = {
|
|
72
|
+
name: dataAttribute.key,
|
|
73
|
+
message: "Invalid property"
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
43
76
|
}
|
|
44
77
|
return true;
|
|
45
78
|
}
|
|
46
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Validate only a field using the attribute key
|
|
82
|
+
* @param {string} key - The field key name you want to validate
|
|
83
|
+
* @returns {DataValidatorSuccessResponse | DataValidatorErrorResponse} - The response of your validation
|
|
84
|
+
*/
|
|
85
|
+
async function validateField(key) {
|
|
86
|
+
const result = await inputValidation({key: key, value: data[key]}, data);
|
|
87
|
+
if (!result) {
|
|
88
|
+
return { error: true, errors: errors[key]}
|
|
89
|
+
}
|
|
90
|
+
return { ok: true}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validate the entire fields using the schema
|
|
95
|
+
* @returns {DataValidatorSuccessResponse | DataValidatorErrorResponse} - The response of your validation
|
|
96
|
+
*/
|
|
47
97
|
async function validate() {
|
|
98
|
+
errors = {};
|
|
48
99
|
let dataArr = Object.keys(data).map((key) => ({ key, value: data[key] }));
|
|
49
100
|
if (dataArr && dataArr.length > 0) {
|
|
50
|
-
if(!Object.keys(
|
|
101
|
+
if(!Object.keys(schema).every((key) => data.hasOwnProperty(key))) {
|
|
51
102
|
return { error: true, errorMessage: "Missing properties"}
|
|
52
103
|
}
|
|
53
|
-
const dataValidators = await
|
|
104
|
+
const dataValidators = options?.abortEarly ? await validateByStrategies.first(dataArr) : await validateByStrategies.all(dataArr);
|
|
54
105
|
|
|
55
106
|
if (dataValidators.some((element) => !element)) {
|
|
56
|
-
return { error: true,
|
|
107
|
+
return { error: true, errors: errors };
|
|
57
108
|
}
|
|
58
109
|
|
|
59
|
-
const dataRuleArr = Object.keys(
|
|
110
|
+
const dataRuleArr = Object.keys(schema).map((key) => ({ key, required: schema[key].required}));
|
|
60
111
|
const dataAttributesKey = dataArr.map((attribute) => attribute.key);
|
|
61
112
|
const dataAttributesRequired = dataRuleArr.filter((rule) => rule.required).map((rule) => rule.key);
|
|
62
113
|
|
|
@@ -64,12 +115,16 @@ export function dataValidate(data, {validationHelpers = {}, rules, dataRule, dat
|
|
|
64
115
|
return { error: true };
|
|
65
116
|
}
|
|
66
117
|
} else if (!dataArr || dataArr.length === 0) {
|
|
67
|
-
return { error: true, errorMessage: "Missing fields for
|
|
118
|
+
return { error: true, errorMessage: "Missing fields for schema"}
|
|
68
119
|
}
|
|
69
120
|
return { ok: true };
|
|
70
121
|
}
|
|
71
122
|
|
|
72
|
-
|
|
123
|
+
function setData(newData) {
|
|
124
|
+
data = newData;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {validate, validateField, setData};
|
|
73
128
|
}
|
|
74
129
|
|
|
75
130
|
/**
|
package/src/main.js
CHANGED
package/src/types.js
CHANGED
|
@@ -6,15 +6,23 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* @typedef {Object} DataField
|
|
8
8
|
* @property {string} name - The name and key of the field
|
|
9
|
-
* @property {
|
|
9
|
+
* @property {any} value - the value of the field
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @typedef {Object} DataValidatorConfigs
|
|
14
14
|
* @property {ValidationHelpers} validationHelpers - The validator functions to help your validations
|
|
15
15
|
* @property {Object} rules - The rules you want to use through validation
|
|
16
|
-
* @property {
|
|
17
|
-
* @property {Object}
|
|
16
|
+
* @property {SchemaRule} schema - The rules you want to use per field
|
|
17
|
+
* @property {Object} errorMessages - The error messages you want to show during errors
|
|
18
|
+
* @property {ValidatorOptions} options - Options
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} ValidatorOptions
|
|
23
|
+
* @property {boolean} propertiesMustMatch - If the form fields doesn't match with the expected structure will triggers an error
|
|
24
|
+
* @property {boolean} abortEarly - Stops when caughts the first error
|
|
25
|
+
* @property {boolean} cache - Defines if the schema will uses cache as default or not
|
|
18
26
|
*/
|
|
19
27
|
|
|
20
28
|
/**
|
|
@@ -31,18 +39,19 @@
|
|
|
31
39
|
*/
|
|
32
40
|
|
|
33
41
|
/**
|
|
34
|
-
* @typedef {Object}
|
|
35
|
-
* @property {
|
|
42
|
+
* @typedef {Object} SchemaRule
|
|
43
|
+
* @property {SchemaRuleField} field - The field which will use the rule
|
|
36
44
|
*/
|
|
37
45
|
|
|
38
46
|
/**
|
|
39
|
-
* @typedef {Object}
|
|
47
|
+
* @typedef {Object} SchemaRuleField
|
|
40
48
|
* @property {string} rule - The validation rule for the field (e.g., "name", "email", "phone", "hasText").
|
|
41
49
|
* @property {boolean} required - Indicates whether the field is required.
|
|
50
|
+
* @property {boolean} cache - Indicates if the field requires cache or not
|
|
42
51
|
*/
|
|
43
52
|
|
|
44
53
|
/**
|
|
45
|
-
* @typedef {Object.<string,
|
|
54
|
+
* @typedef {Object.<string, SchemaRule>} SchemaRule - A dynamic object where the keys are field names and the values define the field rules.
|
|
46
55
|
*/
|
|
47
56
|
|
|
48
57
|
/**
|
|
@@ -58,5 +67,15 @@
|
|
|
58
67
|
* @typedef {Object} DataValidatorErrorResponse
|
|
59
68
|
* @property {boolean} error - Indicates an error occurred.
|
|
60
69
|
* @property {string} [errorMessage] - A message describing the error (optional).
|
|
61
|
-
* @property {Object} [
|
|
70
|
+
* @property {Object.<string, CheckError>} [errors] - Additional error details (optional).
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Error Object
|
|
75
|
+
*
|
|
76
|
+
* @typedef {Object} CheckError
|
|
77
|
+
* @property {string} name - Field name
|
|
78
|
+
* @property {string} code - Error path (optional)
|
|
79
|
+
* @property {string} type - Error type (optional)
|
|
80
|
+
* @property {string} message - Error message (optional)
|
|
62
81
|
*/
|