@cleverbrush/schema 1.0.0-beta.4 → 1.0.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 +118 -320
- package/dist/builders/AnySchemaBuilder.d.ts +76 -0
- package/dist/builders/AnySchemaBuilder.js +112 -0
- package/dist/builders/ArraySchemaBuilder.d.ts +112 -14
- package/dist/builders/ArraySchemaBuilder.js +254 -111
- package/dist/builders/BooleanSchemaBuilder.d.ts +69 -29
- package/dist/builders/BooleanSchemaBuilder.js +134 -74
- package/dist/builders/DateSchemaBuilder.d.ts +177 -0
- package/dist/builders/DateSchemaBuilder.js +433 -0
- package/dist/builders/FunctionSchemaBuilder.d.ts +59 -23
- package/dist/builders/FunctionSchemaBuilder.js +103 -56
- package/dist/builders/NumberSchemaBuilder.d.ts +159 -59
- package/dist/builders/NumberSchemaBuilder.js +345 -165
- package/dist/builders/ObjectSchemaBuilder.d.ts +357 -81
- package/dist/builders/ObjectSchemaBuilder.js +519 -283
- package/dist/builders/SchemaBuilder.d.ts +158 -34
- package/dist/builders/SchemaBuilder.js +258 -71
- package/dist/builders/StringSchemaBuilder.d.ts +138 -13
- package/dist/builders/StringSchemaBuilder.js +261 -115
- package/dist/builders/UnionSchemaBuilder.d.ts +129 -9
- package/dist/builders/UnionSchemaBuilder.js +196 -75
- package/dist/index.d.ts +20 -40
- package/dist/index.js +19 -32
- package/dist/utils/transaction.d.ts +46 -0
- package/dist/utils/transaction.js +178 -0
- package/package.json +27 -14
- package/dist/defaultSchemas.d.ts +0 -4
- package/dist/defaultSchemas.js +0 -45
- package/dist/schema.d.ts +0 -230
- package/dist/schema.js +0 -2
- package/dist/schemaRegistry.d.ts +0 -49
- package/dist/schemaRegistry.js +0 -278
- package/dist/validators/validateArray.d.ts +0 -3
- package/dist/validators/validateArray.js +0 -60
- package/dist/validators/validateBoolean.d.ts +0 -2
- package/dist/validators/validateBoolean.js +0 -30
- package/dist/validators/validateFunction.d.ts +0 -2
- package/dist/validators/validateFunction.js +0 -21
- package/dist/validators/validateNumber.d.ts +0 -2
- package/dist/validators/validateNumber.js +0 -69
- package/dist/validators/validateObject.d.ts +0 -3
- package/dist/validators/validateObject.js +0 -95
- package/dist/validators/validateString.d.ts +0 -2
- package/dist/validators/validateString.js +0 -50
- package/dist/validators/validateUnion.d.ts +0 -3
- package/dist/validators/validateUnion.js +0 -30
- package/src/builders/ArraySchemaBuilder.test.ts +0 -270
- package/src/builders/ArraySchemaBuilder.ts +0 -381
- package/src/builders/BooleanSchemaBuilder.test.ts +0 -196
- package/src/builders/BooleanSchemaBuilder.ts +0 -155
- package/src/builders/FunctionSchemaBuilder.test.ts +0 -134
- package/src/builders/FunctionSchemaBuilder.ts +0 -109
- package/src/builders/NumberSchemaBuilder.test.ts +0 -493
- package/src/builders/NumberSchemaBuilder.ts +0 -789
- package/src/builders/ObjectSchemaBuilder.test.ts +0 -657
- package/src/builders/ObjectSchemaBuilder.ts +0 -794
- package/src/builders/SchemaBuilder.test.ts +0 -73
- package/src/builders/SchemaBuilder.ts +0 -135
- package/src/builders/StringSchemaBuilder.test.ts +0 -318
- package/src/builders/StringSchemaBuilder.ts +0 -392
- package/src/builders/UnionSchemaBuilder.test.ts +0 -162
- package/src/builders/UnionSchemaBuilder.ts +0 -154
- package/src/defaultSchemas.ts +0 -44
- package/src/index.ts +0 -66
- package/src/schema.ts +0 -827
- package/src/schemaRegistry.builders.test.ts +0 -1393
- package/src/schemaRegistry.test.ts +0 -118
- package/src/schemaRegistry.ts +0 -461
- package/src/validators/validateArray.ts +0 -76
- package/src/validators/validateBoolean.ts +0 -36
- package/src/validators/validateFunction.ts +0 -25
- package/src/validators/validateNumber.ts +0 -82
- package/src/validators/validateObject.ts +0 -119
- package/src/validators/validateString.ts +0 -59
- package/src/validators/validateUnion.ts +0 -36
- package/tsconfig.json +0 -16
|
@@ -1,344 +1,580 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import { SchemaBuilder } from './SchemaBuilder.js';
|
|
2
|
+
/**
|
|
3
|
+
* Object schema builder class. Similar to the `object` type
|
|
4
|
+
* in JS. Allows to define a schema for `object` value.
|
|
5
|
+
* Should be used to validate objects with specific properties.
|
|
6
|
+
* Properties should be defined as their own schema builders.
|
|
7
|
+
* You can use any `SchemaBuilder` e.g. `string()`, `number()`,
|
|
8
|
+
* `boolean()`, `array()`, `object()`, etc. to define properties.
|
|
9
|
+
* Which means that you can define nested objects and arrays of
|
|
10
|
+
* any complexity.
|
|
11
|
+
*
|
|
12
|
+
* **NOTE** this class is exported only to give opportunity to extend it
|
|
13
|
+
* by inheriting. It is not recommended to create an instance of this class
|
|
14
|
+
* directly. Use {@link object | object()} function instead.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const schema = object({
|
|
19
|
+
* name: string(),
|
|
20
|
+
* age: number()
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const result = await schema.validate({
|
|
24
|
+
* name: 'John',
|
|
25
|
+
* age: 30
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // result.valid === true
|
|
29
|
+
* // result.object === { name: 'John', age: 30 }
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const schema = object({
|
|
35
|
+
* name: string(),
|
|
36
|
+
* age: number().optional()
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* const result = await schema.validate({
|
|
40
|
+
* name: 'John'
|
|
41
|
+
* });
|
|
42
|
+
* // result.valid === true
|
|
43
|
+
* // result.object === { name: 'John' }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const schema = object({
|
|
49
|
+
* name: string(),
|
|
50
|
+
* age: number();
|
|
51
|
+
* });
|
|
52
|
+
* const result = await schema.validate({
|
|
53
|
+
* name: 'John'
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* // result.valid === false
|
|
57
|
+
* // result.errors[0].message === "is expected to have property 'age'"
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const schema = object({
|
|
63
|
+
* name: string(),
|
|
64
|
+
* address: object({
|
|
65
|
+
* city: string(),
|
|
66
|
+
* country: string()
|
|
67
|
+
* })
|
|
68
|
+
* });
|
|
69
|
+
* const result = await schema.validate({
|
|
70
|
+
* name: 'John',
|
|
71
|
+
* address: {
|
|
72
|
+
* city: 'New York',
|
|
73
|
+
* country: 'USA'
|
|
74
|
+
* }
|
|
75
|
+
* });
|
|
76
|
+
* // result.valid === true
|
|
77
|
+
* // result.object === {
|
|
78
|
+
* // name: 'John',
|
|
79
|
+
* // address: {
|
|
80
|
+
* // city: 'New York',
|
|
81
|
+
* // country: 'USA'
|
|
82
|
+
* // }
|
|
83
|
+
* // }
|
|
84
|
+
* ```
|
|
85
|
+
* @see {@link object}
|
|
86
|
+
*/
|
|
87
|
+
export class ObjectSchemaBuilder extends SchemaBuilder {
|
|
88
|
+
#properties = {};
|
|
89
|
+
#acceptUnknownProps = false;
|
|
90
|
+
static create(props) {
|
|
91
|
+
return new ObjectSchemaBuilder({
|
|
92
|
+
type: 'object',
|
|
93
|
+
...props
|
|
20
94
|
});
|
|
21
95
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
96
|
+
createFromProps(props) {
|
|
97
|
+
return ObjectSchemaBuilder.create(props);
|
|
98
|
+
}
|
|
99
|
+
constructor(props) {
|
|
100
|
+
super(props);
|
|
101
|
+
if (typeof props.properties === 'object' && props.properties) {
|
|
102
|
+
this.#properties = props.properties;
|
|
103
|
+
}
|
|
104
|
+
if (typeof props.acceptUnknownProps === 'boolean') {
|
|
105
|
+
this.#acceptUnknownProps = props.acceptUnknownProps;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
introspect() {
|
|
109
|
+
return {
|
|
110
|
+
...super.introspect(),
|
|
111
|
+
/**
|
|
112
|
+
* Properties defined in schema
|
|
113
|
+
*/
|
|
114
|
+
properties: { ...this.#properties },
|
|
115
|
+
/**
|
|
116
|
+
* If set to `true`, schema validation will not
|
|
117
|
+
* return errors if object contains fields which
|
|
118
|
+
* are not defined in the schema `properties`.
|
|
119
|
+
* Set to `false` by default
|
|
120
|
+
*/
|
|
121
|
+
acceptUnknownProps: this.#acceptUnknownProps
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* @hidden
|
|
126
|
+
*/
|
|
127
|
+
required() {
|
|
128
|
+
return super.required();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* @hidden
|
|
132
|
+
*/
|
|
133
|
+
optional() {
|
|
134
|
+
return super.optional();
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Performs validion of object schema over the `object`.
|
|
138
|
+
* @param context Optional `ValidationContext` settings.
|
|
139
|
+
*/
|
|
140
|
+
async validate(object, context) {
|
|
141
|
+
const prevalidatedResult = await super.preValidate(object, context);
|
|
142
|
+
const { valid, context: prevalidationContext, transaction: validationTransaction, errors: preValidationErrors } = prevalidatedResult;
|
|
143
|
+
const { path, doNotStopOnFirstError } = prevalidationContext;
|
|
144
|
+
let errors = prevalidatedResult.errors || [];
|
|
145
|
+
if (!valid && !doNotStopOnFirstError) {
|
|
146
|
+
return {
|
|
147
|
+
valid,
|
|
148
|
+
errors: preValidationErrors
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const { object: { validatedObject: objToValidate } } = validationTransaction;
|
|
152
|
+
if (!this.isRequired &&
|
|
153
|
+
(typeof objToValidate === 'undefined' || objToValidate === null)) {
|
|
154
|
+
return {
|
|
155
|
+
valid: true,
|
|
156
|
+
object: validationTransaction.commit().validatedObject
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (typeof objToValidate !== 'object') {
|
|
160
|
+
errors.push({
|
|
161
|
+
message: 'must be an object',
|
|
162
|
+
path: path
|
|
163
|
+
});
|
|
164
|
+
if (!doNotStopOnFirstError) {
|
|
165
|
+
if (validationTransaction) {
|
|
166
|
+
validationTransaction.rollback();
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
valid: false,
|
|
170
|
+
errors: [errors[0]]
|
|
171
|
+
};
|
|
40
172
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
173
|
+
}
|
|
174
|
+
const propKeys = Object.keys(this.#properties);
|
|
175
|
+
const objKeys = Object.keys(objToValidate);
|
|
176
|
+
if (propKeys.length === 0) {
|
|
177
|
+
if (objKeys.length === 0) {
|
|
178
|
+
if (doNotStopOnFirstError && errors.length > 0) {
|
|
179
|
+
return {
|
|
180
|
+
valid: false,
|
|
181
|
+
errors
|
|
182
|
+
};
|
|
47
183
|
}
|
|
184
|
+
if (validationTransaction) {
|
|
185
|
+
validationTransaction.commit().validatedObject;
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
valid: true,
|
|
189
|
+
object: {}
|
|
190
|
+
};
|
|
48
191
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
192
|
+
}
|
|
193
|
+
const validationResults = await Promise.all(propKeys.map(async (key) => ({
|
|
194
|
+
key,
|
|
195
|
+
result: await this.#properties[key].validate(objToValidate[key], {
|
|
196
|
+
...context,
|
|
197
|
+
path: `${path}.${key}`
|
|
198
|
+
})
|
|
199
|
+
})));
|
|
200
|
+
const notValidResults = validationResults.filter((res) => !res.result.valid);
|
|
201
|
+
validationResults
|
|
202
|
+
.filter((res) => res.result.valid)
|
|
203
|
+
.forEach(({ key, result }) => {
|
|
204
|
+
objToValidate[key] = result.object;
|
|
205
|
+
});
|
|
206
|
+
errors = [
|
|
207
|
+
...errors,
|
|
208
|
+
...notValidResults.reduce((acc, val) => [...acc, ...(val?.result?.errors || [])], [])
|
|
209
|
+
];
|
|
210
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
211
|
+
const key = objKeys[i];
|
|
212
|
+
if (!(key in this.#properties) && !this.#acceptUnknownProps) {
|
|
213
|
+
errors.push({
|
|
214
|
+
message: `unknown property '${key}'`,
|
|
215
|
+
path: path
|
|
216
|
+
});
|
|
217
|
+
if (!doNotStopOnFirstError) {
|
|
218
|
+
if (validationTransaction) {
|
|
219
|
+
validationTransaction.rollback();
|
|
63
220
|
}
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
if (typeof obj.noUnknownProperties !== 'undefined') {
|
|
71
|
-
this.noUnknownProperties = obj.noUnknownProperties;
|
|
221
|
+
return {
|
|
222
|
+
valid: false,
|
|
223
|
+
errors: [errors[0]]
|
|
224
|
+
};
|
|
225
|
+
}
|
|
72
226
|
}
|
|
73
227
|
}
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
228
|
+
if (notValidResults.length === 0 && errors.length === 0) {
|
|
229
|
+
const commited = validationTransaction.commit();
|
|
230
|
+
return {
|
|
231
|
+
valid: true,
|
|
232
|
+
object: commited.validatedObject
|
|
233
|
+
};
|
|
78
234
|
}
|
|
235
|
+
validationTransaction.rollback();
|
|
236
|
+
return {
|
|
237
|
+
valid: false,
|
|
238
|
+
errors: doNotStopOnFirstError
|
|
239
|
+
? errors
|
|
240
|
+
: errors[0]
|
|
241
|
+
? [errors[0]]
|
|
242
|
+
: []
|
|
243
|
+
};
|
|
79
244
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
...this._schema,
|
|
89
|
-
noUnknownProperties: false
|
|
245
|
+
/**
|
|
246
|
+
* Fields not defined in `properties` will not be validated
|
|
247
|
+
* and will be passed through the validation.
|
|
248
|
+
*/
|
|
249
|
+
acceptUnknownProps() {
|
|
250
|
+
return this.createFromProps({
|
|
251
|
+
...this.introspect(),
|
|
252
|
+
acceptUnknownProps: true
|
|
90
253
|
});
|
|
91
254
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Fields not defined in `properties` will be considered
|
|
257
|
+
* as schema violation. This is the default behavior.
|
|
258
|
+
*/
|
|
259
|
+
notAcceptUnknownProps() {
|
|
260
|
+
return this.createFromProps({
|
|
261
|
+
...this.introspect(),
|
|
262
|
+
acceptUnknownProps: false
|
|
99
263
|
});
|
|
100
264
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
265
|
+
/**
|
|
266
|
+
* @hidden
|
|
267
|
+
*/
|
|
268
|
+
hasType(
|
|
269
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
270
|
+
notUsed) {
|
|
271
|
+
return this.createFromProps({
|
|
272
|
+
...this.introspect()
|
|
108
273
|
});
|
|
109
274
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return
|
|
115
|
-
...this.
|
|
116
|
-
isRequired: true
|
|
275
|
+
/**
|
|
276
|
+
* @hidden
|
|
277
|
+
*/
|
|
278
|
+
clearHasType() {
|
|
279
|
+
return this.createFromProps({
|
|
280
|
+
...this.introspect()
|
|
117
281
|
});
|
|
118
282
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
283
|
+
/**
|
|
284
|
+
* Adds a new property to the object schema. The new property
|
|
285
|
+
* will be validated according to the provided schema.
|
|
286
|
+
* @param propName name of the new property
|
|
287
|
+
* @param schema schema builder of the new property
|
|
288
|
+
*/
|
|
289
|
+
addProp(propName, schema) {
|
|
290
|
+
if (typeof propName !== 'string' || !propName) {
|
|
291
|
+
throw new Error('propName must be a non empty string');
|
|
122
292
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
isNullable: true
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
notNullable() {
|
|
129
|
-
if (this.isNullable === false) {
|
|
130
|
-
return this;
|
|
293
|
+
if (propName in this.#properties) {
|
|
294
|
+
throw new Error(`Property ${propName} already exists`);
|
|
131
295
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
addProps(val) {
|
|
138
|
-
if (typeof val === 'undefined')
|
|
139
|
-
return this;
|
|
140
|
-
return ObjectSchemaBuilder.create({
|
|
141
|
-
...this._schema,
|
|
296
|
+
if (!(schema instanceof SchemaBuilder)) {
|
|
297
|
+
throw new Error('schema must be an instance of the SchemaBuilder class');
|
|
298
|
+
}
|
|
299
|
+
return this.createFromProps({
|
|
300
|
+
...this.introspect(),
|
|
142
301
|
properties: {
|
|
143
|
-
...this.properties,
|
|
144
|
-
|
|
302
|
+
...this.introspect().properties,
|
|
303
|
+
[propName]: schema
|
|
145
304
|
}
|
|
146
305
|
});
|
|
147
306
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
307
|
+
/**
|
|
308
|
+
* @hidden
|
|
309
|
+
* @deprecated this is for internal use, do not use if you are
|
|
310
|
+
* not sure you need it.
|
|
311
|
+
*/
|
|
312
|
+
optimize() {
|
|
313
|
+
return this.createFromProps({
|
|
314
|
+
...this.introspect()
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
addProps(props) {
|
|
318
|
+
if (props instanceof ObjectSchemaBuilder) {
|
|
319
|
+
return this.addProps(props.introspect().properties);
|
|
151
320
|
}
|
|
152
|
-
if (
|
|
153
|
-
|
|
321
|
+
if (typeof props !== 'object') {
|
|
322
|
+
throw new Error('props should be an object');
|
|
154
323
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (Object.keys(newPreprocessors).length === 1) {
|
|
163
|
-
newPreprocessors = undefined;
|
|
324
|
+
if (props === null) {
|
|
325
|
+
throw new Error('props should not be null');
|
|
326
|
+
}
|
|
327
|
+
const newProps = { ...this.#properties };
|
|
328
|
+
for (const key in props) {
|
|
329
|
+
if (key in this.#properties) {
|
|
330
|
+
throw new Error(`property '${key}' already exists`);
|
|
164
331
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
...this.preprocessors
|
|
168
|
-
};
|
|
169
|
-
delete newPreprocessors[property];
|
|
332
|
+
if (!(props[key] instanceof SchemaBuilder)) {
|
|
333
|
+
throw new Error(`${key} is not a SchemaBuilder`);
|
|
170
334
|
}
|
|
335
|
+
newProps[key] = props[key];
|
|
171
336
|
}
|
|
172
|
-
return
|
|
173
|
-
...this.
|
|
174
|
-
|
|
175
|
-
properties: newProperties
|
|
337
|
+
return this.createFromProps({
|
|
338
|
+
...this.introspect(),
|
|
339
|
+
properties: newProps
|
|
176
340
|
});
|
|
177
341
|
}
|
|
178
|
-
|
|
179
|
-
if (typeof
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
342
|
+
omit(propNameOrArrayOrPropsOrBuilder) {
|
|
343
|
+
if (typeof propNameOrArrayOrPropsOrBuilder === 'string') {
|
|
344
|
+
// remove one field
|
|
345
|
+
const propName = propNameOrArrayOrPropsOrBuilder;
|
|
346
|
+
if (!propName || !(propName in this.#properties)) {
|
|
347
|
+
throw new Error(`property ${propName.toString()} does not exists in the schema`);
|
|
348
|
+
}
|
|
349
|
+
return this.createFromProps({
|
|
350
|
+
...this.introspect(),
|
|
351
|
+
properties: (() => {
|
|
352
|
+
const result = { ...this.#properties };
|
|
353
|
+
delete result[propName];
|
|
354
|
+
return result;
|
|
355
|
+
})()
|
|
356
|
+
});
|
|
183
357
|
}
|
|
184
|
-
else {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
358
|
+
else if (Array.isArray(propNameOrArrayOrPropsOrBuilder)) {
|
|
359
|
+
const propsArray = propNameOrArrayOrPropsOrBuilder;
|
|
360
|
+
const distinctKeys = new Map();
|
|
361
|
+
propsArray.forEach((key) => {
|
|
362
|
+
if (typeof key !== 'string' || !key) {
|
|
363
|
+
throw new Error('property name must be a string');
|
|
364
|
+
}
|
|
365
|
+
if (!(key in this.#properties)) {
|
|
366
|
+
throw new Error(`property ${key.toString()} does not exists in the schema`);
|
|
367
|
+
}
|
|
368
|
+
distinctKeys.set(key.toString(), true);
|
|
369
|
+
});
|
|
370
|
+
if (distinctKeys.size === 0) {
|
|
371
|
+
throw new Error('please provide at least one property to omit');
|
|
372
|
+
}
|
|
373
|
+
const props = {
|
|
374
|
+
...this.introspect()
|
|
188
375
|
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
...this._schema,
|
|
192
|
-
preprocessors: {
|
|
193
|
-
...this.preprocessors
|
|
376
|
+
for (const key of distinctKeys.keys()) {
|
|
377
|
+
delete props.properties[key];
|
|
194
378
|
}
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
unsetPropPreprocessor(property) {
|
|
198
|
-
if (typeof this.preprocessors === 'undefined' ||
|
|
199
|
-
!(property in this.preprocessors)) {
|
|
200
|
-
return this;
|
|
379
|
+
return this.createFromProps(props);
|
|
201
380
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
...this.preprocessors
|
|
381
|
+
else if (propNameOrArrayOrPropsOrBuilder instanceof ObjectSchemaBuilder) {
|
|
382
|
+
const propsToOmit = {
|
|
383
|
+
...propNameOrArrayOrPropsOrBuilder.introspect().properties
|
|
384
|
+
};
|
|
385
|
+
const props = {
|
|
386
|
+
...this.introspect()
|
|
209
387
|
};
|
|
210
|
-
|
|
388
|
+
for (const key in propsToOmit) {
|
|
389
|
+
if (key in props.properties) {
|
|
390
|
+
delete props.properties[key];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return this.createFromProps(props);
|
|
211
394
|
}
|
|
212
|
-
|
|
213
|
-
...this._schema,
|
|
214
|
-
preprocessors: newPreprocessors
|
|
215
|
-
});
|
|
395
|
+
throw new Error('this parameter type is not supported');
|
|
216
396
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
397
|
+
/**
|
|
398
|
+
* Adds all properties from `schema` to the current schema.
|
|
399
|
+
* `TSchema & TAnotherSchema` is a good example of the similar concept
|
|
400
|
+
* in the TS type system.
|
|
401
|
+
* @param schema an object schema to take properties from
|
|
402
|
+
*/
|
|
403
|
+
intersect(schema) {
|
|
404
|
+
if (!(schema instanceof ObjectSchemaBuilder)) {
|
|
405
|
+
throw new Error('schema must be an instance of the ObjectSchemaBuilder class');
|
|
220
406
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
407
|
+
const remoteProps = schema.introspect().properties;
|
|
408
|
+
const localProps = this.introspect();
|
|
409
|
+
const newProps = Object.keys(localProps.properties).reduce((acc, curr) => {
|
|
410
|
+
acc[curr] =
|
|
411
|
+
curr in remoteProps ? remoteProps[curr] : localProps[curr];
|
|
412
|
+
return acc;
|
|
413
|
+
}, {});
|
|
414
|
+
return this.createFromProps({
|
|
415
|
+
...this.introspect(),
|
|
416
|
+
properties: newProps
|
|
224
417
|
});
|
|
225
418
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
res = res.makePropOptional(prop);
|
|
236
|
-
}
|
|
237
|
-
return res;
|
|
238
|
-
}
|
|
239
|
-
makePropOptional(property) {
|
|
240
|
-
if (typeof this.properties[property] === 'undefined')
|
|
241
|
-
throw new Error(`Property ${property} does not exists`);
|
|
242
|
-
if (this.properties[property] instanceof SchemaBuilder_js_1.SchemaBuilder) {
|
|
243
|
-
return ObjectSchemaBuilder.create({
|
|
244
|
-
...this._schema,
|
|
245
|
-
properties: {
|
|
246
|
-
...this.properties,
|
|
247
|
-
[property]: this.properties[property].optional()
|
|
248
|
-
}
|
|
419
|
+
partial(propNameOrArray) {
|
|
420
|
+
if (typeof propNameOrArray === 'undefined' ||
|
|
421
|
+
propNameOrArray === null) {
|
|
422
|
+
return this.createFromProps({
|
|
423
|
+
...this.introspect(),
|
|
424
|
+
properties: Object.keys(this.#properties).reduce((acc, key) => {
|
|
425
|
+
acc[key] = this.#properties[key].optional();
|
|
426
|
+
return acc;
|
|
427
|
+
}, {})
|
|
249
428
|
});
|
|
250
429
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
properties
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
430
|
+
if (Array.isArray(propNameOrArray)) {
|
|
431
|
+
const propsArray = propNameOrArray;
|
|
432
|
+
if (propsArray.length === 0) {
|
|
433
|
+
throw new Error('properties cannot be empty');
|
|
434
|
+
}
|
|
435
|
+
const newProps = {
|
|
436
|
+
...this.introspect()
|
|
437
|
+
};
|
|
438
|
+
propsArray.forEach((key) => {
|
|
439
|
+
if (typeof key !== 'string') {
|
|
440
|
+
throw new Error('each propery in property list must be as string value');
|
|
260
441
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
makePropRequired(property) {
|
|
265
|
-
if (typeof this.properties[property] === 'undefined')
|
|
266
|
-
throw new Error(`Property ${property} does not exists`);
|
|
267
|
-
if (this.properties[property] instanceof SchemaBuilder_js_1.SchemaBuilder) {
|
|
268
|
-
return ObjectSchemaBuilder.create({
|
|
269
|
-
...this._schema,
|
|
270
|
-
properties: {
|
|
271
|
-
...this.properties,
|
|
272
|
-
[property]: this.properties[property].required()
|
|
442
|
+
if (!(key in newProps.properties)) {
|
|
443
|
+
throw new Error(`property ${key} does not exists`);
|
|
273
444
|
}
|
|
445
|
+
newProps.properties[key] =
|
|
446
|
+
newProps.properties[key].optional();
|
|
274
447
|
});
|
|
448
|
+
return this.createFromProps(newProps);
|
|
275
449
|
}
|
|
276
|
-
|
|
277
|
-
return
|
|
278
|
-
...this._schema,
|
|
279
|
-
properties: {
|
|
280
|
-
...this.properties,
|
|
281
|
-
[property]: {
|
|
282
|
-
...this.properties[property],
|
|
283
|
-
isRequired: true
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
});
|
|
450
|
+
if (typeof propNameOrArray === 'string') {
|
|
451
|
+
return this.modifyPropSchema(propNameOrArray, (schema) => schema.optional());
|
|
287
452
|
}
|
|
453
|
+
throw new Error('expecting string or string[] parameter');
|
|
288
454
|
}
|
|
289
|
-
|
|
290
|
-
if (typeof
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
455
|
+
pick(properties) {
|
|
456
|
+
if (typeof properties === 'string') {
|
|
457
|
+
const property = properties;
|
|
458
|
+
if (!property) {
|
|
459
|
+
throw new Error('property cannot be empty');
|
|
460
|
+
}
|
|
461
|
+
if (!(property in this.#properties)) {
|
|
462
|
+
throw new Error(`property ${property} does not exists`);
|
|
463
|
+
}
|
|
464
|
+
return this.createFromProps({
|
|
465
|
+
...this.introspect(),
|
|
295
466
|
properties: {
|
|
296
|
-
|
|
297
|
-
[property]: this.properties[property].nullable()
|
|
467
|
+
[property]: this.#properties[property]
|
|
298
468
|
}
|
|
299
469
|
});
|
|
300
470
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
471
|
+
if (Array.isArray(properties)) {
|
|
472
|
+
if (properties.length === 0) {
|
|
473
|
+
throw new Error('properties must be a non empty erray');
|
|
474
|
+
}
|
|
475
|
+
const newProperties = properties.reduce((acc, curr) => {
|
|
476
|
+
if (typeof curr !== 'string' || !curr) {
|
|
477
|
+
throw new Error('each property name must be a non empty string');
|
|
478
|
+
}
|
|
479
|
+
if (!(curr in this.#properties)) {
|
|
480
|
+
throw new Error(`property ${curr} does not exists`);
|
|
310
481
|
}
|
|
482
|
+
acc[curr] = this.#properties[curr];
|
|
483
|
+
return acc;
|
|
484
|
+
}, {});
|
|
485
|
+
return this.createFromProps({
|
|
486
|
+
...this.introspect(),
|
|
487
|
+
properties: newProperties
|
|
311
488
|
});
|
|
312
489
|
}
|
|
490
|
+
if (properties instanceof ObjectSchemaBuilder) {
|
|
491
|
+
const externalSchema = properties;
|
|
492
|
+
const props = Object.keys(externalSchema.introspect().properties).filter((p) => typeof this.#properties[p] !== 'undefined');
|
|
493
|
+
if (props.length === 0) {
|
|
494
|
+
throw new Error('there are no common properties in provided schemas');
|
|
495
|
+
}
|
|
496
|
+
return this.pick(props);
|
|
497
|
+
}
|
|
498
|
+
throw new Error('string, array or ObjectSchemaBuilder is expected');
|
|
313
499
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
500
|
+
/**
|
|
501
|
+
* Modify schema for `propName` and return a new schema.
|
|
502
|
+
* Could be useful if you want to leave all schema intact, but
|
|
503
|
+
* change a type of one property.
|
|
504
|
+
* @param propName name of the property (string)
|
|
505
|
+
* @param callback callback function returning a new schema fo the `propName`. As a first parameter
|
|
506
|
+
* you will receive an old schema for `propName`.
|
|
507
|
+
* @returns
|
|
508
|
+
*/
|
|
509
|
+
modifyPropSchema(propName, callback) {
|
|
510
|
+
if (typeof propName !== 'string' || !propName) {
|
|
511
|
+
throw new Error('propName must be a non empty string');
|
|
325
512
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
});
|
|
513
|
+
if (!(propName in this.#properties)) {
|
|
514
|
+
throw new Error(`property ${propName} does not exists in the schema`);
|
|
515
|
+
}
|
|
516
|
+
if (typeof callback !== 'function') {
|
|
517
|
+
throw new Error('callback must be a function');
|
|
518
|
+
}
|
|
519
|
+
const callbackResult = callback(this.#properties[propName]);
|
|
520
|
+
if (!(callbackResult instanceof SchemaBuilder)) {
|
|
521
|
+
throw new Error('callback must return a SchemaBuilder object');
|
|
337
522
|
}
|
|
523
|
+
const props = {
|
|
524
|
+
...this.introspect()
|
|
525
|
+
};
|
|
526
|
+
props.properties = {
|
|
527
|
+
...props.properties,
|
|
528
|
+
[propName]: callbackResult
|
|
529
|
+
};
|
|
530
|
+
return this.createFromProps(props);
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* An alias for `.partial(prop: string)`
|
|
534
|
+
* @param prop name of the property
|
|
535
|
+
*/
|
|
536
|
+
makePropOptional(prop) {
|
|
537
|
+
return this.modifyPropSchema(prop, (builder) => builder.optional());
|
|
338
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Marks `prop` as required property.
|
|
541
|
+
* If `prop` does not exists in the current schema,
|
|
542
|
+
* an error will be thrown.
|
|
543
|
+
* @param prop name of the property
|
|
544
|
+
*/
|
|
545
|
+
makePropRequired(prop) {
|
|
546
|
+
return this.modifyPropSchema(prop, (builder) => builder.required());
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* `Partial<T>` would be a good example of the
|
|
550
|
+
* same operation in the TS world.
|
|
551
|
+
*/
|
|
552
|
+
makeAllPropsOptional() {
|
|
553
|
+
return this.createFromProps({
|
|
554
|
+
...this.introspect(),
|
|
555
|
+
properties: Object.keys(this.#properties).reduce((acc, curr) => {
|
|
556
|
+
acc[curr] = this.#properties[curr].optional();
|
|
557
|
+
return acc;
|
|
558
|
+
}, {})
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* `Required<T>` would be a good example of the
|
|
563
|
+
* same operation in the TS world.
|
|
564
|
+
*/
|
|
565
|
+
makeAllPropsRequired() {
|
|
566
|
+
return this.createFromProps({
|
|
567
|
+
...this.introspect(),
|
|
568
|
+
properties: Object.keys(this.#properties).reduce((acc, curr) => {
|
|
569
|
+
acc[curr] = this.#properties[curr].required();
|
|
570
|
+
return acc;
|
|
571
|
+
}, {})
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
export function object(props) {
|
|
576
|
+
return ObjectSchemaBuilder.create({
|
|
577
|
+
isRequired: true,
|
|
578
|
+
properties: props
|
|
579
|
+
});
|
|
339
580
|
}
|
|
340
|
-
exports.ObjectSchemaBuilder = ObjectSchemaBuilder;
|
|
341
|
-
const object = (properties) => typeof properties === 'undefined'
|
|
342
|
-
? ObjectSchemaBuilder.create()
|
|
343
|
-
: ObjectSchemaBuilder.create().addProps(properties);
|
|
344
|
-
exports.object = object;
|