Skip to content

Commit af74d94

Browse files
committed
feat: Logic redesign and simplification
* Added Predicate type * Added Meta type * Added Assert type for combining Predicate and Meta * Added assertions for booleans, email, dates, null, number, string, symbol * Added ValidationRunner type for running complex checks BREAKING CHANGE: validator object replaced with validate function, containing sub method sync for synchronous validation BREAKING CHANGE: Length constraint replaced with HasLength functional assertion BREAKING CHANGE: Exists constraint replaced with IsDefined functional assertion BREAKING CHANGE: Each object replaced with Each runner BREAKING CHANGE: Collection object replaced with HasProperties runner BREAKING CHANGE: Type "Key" replaced with in-box "PropertyKey" BREAKING CHANGE: ConstraintCollection type was removed BREAKING CHANGE: ConstraintValidator was renamed to Validator BREAKING CHANGE: ConstraintViolation was renamed to Violation BREAKING CHANGE: d.ts files for exported logic units are now generated and available in dist catalogue
1 parent dcf5727 commit af74d94

52 files changed

Lines changed: 1904 additions & 1923 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.npmignore

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@
22
.husky
33
.idea
44
coverage
5-
/node_modules
5+
node_modules
66
src
77
tests
88

99
.commitlintrc.json
10-
.eslintrc.js
1110
.gitignore
1211
.npmrc
1312
.versionrc.json
13+
codecov.yml
1414
docker-compose.yml
15-
jest.config.ts
16-
rollup.config.ts
15+
eslint.config.mjs
1716
tsconfig.json
17+
vite.config.basic.ts
18+
vite.config.ts
19+
vitest.config.ts
1820
yarn.lock
1921

2022
Makefile

Makefile

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,7 @@ test: node_modules ## Runs autotests
2929
.PHONY: test-coverage
3030
test-coverage: node_modules ## Runs autotests with --coverage
3131
$(TARGET_HEADER)
32-
ifdef reporter
33-
$(YARN) test --coverage --coverageReporters=$(reporter)
34-
else
35-
$(YARN) test --coverage --coverageReporters=text
36-
endif
32+
$(YARN) test:coverage
3733

3834
.PHONY: release
3935
release: ## Bumps version and creates tag

README.md

Lines changed: 99 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
[![Tests Status](https://github.com/modulify/validator/actions/workflows/tests.yml/badge.svg)](https://github.com/modulify/validator/actions)
55
[![npm version](https://badge.fury.io/js/%40modulify%2Fvalidator.svg)](https://www.npmjs.com/package/@modulify/validator)
66

7-
This library provides a declarative validation util.
7+
This library provides a declarative validation utility.
88

9-
The util does not provide any text messages in the constraints produced and gives only metadata that can
9+
The utility does not include text messages in the generated violations but instead provides metadata that can
1010
be used to create a custom view for them.
1111

1212
## Installation
@@ -27,160 +27,125 @@ npm install @modulify/validator --save
2727

2828
```typescript
2929
import {
30-
Collection,
31-
Exists,
32-
Length,
33-
createValidator,
30+
HasLength,
31+
HasProperties,
32+
IsDefined,
33+
IsString,
34+
validate,
3435
} from '@modulify/validator'
3536

36-
const validator = createValidator()
37-
38-
const violations = validator.validate({
37+
const violations = await validate({
3938
form: {
4039
nickname: '',
4140
password: '',
4241
},
43-
}, new Collection({
42+
}, HasProperties({
4443
form: [
45-
new Exists(),
46-
new Collection({
47-
nickname: new Length({ min: 4 }),
48-
password: new Length({ min: 6 }),
44+
IsDefined(),
45+
HasProperties({
46+
nickname: IsString.That(HasLength({ min: 4 })),
47+
password: IsString.That(HasLength({ min: 6 })),
4948
}),
5049
],
51-
}), /* do not set or set to true for async validation */ false) /* [{
52-
by: '@modulify/validator/Length',
50+
})) /* [{
51+
by: '@modulify/validator/IsString',
5352
value: '',
5453
path: ['form', 'nickname'],
5554
reason: 'min',
5655
meta: 4,
5756
}, {
58-
by: '@modulify/validator/Length',
57+
by: '@modulify/validator/IsString',
5958
value: '',
6059
path: ['form', 'password'],
6160
reason: 'min',
6261
meta: 6,
6362
}] */
6463
```
6564

66-
### Constraints
67-
68-
Constraints provide information of how the value should be validated.
69-
70-
Available from the box:
71-
72-
* `Collection` – used for validating objects' structure;
73-
* `Each` – used for validating arrays' elements; applies specified constraints to each element of an array;
74-
* `Exists` – used for checking if a value is defined; useful for finding missing keys;
75-
* `Length` – used for checking arrays' and string's length, available settings (all optional) are:
76-
* `exact` – `number`, array or string should have exactly specified count of elements or characters;
77-
* `max` – `number`, maximum elements in array or maximum characters in string;
78-
* `min` – `number`, minimum elements in array or minimum characters in string;
79-
* `OneOf` – used for restricting which values can be used.
80-
81-
There is no any basic constraint class to extend, but they should follow signature
82-
described in `types/index.d.ts` – `Constraint`.
83-
84-
### Validators
85-
86-
Validators provide validation logic that relies on information provided by constraints.
87-
88-
There is no any basic validator class to extend, but they should follow signature
89-
described in `types/index.d.ts` – `ConstraintValidator`.
90-
91-
### Provider
92-
93-
Provider is used to bind constraints with their validators, provides a validator for a constraint.
94-
95-
All providers should follow signature described in `types/index.d.ts` – `Provider`.
96-
97-
This feature is responsible for extending validation capabilities. Custom provider can be passed into
98-
`createValidator` function or `override` method of `Validator` instance.
99-
100-
There is a built-in provider – `ProviderChain`. It allows to "chain" providers – if
101-
suitable validator was not found in currently used provider, it will try to find it in previous provider that was
102-
overridden by `override` method.
65+
or (for synchronous validation):
10366

10467
```typescript
105-
import type {
106-
Constraint,
107-
ConstraintValidator,
108-
ConstraintViolation,
109-
Key,
110-
Provider,
111-
} from '@modulify/validator'
112-
113-
import {
114-
ProviderChain,
115-
createValidator,
116-
} from '@modulify/validator'
117-
118-
class Email implements Constraint {
119-
public readonly name = '@app/validator/Email'
120-
121-
toViolation (value: unknown, path: Key[]): ConstraintViolation {
122-
return {
123-
by: this.name,
124-
value,
125-
path,
126-
}
127-
}
128-
}
129-
130-
class EmailValidator implements ConstraintValidator {
131-
private readonly _constraint: Email
132-
133-
constructor (constraint: Email) {
134-
this._constraint = constraint
135-
}
136-
137-
validate (value: unknown, path?: Key[]): ConstraintViolation | null {
138-
if (!(typeof value === 'string') || !/\S+@\S+\.\S+/.test(value)) {
139-
return this._constraint.toViolation(value, path)
140-
}
141-
142-
return null
143-
}
144-
}
145-
```
146-
147-
then
148-
149-
```typescript
150-
const provider = new ProviderChain(new class implements Provider {
151-
get (constraint: Constraint) {
152-
return constraint instanceof Email ? new EmailValidator(constraint) : null
153-
}
154-
155-
override (provider: Provider): Provider {
156-
return new ProviderChain(provider, this)
157-
}
158-
})
159-
```
160-
161-
or
162-
163-
```typescript
164-
const provider = new class implements Provider {
165-
get (constraint: Constraint) {
166-
return constraint instanceof Email ? new EmailValidator(constraint) : null
167-
}
168-
169-
override (provider: Provider): Provider {
170-
return new ProviderChain(provider, this)
171-
}
172-
}
173-
```
174-
175-
and then
176-
177-
```typescript
178-
const validator = createValidator(provider)
68+
const violations = validate.sync({
69+
form: {
70+
nickname: '',
71+
password: '',
72+
},
73+
}, HasProperties({
74+
form: [
75+
IsDefined(),
76+
HasProperties({
77+
nickname: IsString.That(HasLength({ min: 4 })),
78+
password: IsString.That(HasLength({ min: 6 })),
79+
}),
80+
],
81+
}))
17982
```
18083

181-
or
182-
183-
```typescript
184-
const validator = createValidator()
185-
const overridden = validator.override(provider) // it creates new validator instance, so validator !== overridden
186-
```
84+
## Exported types
85+
86+
* `Violation` – an object that contains information about a value – why it violates one or more constraints;
87+
includes following fields:
88+
* `value` – value that violates something;
89+
* `path` – path to the value, an empty array for scalar values and represents full path to the value in a complex
90+
object;
91+
* `violates` – indicator of the violated constraint;
92+
* `reason` – indicator of the reason why the constraint is violated;
93+
* `meta` – some data to describe the reason – what exactly the boundaries were not met;
94+
```typescript
95+
import type { Violation } from '@modulify/validator/types'
96+
```
97+
* `Predicate` – function that accepts a value and returns `true` or `false`; logical unit that is used for checking
98+
multiple things: type or if the value satisfies certain criteria; accepts generic argument `T` to specify
99+
the type of the value, if predicate returns `true`;
100+
```typescript
101+
import type { Predicate } from '@modulify/validator/types'
102+
```
103+
* `Assertion` – extension of the `Predicate` type that includes:
104+
* `fqn` – field – some predefined name that will be used as a value for the `violates` field of `Violation`;
105+
* `bail` – field – flag that interrupts further validation if the assertion fails;
106+
* `reason` – field, optional – string or symbol that is used to indicate, why assertion has failed;
107+
always added to a violation object, if present;
108+
* `meta` – field, optional – some metadata to use in further analysis; always added to a violation object, if present;
109+
* `That` – method – used to extend assertion with other assertions;
110+
* `also` – field – readonly array of other assertions that was attached by `That` method;
111+
```typescript
112+
import type { Assertion } from '@modulify/validator/types'
113+
```
114+
115+
## Exported members
116+
117+
* `validate` – function that accepts a value for validation as the first argument, constraints as the second,
118+
and path to a value as the third (that is optional and used mostly for internal purposes, as validation is recursive);
119+
includes method `sync` that has the same arguments set but performs validation synchronously and throws error when
120+
finds an asynchronous constraint;
121+
122+
* `Assert` – creates assertion from logical predicate:
123+
```typescript
124+
const IsSomething = Assert(isSomething, {
125+
fqn: 'Some fqn',
126+
bail: true,
127+
})
128+
```
129+
Arguments:
130+
* Logical predicate
131+
* Options, that includes `fqn`, `bail`, `reason` (optional), and `meta` (optional);
132+
* `HasLength` – checks length property of the specified string or array; can be configured with options:
133+
* `exact` – if the length should be exactly equal the specified value;
134+
* `max` – if the length should be equal or less than the specified value;
135+
* `min` – if the length should be equal or greater than the specified value;
136+
* `bail` – set this to true if you need to interrupt further validation if the assertion fails;
137+
* `IsBoolean` – checks if the value is **boolean**; interrupts further validation if fails;
138+
* `IsDate` – checks if the value is Date **object**; interrupts further validation if fails;
139+
* `IsDefined` – checks if the value is **not undefined**; interrupts further validation if fails;
140+
* `IsEmail` – checks if the value is a **valid email**; interrupts further validation if fails;
141+
* `IsNull` – checks if the value is **null**; interrupts further validation if fails;
142+
* `IsNumber` – checks if the value is **number**; interrupts further validation if fails;
143+
* `IsString` – checks if the value is **string**; interrupts further validation if fails;
144+
* `IsSymbol` – checks if the value is a **symbol**; interrupts further validation if fails;
145+
* `OneOf` – checks if the value equal to one of the specified values; can be configured with:
146+
* `equalTo` – predicate f(a, b) that checks if two values are equal or not;
147+
by default the strict `===` comparison is used
148+
* `bail` – set this to true if you need to interrupt further validation if the assertion fails;
149+
150+
* `Each` – a runner that runs validation for each element in array;
151+
* `HasProperties` – a runner that runs object's structure check.

docker-compose.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: '3.8'
2-
31
services:
42
node:
53
image: node:20

0 commit comments

Comments
 (0)