@cleverbrush/schema 0.0.3
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 +279 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +9 -0
- package/dist/schemaValidator.d.ts +14 -0
- package/dist/schemaValidator.js +170 -0
- package/dist/validators/validateArray.d.ts +2 -0
- package/dist/validators/validateArray.js +49 -0
- package/dist/validators/validateNumber.d.ts +2 -0
- package/dist/validators/validateNumber.js +69 -0
- package/dist/validators/validateObject.d.ts +2 -0
- package/dist/validators/validateObject.js +44 -0
- package/dist/validators/validateString.d.ts +2 -0
- package/dist/validators/validateString.js +50 -0
- package/package.json +20 -0
- package/src/index.ts +137 -0
- package/src/schemaValidator.test.ts +887 -0
- package/src/schemaValidator.ts +256 -0
- package/src/validators/validateArray.ts +66 -0
- package/src/validators/validateNumber.ts +87 -0
- package/src/validators/validateObject.ts +65 -0
- package/src/validators/validateString.ts +64 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { deepExtend } 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
|
+
} from './index';
|
|
17
|
+
import { validateNumber } from './validators/validateNumber';
|
|
18
|
+
import { validateString } from './validators/validateString';
|
|
19
|
+
import { validateArray } from './validators/validateArray';
|
|
20
|
+
import { validateObject } from './validators/validateObject';
|
|
21
|
+
|
|
22
|
+
const defaultSchemaNames = ['string', 'number', 'date', 'array'];
|
|
23
|
+
|
|
24
|
+
const defaultSchemas: { [key in DefaultSchemaType]?: Schema<any> } = {
|
|
25
|
+
number: {
|
|
26
|
+
type: 'number',
|
|
27
|
+
isRequired: true,
|
|
28
|
+
isNullable: false,
|
|
29
|
+
ensureNotNaN: true,
|
|
30
|
+
ensureIsFinite: true
|
|
31
|
+
},
|
|
32
|
+
string: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
isNullable: false,
|
|
35
|
+
isRequired: true
|
|
36
|
+
},
|
|
37
|
+
array: {
|
|
38
|
+
type: 'array'
|
|
39
|
+
},
|
|
40
|
+
object: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
isNullable: false,
|
|
43
|
+
isRequired: true
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const defaultSchemasValidationStrategies = {
|
|
48
|
+
number: (
|
|
49
|
+
obj: any,
|
|
50
|
+
schema: NumberSchemaDefinition<any>,
|
|
51
|
+
validator: ISchemaValidator<any>
|
|
52
|
+
): Promise<ValidationResult> => validateNumber(obj, schema, validator),
|
|
53
|
+
string: (
|
|
54
|
+
obj: any,
|
|
55
|
+
schema: StringSchemaDefinition<any>,
|
|
56
|
+
validator: ISchemaValidator<any>
|
|
57
|
+
): Promise<ValidationResult> => validateString(obj, schema, validator),
|
|
58
|
+
array: (
|
|
59
|
+
obj: any,
|
|
60
|
+
schema: ArraySchemaDefinition<any>,
|
|
61
|
+
validator: ISchemaValidator<any>
|
|
62
|
+
): Promise<ValidationResult> => validateArray(obj, schema, validator)
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const isDefaultType = (name: string): boolean =>
|
|
66
|
+
defaultSchemaNames.indexOf(name) !== -1;
|
|
67
|
+
|
|
68
|
+
export default class SchemaValidator<T = Record<string, never>>
|
|
69
|
+
implements ISchemasProvider<T>, ISchemaValidator<T>
|
|
70
|
+
{
|
|
71
|
+
private _schemasMap = new Map<string, Schema<any>>();
|
|
72
|
+
private _schemasCache: { [K in keyof T]: ISchemaActions<T, K> } = null;
|
|
73
|
+
|
|
74
|
+
public addSchemaType<K, L extends ObjectSchemaDefinitionParam<M>, M = any>(
|
|
75
|
+
name: keyof K,
|
|
76
|
+
schema: L
|
|
77
|
+
): SchemaValidator<T & { [key in keyof K]: L }> {
|
|
78
|
+
if (typeof name !== 'string' || !name)
|
|
79
|
+
throw new Error('Name is required');
|
|
80
|
+
if (typeof schema !== 'object') throw new Error('Object is required');
|
|
81
|
+
if (isDefaultType(name.toString()))
|
|
82
|
+
throw new Error(
|
|
83
|
+
`You can't add a schema named "${name}" because it's a name of a default schema, please consider another name to be used`
|
|
84
|
+
);
|
|
85
|
+
if (this._schemasMap.has(name.toString())) {
|
|
86
|
+
throw new Error(`Schema "${name}" already exists`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this._schemasMap.set(name.toString(), {
|
|
90
|
+
...schema,
|
|
91
|
+
type: 'object'
|
|
92
|
+
} as Schema<any>);
|
|
93
|
+
this._schemasCache = null;
|
|
94
|
+
|
|
95
|
+
return this as any as SchemaValidator<T & { [key in keyof K]: L }>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public get schemas(): { [K in keyof T]: ISchemaActions<T, K> } {
|
|
99
|
+
if (this._schemasCache) return this._schemasCache;
|
|
100
|
+
const res = {};
|
|
101
|
+
for (const key of this._schemasMap.keys()) {
|
|
102
|
+
res[key] = {
|
|
103
|
+
validate: (value: any): Promise<any> =>
|
|
104
|
+
this.validate(this._schemasMap.get(key), value),
|
|
105
|
+
schema: this._schemasMap.get(key)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
this._schemasCache = res as { [K in keyof T]: ISchemaActions<T, K> };
|
|
109
|
+
return this._schemasCache;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private async validateDefaultType(
|
|
113
|
+
name: DefaultSchemaType,
|
|
114
|
+
value: any,
|
|
115
|
+
mergeSchema?: Schema<any>
|
|
116
|
+
): Promise<ValidationResult> {
|
|
117
|
+
const strategy = defaultSchemasValidationStrategies[name] as (
|
|
118
|
+
obj: any,
|
|
119
|
+
schema: Schema<any>,
|
|
120
|
+
validator: ISchemaValidator<any>
|
|
121
|
+
) => ValidationResult;
|
|
122
|
+
let finalSchema = defaultSchemas[name] as CompositeSchema<
|
|
123
|
+
Record<string, never>
|
|
124
|
+
>;
|
|
125
|
+
if (strategy) {
|
|
126
|
+
if (typeof mergeSchema === 'object') {
|
|
127
|
+
finalSchema = deepExtend(finalSchema, mergeSchema);
|
|
128
|
+
}
|
|
129
|
+
if (typeof mergeSchema === 'number') {
|
|
130
|
+
finalSchema = {
|
|
131
|
+
...finalSchema,
|
|
132
|
+
equals: mergeSchema
|
|
133
|
+
} as NumberSchemaDefinition<any>;
|
|
134
|
+
mergeSchema;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let preliminaryResult = await strategy(value, finalSchema, this);
|
|
138
|
+
if (!preliminaryResult.valid) return preliminaryResult;
|
|
139
|
+
|
|
140
|
+
preliminaryResult = await this.checkValidators(finalSchema, value);
|
|
141
|
+
|
|
142
|
+
return preliminaryResult;
|
|
143
|
+
}
|
|
144
|
+
throw new Error('not implemented');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private async checkValidators(
|
|
148
|
+
schema: CompositeSchema<Record<string, never>>,
|
|
149
|
+
value: any
|
|
150
|
+
): Promise<ValidationResult> {
|
|
151
|
+
if (Array.isArray(schema.validators)) {
|
|
152
|
+
const validatorsResults = await Promise.allSettled(
|
|
153
|
+
schema.validators.map((v) => Promise.resolve(v(value)))
|
|
154
|
+
);
|
|
155
|
+
const rejections = validatorsResults
|
|
156
|
+
.filter((f) => f.status === 'rejected')
|
|
157
|
+
.map((f: PromiseRejectedResult) => f.reason);
|
|
158
|
+
const errors = validatorsResults
|
|
159
|
+
.filter(
|
|
160
|
+
(f) =>
|
|
161
|
+
f.status === 'fulfilled' &&
|
|
162
|
+
typeof f.value !== 'boolean' &&
|
|
163
|
+
f.value.valid === false
|
|
164
|
+
)
|
|
165
|
+
.map(
|
|
166
|
+
(f: PromiseFulfilledResult<ValidationResultRaw>) => f.value
|
|
167
|
+
)
|
|
168
|
+
.map((f: ValidationResultRaw) => f.errors);
|
|
169
|
+
|
|
170
|
+
if (rejections.length === 0 && errors.length === 0) {
|
|
171
|
+
return {
|
|
172
|
+
valid: true
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
valid: false,
|
|
177
|
+
errors: [...rejections, ...errors]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
valid: true
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public async validate(
|
|
186
|
+
schema: keyof T | DefaultSchemaType | Schema<any>,
|
|
187
|
+
obj: any
|
|
188
|
+
): Promise<ValidationResult> {
|
|
189
|
+
if (!schema) throw new Error('schemaName is required');
|
|
190
|
+
if (typeof schema === 'string') {
|
|
191
|
+
if (isDefaultType(schema)) {
|
|
192
|
+
return await this.validateDefaultType(
|
|
193
|
+
schema as DefaultSchemaType,
|
|
194
|
+
obj
|
|
195
|
+
);
|
|
196
|
+
} else if (typeof this.schemas[schema as keyof T] !== 'undefined') {
|
|
197
|
+
const objSchema = deepExtend(
|
|
198
|
+
defaultSchemas.object,
|
|
199
|
+
this.schemas[schema as keyof T].schema
|
|
200
|
+
) as ObjectSchemaDefinition<Record<string, never>>;
|
|
201
|
+
const res = await validateObject(obj, objSchema, this);
|
|
202
|
+
if (!res.valid) return res;
|
|
203
|
+
return await this.checkValidators(objSchema, obj);
|
|
204
|
+
} else {
|
|
205
|
+
return await this.validateDefaultType('string', obj, {
|
|
206
|
+
type: 'string',
|
|
207
|
+
equals: schema
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (Array.isArray(schema)) {
|
|
213
|
+
for (let i = 0; i < schema.length; i++) {
|
|
214
|
+
const res = await this.validate(schema[i], obj);
|
|
215
|
+
if (res.valid) {
|
|
216
|
+
return res;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
valid: false,
|
|
221
|
+
errors: ['object does not match any schema']
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (typeof schema === 'number') {
|
|
226
|
+
return await this.validateDefaultType('number', obj, schema);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (typeof schema === 'object') {
|
|
230
|
+
if (typeof schema.type !== 'string')
|
|
231
|
+
throw new Error('Schema has no type');
|
|
232
|
+
|
|
233
|
+
if (isDefaultType(schema.type)) {
|
|
234
|
+
return await this.validateDefaultType(
|
|
235
|
+
schema.type as DefaultSchemaType,
|
|
236
|
+
obj,
|
|
237
|
+
schema
|
|
238
|
+
);
|
|
239
|
+
} else if (schema.type === 'object') {
|
|
240
|
+
const preliminaryResult = await validateObject(
|
|
241
|
+
obj,
|
|
242
|
+
schema,
|
|
243
|
+
this
|
|
244
|
+
);
|
|
245
|
+
if (!preliminaryResult.valid) return preliminaryResult;
|
|
246
|
+
|
|
247
|
+
return await this.checkValidators(
|
|
248
|
+
schema as CompositeSchema<Record<string, never>>,
|
|
249
|
+
obj
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
throw new Error("Coldn't understand the Schema provided");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationResult,
|
|
3
|
+
ArraySchemaDefinition,
|
|
4
|
+
ISchemaValidator
|
|
5
|
+
} from '../index';
|
|
6
|
+
|
|
7
|
+
export const validateArray = async (
|
|
8
|
+
obj: any,
|
|
9
|
+
schema: ArraySchemaDefinition<any>,
|
|
10
|
+
validator: ISchemaValidator<any>
|
|
11
|
+
): Promise<ValidationResult> => {
|
|
12
|
+
if (
|
|
13
|
+
typeof obj === 'undefined' &&
|
|
14
|
+
typeof schema === 'object' &&
|
|
15
|
+
schema.isRequired === false
|
|
16
|
+
) {
|
|
17
|
+
return {
|
|
18
|
+
valid: true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (!Array.isArray(obj))
|
|
22
|
+
return {
|
|
23
|
+
valid: false,
|
|
24
|
+
errors: ['expected type array']
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (typeof schema.minLength === 'number' && obj.length < schema.minLength) {
|
|
28
|
+
return {
|
|
29
|
+
valid: false,
|
|
30
|
+
errors: [`expected to be at least ${schema.minLength} chars long`]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof schema.maxLength === 'number' && obj.length > schema.maxLength) {
|
|
35
|
+
return {
|
|
36
|
+
valid: false,
|
|
37
|
+
errors: [`expected to be at most ${schema.maxLength} chars long`]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof schema.ofType !== 'undefined') {
|
|
42
|
+
const results = await Promise.all(
|
|
43
|
+
obj.map((i) => validator.validate(schema.ofType, i))
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const errors = results
|
|
47
|
+
.filter((r) => !r.valid)
|
|
48
|
+
.map((r) => r.errors)
|
|
49
|
+
.flat(Infinity) as Array<string>;
|
|
50
|
+
|
|
51
|
+
if (errors.length) {
|
|
52
|
+
return {
|
|
53
|
+
valid: false,
|
|
54
|
+
errors
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
valid: true
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
valid: true
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationResult,
|
|
3
|
+
NumberSchemaDefinition,
|
|
4
|
+
ISchemaValidator
|
|
5
|
+
} from '../index';
|
|
6
|
+
|
|
7
|
+
export const validateNumber = async (
|
|
8
|
+
obj: any,
|
|
9
|
+
schema: NumberSchemaDefinition<any>,
|
|
10
|
+
validator: ISchemaValidator<any>
|
|
11
|
+
): Promise<ValidationResult> => {
|
|
12
|
+
if (
|
|
13
|
+
typeof obj === 'undefined' &&
|
|
14
|
+
typeof schema === 'object' &&
|
|
15
|
+
schema.isRequired === false
|
|
16
|
+
) {
|
|
17
|
+
return {
|
|
18
|
+
valid: true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (typeof obj !== 'number')
|
|
22
|
+
return {
|
|
23
|
+
valid: false,
|
|
24
|
+
errors: [`expected type number, but saw ${typeof obj}`]
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (typeof schema === 'number') {
|
|
28
|
+
return obj === schema
|
|
29
|
+
? { valid: true }
|
|
30
|
+
: {
|
|
31
|
+
valid: false,
|
|
32
|
+
errors: [`should be equal to ${schema}`]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const num = obj as number;
|
|
37
|
+
|
|
38
|
+
if (schema.ensureNotNaN && Number.isNaN(num)) {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
errors: ['is not expected to be NaN']
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
schema.ensureIsFinite &&
|
|
47
|
+
!Number.isFinite(num) &&
|
|
48
|
+
schema.ensureNotNaN &&
|
|
49
|
+
!Number.isNaN(num)
|
|
50
|
+
) {
|
|
51
|
+
return {
|
|
52
|
+
valid: false,
|
|
53
|
+
errors: ['is expected to be a finite number']
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof schema.equals !== 'undefined' && num !== schema.equals) {
|
|
58
|
+
return {
|
|
59
|
+
valid: false,
|
|
60
|
+
errors: [`expected to be equal to ${schema.equals}`]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof schema.min !== 'undefined') {
|
|
65
|
+
if (typeof schema.min !== 'number')
|
|
66
|
+
throw new Error('min constraint should be a number');
|
|
67
|
+
if (num < schema.min)
|
|
68
|
+
return {
|
|
69
|
+
valid: false,
|
|
70
|
+
errors: [`expected to be at least ${schema.min}`]
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof schema.max !== 'undefined') {
|
|
75
|
+
if (typeof schema.max !== 'number')
|
|
76
|
+
throw new Error('max constraint should be a number');
|
|
77
|
+
if (num > schema.max)
|
|
78
|
+
return {
|
|
79
|
+
valid: false,
|
|
80
|
+
errors: [`expected to be no more than ${schema.max}`]
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
valid: true
|
|
86
|
+
};
|
|
87
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationResult,
|
|
3
|
+
ObjectSchemaDefinition,
|
|
4
|
+
ISchemaValidator,
|
|
5
|
+
Schema
|
|
6
|
+
} from '../index';
|
|
7
|
+
|
|
8
|
+
export const validateObject = async (
|
|
9
|
+
obj: any,
|
|
10
|
+
schema: ObjectSchemaDefinition<any>,
|
|
11
|
+
validator: ISchemaValidator<any>
|
|
12
|
+
): Promise<ValidationResult> => {
|
|
13
|
+
if (
|
|
14
|
+
typeof obj === 'undefined' &&
|
|
15
|
+
typeof schema === 'object' &&
|
|
16
|
+
schema.isRequired === false
|
|
17
|
+
) {
|
|
18
|
+
return {
|
|
19
|
+
valid: true
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (typeof obj !== 'object') {
|
|
23
|
+
return {
|
|
24
|
+
valid: false,
|
|
25
|
+
errors: [
|
|
26
|
+
`expected to have type='object', but saw '${typeof obj}' instead`
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof schema.properties === 'object' && schema.properties) {
|
|
32
|
+
const errors = (
|
|
33
|
+
await Promise.all(
|
|
34
|
+
Object.entries(schema.properties).map(
|
|
35
|
+
async ([name, schema]: [string, Schema<any>]) => {
|
|
36
|
+
const result = await validator.validate(
|
|
37
|
+
schema,
|
|
38
|
+
obj[name]
|
|
39
|
+
);
|
|
40
|
+
if (result.valid) return result;
|
|
41
|
+
return {
|
|
42
|
+
valid: false,
|
|
43
|
+
errors: (result.errors || []).map(
|
|
44
|
+
(e) => `->${name} ${e}`
|
|
45
|
+
)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
.filter((r) => !r.valid)
|
|
52
|
+
.map((r) => r.errors)
|
|
53
|
+
.flat(Infinity) as string[];
|
|
54
|
+
if (errors.length) {
|
|
55
|
+
return {
|
|
56
|
+
valid: false,
|
|
57
|
+
errors
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
valid: true
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationResult,
|
|
3
|
+
StringSchemaDefinition,
|
|
4
|
+
ISchemaValidator
|
|
5
|
+
} from '../index';
|
|
6
|
+
|
|
7
|
+
export const validateString = async (
|
|
8
|
+
obj: any,
|
|
9
|
+
schema: StringSchemaDefinition<any>,
|
|
10
|
+
validator: ISchemaValidator<any>
|
|
11
|
+
): Promise<ValidationResult> => {
|
|
12
|
+
if (
|
|
13
|
+
typeof obj === 'undefined' &&
|
|
14
|
+
typeof schema === 'object' &&
|
|
15
|
+
schema.isRequired === false
|
|
16
|
+
) {
|
|
17
|
+
return {
|
|
18
|
+
valid: true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (typeof obj !== 'string')
|
|
22
|
+
return {
|
|
23
|
+
valid: false,
|
|
24
|
+
errors: [`expected type string, but saw ${typeof obj}`]
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (typeof schema === 'string') {
|
|
28
|
+
return obj === schema
|
|
29
|
+
? { valid: true }
|
|
30
|
+
: {
|
|
31
|
+
valid: false,
|
|
32
|
+
errors: [`should be equal to ${schema}`]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const str = obj as string;
|
|
37
|
+
|
|
38
|
+
if (typeof schema.equals === 'string' && str !== schema.equals) {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
errors: [
|
|
42
|
+
`expected to be equal to '${schema.equals}' but saw '${str}'`
|
|
43
|
+
]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (typeof schema.minLength === 'number' && str.length < schema.minLength) {
|
|
48
|
+
return {
|
|
49
|
+
valid: false,
|
|
50
|
+
errors: [`expected to be at least ${schema.minLength} chars long`]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof schema.maxLength === 'number' && str.length > schema.maxLength) {
|
|
55
|
+
return {
|
|
56
|
+
valid: false,
|
|
57
|
+
errors: [`expected to be at most ${schema.maxLength} chars long`]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
valid: true
|
|
63
|
+
};
|
|
64
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"rootDir": "./src",
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"target": "esnext",
|
|
7
|
+
"module": "CommonJS",
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"moduleResolution": "Node"
|
|
11
|
+
},
|
|
12
|
+
"files": ["./src/index.ts"],
|
|
13
|
+
"watchOptions": {
|
|
14
|
+
"excludeDirectories": ["./dist"]
|
|
15
|
+
}
|
|
16
|
+
}
|