/
errorFns.ts
212 lines (205 loc) · 11.6 KB
/
errorFns.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import { SingleArrayValidationError } from './array'
import { MinimumObjectSchema, MinimumObjectDefinition, MinimumSchema } from './types'
import { SingleObjectValidationError } from './object'
const JSONstringify = (unknownObject) => {
try {
return JSON.stringify(
unknownObject,
(_key, value) => (typeof value === 'bigint' ? value.toString() : value), // return everything else unchanged
)
} catch (e) {
return String(unknownObject)
}
}
function stringify(value: unknown) {
switch (typeof value) {
case 'string':
case 'object':
return JSONstringify(value)
default:
return String(value)
}
}
const defaultErrorFn = {
parseString: (value: unknown) => `${stringify(value)} is not a string`,
parseNumber: (value: unknown) => `${stringify(value)} is not a number`,
parseBigInt: (value: unknown) => `${stringify(value)} is not a bigint`,
parseBoolean: (value: unknown) => `${stringify(value)} is not a boolean`,
parseNaN: (value: unknown) => `${stringify(value)} is not a NaN`,
parseSymbol: (value: unknown) => `${stringify(value)} is not a symbol`,
parseDate: (value: unknown) => `${stringify(value)} is not a Date`,
// parseEnum: (value: unknown, enums: unknown[]) =>
// `${stringify(value)} is not in the Enum :${stringify(enums)}`,
// parseEnum: (value: unknown, enumV: object) =>
// `${stringify(value)} is not in the Enum :${stringify(enumV)}`,
notNaN: () => 'NaN is not permitted',
parseLiteral: (value: unknown, literal: unknown) =>
`${stringify(value)} is not identical to ${stringify(literal)}`,
parseUndefined: (value: unknown) => `${stringify(value)} is not undefined`,
parseNull: (value: unknown) => `${stringify(value)} is not null`,
parseNullish: (value: unknown) => `${stringify(value)} is not nullish (null or undefined)`,
parseNever: (value: unknown) => `${stringify(value)} doesn't match 'never'`,
// eslint-disable-next-line @typescript-eslint/ban-types
parseInstanceOf: (value: unknown, instanceOfItem: Function) =>
`${stringify(value)} is not an instance of '${instanceOfItem.name}'`,
minimumArrayLength: (value: unknown[], minLength: number) =>
`array contains ${value.length} element(s) but must contain ${minLength}`,
maximumArrayLength: (value: unknown[], maxLength: number) =>
`array contains ${value.length} element(s) but may only contain ${maxLength}`,
requiredArrayLength: (value: unknown[], requiredLength: number) =>
`array contains ${value.length} element(s), but ${requiredLength} are required`,
arrayNonEmpty: (value: unknown[]) => `'${value}' must contain at least one element`,
maximumStringLength: (value: string, maxLength: number) =>
`'${value}' is longer than ${maxLength} character(s)`,
minimumStringLength: (value: string, minLength: number) =>
`'${value}' is shorter than ${minLength} character(s)`,
stringLength: (value: string, length: number) =>
`'${value}' must contain exactly exactly ${length} character(s)`,
notEmptyString: () => `string cannot be empty`,
beOneOf: (value: string, allItems: string[]) => `'${value}' not in '${allItems}'`,
validEmail: (value: string) => `'${value}' is not a valid email`,
validCuid: (value: string) => `'${value}' is not a valid cuid`,
validCuid2: (value: string) => `'${value}' is not a valid cuid2`,
validUuid: (value: string) => `'${value}' is not a valid uuid`,
validURL: (value: string) => `'${value}' is not a valid URL`,
validUlid: (value: string) => `'${value}' is not a valid Ulid`,
validEmoji: (value: string) => `'${value}' is not a valid Emoji`,
validIpv4: (value: string) => `'${value}' is not a valid IPv4 address`,
validIpv6: (value: string) => `'${value}' is not a valid IPv6 address`,
validIp: (value: string) => `'${value}' is not a valid IP address`,
validDateTime: (value: string) => `'${value}' is not a valid DateTime string`,
includes: (value: string, includedString: string) =>
`'${value}' doesn't include '${includedString}'`,
startsWith: (value: string, startString: string) =>
`'${value}' doesn't start with '${startString}'`,
endsWith: (value: string, endString: string) => `'${value}' doesn't end with '${endString}'`,
validateAgainstRegex: (value: string) => `'${value}' doesn't pass the regex test`,
beTrue: (_value: boolean) => `value must be true`,
beFalse: (_value: boolean) => `value must be false`,
greaterThan: (value: number, greaterThanValue: number) =>
`${value} is not greater than ${greaterThanValue}`,
greaterThanOrEqualTo: (value: number, greaterThanOrEqualToValue: number) =>
`${value} is not greater than or equal to ${greaterThanOrEqualToValue}`,
lesserThan: (value: number, lesserThanValue: number) =>
`${value} is not lesser than ${lesserThanValue}`,
lesserThanOrEqualTo: (value: number, lesserThanOrEqualToValue: number) =>
`${value} is not lesser than or equal to ${lesserThanOrEqualToValue}`,
integer: (value: number) => `${value} is not an integer`,
positive: (value: number) => `${value} is not positive`,
nonNegative: (value: number) => `${value} is not positive`,
negative: (value: number) => `${value} is not negative`,
nonPositive: (value: number) => `${value} is not negative`,
multipleOf: (value: number, multipleOf: number) => `${value} is not a multiple of ${multipleOf}`,
finite: (value: number) => `${value} is not finite`,
safe: (value: number) => `${value} is not safe`,
bigIntGreaterThan: (value: bigint, greaterThanValue: bigint) =>
`${value} is not greater than ${greaterThanValue}`,
bigIntGreaterThanOrEqualTo: (value: bigint, greaterThanOrEqualToValue: bigint) =>
`${value} is not greater than or equal to ${greaterThanOrEqualToValue}`,
bigIntLesserThan: (value: bigint, lesserThanValue: bigint) =>
`${value} is not lesser than ${lesserThanValue}`,
bigIntLesserThanOrEqualTo: (value: bigint, lesserThanOrEqualToValue: bigint) =>
`${value} is not lesser than or equal to ${lesserThanOrEqualToValue}`,
bigIntInteger: (value: bigint) => `${value} is not an integer`,
bigIntPositive: (value: bigint) => `${value} is not positive`,
bigIntNonNegative: (value: bigint) => `${value} is not positive`,
bigIntNegative: (value: bigint) => `${value} is not negative`,
bigIntNonPositive: (value: bigint) => `${value} is not negative`,
bigIntMultipleOf: (value: bigint, multipleOf: bigint) =>
`${value} is not a multiple of ${multipleOf}`,
after: (value: Date, afterDate: Date) => `'${value}' is not after '${afterDate}'`,
before: (value: Date, beforeDate: Date) => `'${value}' is not after '${beforeDate}'`,
invalidArrayElementsFn(
invalidValue: unknown[],
arrayTypeString: string,
errors: SingleArrayValidationError[],
) {
const elementString = errors
.map(([item, itemErrors]) =>
itemErrors.map((error) => `\nat index ${item}: ${error}`).join(''),
)
.join(', ')
return `The array ${stringify(invalidValue)} is not of type ${arrayTypeString}.${elementString}`
},
extraArrayItemsFn: (value: unknown[], extraElementFoundStartingAtIndex: number) =>
`${stringify(value)} contains extra elements starting at ${extraElementFoundStartingAtIndex}`,
arrayDefinitionElementMustBeOptional: (value: unknown[], index: number) =>
`the required element at index: ${index} cannot follow an optional element`,
elementRequiredAt: (value: unknown[], index: number) =>
`no element found at index: ${index} and one is required`,
restCantFollowRest: (value: unknown[], index: number) =>
`the rest element found at index: ${index} cannot follow another rest element`,
optionalElementCantFollowRest: (value: unknown[], index: number) =>
`the optional element found at index: ${index} cannot follow a rest element`,
parseArray: (value: unknown, arrayType: string) =>
`${stringify(value)} is not of type :${arrayType}`,
parseObject: (value: unknown, objectType: string) =>
`${stringify(value)} is not of type :${objectType}`,
invalidObjectPropertiesFn(
invalidValue: object,
objectTypeString: string,
errors: SingleObjectValidationError[],
) {
const elementString = errors
.map(([item, itemErrors]) =>
itemErrors.map((error) => `\n${stringify(item)}: ${error}`).join(''),
)
.join(', ')
return `The object ${stringify(
invalidValue,
)} is not of type ${objectTypeString}.${elementString}`
},
missingProperty: (value: object, property: PropertyKey) =>
`property: ${stringify(property)} not found in ${stringify(value)}`,
missingPropertyInDef: (value: object, property: PropertyKey) =>
`property: ${stringify(property)} not found in object definition: ${stringify(value)}`,
extraKeysFound: (
value: object,
extraKeys: (string | symbol)[],
_objectDef: MinimumObjectDefinition,
) => `${stringify(value)} contains the following extra properties:${stringify(extraKeys)}`,
missingItemInItemSchemas: (value: unknown[], key: PropertyKey) =>
`item: ${stringify(key)} not found in ${stringify(value)}`,
unableToSelectItemFromArray: (_value: MinimumSchema, _keys: PropertyKey[]) =>
`unable to select items from an infinite array`,
keyNotFoundInDiscriminatedUnionDef: (key: PropertyKey, unionDefType: string) =>
`property '${String(key)}' not found in object definition: ${unionDefType}`,
// keyNotFoundInDiscriminatedUnion: (property: string, value: object) =>
// `property '${property}' not found in object: ${stringify(value)}`,
noKeyMatchFoundInDiscriminatedUnion: (
value: object,
keys: string[],
_schemas: MinimumObjectSchema[],
) => `no key matches for the discriminatedUnionKey(s): ${keys} found in: ${stringify(value)}`,
noMatchFoundInUnion: (value: unknown, _schemas: MinimumSchema[]) =>
`no schemas matched: ${stringify(value)}`,
// schemaIsNotObjectSchema: (schema: MinimumSchema) =>
// `${stringify(schema)} is not an object schema`,
// discriminatedUnionValueIsNotAnObject: (value: unknown) =>
// `value ${stringify(value)} is not of type object`,
noMatchFoundInLiteralUnion: (value: unknown, literalUnionDef: readonly unknown[]) =>
`value ${stringify(value)} not found in: ${stringify(literalUnionDef)}`,
parseMap: (value: unknown) => `${stringify(value)} is not an instance of a Map`,
minimumMapLength: (value: Map<unknown, unknown>, minLength: number) =>
`${value.size} is less elements than the minimum of ${minLength}`,
maximumMapLength: (value: Map<unknown, unknown>, maxLength: number) =>
`${value.size} is more elements than the maximum of ${maxLength}`,
requiredMapLength: (value: Map<unknown, unknown>, requiredLength: number) =>
`${value.size} is not the required ${requiredLength} elements`,
mapNonEmpty: (value: Map<unknown, unknown>) =>
`${stringify(value)} must contain at least one element`,
parseSet: (value: unknown) => `${stringify(value)} is not an instance of a Set`,
minimumSetLength: (value: Set<unknown>, minLength: number) =>
`${value.size} is less elements than the minimum of ${minLength}`,
maximumSetLength: (value: Set<unknown>, maxLength: number) =>
`${value.size} is more elements than the maximum of ${maxLength}`,
requiredSetLength: (value: Set<unknown>, requiredLength: number) =>
`${value.size} is not the required ${requiredLength} elements`,
setNonEmpty: (value: Set<unknown>) => `${stringify(value)} must contain at least one element`,
parsePromise: (value: unknown) => `${stringify(value)} doesn't have a 'then' and 'catch' method`,
parseRecord: (value: unknown) => `${stringify(value)} is not a valid object`,
parseFunction: (value: unknown) => `${stringify(value)} is not a valid function`,
parseCustom: (value: unknown) => `${stringify(value)} could not be parsed`,
}
export default defaultErrorFn
export type DefaultErrorFn = typeof defaultErrorFn