@cleverbrush/schema 0.0.16 → 1.0.0-beta.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.
Files changed (75) hide show
  1. package/README.md +224 -149
  2. package/dist/builders/AliasSchemaBuilder.d.ts +14 -0
  3. package/dist/builders/AliasSchemaBuilder.js +91 -0
  4. package/dist/builders/ArraySchemaBuilder.d.ts +15 -0
  5. package/dist/builders/ArraySchemaBuilder.js +141 -0
  6. package/dist/builders/BooleanSchemaBuilder.d.ts +31 -0
  7. package/dist/builders/BooleanSchemaBuilder.js +90 -0
  8. package/dist/builders/FunctionSchemaBuilder.d.ts +25 -0
  9. package/dist/builders/FunctionSchemaBuilder.js +66 -0
  10. package/dist/builders/NumberSchemaBuilder.d.ts +61 -0
  11. package/dist/builders/NumberSchemaBuilder.js +206 -0
  12. package/dist/builders/ObjectSchemaBuilder.d.ts +83 -0
  13. package/dist/builders/ObjectSchemaBuilder.js +344 -0
  14. package/dist/builders/SchemaBuilder.d.ts +36 -0
  15. package/dist/builders/SchemaBuilder.js +88 -0
  16. package/dist/builders/StringSchemaBuilder.d.ts +14 -0
  17. package/dist/builders/StringSchemaBuilder.js +140 -0
  18. package/dist/builders/UnionSchemaBuilder.d.ts +10 -0
  19. package/dist/builders/UnionSchemaBuilder.js +100 -0
  20. package/dist/defaultSchemas.d.ts +4 -0
  21. package/dist/defaultSchemas.js +45 -0
  22. package/dist/index.d.ts +38 -90
  23. package/dist/index.js +30 -4
  24. package/dist/schema.d.ts +242 -0
  25. package/dist/schema.js +2 -0
  26. package/dist/schemaRegistry.d.ts +54 -0
  27. package/dist/schemaRegistry.js +301 -0
  28. package/dist/validators/validateArray.d.ts +3 -2
  29. package/dist/validators/validateBoolean.d.ts +2 -2
  30. package/dist/validators/validateBoolean.js +1 -4
  31. package/dist/validators/validateFunction.d.ts +2 -0
  32. package/dist/validators/validateFunction.js +21 -0
  33. package/dist/validators/validateNumber.d.ts +2 -2
  34. package/dist/validators/validateNumber.js +1 -1
  35. package/dist/validators/validateObject.d.ts +3 -2
  36. package/dist/validators/validateObject.js +33 -11
  37. package/dist/validators/validateString.d.ts +2 -2
  38. package/dist/validators/validateString.js +1 -1
  39. package/dist/validators/validateUnion.d.ts +3 -0
  40. package/dist/validators/validateUnion.js +30 -0
  41. package/package.json +3 -3
  42. package/src/builders/AliasSchemaBuilder.test.ts +124 -0
  43. package/src/builders/AliasSchemaBuilder.ts +250 -0
  44. package/src/builders/ArraySchemaBuilder.test.ts +270 -0
  45. package/src/builders/ArraySchemaBuilder.ts +381 -0
  46. package/src/builders/BooleanSchemaBuilder.test.ts +196 -0
  47. package/src/builders/BooleanSchemaBuilder.ts +155 -0
  48. package/src/builders/FunctionSchemaBuilder.test.ts +134 -0
  49. package/src/builders/FunctionSchemaBuilder.ts +109 -0
  50. package/src/builders/NumberSchemaBuilder.test.ts +493 -0
  51. package/src/builders/NumberSchemaBuilder.ts +789 -0
  52. package/src/builders/ObjectSchemaBuilder.test.ts +657 -0
  53. package/src/builders/ObjectSchemaBuilder.ts +794 -0
  54. package/src/builders/SchemaBuilder.test.ts +73 -0
  55. package/src/builders/SchemaBuilder.ts +135 -0
  56. package/src/builders/StringSchemaBuilder.test.ts +318 -0
  57. package/src/builders/StringSchemaBuilder.ts +392 -0
  58. package/src/builders/UnionSchemaBuilder.test.ts +162 -0
  59. package/src/builders/UnionSchemaBuilder.ts +159 -0
  60. package/src/defaultSchemas.ts +44 -0
  61. package/src/index.ts +70 -190
  62. package/src/schema.ts +922 -0
  63. package/src/schemaRegistry.builders.test.ts +1438 -0
  64. package/src/{schemaValidator.test.ts → schemaRegistry.test.ts} +561 -185
  65. package/src/schemaRegistry.ts +532 -0
  66. package/src/validators/validateArray.ts +4 -7
  67. package/src/validators/validateBoolean.ts +2 -11
  68. package/src/validators/validateFunction.ts +25 -0
  69. package/src/validators/validateNumber.ts +2 -7
  70. package/src/validators/validateObject.ts +32 -21
  71. package/src/validators/validateString.ts +2 -7
  72. package/src/validators/validateUnion.ts +36 -0
  73. package/dist/schemaValidator.d.ts +0 -16
  74. package/dist/schemaValidator.js +0 -234
  75. package/src/schemaValidator.ts +0 -367
@@ -1,14 +1,10 @@
1
- import {
2
- ValidationResult,
3
- ObjectSchemaDefinition,
4
- ISchemaValidator,
5
- Schema
6
- } from '../index';
1
+ import { ObjectSchema, Schema, ValidationResult } from '../schema.js';
2
+ import SchemaRegistry from '../schemaRegistry.js';
7
3
 
8
4
  export const validateObject = async (
9
5
  obj: any,
10
- schema: ObjectSchemaDefinition<any>,
11
- validator: ISchemaValidator<any, unknown[]>
6
+ schema: ObjectSchema<any>,
7
+ validator: SchemaRegistry<any>
12
8
  ): Promise<ValidationResult> => {
13
9
  if (
14
10
  typeof obj === 'undefined' &&
@@ -33,11 +29,14 @@ export const validateObject = async (
33
29
  Object.keys(schema.preprocessors).map(async (key: string) => {
34
30
  if (key === '*') return;
35
31
  if (typeof schema.preprocessors[key] === 'function') {
36
- obj[key] = await Promise.resolve(
37
- (schema.preprocessors[key] as (unknown) => unknown)(
38
- obj[key]
39
- )
32
+ const res = await Promise.resolve(
33
+ (schema.preprocessors[key] as any)(obj[key])
40
34
  );
35
+ if (typeof res !== 'undefined') {
36
+ obj[key] = res;
37
+ } else {
38
+ delete obj[key];
39
+ }
41
40
  } else {
42
41
  const preprocessor = validator.preprocessors.get(
43
42
  schema.preprocessors[key] as string
@@ -47,16 +46,19 @@ export const validateObject = async (
47
46
  `preprocessor '${schema.preprocessors[key]}' is unknown`
48
47
  );
49
48
  }
50
- obj[key] = await Promise.resolve(preprocessor(obj[key]));
49
+ const res = await Promise.resolve(preprocessor(obj[key]));
50
+ if (typeof res !== 'undefined') {
51
+ obj[key] = res;
52
+ } else {
53
+ delete obj[key];
54
+ }
51
55
  }
52
56
  })
53
57
  );
54
58
  if ('*' in schema.preprocessors) {
55
59
  const objectPreprocessor = schema.preprocessors['*'];
56
60
  if (typeof objectPreprocessor === 'function') {
57
- await Promise.resolve(
58
- (objectPreprocessor as (unknown) => unknown)(obj)
59
- );
61
+ await Promise.resolve((objectPreprocessor as any)(obj));
60
62
  } else {
61
63
  const preprocessor = validator.preprocessors.get(
62
64
  objectPreprocessor as string
@@ -72,10 +74,10 @@ export const validateObject = async (
72
74
  }
73
75
 
74
76
  if (typeof schema.properties === 'object' && schema.properties) {
75
- const errors = (
76
- await Promise.all(
77
+ const errors = [
78
+ ...(await Promise.all(
77
79
  Object.entries(schema.properties).map(
78
- async ([name, schema]: [string, Schema<any>]) => {
80
+ async ([name, schema]: [string, Schema]) => {
79
81
  const result = await validator.validate(
80
82
  schema,
81
83
  obj[name]
@@ -89,11 +91,20 @@ export const validateObject = async (
89
91
  };
90
92
  }
91
93
  )
92
- )
93
- )
94
+ )),
95
+ ...(schema.noUnknownProperties === false
96
+ ? []
97
+ : Object.keys(obj)
98
+ .filter((k) => !(k in schema.properties))
99
+ .map((k) => ({
100
+ valid: false,
101
+ errors: [`-> ${k} - unknown field`]
102
+ })))
103
+ ]
94
104
  .filter((r) => !r.valid)
95
105
  .map((r) => r.errors)
96
106
  .flat(Infinity) as string[];
107
+
97
108
  if (errors.length) {
98
109
  return {
99
110
  valid: false,
@@ -1,13 +1,8 @@
1
- import {
2
- ValidationResult,
3
- StringSchemaDefinition,
4
- ISchemaValidator
5
- } from '../index';
1
+ import { StringSchema, ValidationResult } from '../schema.js';
6
2
 
7
3
  export const validateString = async (
8
4
  obj: any,
9
- schema: StringSchemaDefinition<any>,
10
- validator: ISchemaValidator<any>
5
+ schema: StringSchema
11
6
  ): Promise<ValidationResult> => {
12
7
  if (
13
8
  typeof obj === 'undefined' &&
@@ -0,0 +1,36 @@
1
+ import { UnionSchema, ValidationResult } from '../schema.js';
2
+ import SchemaRegistry from '../schemaRegistry.js';
3
+
4
+ export const validateUnion = async (
5
+ obj: any,
6
+ schema: UnionSchema<any>,
7
+ validator: SchemaRegistry<any>
8
+ ): Promise<ValidationResult> => {
9
+ if (typeof obj === 'undefined' && schema.isRequired === false) {
10
+ return {
11
+ valid: true
12
+ };
13
+ }
14
+
15
+ if (obj === null && schema.isNullable) {
16
+ return {
17
+ valid: true
18
+ };
19
+ }
20
+
21
+ if (Array.isArray(schema.variants) && schema.variants.length > 0) {
22
+ for (let i = 0; i < schema.variants.length; i++) {
23
+ const variant = schema.variants[i];
24
+ const result = await validator.validate(variant, obj);
25
+ if (result.valid) {
26
+ return result;
27
+ }
28
+ }
29
+ return {
30
+ valid: false,
31
+ errors: ['object does not satisfy any scheme']
32
+ };
33
+ }
34
+
35
+ throw new Error('variants should be non empty array');
36
+ };
@@ -1,16 +0,0 @@
1
- import { Merge } from '@cleverbrush/deep';
2
- import { ISchemasProvider, Schema, ISchemaActions, ObjectSchemaDefinitionParam, ValidationResult, ISchemaValidator, DefaultSchemaType, Cons, Unfold } from './index';
3
- export default class SchemaValidator<T extends Record<string, never> = Record<string, never>, SchemaTypesStructures extends unknown[] = []> implements ISchemasProvider<SchemaTypesStructures>, ISchemaValidator<T, SchemaTypesStructures> {
4
- private _schemasMap;
5
- private _schemasCache;
6
- private _preprocessorsMap;
7
- get preprocessors(): Map<string, (value: unknown) => unknown>;
8
- addPreprocessor(name: string, preprocessor: (value: unknown) => unknown | Promise<unknown>): SchemaValidator<T, SchemaTypesStructures>;
9
- addSchemaType<K, L extends ObjectSchemaDefinitionParam<M> | Array<Schema<any>>, M = any>(name: keyof K, schema: L): SchemaValidator<T & {
10
- [key in keyof K]: L;
11
- }, Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>>;
12
- get schemas(): Merge<SchemaTypesStructures>;
13
- private validateDefaultType;
14
- private checkValidators;
15
- validate<K>(schema: keyof T | DefaultSchemaType | Schema<K>, obj: any): Promise<ValidationResult>;
16
- }
@@ -1,234 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const deep_1 = require("@cleverbrush/deep");
4
- const validateNumber_1 = require("./validators/validateNumber");
5
- const validateBoolean_1 = require("./validators/validateBoolean");
6
- const validateString_1 = require("./validators/validateString");
7
- const validateArray_1 = require("./validators/validateArray");
8
- const validateObject_1 = require("./validators/validateObject");
9
- const defaultSchemaNames = ['string', 'boolean', 'number', 'date', 'array'];
10
- const defaultSchemas = {
11
- number: {
12
- type: 'number',
13
- isRequired: true,
14
- isNullable: false,
15
- ensureNotNaN: true,
16
- ensureIsFinite: true
17
- },
18
- boolean: {
19
- type: 'boolean',
20
- isRequired: true,
21
- isNullable: false
22
- },
23
- string: {
24
- type: 'string',
25
- isNullable: false,
26
- isRequired: true
27
- },
28
- array: {
29
- type: 'array'
30
- },
31
- object: {
32
- type: 'object',
33
- isNullable: false,
34
- isRequired: true
35
- }
36
- };
37
- const defaultSchemasValidationStrategies = {
38
- number: (obj, schema, validator) => (0, validateNumber_1.validateNumber)(obj, schema, validator),
39
- boolean: (obj, schema, validator) => (0, validateBoolean_1.validateBoolean)(obj, schema, validator),
40
- string: (obj, schema, validator) => (0, validateString_1.validateString)(obj, schema, validator),
41
- array: (obj, schema, validator) => (0, validateArray_1.validateArray)(obj, schema, validator)
42
- };
43
- const isDefaultType = (name) => defaultSchemaNames.indexOf(name) !== -1;
44
- class SchemaValidator {
45
- _schemasMap = new Map();
46
- _schemasCache = null;
47
- _preprocessorsMap = new Map();
48
- get preprocessors() {
49
- return this._preprocessorsMap;
50
- }
51
- addPreprocessor(name, preprocessor) {
52
- if (this._preprocessorsMap.has(name))
53
- throw new Error(`Preprocessor '${name}' is already registered`);
54
- this._preprocessorsMap.set(name, preprocessor);
55
- return this;
56
- }
57
- addSchemaType(name, schema) {
58
- if (typeof name !== 'string' || !name)
59
- throw new Error('Name is required');
60
- if (isDefaultType(name.toString()))
61
- throw new Error(`You can't add a schema named "${name}" because it's a name of a default schema, please consider another name to be used`);
62
- if (this._schemasMap.has(name.toString())) {
63
- throw new Error(`Schema "${name}" already exists`);
64
- }
65
- if (Array.isArray(schema)) {
66
- this._schemasMap.set(name.toString(), schema);
67
- this._schemasCache = null;
68
- return this;
69
- }
70
- if (typeof schema !== 'object')
71
- throw new Error('Array or Object is required');
72
- this._schemasMap.set(name.toString(), {
73
- ...schema,
74
- type: 'object'
75
- });
76
- this._schemasCache = null;
77
- return this;
78
- }
79
- get schemas() {
80
- if (this._schemasCache)
81
- return this._schemasCache;
82
- const res = {};
83
- for (const key of this._schemasMap.keys()) {
84
- const parts = key.split('.');
85
- let curr = res;
86
- for (let i = 0; i < parts.length; i++) {
87
- if (i === parts.length - 1) {
88
- curr[parts[i]] = {
89
- validate: (value) => this.validate(this._schemasMap.get(key), value),
90
- schema: this._schemasMap.get(key)
91
- };
92
- }
93
- else {
94
- if (typeof curr[parts[i]] === 'undefined') {
95
- curr[parts[i]] = {};
96
- }
97
- }
98
- curr = curr[parts[i]];
99
- }
100
- }
101
- this._schemasCache = res;
102
- return this._schemasCache;
103
- }
104
- async validateDefaultType(name, value, mergeSchema) {
105
- const strategy = defaultSchemasValidationStrategies[name];
106
- let finalSchema = defaultSchemas[name];
107
- if (strategy) {
108
- if (typeof mergeSchema === 'object') {
109
- finalSchema = (0, deep_1.deepExtend)(finalSchema, mergeSchema);
110
- }
111
- if (typeof mergeSchema === 'number') {
112
- finalSchema = {
113
- ...finalSchema,
114
- equals: mergeSchema
115
- };
116
- mergeSchema;
117
- }
118
- let preliminaryResult = await strategy(value, finalSchema, this);
119
- if (!preliminaryResult.valid)
120
- return preliminaryResult;
121
- preliminaryResult = await this.checkValidators(finalSchema, value);
122
- return preliminaryResult;
123
- }
124
- throw new Error('not implemented');
125
- }
126
- async checkValidators(schema, value) {
127
- if (!schema.isRequired && typeof value === 'undefined') {
128
- return {
129
- valid: true
130
- };
131
- }
132
- if (Array.isArray(schema.validators)) {
133
- const validatorsResults = await Promise.allSettled(schema.validators.map((v) => Promise.resolve(v(value))));
134
- const rejections = validatorsResults
135
- .filter((f) => f.status === 'rejected')
136
- .map((f) => f.reason);
137
- const errors = validatorsResults
138
- .filter((f) => f.status === 'fulfilled' &&
139
- typeof f.value !== 'boolean' &&
140
- f.value.valid === false)
141
- .map((f) => f.value)
142
- .map((f) => f.errors);
143
- if (rejections.length === 0 && errors.length === 0) {
144
- return {
145
- valid: true
146
- };
147
- }
148
- return {
149
- valid: false,
150
- errors: [...rejections, ...errors]
151
- };
152
- }
153
- return {
154
- valid: true
155
- };
156
- }
157
- async validate(schema, obj) {
158
- if (!schema)
159
- throw new Error('schemaName is required');
160
- if (typeof schema === 'string') {
161
- if (isDefaultType(schema)) {
162
- return await this.validateDefaultType(schema, obj);
163
- }
164
- else if (typeof this._schemasMap.get(schema) !== 'undefined') {
165
- if (Array.isArray(this._schemasMap.get(schema))) {
166
- return this.validate(this._schemasMap.get(schema), obj);
167
- }
168
- const objSchema = (0, deep_1.deepExtend)(defaultSchemas.object, this._schemasMap.get(schema));
169
- const res = await (0, validateObject_1.validateObject)(obj, objSchema, this);
170
- if (!res.valid)
171
- return res;
172
- return await this.checkValidators(objSchema, obj);
173
- }
174
- else {
175
- return await this.validateDefaultType('string', obj, {
176
- type: 'string',
177
- equals: schema
178
- });
179
- }
180
- }
181
- if (Array.isArray(schema)) {
182
- for (let i = 0; i < schema.length; i++) {
183
- const res = await this.validate(schema[i], obj);
184
- if (res.valid) {
185
- return res;
186
- }
187
- }
188
- return {
189
- valid: false,
190
- errors: ['object does not match any schema']
191
- };
192
- }
193
- if (typeof schema === 'number') {
194
- return await this.validateDefaultType('number', obj, schema);
195
- }
196
- if (typeof schema === 'object') {
197
- if (typeof schema.type !== 'string')
198
- throw new Error('Schema has no type');
199
- if (isDefaultType(schema.type)) {
200
- return await this.validateDefaultType(schema.type, obj, schema);
201
- }
202
- else if (schema.type === 'alias') {
203
- const alias = this._schemasMap.get(schema.schemaName);
204
- if (typeof alias === 'undefined') {
205
- throw new Error(`Unknown schema alias - ${schema.schemaName}`);
206
- }
207
- if (Array.isArray(alias)) {
208
- if ((!schema.isRequired && typeof obj === 'undefined') ||
209
- (schema.isNullable && obj === null)) {
210
- return {
211
- valid: true
212
- };
213
- }
214
- return await this.validate(alias, obj);
215
- }
216
- if (typeof alias !== 'object')
217
- throw new Error('it is only possible to use a full schema schema definition as alias');
218
- const schemaToMerge = { ...schema };
219
- delete schemaToMerge.type;
220
- delete schemaToMerge.schemaName;
221
- const finalSchema = (0, deep_1.deepExtend)(alias, schemaToMerge);
222
- return await this.validate(finalSchema, obj);
223
- }
224
- else if (schema.type === 'object') {
225
- const preliminaryResult = await (0, validateObject_1.validateObject)(obj, schema, this);
226
- if (!preliminaryResult.valid)
227
- return preliminaryResult;
228
- return await this.checkValidators(schema, obj);
229
- }
230
- }
231
- throw new Error("Coldn't understand the Schema provided");
232
- }
233
- }
234
- exports.default = SchemaValidator;