@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,367 +0,0 @@
1
- import { deepExtend, Merge } from '@cleverbrush/deep';
2
- import {
3
- ISchemasProvider,
4
- Schema,
5
- ISchemaActions,
6
- ObjectSchemaDefinitionParam,
7
- ValidationResult,
8
- NumberSchemaDefinition,
9
- StringSchemaDefinition,
10
- CompositeSchema,
11
- ValidationResultRaw,
12
- ArraySchemaDefinition,
13
- ISchemaValidator,
14
- DefaultSchemaType,
15
- ObjectSchemaDefinition,
16
- BooleanSchemaDefinition,
17
- Cons,
18
- Unfold
19
- } from './index';
20
- import { validateNumber } from './validators/validateNumber';
21
- import { validateBoolean } from './validators/validateBoolean';
22
- import { validateString } from './validators/validateString';
23
- import { validateArray } from './validators/validateArray';
24
- import { validateObject } from './validators/validateObject';
25
-
26
- const defaultSchemaNames = ['string', 'boolean', 'number', 'date', 'array'];
27
-
28
- const defaultSchemas: { [key in DefaultSchemaType]?: Schema<any> } = {
29
- number: {
30
- type: 'number',
31
- isRequired: true,
32
- isNullable: false,
33
- ensureNotNaN: true,
34
- ensureIsFinite: true
35
- },
36
- boolean: {
37
- type: 'boolean',
38
- isRequired: true,
39
- isNullable: false
40
- },
41
- string: {
42
- type: 'string',
43
- isNullable: false,
44
- isRequired: true
45
- },
46
- array: {
47
- type: 'array'
48
- },
49
- object: {
50
- type: 'object',
51
- isNullable: false,
52
- isRequired: true
53
- }
54
- };
55
-
56
- const defaultSchemasValidationStrategies = {
57
- number: (
58
- obj: any,
59
- schema: NumberSchemaDefinition<any>,
60
- validator: ISchemaValidator<any, unknown[]>
61
- ): Promise<ValidationResult> => validateNumber(obj, schema, validator),
62
- boolean: (
63
- obj: any,
64
- schema: BooleanSchemaDefinition<any>,
65
- validator: ISchemaValidator<any, unknown[]>
66
- ): Promise<ValidationResult> => validateBoolean(obj, schema, validator),
67
- string: (
68
- obj: any,
69
- schema: StringSchemaDefinition<any>,
70
- validator: ISchemaValidator<any, unknown[]>
71
- ): Promise<ValidationResult> => validateString(obj, schema, validator),
72
- array: (
73
- obj: any,
74
- schema: ArraySchemaDefinition<any>,
75
- validator: ISchemaValidator<any, unknown[]>
76
- ): Promise<ValidationResult> => validateArray(obj, schema, validator)
77
- };
78
-
79
- const isDefaultType = (name: string): boolean =>
80
- defaultSchemaNames.indexOf(name) !== -1;
81
-
82
- export default class SchemaValidator<
83
- T extends Record<string, never> = Record<string, never>,
84
- SchemaTypesStructures extends unknown[] = []
85
- > implements
86
- ISchemasProvider<SchemaTypesStructures>,
87
- ISchemaValidator<T, SchemaTypesStructures>
88
- {
89
- private _schemasMap = new Map<string, Schema<any>>();
90
- private _schemasCache: Merge<SchemaTypesStructures> = null;
91
- private _preprocessorsMap = new Map<
92
- string,
93
- (value: unknown) => unknown | Promise<unknown> | undefined
94
- >();
95
-
96
- public get preprocessors(): Map<string, (value: unknown) => unknown> {
97
- return this._preprocessorsMap;
98
- }
99
-
100
- public addPreprocessor(
101
- name: string,
102
- preprocessor: (value: unknown) => unknown | Promise<unknown>
103
- ): SchemaValidator<T, SchemaTypesStructures> {
104
- if (this._preprocessorsMap.has(name))
105
- throw new Error(`Preprocessor '${name}' is already registered`);
106
- this._preprocessorsMap.set(name, preprocessor);
107
- return this;
108
- }
109
-
110
- public addSchemaType<
111
- K,
112
- L extends ObjectSchemaDefinitionParam<M> | Array<Schema<any>>,
113
- M = any
114
- >(
115
- name: keyof K,
116
- schema: L
117
- ): SchemaValidator<
118
- T & { [key in keyof K]: L },
119
- Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
120
- > {
121
- if (typeof name !== 'string' || !name)
122
- throw new Error('Name is required');
123
-
124
- if (isDefaultType(name.toString()))
125
- throw new Error(
126
- `You can't add a schema named "${name}" because it's a name of a default schema, please consider another name to be used`
127
- );
128
- if (this._schemasMap.has(name.toString())) {
129
- throw new Error(`Schema "${name}" already exists`);
130
- }
131
-
132
- if (Array.isArray(schema)) {
133
- this._schemasMap.set(name.toString(), schema as Schema<any>);
134
- this._schemasCache = null;
135
- return this as any as SchemaValidator<
136
- T & { [key in keyof K]: L },
137
- Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
138
- >;
139
- }
140
-
141
- if (typeof schema !== 'object')
142
- throw new Error('Array or Object is required');
143
- this._schemasMap.set(name.toString(), {
144
- ...schema,
145
- type: 'object'
146
- } as Schema<any>);
147
- this._schemasCache = null;
148
-
149
- return this as any as SchemaValidator<
150
- T & { [key in keyof K]: L },
151
- Cons<Unfold<keyof K, ISchemaActions<L>>, SchemaTypesStructures>
152
- >;
153
- }
154
-
155
- public get schemas(): Merge<SchemaTypesStructures> {
156
- if (this._schemasCache) return this._schemasCache;
157
- const res = {};
158
- for (const key of this._schemasMap.keys()) {
159
- const parts = key.split('.');
160
- let curr = res;
161
- for (let i = 0; i < parts.length; i++) {
162
- if (i === parts.length - 1) {
163
- curr[parts[i]] = {
164
- validate: (value: any): Promise<any> =>
165
- this.validate(this._schemasMap.get(key), value),
166
- schema: this._schemasMap.get(key)
167
- };
168
- } else {
169
- if (typeof curr[parts[i]] === 'undefined') {
170
- curr[parts[i]] = {};
171
- }
172
- }
173
- curr = curr[parts[i]];
174
- }
175
- }
176
- this._schemasCache = res as Merge<SchemaTypesStructures>;
177
- return this._schemasCache;
178
- }
179
-
180
- private async validateDefaultType(
181
- name: DefaultSchemaType,
182
- value: any,
183
- mergeSchema?: Schema<any>
184
- ): Promise<ValidationResult> {
185
- const strategy = defaultSchemasValidationStrategies[name] as (
186
- obj: any,
187
- schema: Schema<any>,
188
- validator: ISchemaValidator<any, unknown[]>
189
- ) => ValidationResult;
190
- let finalSchema = defaultSchemas[name] as CompositeSchema<
191
- Record<string, never>
192
- >;
193
- if (strategy) {
194
- if (typeof mergeSchema === 'object') {
195
- finalSchema = deepExtend(finalSchema, mergeSchema) as any;
196
- }
197
- if (typeof mergeSchema === 'number') {
198
- finalSchema = {
199
- ...finalSchema,
200
- equals: mergeSchema
201
- } as NumberSchemaDefinition<any>;
202
- mergeSchema;
203
- }
204
-
205
- let preliminaryResult = await strategy(
206
- value,
207
- finalSchema,
208
- this as any
209
- );
210
- if (!preliminaryResult.valid) return preliminaryResult;
211
-
212
- preliminaryResult = await this.checkValidators(finalSchema, value);
213
-
214
- return preliminaryResult;
215
- }
216
- throw new Error('not implemented');
217
- }
218
-
219
- private async checkValidators(
220
- schema: CompositeSchema<Record<string, never>>,
221
- value: any
222
- ): Promise<ValidationResult> {
223
- if (!schema.isRequired && typeof value === 'undefined') {
224
- return {
225
- valid: true
226
- };
227
- }
228
- if (Array.isArray(schema.validators)) {
229
- const validatorsResults = await Promise.allSettled(
230
- schema.validators.map((v) => Promise.resolve(v(value)))
231
- );
232
- const rejections = validatorsResults
233
- .filter((f) => f.status === 'rejected')
234
- .map((f: PromiseRejectedResult) => f.reason);
235
- const errors = validatorsResults
236
- .filter(
237
- (f) =>
238
- f.status === 'fulfilled' &&
239
- typeof f.value !== 'boolean' &&
240
- f.value.valid === false
241
- )
242
- .map(
243
- (f: PromiseFulfilledResult<ValidationResultRaw>) => f.value
244
- )
245
- .map((f: ValidationResultRaw) => f.errors);
246
-
247
- if (rejections.length === 0 && errors.length === 0) {
248
- return {
249
- valid: true
250
- };
251
- }
252
- return {
253
- valid: false,
254
- errors: [...rejections, ...errors]
255
- };
256
- }
257
- return {
258
- valid: true
259
- };
260
- }
261
-
262
- public async validate<K>(
263
- schema: keyof T | DefaultSchemaType | Schema<K>,
264
- obj: any
265
- ): Promise<ValidationResult> {
266
- if (!schema) throw new Error('schemaName is required');
267
- if (typeof schema === 'string') {
268
- if (isDefaultType(schema)) {
269
- return await this.validateDefaultType(
270
- schema as DefaultSchemaType,
271
- obj
272
- );
273
- } else if (typeof this._schemasMap.get(schema) !== 'undefined') {
274
- if (Array.isArray(this._schemasMap.get(schema))) {
275
- return this.validate(this._schemasMap.get(schema), obj);
276
- }
277
- const objSchema = deepExtend(
278
- defaultSchemas.object,
279
- this._schemasMap.get(schema)
280
- ) as ObjectSchemaDefinition<Record<string, never>>;
281
- const res = await validateObject(obj, objSchema, this as any);
282
- if (!res.valid) return res;
283
- return await this.checkValidators(objSchema, obj);
284
- } else {
285
- return await this.validateDefaultType('string', obj, {
286
- type: 'string',
287
- equals: schema
288
- });
289
- }
290
- }
291
-
292
- if (Array.isArray(schema)) {
293
- for (let i = 0; i < schema.length; i++) {
294
- const res = await this.validate(schema[i], obj);
295
- if (res.valid) {
296
- return res;
297
- }
298
- }
299
- return {
300
- valid: false,
301
- errors: ['object does not match any schema']
302
- };
303
- }
304
-
305
- if (typeof schema === 'number') {
306
- return await this.validateDefaultType('number', obj, schema);
307
- }
308
-
309
- if (typeof schema === 'object') {
310
- if (typeof schema.type !== 'string')
311
- throw new Error('Schema has no type');
312
-
313
- if (isDefaultType(schema.type)) {
314
- return await this.validateDefaultType(
315
- schema.type as DefaultSchemaType,
316
- obj,
317
- schema
318
- );
319
- } else if (schema.type === 'alias') {
320
- const alias = this._schemasMap.get(schema.schemaName);
321
- if (typeof alias === 'undefined') {
322
- throw new Error(
323
- `Unknown schema alias - ${schema.schemaName}`
324
- );
325
- }
326
- if (Array.isArray(alias)) {
327
- if (
328
- (!schema.isRequired && typeof obj === 'undefined') ||
329
- (schema.isNullable && obj === null)
330
- ) {
331
- return {
332
- valid: true
333
- };
334
- }
335
- return await this.validate(alias, obj);
336
- }
337
- if (typeof alias !== 'object')
338
- throw new Error(
339
- 'it is only possible to use a full schema schema definition as alias'
340
- );
341
-
342
- const schemaToMerge = { ...(schema as any) };
343
- delete schemaToMerge.type;
344
- delete schemaToMerge.schemaName;
345
- const finalSchema: Schema<any> = deepExtend(
346
- alias,
347
- schemaToMerge
348
- );
349
- return await this.validate(finalSchema, obj);
350
- } else if (schema.type === 'object') {
351
- const preliminaryResult = await validateObject(
352
- obj,
353
- schema,
354
- this as any
355
- );
356
- if (!preliminaryResult.valid) return preliminaryResult;
357
-
358
- return await this.checkValidators(
359
- schema as CompositeSchema<Record<string, never>>,
360
- obj
361
- );
362
- }
363
- }
364
-
365
- throw new Error("Coldn't understand the Schema provided");
366
- }
367
- }