@orioro/util 0.0.0 → 0.2.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 +1 -0
- package/babel.config.cjs +13 -0
- package/coverage/clover.xml +488 -45
- package/coverage/coverage-final.json +28 -2
- package/coverage/lcov-report/ValidationError.ts.html +184 -0
- package/coverage/lcov-report/base.css +19 -7
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +248 -58
- package/coverage/lcov-report/prettify.js +1 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +52 -14
- package/coverage/lcov-report/src/debug/deepFreeze.ts.html +157 -0
- package/coverage/lcov-report/src/debug/index.html +146 -0
- package/coverage/lcov-report/src/debug/index.ts.html +91 -0
- package/coverage/lcov-report/src/debug/wait.ts.html +127 -0
- package/coverage/lcov-report/src/index.html +131 -0
- package/coverage/lcov-report/src/interpolate/index.html +116 -0
- package/coverage/lcov-report/src/interpolate/index.ts.html +277 -0
- package/coverage/lcov-report/src/maybeFn.ts.html +94 -0
- package/coverage/lcov-report/src/promise/index.html +146 -0
- package/coverage/lcov-report/src/promise/index.ts.html +91 -0
- package/coverage/lcov-report/src/promise/promiseReduce.ts.html +130 -0
- package/coverage/lcov-report/src/promise/resolveNestedPromises.ts.html +271 -0
- package/coverage/lcov-report/src/switchValue.ts.html +253 -0
- package/coverage/lcov-report/src/typeOf.ts.html +328 -0
- package/coverage/lcov-report/src/validate/ValidationError.ts.html +184 -0
- package/coverage/lcov-report/src/validate/async/index.html +131 -0
- package/coverage/lcov-report/src/validate/async/index.ts.html +241 -0
- package/coverage/lcov-report/src/validate/async/parseValidator.ts.html +136 -0
- package/coverage/lcov-report/src/validate/async/validateAsyncFn.ts.html +208 -0
- package/coverage/lcov-report/src/validate/async/validators/and.ts.html +154 -0
- package/coverage/lcov-report/src/validate/async/validators/index.html +146 -0
- package/coverage/lcov-report/src/validate/async/validators/index.ts.html +91 -0
- package/coverage/lcov-report/src/validate/async/validators/logical.ts.html +253 -0
- package/coverage/lcov-report/src/validate/async/validators/or.ts.html +151 -0
- package/coverage/lcov-report/src/validate/async/validators/shape.ts.html +565 -0
- package/coverage/lcov-report/src/validate/common/ValidationError.ts.html +184 -0
- package/coverage/lcov-report/src/validate/common/index.html +116 -0
- package/coverage/lcov-report/src/validate/common/util/defaultErrorMessage.ts.html +163 -0
- package/coverage/lcov-report/src/validate/common/util/index.html +161 -0
- package/coverage/lcov-report/src/validate/common/util/index.ts.html +94 -0
- package/coverage/lcov-report/src/validate/common/util/parseValidator.ts.html +316 -0
- package/coverage/lcov-report/src/validate/common/util/parseValidatorInput.ts.html +316 -0
- package/coverage/lcov-report/src/validate/common/util/resolveValidationResult.ts.html +277 -0
- package/coverage/lcov-report/src/validate/common/util/validatorParser.ts.html +316 -0
- package/coverage/lcov-report/src/validate/common/validators/index.html +131 -0
- package/coverage/lcov-report/src/validate/common/validators/index.ts.html +88 -0
- package/coverage/lcov-report/src/validate/common/validators/type.ts.html +388 -0
- package/coverage/lcov-report/src/validate/fmtValidationResult.ts.html +268 -0
- package/coverage/lcov-report/src/validate/index.html +116 -0
- package/coverage/lcov-report/src/validate/index.ts.html +94 -0
- package/coverage/lcov-report/src/validate/makeValidate.ts.html +634 -0
- package/coverage/lcov-report/src/validate/specUtil/commonTests.js.html +1324 -0
- package/coverage/lcov-report/src/validate/specUtil/index.html +116 -0
- package/coverage/lcov-report/src/validate/sync/index.html +131 -0
- package/coverage/lcov-report/src/validate/sync/index.ts.html +244 -0
- package/coverage/lcov-report/src/validate/sync/parseValidator.ts.html +136 -0
- package/coverage/lcov-report/src/validate/sync/validateSyncFn.ts.html +223 -0
- package/coverage/lcov-report/src/validate/sync/validators/and.ts.html +148 -0
- package/coverage/lcov-report/src/validate/sync/validators/index.html +146 -0
- package/coverage/lcov-report/src/validate/sync/validators/index.ts.html +91 -0
- package/coverage/lcov-report/src/validate/sync/validators/logical.ts.html +226 -0
- package/coverage/lcov-report/src/validate/sync/validators/or.ts.html +130 -0
- package/coverage/lcov-report/src/validate/sync/validators/shape.ts.html +523 -0
- package/coverage/lcov-report/src/validate/sync/validators/type.ts.html +154 -0
- package/coverage/lcov-report/src/validate/syncValidators/and.ts.html +157 -0
- package/coverage/lcov-report/src/validate/syncValidators/index.html +176 -0
- package/coverage/lcov-report/src/validate/syncValidators/index.ts.html +97 -0
- package/coverage/lcov-report/src/validate/syncValidators/or.ts.html +127 -0
- package/coverage/lcov-report/src/validate/syncValidators/shape.ts.html +559 -0
- package/coverage/lcov-report/src/validate/syncValidators/string.ts.html +163 -0
- package/coverage/lcov-report/src/validate/syncValidators/type.ts.html +154 -0
- package/coverage/lcov-report/src/validate/util/defaultErrorMessage.ts.html +169 -0
- package/coverage/lcov-report/src/validate/util/index.html +146 -0
- package/coverage/lcov-report/src/validate/util/index.ts.html +91 -0
- package/coverage/lcov-report/src/validate/util/resolveValidationResult.ts.html +253 -0
- package/coverage/lcov-report/src/validate/validate.ts.html +220 -0
- package/coverage/lcov-report/src/validate/validateAsync.ts.html +220 -0
- package/coverage/lcov-report/src/validate/validators/and.ts.html +157 -0
- package/coverage/lcov-report/src/validate/validators/index.html +176 -0
- package/coverage/lcov-report/src/validate/validators/index.ts.html +97 -0
- package/coverage/lcov-report/src/validate/validators/or.ts.html +127 -0
- package/coverage/lcov-report/src/validate/validators/shape.ts.html +541 -0
- package/coverage/lcov-report/src/validate/validators/type.ts.html +154 -0
- package/coverage/lcov-report/src/validate_/ValidationError.ts.html +184 -0
- package/coverage/lcov-report/src/validate_/fmtValidationResult.ts.html +268 -0
- package/coverage/lcov-report/src/validate_/index.html +161 -0
- package/coverage/lcov-report/src/validate_/makeValidate.ts.html +634 -0
- package/coverage/lcov-report/src/validate_/validate.ts.html +220 -0
- package/coverage/lcov-report/switchValue.ts.html +253 -0
- package/coverage/lcov-report/typeOf.ts.html +331 -0
- package/coverage/lcov-report/validate.ts.html +757 -0
- package/coverage/lcov.info +1045 -74
- package/dist/index.mjs +1437 -73
- package/jest.config.js +6 -0
- package/package.json +27 -27
- package/rollup.config.mjs +6 -0
- package/src/PromiseLikeEventEmitter/index.ts +35 -0
- package/src/array/arrayChunk.ts +7 -0
- package/src/array/index.ts +1 -0
- package/src/debug/debugFn/index.ts +48 -0
- package/src/debug/debugFn/util.ts +27 -0
- package/src/debug/deepFreeze.ts +26 -0
- package/src/debug/index.ts +3 -0
- package/src/debug/wait.ts +14 -0
- package/src/index.ts +9 -0
- package/src/interpolate/index.spec.ts +20 -0
- package/src/interpolate/index.ts +64 -0
- package/src/maybeFn.ts +3 -0
- package/src/promise/batchFn.spec.ts +92 -0
- package/src/promise/batchFn.ts +176 -0
- package/src/promise/index.ts +3 -0
- package/src/promise/promiseReduce.ts +15 -0
- package/src/promise/resolveNestedPromises.spec.ts +205 -0
- package/src/promise/resolveNestedPromises.ts +83 -0
- package/src/promise/types.ts +2 -0
- package/src/resolvePaths/index.spec.ts +42 -0
- package/src/resolvePaths/index.ts +21 -0
- package/src/switchValue.spec.ts +30 -0
- package/src/switchValue.ts +59 -0
- package/src/typeOf.spec.ts +47 -0
- package/src/typeOf.ts +81 -0
- package/src/validate/__snapshots__/index.spec.ts.snap +9 -0
- package/src/validate/async/index.spec.ts +236 -0
- package/src/validate/async/index.ts +52 -0
- package/src/validate/async/validateAsyncFn.ts +41 -0
- package/src/validate/async/validators/index.ts +2 -0
- package/src/validate/async/validators/logical.ts +56 -0
- package/src/validate/async/validators/shape.ts +160 -0
- package/src/validate/async/validators/tmpand.ts +24 -0
- package/src/validate/async/validators/tmpor.ts +21 -0
- package/src/validate/common/ValidationError.ts +33 -0
- package/src/validate/common/util/defaultErrorMessage.ts +26 -0
- package/src/validate/common/util/index.ts +3 -0
- package/src/validate/common/util/parseValidatorInput.ts +77 -0
- package/src/validate/common/util/resolveValidationResult.ts +64 -0
- package/src/validate/common/validators/index.ts +1 -0
- package/src/validate/common/validators/type.ts +101 -0
- package/src/validate/index.spec.ts +5 -0
- package/src/validate/index.ts +3 -0
- package/src/validate/specUtil/commonTests.js +413 -0
- package/src/validate/sync/index.spec.ts +81 -0
- package/src/validate/sync/index.ts +53 -0
- package/src/validate/sync/validateSyncFn.ts +46 -0
- package/src/validate/sync/validators/index.ts +2 -0
- package/src/validate/sync/validators/logical.ts +47 -0
- package/src/validate/sync/validators/shape.ts +146 -0
- package/src/validate/types/async.ts +20 -0
- package/src/validate/types/common.ts +70 -0
- package/src/validate/types/index.ts +3 -0
- package/src/validate/types/sync.ts +20 -0
- package/tsconfig.json +11 -0
- package/array/index.js +0 -11
- package/coverage/lcov-report/array/index.html +0 -93
- package/coverage/lcov-report/array/index.js.html +0 -98
- package/coverage/lcov-report/coverage/coverage-final.json.html +0 -95
- package/coverage/lcov-report/coverage/index.html +0 -93
- package/coverage/lcov-report/coverage/lcov-report/index.html +0 -106
- package/coverage/lcov-report/coverage/lcov-report/prettify.js.html +0 -68
- package/coverage/lcov-report/coverage/lcov-report/sorter.js.html +0 -539
- package/coverage/lcov-report/fn/index.html +0 -93
- package/coverage/lcov-report/fn/index.js.html +0 -215
- package/dist/index.js +0 -116
- package/fn/index.js +0 -50
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { validateAsync } from './'
|
|
2
|
+
import { wait } from '../../debug'
|
|
3
|
+
import { commonTests } from '../specUtil/commonTests'
|
|
4
|
+
import { typeValidator } from '../common/validators'
|
|
5
|
+
import { validate } from '../sync'
|
|
6
|
+
import { AsyncValidatorSystem } from '../types'
|
|
7
|
+
|
|
8
|
+
//
|
|
9
|
+
// Async api should match the sync api
|
|
10
|
+
// in most methods
|
|
11
|
+
//
|
|
12
|
+
commonTests(
|
|
13
|
+
{
|
|
14
|
+
describe,
|
|
15
|
+
test,
|
|
16
|
+
//
|
|
17
|
+
// Wrap the expects declartion into the async resolution mode
|
|
18
|
+
//
|
|
19
|
+
expect: (value) => expect(value).resolves,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
validate: validateAsync,
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
describe('basic', () => {
|
|
27
|
+
test('basic', async () => {
|
|
28
|
+
const asyncValidator = async (input) => {
|
|
29
|
+
await wait(100)
|
|
30
|
+
return typeof input === 'string' && input.length > 5
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
expect(validateAsync(asyncValidator, 'Some text')).resolves.toEqual(true)
|
|
34
|
+
|
|
35
|
+
expect(validateAsync(asyncValidator, 'Some')).resolves.toEqual({
|
|
36
|
+
input: 'Some',
|
|
37
|
+
message: "Invalid input: 'Some'.",
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('simulate http request', async () => {
|
|
43
|
+
const DB = [
|
|
44
|
+
{ username: 'user1' },
|
|
45
|
+
{ username: 'user2' },
|
|
46
|
+
{ username: 'user3' },
|
|
47
|
+
{ username: 'user4' },
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
async function usernameIsAvailable(input) {
|
|
51
|
+
await wait(500)
|
|
52
|
+
return DB.every((user) => user.username !== input)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const validator = validateAsync.obj({
|
|
56
|
+
username: validateAsync.and([
|
|
57
|
+
[validateAsync.type('string'), 'Is required'],
|
|
58
|
+
[(str) => str.length >= 4, 'Must have at least 4 chars'],
|
|
59
|
+
[(str) => str.length <= 10, 'Must have at most 10 chars'],
|
|
60
|
+
[(str) => usernameIsAvailable(str), 'Username is taken'],
|
|
61
|
+
]),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
await expect(
|
|
65
|
+
validateAsync(validator, {
|
|
66
|
+
username: 'user5',
|
|
67
|
+
}),
|
|
68
|
+
).resolves.toEqual(true)
|
|
69
|
+
|
|
70
|
+
await expect(
|
|
71
|
+
validateAsync(validator, {
|
|
72
|
+
username: 'u',
|
|
73
|
+
}),
|
|
74
|
+
).resolves.toMatchObject({
|
|
75
|
+
nestedErrors: [
|
|
76
|
+
{
|
|
77
|
+
path: 'username',
|
|
78
|
+
message: 'Must have at least 4 chars',
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
await expect(
|
|
84
|
+
validateAsync(validator, {
|
|
85
|
+
username: 'user4',
|
|
86
|
+
}),
|
|
87
|
+
).resolves.toMatchObject({
|
|
88
|
+
nestedErrors: [
|
|
89
|
+
{
|
|
90
|
+
path: 'username',
|
|
91
|
+
message: 'Username is taken',
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
describe('obj', () => {
|
|
98
|
+
describe('series execution', () => {
|
|
99
|
+
const validator = validateAsync.obj({
|
|
100
|
+
param1: (input) => wait(300, typeof input === 'string'),
|
|
101
|
+
param2: (input) => wait(600, typeof input === 'number'),
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('success', async () => {
|
|
105
|
+
const start = Date.now()
|
|
106
|
+
|
|
107
|
+
await expect(
|
|
108
|
+
validateAsync(validator, {
|
|
109
|
+
param1: 'String value',
|
|
110
|
+
param2: 88,
|
|
111
|
+
}),
|
|
112
|
+
).resolves.toEqual(true)
|
|
113
|
+
|
|
114
|
+
//
|
|
115
|
+
// Validation calls are executed in series
|
|
116
|
+
//
|
|
117
|
+
expect(Date.now() - start).toBeGreaterThan(900)
|
|
118
|
+
expect(Date.now() - start).toBeLessThan(1000)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('failure', async () => {
|
|
122
|
+
const start = Date.now()
|
|
123
|
+
|
|
124
|
+
await expect(
|
|
125
|
+
validateAsync(validator, {
|
|
126
|
+
param1: 'String value',
|
|
127
|
+
param2: 'string value',
|
|
128
|
+
}),
|
|
129
|
+
).resolves.toEqual({
|
|
130
|
+
input: { param1: 'String value', param2: 'string value' },
|
|
131
|
+
nestedErrors: [
|
|
132
|
+
{
|
|
133
|
+
input: 'string value',
|
|
134
|
+
message: "Invalid input: 'string value'.",
|
|
135
|
+
path: 'param2',
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
message:
|
|
139
|
+
'Invalid input: `{"param1":"String value","param2":"string value"}`.\n' +
|
|
140
|
+
" - param2: Invalid input: 'string value'.",
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
//
|
|
144
|
+
// Validation calls are executed in series
|
|
145
|
+
//
|
|
146
|
+
expect(Date.now() - start).toBeGreaterThan(900)
|
|
147
|
+
expect(Date.now() - start).toBeLessThan(1000)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
describe('validateAsync.assertValid', () => {
|
|
153
|
+
test('basic', async () => {
|
|
154
|
+
await expect(validateAsync.assertValid('string!', 'test')).resolves.toEqual(
|
|
155
|
+
'test',
|
|
156
|
+
)
|
|
157
|
+
await expect(validateAsync.assertValid('string!', null)).rejects.toThrow(
|
|
158
|
+
"Invalid input: 'null'. Expected type(s) `string!`, but got type `null`",
|
|
159
|
+
)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('shorthand', async () => {
|
|
163
|
+
const validator = {
|
|
164
|
+
param1: 'string!',
|
|
165
|
+
param2: {
|
|
166
|
+
param21: async (value) => {
|
|
167
|
+
const allowedTypes = await wait(400, 'number! | string!')
|
|
168
|
+
|
|
169
|
+
return validate(allowedTypes, value)
|
|
170
|
+
},
|
|
171
|
+
param22: 'function',
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const input1 = {
|
|
176
|
+
param1: 'Some string',
|
|
177
|
+
param2: {
|
|
178
|
+
param21: 2,
|
|
179
|
+
param22: () => {},
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
await expect(validateAsync.assertValid(validator, input1)).resolves.toEqual(
|
|
184
|
+
input1,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
const input2 = {
|
|
188
|
+
param1: 'Some string',
|
|
189
|
+
param2: {
|
|
190
|
+
param21: 2,
|
|
191
|
+
param22: 3,
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await expect(validateAsync.assertValid(validator, input2)).rejects.toThrow(
|
|
196
|
+
"param22: Invalid input: '3'. Expected type(s) `function`, but got type `number`",
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
const input3 = {
|
|
200
|
+
param1: 'Some string',
|
|
201
|
+
param2: {
|
|
202
|
+
param21: null,
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
await expect(validateAsync.assertValid(validator, input3)).rejects.toThrow(
|
|
207
|
+
"param21: Invalid input: 'null'. Expected type(s) `number! | string!`, but got type `null`",
|
|
208
|
+
)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('shorthand 2', async () => {
|
|
212
|
+
const validator: AsyncValidatorSystem['ValidatorInput'] = {
|
|
213
|
+
givenName: [
|
|
214
|
+
async (givenName) => {
|
|
215
|
+
await wait(500)
|
|
216
|
+
return typeof givenName === 'string' && givenName.length > 3
|
|
217
|
+
},
|
|
218
|
+
'Nome precisa ter ao menos 3 caracteres',
|
|
219
|
+
],
|
|
220
|
+
familyName: [
|
|
221
|
+
async (familyName) => {
|
|
222
|
+
await wait(500)
|
|
223
|
+
return typeof familyName === 'string' && familyName.length > 3
|
|
224
|
+
},
|
|
225
|
+
'Sobrenome precisa ter ao menos 3 caracteres',
|
|
226
|
+
],
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await expect(
|
|
230
|
+
validateAsync(validator, {
|
|
231
|
+
givenName: 'Test',
|
|
232
|
+
familyName: 'Test',
|
|
233
|
+
}),
|
|
234
|
+
).resolves.toEqual(true)
|
|
235
|
+
})
|
|
236
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as common from '../common/validators'
|
|
2
|
+
import * as asyncValidators from './validators'
|
|
3
|
+
import { validateAsyncFn } from './validateAsyncFn'
|
|
4
|
+
import { DetailedInvalid, AsyncValidatorSystem, Valid } from '../types'
|
|
5
|
+
import { ValidationError } from '../common/ValidationError'
|
|
6
|
+
|
|
7
|
+
async function assertValidAsync<InputT = any>(
|
|
8
|
+
validator: AsyncValidatorSystem['ValidatorInput'],
|
|
9
|
+
input: InputT,
|
|
10
|
+
): Promise<InputT> {
|
|
11
|
+
const validationResult = await validateAsync(validator, input)
|
|
12
|
+
|
|
13
|
+
if (validationResult === true) {
|
|
14
|
+
return input
|
|
15
|
+
} else {
|
|
16
|
+
throw new ValidationError(validationResult)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ValidateAsync {
|
|
21
|
+
(
|
|
22
|
+
validatorInput: AsyncValidatorSystem['ValidatorInput'],
|
|
23
|
+
input: any,
|
|
24
|
+
): Promise<Valid | DetailedInvalid>
|
|
25
|
+
type: typeof common.typeValidator
|
|
26
|
+
|
|
27
|
+
obj: typeof asyncValidators.obj
|
|
28
|
+
objOf: typeof asyncValidators.objOf
|
|
29
|
+
tuple: typeof asyncValidators.tuple
|
|
30
|
+
arrayOf: typeof asyncValidators.arrayOf
|
|
31
|
+
|
|
32
|
+
and: typeof asyncValidators.and
|
|
33
|
+
or: typeof asyncValidators.or
|
|
34
|
+
not: typeof asyncValidators.not
|
|
35
|
+
|
|
36
|
+
assertValid: typeof assertValidAsync
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const validateAsync: ValidateAsync = validateAsyncFn as ValidateAsync
|
|
40
|
+
|
|
41
|
+
validateAsync.type = common.typeValidator
|
|
42
|
+
|
|
43
|
+
validateAsync.obj = asyncValidators.obj
|
|
44
|
+
validateAsync.objOf = asyncValidators.objOf
|
|
45
|
+
validateAsync.tuple = asyncValidators.tuple
|
|
46
|
+
validateAsync.arrayOf = asyncValidators.arrayOf
|
|
47
|
+
|
|
48
|
+
validateAsync.and = asyncValidators.and
|
|
49
|
+
validateAsync.or = asyncValidators.or
|
|
50
|
+
validateAsync.not = asyncValidators.not
|
|
51
|
+
|
|
52
|
+
validateAsync.assertValid = assertValidAsync
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DetailedInvalid,
|
|
3
|
+
Valid,
|
|
4
|
+
AsyncValidatorSystem,
|
|
5
|
+
ValidatorResult,
|
|
6
|
+
} from '../types'
|
|
7
|
+
import { obj as asyncObjValidator } from './validators/shape'
|
|
8
|
+
|
|
9
|
+
import { parseValidatorInput, resolveValidationResult } from '../common/util'
|
|
10
|
+
|
|
11
|
+
export async function validateAsyncFn(
|
|
12
|
+
validatorInput: AsyncValidatorSystem['ValidatorInput'],
|
|
13
|
+
input: any,
|
|
14
|
+
): Promise<Valid | DetailedInvalid> {
|
|
15
|
+
let result: ValidatorResult
|
|
16
|
+
|
|
17
|
+
const [validatorFn, errorMessage] = parseValidatorInput<
|
|
18
|
+
AsyncValidatorSystem['ValidatorInput'],
|
|
19
|
+
AsyncValidatorSystem['ValidatorFn'],
|
|
20
|
+
AsyncValidatorSystem['Validator']
|
|
21
|
+
>(
|
|
22
|
+
{
|
|
23
|
+
objValidator: asyncObjValidator,
|
|
24
|
+
},
|
|
25
|
+
validatorInput,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
result = await validatorFn(input, {
|
|
30
|
+
validateAsync: validateAsyncFn,
|
|
31
|
+
})
|
|
32
|
+
} catch (err) {
|
|
33
|
+
result = err as Error
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return resolveValidationResult({
|
|
37
|
+
errorMessage,
|
|
38
|
+
input,
|
|
39
|
+
result,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { promiseReduce } from '../../../promise'
|
|
2
|
+
import { DetailedInvalid, Valid, AsyncValidatorSystem } from '../../types'
|
|
3
|
+
|
|
4
|
+
//
|
|
5
|
+
// And operator (serial)
|
|
6
|
+
//
|
|
7
|
+
export function and(
|
|
8
|
+
validators: AsyncValidatorSystem['ValidatorInput'][],
|
|
9
|
+
): AsyncValidatorSystem['ValidatorFn'] {
|
|
10
|
+
return async function validateAnd(input, { validateAsync }) {
|
|
11
|
+
const result = await promiseReduce(
|
|
12
|
+
validators,
|
|
13
|
+
(acc, validator) =>
|
|
14
|
+
acc !== true ? acc : validateAsync(validator, input),
|
|
15
|
+
true as Valid | DetailedInvalid,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if (result === true) {
|
|
19
|
+
return true
|
|
20
|
+
} else {
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//
|
|
27
|
+
// Or operator (serial)
|
|
28
|
+
//
|
|
29
|
+
export function or(
|
|
30
|
+
validators: AsyncValidatorSystem['ValidatorInput'][],
|
|
31
|
+
): AsyncValidatorSystem['ValidatorFn'] {
|
|
32
|
+
return async function validateOr(input, { validateAsync }) {
|
|
33
|
+
const someIsValid = await promiseReduce(
|
|
34
|
+
validators,
|
|
35
|
+
async (acc, validator) => {
|
|
36
|
+
return acc === true || (await validateAsync(validator, input)) === true
|
|
37
|
+
},
|
|
38
|
+
false as boolean,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return someIsValid
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//
|
|
46
|
+
// Not
|
|
47
|
+
//
|
|
48
|
+
export function not(
|
|
49
|
+
validator: AsyncValidatorSystem['ValidatorInput'],
|
|
50
|
+
): AsyncValidatorSystem['ValidatorFn'] {
|
|
51
|
+
return async function validateNot(input, { validateAsync }) {
|
|
52
|
+
const result = await validateAsync(validator, input)
|
|
53
|
+
|
|
54
|
+
return result !== true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DetailedInvalid,
|
|
3
|
+
AsyncValidatorSystem,
|
|
4
|
+
AsyncValidateFn,
|
|
5
|
+
} from '../../types'
|
|
6
|
+
import { typeValidator } from '../../common/validators/type'
|
|
7
|
+
import { getProperty } from 'dot-prop'
|
|
8
|
+
import { promiseReduce } from '../../../promise'
|
|
9
|
+
|
|
10
|
+
async function _shapeGeneralValidator({
|
|
11
|
+
input,
|
|
12
|
+
baseType,
|
|
13
|
+
validateAsync,
|
|
14
|
+
collectNestedErrors,
|
|
15
|
+
}: {
|
|
16
|
+
input: any
|
|
17
|
+
baseType: 'object' | 'array'
|
|
18
|
+
validateAsync: AsyncValidateFn
|
|
19
|
+
collectNestedErrors: () => Promise<DetailedInvalid[]>
|
|
20
|
+
}) {
|
|
21
|
+
const objectValidation = await validateAsync(
|
|
22
|
+
typeValidator({ type: baseType, required: true }),
|
|
23
|
+
input,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if (objectValidation !== true) {
|
|
27
|
+
return objectValidation
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const nestedErrors = await collectNestedErrors()
|
|
31
|
+
|
|
32
|
+
return nestedErrors.length === 0
|
|
33
|
+
? true
|
|
34
|
+
: {
|
|
35
|
+
input,
|
|
36
|
+
nestedErrors,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function obj(objShape: {
|
|
41
|
+
[key: string]: AsyncValidatorSystem['ValidatorInput']
|
|
42
|
+
}): AsyncValidatorSystem['ValidatorFn'] {
|
|
43
|
+
return async function asyncValidateObj(input, { validateAsync }) {
|
|
44
|
+
return _shapeGeneralValidator({
|
|
45
|
+
input,
|
|
46
|
+
baseType: 'object',
|
|
47
|
+
validateAsync,
|
|
48
|
+
collectNestedErrors: () =>
|
|
49
|
+
promiseReduce(
|
|
50
|
+
Object.entries(objShape),
|
|
51
|
+
async (acc, [path, pathValidator]) => {
|
|
52
|
+
const pathInput = getProperty(input, path)
|
|
53
|
+
const pathResult = await validateAsync(pathValidator, pathInput)
|
|
54
|
+
|
|
55
|
+
return pathResult === true
|
|
56
|
+
? acc
|
|
57
|
+
: [
|
|
58
|
+
...acc,
|
|
59
|
+
{
|
|
60
|
+
...pathResult,
|
|
61
|
+
path,
|
|
62
|
+
},
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
[] as DetailedInvalid[],
|
|
66
|
+
),
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function objOf(
|
|
72
|
+
ofType: AsyncValidatorSystem['ValidatorInput'],
|
|
73
|
+
): AsyncValidatorSystem['ValidatorFn'] {
|
|
74
|
+
return async function asyncValidateObjOf(input, { validateAsync }) {
|
|
75
|
+
return _shapeGeneralValidator({
|
|
76
|
+
input,
|
|
77
|
+
baseType: 'object',
|
|
78
|
+
validateAsync,
|
|
79
|
+
collectNestedErrors: () =>
|
|
80
|
+
promiseReduce(
|
|
81
|
+
Object.entries(input),
|
|
82
|
+
async (acc, [key, keyInput]) => {
|
|
83
|
+
const indexResult = await validateAsync(ofType, keyInput)
|
|
84
|
+
|
|
85
|
+
return indexResult === true
|
|
86
|
+
? acc
|
|
87
|
+
: [
|
|
88
|
+
...acc,
|
|
89
|
+
{
|
|
90
|
+
...indexResult,
|
|
91
|
+
path: key,
|
|
92
|
+
},
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
[] as DetailedInvalid[],
|
|
96
|
+
),
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function tuple(
|
|
102
|
+
tupleShape: AsyncValidatorSystem['ValidatorInput'][],
|
|
103
|
+
): AsyncValidatorSystem['ValidatorFn'] {
|
|
104
|
+
return async function asyncValidateTuple(input, { validateAsync }) {
|
|
105
|
+
return _shapeGeneralValidator({
|
|
106
|
+
input,
|
|
107
|
+
baseType: 'array',
|
|
108
|
+
validateAsync,
|
|
109
|
+
collectNestedErrors: () =>
|
|
110
|
+
promiseReduce(
|
|
111
|
+
tupleShape,
|
|
112
|
+
async (acc, indexValidator, index) => {
|
|
113
|
+
const indexInput = input[index]
|
|
114
|
+
const indexResult = await validateAsync(indexValidator, indexInput)
|
|
115
|
+
|
|
116
|
+
return indexResult === true
|
|
117
|
+
? acc
|
|
118
|
+
: [
|
|
119
|
+
...acc,
|
|
120
|
+
{
|
|
121
|
+
...indexResult,
|
|
122
|
+
path: index + '',
|
|
123
|
+
},
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
[] as DetailedInvalid[],
|
|
127
|
+
),
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function arrayOf(
|
|
133
|
+
ofType: AsyncValidatorSystem['ValidatorInput'],
|
|
134
|
+
): AsyncValidatorSystem['ValidatorFn'] {
|
|
135
|
+
return async function asyncValidateArrayOf(input, { validateAsync }) {
|
|
136
|
+
return _shapeGeneralValidator({
|
|
137
|
+
input,
|
|
138
|
+
baseType: 'array',
|
|
139
|
+
validateAsync,
|
|
140
|
+
collectNestedErrors: () =>
|
|
141
|
+
promiseReduce(
|
|
142
|
+
input as any[],
|
|
143
|
+
async (acc, indexInput, index) => {
|
|
144
|
+
const indexResult = await validateAsync(ofType, indexInput)
|
|
145
|
+
|
|
146
|
+
return indexResult === true
|
|
147
|
+
? acc
|
|
148
|
+
: [
|
|
149
|
+
...acc,
|
|
150
|
+
{
|
|
151
|
+
...indexResult,
|
|
152
|
+
path: index + '',
|
|
153
|
+
},
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
[] as DetailedInvalid[],
|
|
157
|
+
),
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { promiseReduce } from '../../../promise'
|
|
2
|
+
import { DetailedInvalid, Valid, AsyncValidatorSystem } from '../../types'
|
|
3
|
+
|
|
4
|
+
//
|
|
5
|
+
// And operator (serial)
|
|
6
|
+
//
|
|
7
|
+
export function and(
|
|
8
|
+
validators: AsyncValidatorSystem['ValidatorInput'][],
|
|
9
|
+
): AsyncValidatorSystem['ValidatorFn'] {
|
|
10
|
+
return async function validateAnd(input, { validateAsync }) {
|
|
11
|
+
const result = await promiseReduce(
|
|
12
|
+
validators,
|
|
13
|
+
(acc, validator) =>
|
|
14
|
+
acc !== true ? acc : validateAsync(validator, input),
|
|
15
|
+
true as Valid | DetailedInvalid,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if (result === true) {
|
|
19
|
+
return true
|
|
20
|
+
} else {
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { promiseReduce } from '../../../promise'
|
|
2
|
+
import { AsyncValidatorSystem } from '../../types'
|
|
3
|
+
|
|
4
|
+
//
|
|
5
|
+
// Or operator (serial)
|
|
6
|
+
//
|
|
7
|
+
export function or(
|
|
8
|
+
validators: AsyncValidatorSystem['ValidatorInput'][],
|
|
9
|
+
): AsyncValidatorSystem['ValidatorFn'] {
|
|
10
|
+
return async function validateOr(input, { validateAsync }) {
|
|
11
|
+
const someIsValid = await promiseReduce(
|
|
12
|
+
validators,
|
|
13
|
+
async (acc, validator) => {
|
|
14
|
+
return acc === true || (await validateAsync(validator, input)) === true
|
|
15
|
+
},
|
|
16
|
+
false as boolean,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
return someIsValid
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DetailedInvalid } from '../types'
|
|
2
|
+
|
|
3
|
+
export class ValidationError extends Error {
|
|
4
|
+
input: any
|
|
5
|
+
code?: string
|
|
6
|
+
error?: Error
|
|
7
|
+
path?: string
|
|
8
|
+
nestedErrors?: DetailedInvalid[]
|
|
9
|
+
|
|
10
|
+
name: string = 'ValidationError'
|
|
11
|
+
|
|
12
|
+
constructor({ message, ...details }: DetailedInvalid) {
|
|
13
|
+
super(message || `Invalid input ${details.input}`)
|
|
14
|
+
|
|
15
|
+
// Capture correct stack trace in V8 environments (Node.js, Google Chrome)
|
|
16
|
+
if (Error.captureStackTrace) {
|
|
17
|
+
Error.captureStackTrace(this, ValidationError)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Object.assign(this, details)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
toJSON(): DetailedInvalid {
|
|
24
|
+
return {
|
|
25
|
+
message: this.message,
|
|
26
|
+
input: this.input,
|
|
27
|
+
code: this.code,
|
|
28
|
+
error: this.error,
|
|
29
|
+
path: this.path,
|
|
30
|
+
nestedErrors: this.nestedErrors,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { typeOf } from '../../../typeOf'
|
|
2
|
+
import { DetailedInvalid } from '../../types'
|
|
3
|
+
|
|
4
|
+
export function defaultErrorMessage({
|
|
5
|
+
input,
|
|
6
|
+
message,
|
|
7
|
+
nestedErrors,
|
|
8
|
+
expectedTypes,
|
|
9
|
+
}: DetailedInvalid): string {
|
|
10
|
+
if (message) {
|
|
11
|
+
return message
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (nestedErrors) {
|
|
15
|
+
return `Invalid input: \`${JSON.stringify(input)}\`.\n${nestedErrors
|
|
16
|
+
.map((error: DetailedInvalid) => ` - ${error.path}: ${error.message}`)
|
|
17
|
+
.join('\n')}`
|
|
18
|
+
} else if (expectedTypes) {
|
|
19
|
+
const inputType = typeOf(input)
|
|
20
|
+
return `Invalid input: '${input}'. Expected type(s) \`${expectedTypes}\`, but got type \`${
|
|
21
|
+
inputType === null ? 'unknown' : inputType
|
|
22
|
+
}\``
|
|
23
|
+
} else {
|
|
24
|
+
return `Invalid input: '${input}'.`
|
|
25
|
+
}
|
|
26
|
+
}
|