@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 CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
- # Validation | The Shelf
2
+ # Validation core | The Shelf
3
3
 
4
- The validation package provides a universal interaction layer with an actual data validation solution.
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 instance of the validator needs to be imported and one of the drivers must be set.
14
+ The basic set up looks like this.
21
15
 
22
16
  ```ts
23
- import validator, { Zod as SelectedDriver } from '@theshelf/validation';
17
+ import Validator from '@theshelf/validation';
18
+ import driver from '/path/to/driver';
24
19
 
25
- // Set the driver before performing any operation (the Zod driver is used by default)
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
- ### Configuration
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
- ### Validation scheme options
45
+ ## Validation schema
60
46
 
61
- A basic validation scheme has the following structure.
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
- ### Validation result structure
86
+ ## Validation result
101
87
 
102
88
  The validation result has two fields:
103
89
 
@@ -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 implements Driver {
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
- constructor(driver) {
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
- return this.#driver.validate(data, schema);
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
  }
@@ -1,5 +1,6 @@
1
1
  import type ValidationResult from './ValidationResult.js';
2
2
  import type { ValidationSchema } from './types.js';
3
3
  export interface Driver {
4
+ get name(): string;
4
5
  validate(data: unknown, schema: ValidationSchema): ValidationResult;
5
6
  }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export * from './definitions/constants.js';
2
- export type * from './definitions/interfaces.js';
3
- export type * from './definitions/types.js';
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 * from './definitions/constants.js';
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.3.2",
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 && npm run test",
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
- "dependencies": {
25
- "zod": "4.1.12"
23
+ "peerDependencies": {
24
+ "@theshelf/logging": "^0.4.0"
26
25
  }
27
26
  }
@@ -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
- }
@@ -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
- }