@theshelf/validation 0.3.2 → 0.4.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/README.md +10 -24
- package/dist/Validator.d.ts +3 -2
- package/dist/Validator.js +13 -2
- package/dist/definitions/interfaces.d.ts +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/package.json +6 -7
- package/dist/drivers/Zod.d.ts +0 -8
- package/dist/drivers/Zod.js +0 -140
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
# Validation | The Shelf
|
|
2
|
+
# Validation core | The Shelf
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
This package contains the definition of the validation operations. It uses a interchangeable driver system for performing the actual operations.
|
|
5
5
|
|
|
6
6
|
## Installation
|
|
7
7
|
|
|
@@ -9,34 +9,20 @@ The validation package provides a universal interaction layer with an actual dat
|
|
|
9
9
|
npm install @theshelf/validation
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
## Drivers
|
|
13
|
-
|
|
14
|
-
Currently, there is only one driver available:
|
|
15
|
-
|
|
16
|
-
* **Zod** - driver for the currently popular Zod library.
|
|
17
|
-
|
|
18
12
|
## How to use
|
|
19
13
|
|
|
20
|
-
The
|
|
14
|
+
The basic set up looks like this.
|
|
21
15
|
|
|
22
16
|
```ts
|
|
23
|
-
import
|
|
17
|
+
import Validator from '@theshelf/validation';
|
|
18
|
+
import driver from '/path/to/driver';
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
validator.driver = new SelectedDriver(/* configuration */);
|
|
20
|
+
const validator = new Validator(driver);
|
|
27
21
|
|
|
28
22
|
// Perform operations with the validator instance
|
|
29
23
|
```
|
|
30
24
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
The validator instance does not have any configuration options.
|
|
34
|
-
|
|
35
|
-
#### Zod driver
|
|
36
|
-
|
|
37
|
-
No configuration options.
|
|
38
|
-
|
|
39
|
-
### Operations
|
|
25
|
+
## Operations
|
|
40
26
|
|
|
41
27
|
```ts
|
|
42
28
|
import { ValidationSchema, ValidationResult } from '@theshelf/validation';
|
|
@@ -56,9 +42,9 @@ const schema: ValidationSchema = {
|
|
|
56
42
|
const result: ValidationResult = validator.validate(data, schema);
|
|
57
43
|
```
|
|
58
44
|
|
|
59
|
-
|
|
45
|
+
## Validation schema
|
|
60
46
|
|
|
61
|
-
A basic validation
|
|
47
|
+
A basic validation schema has the following structure.
|
|
62
48
|
|
|
63
49
|
```ts
|
|
64
50
|
const schema: ValidationSchema = {
|
|
@@ -97,7 +83,7 @@ The following types are supported:
|
|
|
97
83
|
* **URL**
|
|
98
84
|
* `required: boolean`
|
|
99
85
|
|
|
100
|
-
|
|
86
|
+
## Validation result
|
|
101
87
|
|
|
102
88
|
The validation result has two fields:
|
|
103
89
|
|
package/dist/Validator.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type Logger from '@theshelf/logging';
|
|
1
2
|
import type { Driver } from './definitions/interfaces.js';
|
|
2
3
|
import type { ValidationSchema } from './definitions/types.js';
|
|
3
4
|
import type ValidationResult from './definitions/ValidationResult.js';
|
|
4
|
-
export default class Validator
|
|
5
|
+
export default class Validator {
|
|
5
6
|
#private;
|
|
6
|
-
constructor(driver: Driver);
|
|
7
|
+
constructor(driver: Driver, logger?: Logger);
|
|
7
8
|
validate(data: unknown, schema: ValidationSchema): ValidationResult;
|
|
8
9
|
}
|
package/dist/Validator.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
export default class Validator {
|
|
2
2
|
#driver;
|
|
3
|
-
|
|
3
|
+
#logger;
|
|
4
|
+
#logPrefix;
|
|
5
|
+
constructor(driver, logger) {
|
|
4
6
|
this.#driver = driver;
|
|
7
|
+
this.#logger = logger?.for(Validator.name);
|
|
8
|
+
this.#logPrefix = `${this.#driver.name} ->`;
|
|
5
9
|
}
|
|
6
10
|
validate(data, schema) {
|
|
7
|
-
|
|
11
|
+
this.#logger?.debug(this.#logPrefix, 'Validating schema', schema);
|
|
12
|
+
try {
|
|
13
|
+
return this.#driver.validate(data, schema);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
this.#logger?.error(this.#logPrefix, 'Validating schema', schema, 'failed with error', error);
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
8
19
|
}
|
|
9
20
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export
|
|
2
|
-
export type
|
|
3
|
-
export type
|
|
1
|
+
export { FieldTypes, MAX_EMAIL_LENGTH, MAX_URL_LENGTH } from './definitions/constants.js';
|
|
2
|
+
export type { Driver } from './definitions/interfaces.js';
|
|
3
|
+
export type { ValidationType, ValidationTypes, StringProperties, NumberProperties, ArrayProperties, BooleanProperties, DateProperties, DateTimeProperties, UUIDProperties, EmailProperties, URLProperties, EnumProperties, Message, Validation, ValidationSchema } from './definitions/types.js';
|
|
4
|
+
export { default as ValidationResult } from './definitions/ValidationResult.js';
|
|
4
5
|
export { default as UnknownValidator } from './errors/UnknownValidator.js';
|
|
5
6
|
export { default as ValidationError } from './errors/ValidationError.js';
|
|
6
|
-
export { default as ZodDriver } from './drivers/Zod.js';
|
|
7
7
|
export { default } from './Validator.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { FieldTypes, MAX_EMAIL_LENGTH, MAX_URL_LENGTH } from './definitions/constants.js';
|
|
2
|
+
export { default as ValidationResult } from './definitions/ValidationResult.js';
|
|
2
3
|
export { default as UnknownValidator } from './errors/UnknownValidator.js';
|
|
3
4
|
export { default as ValidationError } from './errors/ValidationError.js';
|
|
4
|
-
export { default as ZodDriver } from './drivers/Zod.js';
|
|
5
5
|
export { default } from './Validator.js';
|
package/package.json
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theshelf/validation",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "git+https://github.com/MaskingTechnology/theshelf.git"
|
|
8
8
|
},
|
|
9
|
+
"license": "MIT",
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "tsc",
|
|
11
12
|
"clean": "rimraf dist",
|
|
12
|
-
"test": "vitest run",
|
|
13
|
-
"test-coverage": "vitest run --coverage",
|
|
14
13
|
"lint": "eslint",
|
|
15
|
-
"review": "npm run build && npm run lint
|
|
14
|
+
"review": "npm run build && npm run lint",
|
|
16
15
|
"prepublishOnly": "npm run clean && npm run build"
|
|
17
16
|
},
|
|
18
17
|
"files": [
|
|
19
18
|
"README.md",
|
|
20
19
|
"dist"
|
|
21
20
|
],
|
|
22
|
-
"types": "dist/index.d.ts",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
23
22
|
"exports": "./dist/index.js",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@theshelf/logging": "^0.4.0"
|
|
26
25
|
}
|
|
27
26
|
}
|
package/dist/drivers/Zod.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import ValidationResult from '../definitions/ValidationResult.js';
|
|
2
|
-
import type { Driver } from '../definitions/interfaces.js';
|
|
3
|
-
import type { ValidationSchema } from '../definitions/types.js';
|
|
4
|
-
export default class Zod implements Driver {
|
|
5
|
-
#private;
|
|
6
|
-
constructor();
|
|
7
|
-
validate(data: unknown, schema: ValidationSchema): ValidationResult;
|
|
8
|
-
}
|
package/dist/drivers/Zod.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import ValidationResult from '../definitions/ValidationResult.js';
|
|
3
|
-
import { FieldTypes, MAX_EMAIL_LENGTH, MAX_URL_LENGTH } from '../definitions/constants.js';
|
|
4
|
-
import UnknownValidator from '../errors/UnknownValidator.js';
|
|
5
|
-
// Zod is so type heavy that we've chosen for inferred types to be used.
|
|
6
|
-
// This is a trade-off between readability and verbosity.
|
|
7
|
-
export default class Zod {
|
|
8
|
-
#validations = new Map();
|
|
9
|
-
constructor() {
|
|
10
|
-
this.#validations.set(FieldTypes.STRING, (value) => this.#validateString(value));
|
|
11
|
-
this.#validations.set(FieldTypes.NUMBER, (value) => this.#validateNumber(value));
|
|
12
|
-
this.#validations.set(FieldTypes.BOOLEAN, (value) => this.#validateBoolean(value));
|
|
13
|
-
this.#validations.set(FieldTypes.DATE, (value) => this.#validateDate(value));
|
|
14
|
-
this.#validations.set(FieldTypes.DATETIME, (value) => this.#validateDateTime(value));
|
|
15
|
-
this.#validations.set(FieldTypes.UUID, (value) => this.#validateUuid(value));
|
|
16
|
-
this.#validations.set(FieldTypes.EMAIL, (value) => this.#validateEmail(value));
|
|
17
|
-
this.#validations.set(FieldTypes.ARRAY, (value) => this.#validateArray(value));
|
|
18
|
-
this.#validations.set(FieldTypes.URL, (value) => this.#validateUrl(value));
|
|
19
|
-
this.#validations.set(FieldTypes.ENUM, (value) => this.#validateEnum(value));
|
|
20
|
-
}
|
|
21
|
-
validate(data, schema) {
|
|
22
|
-
const validator = this.#buildValidator(schema);
|
|
23
|
-
const result = validator.safeParse(data);
|
|
24
|
-
if (result.success === false) {
|
|
25
|
-
const issues = result.error.issues;
|
|
26
|
-
const messages = this.#getMessages(issues, schema);
|
|
27
|
-
return new ValidationResult(true, messages);
|
|
28
|
-
}
|
|
29
|
-
return new ValidationResult(false);
|
|
30
|
-
}
|
|
31
|
-
#buildValidator(schema) {
|
|
32
|
-
return Object.entries(schema)
|
|
33
|
-
.reduce((partialSchema, [key, value]) => {
|
|
34
|
-
const fieldValidation = this.#getValidation(value);
|
|
35
|
-
return partialSchema.extend({ [key]: fieldValidation });
|
|
36
|
-
}, z.object({})).strict();
|
|
37
|
-
}
|
|
38
|
-
#getValidation(schema) {
|
|
39
|
-
for (const [key, validation] of Object.entries(schema)) {
|
|
40
|
-
if (key === 'message')
|
|
41
|
-
continue;
|
|
42
|
-
const validator = this.#validations.get(key.toLowerCase());
|
|
43
|
-
if (validator === undefined) {
|
|
44
|
-
throw new UnknownValidator(key);
|
|
45
|
-
}
|
|
46
|
-
return validator(validation);
|
|
47
|
-
}
|
|
48
|
-
return z.never();
|
|
49
|
-
}
|
|
50
|
-
#validateString(value) {
|
|
51
|
-
let validation = z.string();
|
|
52
|
-
if (value.minLength !== undefined)
|
|
53
|
-
validation = validation.min(value.minLength);
|
|
54
|
-
if (value.maxLength !== undefined)
|
|
55
|
-
validation = validation.max(value.maxLength);
|
|
56
|
-
if (value.pattern !== undefined)
|
|
57
|
-
validation = validation.regex(new RegExp(value.pattern));
|
|
58
|
-
return this.#checkRequired(value, validation);
|
|
59
|
-
}
|
|
60
|
-
#validateNumber(value) {
|
|
61
|
-
let validation = z.number();
|
|
62
|
-
if (value.minValue !== undefined)
|
|
63
|
-
validation = validation.min(value.minValue);
|
|
64
|
-
if (value.maxValue !== undefined)
|
|
65
|
-
validation = validation.max(value.maxValue);
|
|
66
|
-
return this.#checkRequired(value, validation);
|
|
67
|
-
}
|
|
68
|
-
#validateBoolean(value) {
|
|
69
|
-
const validation = z.boolean();
|
|
70
|
-
return this.#checkRequired(value, validation);
|
|
71
|
-
}
|
|
72
|
-
#validateDate(value) {
|
|
73
|
-
const validation = z.iso.date();
|
|
74
|
-
return this.#checkRequired(value, validation);
|
|
75
|
-
}
|
|
76
|
-
#validateDateTime(value) {
|
|
77
|
-
const validation = z.iso.datetime();
|
|
78
|
-
return this.#checkRequired(value, validation);
|
|
79
|
-
}
|
|
80
|
-
#validateUuid(value) {
|
|
81
|
-
const validation = z.uuid();
|
|
82
|
-
return this.#checkRequired(value, validation);
|
|
83
|
-
}
|
|
84
|
-
#validateEmail(value) {
|
|
85
|
-
const validation = z.email().max(MAX_EMAIL_LENGTH);
|
|
86
|
-
return this.#checkRequired(value, validation);
|
|
87
|
-
}
|
|
88
|
-
#validateArray(value) {
|
|
89
|
-
let validation = value.validations === undefined
|
|
90
|
-
? z.array(z.unknown())
|
|
91
|
-
: z.array(this.#getValidation(value.validations));
|
|
92
|
-
if (value.minLength !== undefined)
|
|
93
|
-
validation = validation.min(value.minLength);
|
|
94
|
-
if (value.maxLength !== undefined)
|
|
95
|
-
validation = validation.max(value.maxLength);
|
|
96
|
-
return this.#checkRequired(value, validation);
|
|
97
|
-
}
|
|
98
|
-
#validateUrl(value) {
|
|
99
|
-
let validation = z.url().max(MAX_URL_LENGTH);
|
|
100
|
-
if (value.protocols !== undefined) {
|
|
101
|
-
const expression = value.protocols.join('|');
|
|
102
|
-
validation = validation.regex(new RegExp(`^(${expression}):.*`));
|
|
103
|
-
}
|
|
104
|
-
return this.#checkRequired(value, validation);
|
|
105
|
-
}
|
|
106
|
-
#validateEnum(value) {
|
|
107
|
-
const validation = value.values === undefined
|
|
108
|
-
? z.enum([])
|
|
109
|
-
: z.enum(value.values);
|
|
110
|
-
return this.#checkRequired(value, validation);
|
|
111
|
-
}
|
|
112
|
-
#checkRequired(value, validation) {
|
|
113
|
-
return value.required
|
|
114
|
-
? validation
|
|
115
|
-
: validation.optional();
|
|
116
|
-
}
|
|
117
|
-
#getMessages(issues, scheme) {
|
|
118
|
-
const messages = new Map();
|
|
119
|
-
for (const issue of issues) {
|
|
120
|
-
if (issue.code === 'unrecognized_keys') {
|
|
121
|
-
this.#mapUnrecognizedKeys(issue, scheme, messages);
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
const field = String(issue.path[0]);
|
|
125
|
-
const message = this.#getMessageByField(field, scheme);
|
|
126
|
-
messages.set(field, message);
|
|
127
|
-
}
|
|
128
|
-
return messages;
|
|
129
|
-
}
|
|
130
|
-
#mapUnrecognizedKeys(issue, scheme, messages) {
|
|
131
|
-
for (const key of issue.keys) {
|
|
132
|
-
const message = this.#getMessageByField(key, scheme);
|
|
133
|
-
messages.set(key, message);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
#getMessageByField(path, scheme) {
|
|
137
|
-
const field = scheme[path];
|
|
138
|
-
return field?.message ?? 'Invalid field';
|
|
139
|
-
}
|
|
140
|
-
}
|