@modulify/validator 0.0.1 → 0.1.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/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [0.1.0](https://github.com/modulify/validator/compare/v0.0.2...v0.1.0) (2024-12-22)
6
+
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * validator object replaced with validate function, containing sub method sync for synchronous validation
11
+ * Length constraint replaced with HasLength functional assertion
12
+ * Exists constraint replaced with IsDefined functional assertion
13
+ * Each object replaced with Each runner
14
+ * Collection object replaced with HasProperties runner
15
+ * Type "Key" replaced with in-box "PropertyKey"
16
+ * ConstraintCollection type was removed
17
+ * ConstraintValidator was renamed to Validator
18
+ * ConstraintViolation was renamed to Violation
19
+ * d.ts files for exported logic units are now generated and available in dist catalogue
20
+
21
+ ### Features
22
+
23
+ * Logic redesign and simplification ([b829a1e](https://github.com/modulify/validator/commit/b829a1eb0373cc6069451eb1cdf4767accbe2ee3))
24
+
25
+
26
+ ### Fixes
27
+
28
+ * Removed sourcemap ([7e469d0](https://github.com/modulify/validator/commit/7e469d0ca60d854c62a79c54e9b3dba1d3b72657))
29
+ * test coverage ([84912c4](https://github.com/modulify/validator/commit/84912c4a33d58b385a1e0c764c57b3806d658613))
30
+
31
+ ### [0.0.2](https://github.com/modulify/validator/compare/v0.0.1...v0.0.2) (2024-02-05)
32
+
5
33
  ### 0.0.1 (2024-02-05)
6
34
 
7
35
 
package/README.md CHANGED
@@ -1,52 +1,60 @@
1
- # `@modulify/validator`
1
+ # <img src="./logo.png" alt="Logo" width="36" /> `@modulify/validator`
2
2
 
3
3
  [![codecov](https://codecov.io/gh/modulify/validator/branch/main/graph/badge.svg)](https://codecov.io/gh/modulify/validator)
4
4
  [![Tests Status](https://github.com/modulify/validator/actions/workflows/tests.yml/badge.svg)](https://github.com/modulify/validator/actions)
5
5
  [![npm version](https://badge.fury.io/js/%40modulify%2Fvalidator.svg)](https://www.npmjs.com/package/@modulify/validator)
6
6
 
7
+ This library provides a declarative validation utility.
7
8
 
8
- This library provides a declarative validation util.
9
-
10
- 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
11
10
  be used to create a custom view for them.
12
11
 
13
12
  ## Installation
14
13
 
15
- No installation yet
14
+ Using `yarn`:
15
+
16
+ ```
17
+ yarn add @modulify/validator
18
+ ```
19
+
20
+ or, using `npm`:
21
+
22
+ ```
23
+ npm install @modulify/validator --save
24
+ ```
16
25
 
17
26
  ## Usage
18
27
 
19
28
  ```typescript
20
29
  import {
21
- Collection,
22
- Exists,
23
- Length,
24
- createValidator,
30
+ HasLength,
31
+ HasProperties,
32
+ IsDefined,
33
+ IsString,
34
+ validate,
25
35
  } from '@modulify/validator'
26
36
 
27
- const validator = createValidator()
28
-
29
- const violations = validator.validate({
37
+ const violations = await validate({
30
38
  form: {
31
39
  nickname: '',
32
40
  password: '',
33
41
  },
34
- }, new Collection({
42
+ }, HasProperties({
35
43
  form: [
36
- new Exists(),
37
- new Collection({
38
- nickname: new Length({ min: 4 }),
39
- password: new Length({ min: 6 }),
44
+ IsDefined(),
45
+ HasProperties({
46
+ nickname: IsString.That(HasLength({ min: 4 })),
47
+ password: IsString.That(HasLength({ min: 6 })),
40
48
  }),
41
49
  ],
42
- }), /* do not set or set to true for async validation */ false) /* [{
43
- by: '@modulify/validator/Length',
50
+ })) /* [{
51
+ by: '@modulify/validator/IsString',
44
52
  value: '',
45
53
  path: ['form', 'nickname'],
46
54
  reason: 'min',
47
55
  meta: 4,
48
56
  }, {
49
- by: '@modulify/validator/Length',
57
+ by: '@modulify/validator/IsString',
50
58
  value: '',
51
59
  path: ['form', 'password'],
52
60
  reason: 'min',
@@ -54,124 +62,90 @@ const violations = validator.validate({
54
62
  }] */
55
63
  ```
56
64
 
57
- ### Constraints
58
-
59
- Constraints provide information of how the value should be validated.
60
-
61
- Available from the box:
62
-
63
- * `Collection` &ndash; used for validating objects' structure;
64
- * `Each` &ndash; used for validating arrays' elements; applies specified constraints to each element of an array;
65
- * `Exists` &ndash; used for checking if a value is defined; useful for finding missing keys;
66
- * `Length` &ndash; used for checking arrays' and string's length, available settings (all optional) are:
67
- * `exact` &ndash; `number`, array or string should have exactly specified count of elements or characters;
68
- * `max` &ndash; `number`, maximum elements in array or maximum characters in string;
69
- * `min` &ndash; `number`, minimum elements in array or minimum characters in string;
70
- * `OneOf` &ndash; used for restricting which values can be used.
71
-
72
- There is no any basic constraint class to extend, but they should follow signature
73
- described in `types/index.d.ts` &ndash; `Constraint`.
74
-
75
- ### Validators
76
-
77
- Validators provide validation logic that relies on information provided by constraints.
78
-
79
- There is no any basic validator class to extend, but they should follow signature
80
- described in `types/index.d.ts` &ndash; `ConstraintValidator`.
81
-
82
- ### Provider
83
-
84
- Provider is used to bind constraints with their validators, provides a validator for a constraint.
85
-
86
- All providers should follow signature described in `types/index.d.ts` &ndash; `Provider`.
87
-
88
- This feature is responsible for extending validation capabilities. Custom provider can be passed into
89
- `createValidator` function or `override` method of `Validator` instance.
90
-
91
- There is a built-in provider &ndash; `ProviderChain`. It allows to "chain" providers &ndash; if
92
- suitable validator was not found in currently used provider, it will try to find it in previous provider that was
93
- overridden by `override` method.
65
+ or (for synchronous validation):
94
66
 
95
67
  ```typescript
96
- import type {
97
- Constraint,
98
- ConstraintValidator,
99
- ConstraintViolation,
100
- Key,
101
- Provider,
102
- } from '@modulify/validator'
103
-
104
- import {
105
- ProviderChain,
106
- createValidator,
107
- } from '@modulify/validator'
108
-
109
- class Email implements Constraint {
110
- public readonly name = '@app/validator/Email'
111
-
112
- toViolation (value: unknown, path: Key[]): ConstraintViolation {
113
- return {
114
- by: this.name,
115
- value,
116
- path,
117
- }
118
- }
119
- }
120
-
121
- class EmailValidator implements ConstraintValidator {
122
- private readonly _constraint: Email
123
-
124
- constructor (constraint: Email) {
125
- this._constraint = constraint
126
- }
127
-
128
- validate (value: unknown, path?: Key[]): ConstraintViolation | null {
129
- if (!(typeof value === 'string') || !/\S+@\S+\.\S+/.test(value)) {
130
- return this._constraint.toViolation(value, path)
131
- }
132
-
133
- return null
134
- }
135
- }
136
- ```
137
-
138
- then
139
-
140
- ```typescript
141
- const provider = new ProviderChain(new class implements Provider {
142
- get (constraint: Constraint) {
143
- return constraint instanceof Email ? new EmailValidator(constraint) : null
144
- }
145
-
146
- override (provider: Provider): Provider {
147
- return new ProviderChain(provider, this)
148
- }
149
- })
150
- ```
151
-
152
- or
153
-
154
- ```typescript
155
- const provider = new class implements Provider {
156
- get (constraint: Constraint) {
157
- return constraint instanceof Email ? new EmailValidator(constraint) : null
158
- }
159
-
160
- override (provider: Provider): Provider {
161
- return new ProviderChain(provider, this)
162
- }
163
- }
164
- ```
165
-
166
- and then
167
-
168
- ```typescript
169
- 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
+ }))
170
82
  ```
171
83
 
172
- or
173
-
174
- ```typescript
175
- const validator = createValidator()
176
- const overridden = validator.override(provider) // it creates new validator instance, so validator !== overridden
177
- ```
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.
@@ -0,0 +1,2 @@
1
+ import { Assertion, Meta, Predicate } from '../../types';
2
+ export declare const Assert: <T = unknown, M = unknown>(predicate: Predicate<T>, options: Meta<M>) => Assertion<T, M>;
@@ -0,0 +1,7 @@
1
+ import { Assertion } from '../../types';
2
+ export declare const HasLength: ({ exact, max, min, bail, }: {
3
+ exact?: number | null;
4
+ max?: number | null;
5
+ min?: number | null;
6
+ bail?: boolean;
7
+ }) => Assertion<string | unknown[], unknown>;
@@ -0,0 +1,3 @@
1
+ import { Assertion, Violation } from '../../types';
2
+ declare const check: <T = unknown, M = unknown>(assert: Assertion<T, M>, value: unknown, path?: PropertyKey[]) => null | Violation<M>;
3
+ export default check;
@@ -0,0 +1,16 @@
1
+ import { Assert } from './Assert';
2
+ import { Assertion } from '../../types';
3
+ export { Assert };
4
+ export { HasLength } from './HasLength';
5
+ export declare const IsBoolean: Assertion<boolean, unknown>;
6
+ export declare const IsDate: Assertion<Date, unknown>;
7
+ export declare const IsDefined: Assertion<unknown, unknown>;
8
+ export declare const IsEmail: Assertion<string, unknown>;
9
+ export declare const IsNull: Assertion<null, unknown>;
10
+ export declare const IsNumber: Assertion<number, unknown>;
11
+ export declare const IsString: Assertion<string, unknown>;
12
+ export declare const IsSymbol: Assertion<symbol, unknown>;
13
+ export declare const OneOf: <Actual = unknown>(values: Actual[] | Record<string, Actual>, { equalTo, bail, }?: {
14
+ equalTo?: (a: Actual, b: unknown) => boolean;
15
+ bail?: boolean;
16
+ }) => Assertion<Actual, Actual[]>;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const predicates = require("./predicates.cjs");
4
+ const delegate = (p) => {
5
+ return (value) => p(value);
6
+ };
7
+ const Assert = (predicate, options) => {
8
+ const extender = (asserts = []) => {
9
+ return {
10
+ That(...asserts2) {
11
+ return Object.assign(delegate(predicate), options, extender(asserts2));
12
+ },
13
+ get also() {
14
+ return asserts;
15
+ }
16
+ };
17
+ };
18
+ return Object.assign(delegate(predicate), options, extender());
19
+ };
20
+ const isGTE = (min) => (x) => predicates.isNumber(x) && x >= min;
21
+ const isLTE = (max) => (x) => predicates.isNumber(x) && x <= max;
22
+ const IsEqual = (exact) => Assert((x) => predicates.isExact(exact)(x.length), {
23
+ fqn: "@modulify/validator/HasLength[exact]",
24
+ bail: false,
25
+ reason: "exact",
26
+ meta: exact
27
+ });
28
+ const IsGTE = (min) => Assert((x) => isGTE(min)(x.length), {
29
+ fqn: "@modulify/validator/HasLength[min]",
30
+ bail: false,
31
+ reason: "min",
32
+ meta: min
33
+ });
34
+ const IsLTE = (max) => Assert((x) => isLTE(max)(x.length), {
35
+ fqn: "@modulify/validator/HasLength[max]",
36
+ bail: false,
37
+ reason: "max",
38
+ meta: max
39
+ });
40
+ const HasLength = ({
41
+ exact = null,
42
+ max = null,
43
+ min = null,
44
+ bail = false
45
+ }) => Assert(predicates.Or(predicates.isString, predicates.isArray), {
46
+ fqn: "@modulify/validator/HasLength",
47
+ bail,
48
+ reason: "unsupported"
49
+ }).That(
50
+ ...exact !== null ? [IsEqual(exact)] : [],
51
+ ...max !== null ? [IsLTE(max)] : [],
52
+ ...min !== null ? [IsGTE(min)] : []
53
+ );
54
+ const IsBoolean = Assert(predicates.isBoolean, {
55
+ fqn: "@modulify/validator/IsBoolean",
56
+ bail: true
57
+ });
58
+ const IsDate = Assert(predicates.isDate, {
59
+ fqn: "@modulify/validator/IsDate",
60
+ bail: true
61
+ });
62
+ const IsDefined = Assert(predicates.Not(predicates.isUndefined), {
63
+ fqn: "@modulify/validator/IsDefined",
64
+ bail: true,
65
+ reason: "undefined"
66
+ });
67
+ const IsEmail = Assert(predicates.isEmail, {
68
+ fqn: "@modulify/validator/IsEmail",
69
+ bail: true
70
+ });
71
+ const IsNull = Assert(predicates.isNull, {
72
+ fqn: "@modulify/validator/IsNull",
73
+ bail: true
74
+ });
75
+ const IsNumber = Assert(predicates.isNumber, {
76
+ fqn: "@modulify/validator/IsNumber",
77
+ bail: true
78
+ });
79
+ const IsString = Assert(predicates.isString, {
80
+ fqn: "@modulify/validator/IsString",
81
+ bail: true
82
+ });
83
+ const IsSymbol = Assert(predicates.isSymbol, {
84
+ fqn: "@modulify/validator/IsSymbol",
85
+ bail: true
86
+ });
87
+ const OneOf = (values, {
88
+ equalTo = (a, b) => a === b,
89
+ bail = false
90
+ } = {}) => {
91
+ const haystack = predicates.isArray(values) ? values : Object.values(values);
92
+ const oneOf = (value) => haystack.some((allowed) => equalTo(allowed, value));
93
+ return Assert(oneOf, {
94
+ fqn: "@modulify/validator/OneOf",
95
+ bail,
96
+ meta: haystack
97
+ });
98
+ };
99
+ exports.Assert = Assert;
100
+ exports.HasLength = HasLength;
101
+ exports.IsBoolean = IsBoolean;
102
+ exports.IsDate = IsDate;
103
+ exports.IsDefined = IsDefined;
104
+ exports.IsEmail = IsEmail;
105
+ exports.IsNull = IsNull;
106
+ exports.IsNumber = IsNumber;
107
+ exports.IsString = IsString;
108
+ exports.IsSymbol = IsSymbol;
109
+ exports.OneOf = OneOf;
@@ -0,0 +1,2 @@
1
+ export * from './assertions/index'
2
+ export {}
@@ -0,0 +1,109 @@
1
+ import { Or, isArray, isString, isExact, isNumber, isBoolean, isDate, Not, isUndefined, isEmail, isNull, isSymbol } from "./predicates.mjs";
2
+ const delegate = (p) => {
3
+ return (value) => p(value);
4
+ };
5
+ const Assert = (predicate, options) => {
6
+ const extender = (asserts = []) => {
7
+ return {
8
+ That(...asserts2) {
9
+ return Object.assign(delegate(predicate), options, extender(asserts2));
10
+ },
11
+ get also() {
12
+ return asserts;
13
+ }
14
+ };
15
+ };
16
+ return Object.assign(delegate(predicate), options, extender());
17
+ };
18
+ const isGTE = (min) => (x) => isNumber(x) && x >= min;
19
+ const isLTE = (max) => (x) => isNumber(x) && x <= max;
20
+ const IsEqual = (exact) => Assert((x) => isExact(exact)(x.length), {
21
+ fqn: "@modulify/validator/HasLength[exact]",
22
+ bail: false,
23
+ reason: "exact",
24
+ meta: exact
25
+ });
26
+ const IsGTE = (min) => Assert((x) => isGTE(min)(x.length), {
27
+ fqn: "@modulify/validator/HasLength[min]",
28
+ bail: false,
29
+ reason: "min",
30
+ meta: min
31
+ });
32
+ const IsLTE = (max) => Assert((x) => isLTE(max)(x.length), {
33
+ fqn: "@modulify/validator/HasLength[max]",
34
+ bail: false,
35
+ reason: "max",
36
+ meta: max
37
+ });
38
+ const HasLength = ({
39
+ exact = null,
40
+ max = null,
41
+ min = null,
42
+ bail = false
43
+ }) => Assert(Or(isString, isArray), {
44
+ fqn: "@modulify/validator/HasLength",
45
+ bail,
46
+ reason: "unsupported"
47
+ }).That(
48
+ ...exact !== null ? [IsEqual(exact)] : [],
49
+ ...max !== null ? [IsLTE(max)] : [],
50
+ ...min !== null ? [IsGTE(min)] : []
51
+ );
52
+ const IsBoolean = Assert(isBoolean, {
53
+ fqn: "@modulify/validator/IsBoolean",
54
+ bail: true
55
+ });
56
+ const IsDate = Assert(isDate, {
57
+ fqn: "@modulify/validator/IsDate",
58
+ bail: true
59
+ });
60
+ const IsDefined = Assert(Not(isUndefined), {
61
+ fqn: "@modulify/validator/IsDefined",
62
+ bail: true,
63
+ reason: "undefined"
64
+ });
65
+ const IsEmail = Assert(isEmail, {
66
+ fqn: "@modulify/validator/IsEmail",
67
+ bail: true
68
+ });
69
+ const IsNull = Assert(isNull, {
70
+ fqn: "@modulify/validator/IsNull",
71
+ bail: true
72
+ });
73
+ const IsNumber = Assert(isNumber, {
74
+ fqn: "@modulify/validator/IsNumber",
75
+ bail: true
76
+ });
77
+ const IsString = Assert(isString, {
78
+ fqn: "@modulify/validator/IsString",
79
+ bail: true
80
+ });
81
+ const IsSymbol = Assert(isSymbol, {
82
+ fqn: "@modulify/validator/IsSymbol",
83
+ bail: true
84
+ });
85
+ const OneOf = (values, {
86
+ equalTo = (a, b) => a === b,
87
+ bail = false
88
+ } = {}) => {
89
+ const haystack = isArray(values) ? values : Object.values(values);
90
+ const oneOf = (value) => haystack.some((allowed) => equalTo(allowed, value));
91
+ return Assert(oneOf, {
92
+ fqn: "@modulify/validator/OneOf",
93
+ bail,
94
+ meta: haystack
95
+ });
96
+ };
97
+ export {
98
+ Assert,
99
+ HasLength,
100
+ IsBoolean,
101
+ IsDate,
102
+ IsDefined,
103
+ IsEmail,
104
+ IsNull,
105
+ IsNumber,
106
+ IsString,
107
+ IsSymbol,
108
+ OneOf
109
+ };