@orioro/util 0.0.0 → 0.1.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 (163) hide show
  1. package/README.md +1 -0
  2. package/babel.config.cjs +13 -0
  3. package/coverage/clover.xml +488 -45
  4. package/coverage/coverage-final.json +28 -2
  5. package/coverage/lcov-report/ValidationError.ts.html +184 -0
  6. package/coverage/lcov-report/base.css +19 -7
  7. package/coverage/lcov-report/block-navigation.js +87 -0
  8. package/coverage/lcov-report/favicon.png +0 -0
  9. package/coverage/lcov-report/index.html +248 -58
  10. package/coverage/lcov-report/prettify.js +1 -0
  11. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  12. package/coverage/lcov-report/sorter.js +52 -14
  13. package/coverage/lcov-report/src/debug/deepFreeze.ts.html +157 -0
  14. package/coverage/lcov-report/src/debug/index.html +146 -0
  15. package/coverage/lcov-report/src/debug/index.ts.html +91 -0
  16. package/coverage/lcov-report/src/debug/wait.ts.html +127 -0
  17. package/coverage/lcov-report/src/index.html +131 -0
  18. package/coverage/lcov-report/src/interpolate/index.html +116 -0
  19. package/coverage/lcov-report/src/interpolate/index.ts.html +277 -0
  20. package/coverage/lcov-report/src/maybeFn.ts.html +94 -0
  21. package/coverage/lcov-report/src/promise/index.html +146 -0
  22. package/coverage/lcov-report/src/promise/index.ts.html +91 -0
  23. package/coverage/lcov-report/src/promise/promiseReduce.ts.html +130 -0
  24. package/coverage/lcov-report/src/promise/resolveNestedPromises.ts.html +271 -0
  25. package/coverage/lcov-report/src/switchValue.ts.html +253 -0
  26. package/coverage/lcov-report/src/typeOf.ts.html +328 -0
  27. package/coverage/lcov-report/src/validate/ValidationError.ts.html +184 -0
  28. package/coverage/lcov-report/src/validate/async/index.html +131 -0
  29. package/coverage/lcov-report/src/validate/async/index.ts.html +241 -0
  30. package/coverage/lcov-report/src/validate/async/parseValidator.ts.html +136 -0
  31. package/coverage/lcov-report/src/validate/async/validateAsyncFn.ts.html +208 -0
  32. package/coverage/lcov-report/src/validate/async/validators/and.ts.html +154 -0
  33. package/coverage/lcov-report/src/validate/async/validators/index.html +146 -0
  34. package/coverage/lcov-report/src/validate/async/validators/index.ts.html +91 -0
  35. package/coverage/lcov-report/src/validate/async/validators/logical.ts.html +253 -0
  36. package/coverage/lcov-report/src/validate/async/validators/or.ts.html +151 -0
  37. package/coverage/lcov-report/src/validate/async/validators/shape.ts.html +565 -0
  38. package/coverage/lcov-report/src/validate/common/ValidationError.ts.html +184 -0
  39. package/coverage/lcov-report/src/validate/common/index.html +116 -0
  40. package/coverage/lcov-report/src/validate/common/util/defaultErrorMessage.ts.html +163 -0
  41. package/coverage/lcov-report/src/validate/common/util/index.html +161 -0
  42. package/coverage/lcov-report/src/validate/common/util/index.ts.html +94 -0
  43. package/coverage/lcov-report/src/validate/common/util/parseValidator.ts.html +316 -0
  44. package/coverage/lcov-report/src/validate/common/util/parseValidatorInput.ts.html +316 -0
  45. package/coverage/lcov-report/src/validate/common/util/resolveValidationResult.ts.html +277 -0
  46. package/coverage/lcov-report/src/validate/common/util/validatorParser.ts.html +316 -0
  47. package/coverage/lcov-report/src/validate/common/validators/index.html +131 -0
  48. package/coverage/lcov-report/src/validate/common/validators/index.ts.html +88 -0
  49. package/coverage/lcov-report/src/validate/common/validators/type.ts.html +388 -0
  50. package/coverage/lcov-report/src/validate/fmtValidationResult.ts.html +268 -0
  51. package/coverage/lcov-report/src/validate/index.html +116 -0
  52. package/coverage/lcov-report/src/validate/index.ts.html +94 -0
  53. package/coverage/lcov-report/src/validate/makeValidate.ts.html +634 -0
  54. package/coverage/lcov-report/src/validate/specUtil/commonTests.js.html +1324 -0
  55. package/coverage/lcov-report/src/validate/specUtil/index.html +116 -0
  56. package/coverage/lcov-report/src/validate/sync/index.html +131 -0
  57. package/coverage/lcov-report/src/validate/sync/index.ts.html +244 -0
  58. package/coverage/lcov-report/src/validate/sync/parseValidator.ts.html +136 -0
  59. package/coverage/lcov-report/src/validate/sync/validateSyncFn.ts.html +223 -0
  60. package/coverage/lcov-report/src/validate/sync/validators/and.ts.html +148 -0
  61. package/coverage/lcov-report/src/validate/sync/validators/index.html +146 -0
  62. package/coverage/lcov-report/src/validate/sync/validators/index.ts.html +91 -0
  63. package/coverage/lcov-report/src/validate/sync/validators/logical.ts.html +226 -0
  64. package/coverage/lcov-report/src/validate/sync/validators/or.ts.html +130 -0
  65. package/coverage/lcov-report/src/validate/sync/validators/shape.ts.html +523 -0
  66. package/coverage/lcov-report/src/validate/sync/validators/type.ts.html +154 -0
  67. package/coverage/lcov-report/src/validate/syncValidators/and.ts.html +157 -0
  68. package/coverage/lcov-report/src/validate/syncValidators/index.html +176 -0
  69. package/coverage/lcov-report/src/validate/syncValidators/index.ts.html +97 -0
  70. package/coverage/lcov-report/src/validate/syncValidators/or.ts.html +127 -0
  71. package/coverage/lcov-report/src/validate/syncValidators/shape.ts.html +559 -0
  72. package/coverage/lcov-report/src/validate/syncValidators/string.ts.html +163 -0
  73. package/coverage/lcov-report/src/validate/syncValidators/type.ts.html +154 -0
  74. package/coverage/lcov-report/src/validate/util/defaultErrorMessage.ts.html +169 -0
  75. package/coverage/lcov-report/src/validate/util/index.html +146 -0
  76. package/coverage/lcov-report/src/validate/util/index.ts.html +91 -0
  77. package/coverage/lcov-report/src/validate/util/resolveValidationResult.ts.html +253 -0
  78. package/coverage/lcov-report/src/validate/validate.ts.html +220 -0
  79. package/coverage/lcov-report/src/validate/validateAsync.ts.html +220 -0
  80. package/coverage/lcov-report/src/validate/validators/and.ts.html +157 -0
  81. package/coverage/lcov-report/src/validate/validators/index.html +176 -0
  82. package/coverage/lcov-report/src/validate/validators/index.ts.html +97 -0
  83. package/coverage/lcov-report/src/validate/validators/or.ts.html +127 -0
  84. package/coverage/lcov-report/src/validate/validators/shape.ts.html +541 -0
  85. package/coverage/lcov-report/src/validate/validators/type.ts.html +154 -0
  86. package/coverage/lcov-report/src/validate_/ValidationError.ts.html +184 -0
  87. package/coverage/lcov-report/src/validate_/fmtValidationResult.ts.html +268 -0
  88. package/coverage/lcov-report/src/validate_/index.html +161 -0
  89. package/coverage/lcov-report/src/validate_/makeValidate.ts.html +634 -0
  90. package/coverage/lcov-report/src/validate_/validate.ts.html +220 -0
  91. package/coverage/lcov-report/switchValue.ts.html +253 -0
  92. package/coverage/lcov-report/typeOf.ts.html +331 -0
  93. package/coverage/lcov-report/validate.ts.html +757 -0
  94. package/coverage/lcov.info +1045 -74
  95. package/dist/index.mjs +1428 -74
  96. package/jest.config.js +6 -0
  97. package/package.json +27 -27
  98. package/rollup.config.mjs +6 -0
  99. package/src/PromiseLikeEventEmitter/index.ts +35 -0
  100. package/src/array/arrayChunk.ts +7 -0
  101. package/src/array/index.ts +1 -0
  102. package/src/debug/debugFn/index.ts +48 -0
  103. package/src/debug/debugFn/util.ts +27 -0
  104. package/src/debug/deepFreeze.ts +26 -0
  105. package/src/debug/index.ts +3 -0
  106. package/src/debug/wait.ts +14 -0
  107. package/src/index.ts +8 -0
  108. package/src/interpolate/index.spec.ts +20 -0
  109. package/src/interpolate/index.ts +64 -0
  110. package/src/maybeFn.ts +3 -0
  111. package/src/promise/batchFn.spec.ts +92 -0
  112. package/src/promise/batchFn.ts +176 -0
  113. package/src/promise/index.ts +3 -0
  114. package/src/promise/promiseReduce.ts +15 -0
  115. package/src/promise/resolveNestedPromises.spec.ts +205 -0
  116. package/src/promise/resolveNestedPromises.ts +83 -0
  117. package/src/promise/types.ts +2 -0
  118. package/src/switchValue.spec.ts +30 -0
  119. package/src/switchValue.ts +59 -0
  120. package/src/typeOf.spec.ts +47 -0
  121. package/src/typeOf.ts +81 -0
  122. package/src/validate/__snapshots__/index.spec.ts.snap +9 -0
  123. package/src/validate/async/index.spec.ts +236 -0
  124. package/src/validate/async/index.ts +52 -0
  125. package/src/validate/async/validateAsyncFn.ts +41 -0
  126. package/src/validate/async/validators/index.ts +2 -0
  127. package/src/validate/async/validators/logical.ts +56 -0
  128. package/src/validate/async/validators/shape.ts +160 -0
  129. package/src/validate/async/validators/tmpand.ts +24 -0
  130. package/src/validate/async/validators/tmpor.ts +21 -0
  131. package/src/validate/common/ValidationError.ts +33 -0
  132. package/src/validate/common/util/defaultErrorMessage.ts +26 -0
  133. package/src/validate/common/util/index.ts +3 -0
  134. package/src/validate/common/util/parseValidatorInput.ts +77 -0
  135. package/src/validate/common/util/resolveValidationResult.ts +64 -0
  136. package/src/validate/common/validators/index.ts +1 -0
  137. package/src/validate/common/validators/type.ts +101 -0
  138. package/src/validate/index.spec.ts +5 -0
  139. package/src/validate/index.ts +3 -0
  140. package/src/validate/specUtil/commonTests.js +413 -0
  141. package/src/validate/sync/index.spec.ts +81 -0
  142. package/src/validate/sync/index.ts +53 -0
  143. package/src/validate/sync/validateSyncFn.ts +46 -0
  144. package/src/validate/sync/validators/index.ts +2 -0
  145. package/src/validate/sync/validators/logical.ts +47 -0
  146. package/src/validate/sync/validators/shape.ts +146 -0
  147. package/src/validate/types/async.ts +20 -0
  148. package/src/validate/types/common.ts +70 -0
  149. package/src/validate/types/index.ts +3 -0
  150. package/src/validate/types/sync.ts +20 -0
  151. package/tsconfig.json +11 -0
  152. package/array/index.js +0 -11
  153. package/coverage/lcov-report/array/index.html +0 -93
  154. package/coverage/lcov-report/array/index.js.html +0 -98
  155. package/coverage/lcov-report/coverage/coverage-final.json.html +0 -95
  156. package/coverage/lcov-report/coverage/index.html +0 -93
  157. package/coverage/lcov-report/coverage/lcov-report/index.html +0 -106
  158. package/coverage/lcov-report/coverage/lcov-report/prettify.js.html +0 -68
  159. package/coverage/lcov-report/coverage/lcov-report/sorter.js.html +0 -539
  160. package/coverage/lcov-report/fn/index.html +0 -93
  161. package/coverage/lcov-report/fn/index.js.html +0 -215
  162. package/dist/index.js +0 -116
  163. 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,2 @@
1
+ export * from './shape'
2
+ export * from './logical'
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ export * from './defaultErrorMessage'
2
+ export * from './resolveValidationResult'
3
+ export * from './parseValidatorInput'