@zerotal/validator 1.6.3 → 1.7.2
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/api-surface.md +369 -0
- package/package.json +3 -2
- package/src/FormRequest.ts +28 -0
- package/src/Validator.ts +11 -1
package/api-surface.md
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# @zerotal/validator — public API surface
|
|
2
|
+
|
|
3
|
+
<!-- AUTO-GENERATED by scripts/api-surface.ts. Do not edit by hand.
|
|
4
|
+
Run `bun run api:surface` to regenerate after an intentional API change. -->
|
|
5
|
+
|
|
6
|
+
## . `(./src/index.ts)`
|
|
7
|
+
|
|
8
|
+
class ArrayRule = {
|
|
9
|
+
new <T extends FieldRule>(item: T): ArrayRule<T>
|
|
10
|
+
accepted: (message?: string) => ArrayRule<T>
|
|
11
|
+
bail: () => ArrayRule<T>
|
|
12
|
+
custom: (fn: CustomFn, message?: string) => ArrayRule<T>
|
|
13
|
+
declined: (message?: string) => ArrayRule<T>
|
|
14
|
+
default: (value: unknown) => ArrayRule<T>
|
|
15
|
+
max: (length: number, message?: string) => ArrayRule<T>
|
|
16
|
+
min: (length: number, message?: string) => ArrayRule<T>
|
|
17
|
+
nullable: () => ArrayRule<T>
|
|
18
|
+
optional: () => ArrayRule<T>
|
|
19
|
+
prohibitedIf: (field: string, value: unknown) => ArrayRule<T>
|
|
20
|
+
prohibitedUnless: (field: string, value: unknown) => ArrayRule<T>
|
|
21
|
+
readonly _def: ArrayDef<T['_def']>
|
|
22
|
+
required: (message?: string) => ArrayRule<T>
|
|
23
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => ArrayRule<T>
|
|
24
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => ArrayRule<T>
|
|
25
|
+
requiredWith: (fields: string[]) => ArrayRule<T>
|
|
26
|
+
requiredWithAll: (fields: string[]) => ArrayRule<T>
|
|
27
|
+
requiredWithout: (fields: string[]) => ArrayRule<T>
|
|
28
|
+
requiredWithoutAll: (fields: string[]) => ArrayRule<T>
|
|
29
|
+
size: (n: number, message?: string) => ArrayRule<T>
|
|
30
|
+
sometimes: () => ArrayRule<T>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class BooleanRule = {
|
|
34
|
+
new (): BooleanRule
|
|
35
|
+
accepted: (message?: string) => BooleanRule
|
|
36
|
+
bail: () => BooleanRule
|
|
37
|
+
custom: (fn: CustomFn, message?: string) => BooleanRule
|
|
38
|
+
declined: (message?: string) => BooleanRule
|
|
39
|
+
default: (value: unknown) => BooleanRule
|
|
40
|
+
nullable: () => BooleanRule
|
|
41
|
+
optional: () => BooleanRule
|
|
42
|
+
prohibitedIf: (field: string, value: unknown) => BooleanRule
|
|
43
|
+
prohibitedUnless: (field: string, value: unknown) => BooleanRule
|
|
44
|
+
readonly _def: BooleanDef
|
|
45
|
+
required: (message?: string) => BooleanRule
|
|
46
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => BooleanRule
|
|
47
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => BooleanRule
|
|
48
|
+
requiredWith: (fields: string[]) => BooleanRule
|
|
49
|
+
requiredWithAll: (fields: string[]) => BooleanRule
|
|
50
|
+
requiredWithout: (fields: string[]) => BooleanRule
|
|
51
|
+
requiredWithoutAll: (fields: string[]) => BooleanRule
|
|
52
|
+
sometimes: () => BooleanRule
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class DateRule = {
|
|
56
|
+
new (): DateRule
|
|
57
|
+
accepted: (message?: string) => DateRule
|
|
58
|
+
after: (date: Date | string, message?: string) => DateRule
|
|
59
|
+
afterOrEqual: (date: Date | string, message?: string) => DateRule
|
|
60
|
+
bail: () => DateRule
|
|
61
|
+
before: (date: Date | string, message?: string) => DateRule
|
|
62
|
+
beforeOrEqual: (date: Date | string, message?: string) => DateRule
|
|
63
|
+
custom: (fn: CustomFn, message?: string) => DateRule
|
|
64
|
+
declined: (message?: string) => DateRule
|
|
65
|
+
default: (value: unknown) => DateRule
|
|
66
|
+
nullable: () => DateRule
|
|
67
|
+
optional: () => DateRule
|
|
68
|
+
prohibitedIf: (field: string, value: unknown) => DateRule
|
|
69
|
+
prohibitedUnless: (field: string, value: unknown) => DateRule
|
|
70
|
+
readonly _def: DateDef
|
|
71
|
+
required: (message?: string) => DateRule
|
|
72
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => DateRule
|
|
73
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => DateRule
|
|
74
|
+
requiredWith: (fields: string[]) => DateRule
|
|
75
|
+
requiredWithAll: (fields: string[]) => DateRule
|
|
76
|
+
requiredWithout: (fields: string[]) => DateRule
|
|
77
|
+
requiredWithoutAll: (fields: string[]) => DateRule
|
|
78
|
+
sometimes: () => DateRule
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class FieldRule = {
|
|
82
|
+
new (): FieldRule
|
|
83
|
+
accepted: (message?: string) => FieldRule
|
|
84
|
+
bail: () => FieldRule
|
|
85
|
+
custom: (fn: CustomFn, message?: string) => FieldRule
|
|
86
|
+
declined: (message?: string) => FieldRule
|
|
87
|
+
default: (value: unknown) => FieldRule
|
|
88
|
+
nullable: () => FieldRule
|
|
89
|
+
optional: () => FieldRule
|
|
90
|
+
prohibitedIf: (field: string, value: unknown) => FieldRule
|
|
91
|
+
prohibitedUnless: (field: string, value: unknown) => FieldRule
|
|
92
|
+
readonly _def: FieldRuleDefinition
|
|
93
|
+
required: (message?: string) => FieldRule
|
|
94
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => FieldRule
|
|
95
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => FieldRule
|
|
96
|
+
requiredWith: (fields: string[]) => FieldRule
|
|
97
|
+
requiredWithAll: (fields: string[]) => FieldRule
|
|
98
|
+
requiredWithout: (fields: string[]) => FieldRule
|
|
99
|
+
requiredWithoutAll: (fields: string[]) => FieldRule
|
|
100
|
+
sometimes: () => FieldRule
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class FileRule = {
|
|
104
|
+
new (): FileRule
|
|
105
|
+
accepted: (message?: string) => FileRule
|
|
106
|
+
bail: () => FileRule
|
|
107
|
+
custom: (fn: CustomFn, message?: string) => FileRule
|
|
108
|
+
declined: (message?: string) => FileRule
|
|
109
|
+
default: (value: unknown) => FileRule
|
|
110
|
+
max: (kb: number, message?: string) => FileRule
|
|
111
|
+
mimes: (extensions: string[], message?: string) => FileRule
|
|
112
|
+
min: (kb: number, message?: string) => FileRule
|
|
113
|
+
nullable: () => FileRule
|
|
114
|
+
optional: () => FileRule
|
|
115
|
+
prohibitedIf: (field: string, value: unknown) => FileRule
|
|
116
|
+
prohibitedUnless: (field: string, value: unknown) => FileRule
|
|
117
|
+
readonly _def: FileDef
|
|
118
|
+
required: (message?: string) => FileRule
|
|
119
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => FileRule
|
|
120
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => FileRule
|
|
121
|
+
requiredWith: (fields: string[]) => FileRule
|
|
122
|
+
requiredWithAll: (fields: string[]) => FileRule
|
|
123
|
+
requiredWithout: (fields: string[]) => FileRule
|
|
124
|
+
requiredWithoutAll: (fields: string[]) => FileRule
|
|
125
|
+
sometimes: () => FileRule
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
class FormRequest = {
|
|
129
|
+
new (): FormRequest
|
|
130
|
+
static macro: (name: string, fn: (this: FormRequest, ...args: unknown[]) => unknown) => void
|
|
131
|
+
static validate: <T extends FormRequest>(this: new () => T) => Promise<Infer<ExtractDefs<ReturnType<T['rules']>>>>
|
|
132
|
+
authorize: () => boolean | Promise<boolean>
|
|
133
|
+
rules: (_r: RuleBuilder) => Record<string, FieldRule>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class NumberRule = {
|
|
137
|
+
new (): NumberRule
|
|
138
|
+
accepted: (message?: string) => NumberRule
|
|
139
|
+
bail: () => NumberRule
|
|
140
|
+
between: (min: number, max: number, message?: string) => NumberRule
|
|
141
|
+
custom: (fn: CustomFn, message?: string) => NumberRule
|
|
142
|
+
declined: (message?: string) => NumberRule
|
|
143
|
+
default: (value: unknown) => NumberRule
|
|
144
|
+
exists: (table: string, column: string, message?: string) => NumberRule
|
|
145
|
+
integer: (message?: string) => NumberRule
|
|
146
|
+
max: (value: number, message?: string) => NumberRule
|
|
147
|
+
min: (value: number, message?: string) => NumberRule
|
|
148
|
+
notIn: (values: number[], message?: string) => NumberRule
|
|
149
|
+
nullable: () => NumberRule
|
|
150
|
+
optional: () => NumberRule
|
|
151
|
+
positive: (message?: string) => NumberRule
|
|
152
|
+
prohibitedIf: (field: string, value: unknown) => NumberRule
|
|
153
|
+
prohibitedUnless: (field: string, value: unknown) => NumberRule
|
|
154
|
+
readonly _def: NumberDef
|
|
155
|
+
required: (message?: string) => NumberRule
|
|
156
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => NumberRule
|
|
157
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => NumberRule
|
|
158
|
+
requiredWith: (fields: string[]) => NumberRule
|
|
159
|
+
requiredWithAll: (fields: string[]) => NumberRule
|
|
160
|
+
requiredWithout: (fields: string[]) => NumberRule
|
|
161
|
+
requiredWithoutAll: (fields: string[]) => NumberRule
|
|
162
|
+
sometimes: () => NumberRule
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
class ObjectRule = {
|
|
166
|
+
new <S extends Record<string, FieldRule>>(shape: S): ObjectRule<S>
|
|
167
|
+
accepted: (message?: string) => ObjectRule<S>
|
|
168
|
+
bail: () => ObjectRule<S>
|
|
169
|
+
custom: (fn: CustomFn, message?: string) => ObjectRule<S>
|
|
170
|
+
declined: (message?: string) => ObjectRule<S>
|
|
171
|
+
default: (value: unknown) => ObjectRule<S>
|
|
172
|
+
nullable: () => ObjectRule<S>
|
|
173
|
+
optional: () => ObjectRule<S>
|
|
174
|
+
prohibitedIf: (field: string, value: unknown) => ObjectRule<S>
|
|
175
|
+
prohibitedUnless: (field: string, value: unknown) => ObjectRule<S>
|
|
176
|
+
readonly _def: ObjectDef<{ [K in keyof S]: S[K]['_def']; }>
|
|
177
|
+
required: (message?: string) => ObjectRule<S>
|
|
178
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => ObjectRule<S>
|
|
179
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => ObjectRule<S>
|
|
180
|
+
requiredWith: (fields: string[]) => ObjectRule<S>
|
|
181
|
+
requiredWithAll: (fields: string[]) => ObjectRule<S>
|
|
182
|
+
requiredWithout: (fields: string[]) => ObjectRule<S>
|
|
183
|
+
requiredWithoutAll: (fields: string[]) => ObjectRule<S>
|
|
184
|
+
sometimes: () => ObjectRule<S>
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
class PasswordRule = {
|
|
188
|
+
new (): PasswordRule
|
|
189
|
+
accepted: (message?: string) => PasswordRule
|
|
190
|
+
bail: () => PasswordRule
|
|
191
|
+
confirmed: (message?: string) => PasswordRule
|
|
192
|
+
custom: (fn: CustomFn, message?: string) => PasswordRule
|
|
193
|
+
declined: (message?: string) => PasswordRule
|
|
194
|
+
default: (value: unknown) => PasswordRule
|
|
195
|
+
min: (length: number, message?: string) => PasswordRule
|
|
196
|
+
mixedCase: (message?: string) => PasswordRule
|
|
197
|
+
nullable: () => PasswordRule
|
|
198
|
+
numbers: (message?: string) => PasswordRule
|
|
199
|
+
optional: () => PasswordRule
|
|
200
|
+
prohibitedIf: (field: string, value: unknown) => PasswordRule
|
|
201
|
+
prohibitedUnless: (field: string, value: unknown) => PasswordRule
|
|
202
|
+
readonly _def: FieldRuleDefinition & { type: 'string';}
|
|
203
|
+
required: (message?: string) => PasswordRule
|
|
204
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => PasswordRule
|
|
205
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => PasswordRule
|
|
206
|
+
requiredWith: (fields: string[]) => PasswordRule
|
|
207
|
+
requiredWithAll: (fields: string[]) => PasswordRule
|
|
208
|
+
requiredWithout: (fields: string[]) => PasswordRule
|
|
209
|
+
requiredWithoutAll: (fields: string[]) => PasswordRule
|
|
210
|
+
sometimes: () => PasswordRule
|
|
211
|
+
symbols: (message?: string) => PasswordRule
|
|
212
|
+
uncompromised: (message?: string) => PasswordRule
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
class PrecognitionResponseError = {
|
|
216
|
+
new (precognitionStatus: 204 | 422, errors?: ValidationErrors): PrecognitionResponseError
|
|
217
|
+
readonly code: string
|
|
218
|
+
readonly context?: Record<string, unknown> | undefined
|
|
219
|
+
readonly errors: ValidationErrors
|
|
220
|
+
readonly name: 'PrecognitionResponse'
|
|
221
|
+
readonly precognitionStatus: 204 | 422
|
|
222
|
+
readonly status: number
|
|
223
|
+
toResponse: () => Response
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
class RuleBuilder = {
|
|
227
|
+
new (): RuleBuilder
|
|
228
|
+
array: <T extends FieldRule>(item: T) => ArrayRule<T>
|
|
229
|
+
boolean: () => BooleanRule
|
|
230
|
+
date: () => DateRule
|
|
231
|
+
email: (message?: string) => StringRule
|
|
232
|
+
file: () => FileRule
|
|
233
|
+
number: () => NumberRule
|
|
234
|
+
object: <S extends Record<string, FieldRule>>(shape: S) => ObjectRule<S>
|
|
235
|
+
password: () => PasswordRule
|
|
236
|
+
required: (message?: string) => StringRule
|
|
237
|
+
string: () => StringRule
|
|
238
|
+
url: (message?: string) => StringRule
|
|
239
|
+
uuid: (message?: string) => StringRule
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
class StringRule = {
|
|
243
|
+
new (): StringRule
|
|
244
|
+
accepted: (message?: string) => StringRule
|
|
245
|
+
alpha: (message?: string) => StringRule
|
|
246
|
+
alphaDash: (message?: string) => StringRule
|
|
247
|
+
alphaNum: (message?: string) => StringRule
|
|
248
|
+
bail: () => StringRule
|
|
249
|
+
confirmed: (message?: string) => StringRule
|
|
250
|
+
custom: (fn: CustomFn, message?: string) => StringRule
|
|
251
|
+
declined: (message?: string) => StringRule
|
|
252
|
+
default: (value: unknown) => StringRule
|
|
253
|
+
digits: (n: number, message?: string) => StringRule
|
|
254
|
+
digitsBetween: (min: number, max: number, message?: string) => StringRule
|
|
255
|
+
email: (message?: string) => StringRule
|
|
256
|
+
endsWith: (suffix: string, message?: string) => StringRule
|
|
257
|
+
exists: (table: string, column: string, message?: string) => StringRule
|
|
258
|
+
in: (values: string[], message?: string) => StringRule
|
|
259
|
+
ip: (message?: string) => StringRule
|
|
260
|
+
json: (message?: string) => StringRule
|
|
261
|
+
lowercase: () => StringRule
|
|
262
|
+
matches: (pattern: RegExp, message?: string) => StringRule
|
|
263
|
+
max: (length: number, message?: string) => StringRule
|
|
264
|
+
min: (length: number, message?: string) => StringRule
|
|
265
|
+
notIn: (values: string[], message?: string) => StringRule
|
|
266
|
+
nullable: () => StringRule
|
|
267
|
+
numeric: (message?: string) => StringRule
|
|
268
|
+
optional: () => StringRule
|
|
269
|
+
password: () => StringRule
|
|
270
|
+
present: (message?: string) => StringRule
|
|
271
|
+
prohibited: (message?: string) => StringRule
|
|
272
|
+
prohibitedIf: (field: string, value: unknown) => StringRule
|
|
273
|
+
prohibitedUnless: (field: string, value: unknown) => StringRule
|
|
274
|
+
readonly _def: StringDef
|
|
275
|
+
regex: (pattern: RegExp, message?: string) => StringRule
|
|
276
|
+
required: (message?: string) => StringRule
|
|
277
|
+
requiredIf: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => StringRule
|
|
278
|
+
requiredUnless: (fieldOrFn: string | ((input: Record<string, unknown>) => boolean), value?: unknown) => StringRule
|
|
279
|
+
requiredWith: (fields: string[]) => StringRule
|
|
280
|
+
requiredWithAll: (fields: string[]) => StringRule
|
|
281
|
+
requiredWithout: (fields: string[]) => StringRule
|
|
282
|
+
requiredWithoutAll: (fields: string[]) => StringRule
|
|
283
|
+
sameAs: (field: string, message?: string) => StringRule
|
|
284
|
+
size: (n: number, message?: string) => StringRule
|
|
285
|
+
sometimes: () => StringRule
|
|
286
|
+
startsWith: (prefix: string, message?: string) => StringRule
|
|
287
|
+
trim: () => StringRule
|
|
288
|
+
unique: (table: string, column: string, ignoreOrOpts?: string | number | UniqueOptions, message?: string) => StringRule
|
|
289
|
+
uppercase: () => StringRule
|
|
290
|
+
url: (message?: string) => StringRule
|
|
291
|
+
uuid: (message?: string) => StringRule
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
class ValidationJsonError = {
|
|
295
|
+
new (errors: ValidationErrors): ValidationJsonError
|
|
296
|
+
readonly code: string
|
|
297
|
+
readonly context?: Record<string, unknown> | undefined
|
|
298
|
+
readonly errors: ValidationErrors
|
|
299
|
+
readonly name: 'ValidationJsonError'
|
|
300
|
+
readonly status: number
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
class ValidationRedirectError = {
|
|
304
|
+
new (redirectTo: string, errors: ValidationErrors): ValidationRedirectError
|
|
305
|
+
readonly code: string
|
|
306
|
+
readonly context?: Record<string, unknown> | undefined
|
|
307
|
+
readonly errors: ValidationErrors
|
|
308
|
+
readonly name: 'ValidationRedirectError'
|
|
309
|
+
readonly redirectTo: string
|
|
310
|
+
readonly status: number
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const Validator = { check<S extends Record<string, FieldRule>>(data: Record<string, unknown>, factory: (rules: RuleBuilder) => S): ValidationOutcome<Infer<ExtractDefs<S>>>;}
|
|
314
|
+
|
|
315
|
+
function registerDbRuleRunner = (runner: DbRuleRunner) => void
|
|
316
|
+
|
|
317
|
+
function runStringRules = (data: Record<string, unknown>, rules: StringRules) => Record<string, string[]>
|
|
318
|
+
|
|
319
|
+
function runStringRulesAsync = (data: Record<string, unknown>, rules: StringRules) => Promise<Record<string, string[]>>
|
|
320
|
+
|
|
321
|
+
function runValidation = <S extends Schema>(schema: S, input: Record<string, unknown>) => ValidationOutcome<unknown>
|
|
322
|
+
|
|
323
|
+
function runValidationAsync = <S extends Schema>(schema: S, input: Record<string, unknown>) => Promise<ValidationOutcome<unknown>>
|
|
324
|
+
|
|
325
|
+
function validate = <S extends Record<string, FieldRule>>(ctx: HttpContext, factory: (rules: RuleBuilder) => S) => Promise<Infer<ExtractDefs<S>>>
|
|
326
|
+
|
|
327
|
+
function ValidatorConfig = (options?: Partial<ValidatorConfigShape>) => ValidatorConfigShape
|
|
328
|
+
|
|
329
|
+
interface FieldRuleDefinition = {
|
|
330
|
+
bail?: boolean
|
|
331
|
+
children?: FieldRuleDefinition
|
|
332
|
+
defaultVal: unknown
|
|
333
|
+
nullable: boolean
|
|
334
|
+
prohibitedIf?: { field: string; value: unknown;}
|
|
335
|
+
prohibitedUnless?: { field: string; value: unknown;}
|
|
336
|
+
required: boolean
|
|
337
|
+
requiredIf?: { field: string; value: unknown;} | { fn: (input: Record<string, unknown>) => boolean;}
|
|
338
|
+
requiredMessage?: string
|
|
339
|
+
requiredUnless?: { field: string; value: unknown;} | { fn: (input: Record<string, unknown>) => boolean;}
|
|
340
|
+
requiredWith?: string[]
|
|
341
|
+
requiredWithAll?: string[]
|
|
342
|
+
requiredWithout?: string[]
|
|
343
|
+
requiredWithoutAll?: string[]
|
|
344
|
+
rules: { name: string; args: unknown[]; message?: string; fn?: CustomFn;}[]
|
|
345
|
+
shape?: Record<string, FieldRuleDefinition>
|
|
346
|
+
sometimes?: boolean
|
|
347
|
+
type: 'string' | 'number' | 'boolean' | 'object' | 'date' | 'file' | 'array'
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
interface UniqueOptions = {
|
|
351
|
+
ignoreId?: string | number
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
interface ValidatorConfigShape = {
|
|
355
|
+
locale: string
|
|
356
|
+
stopOnFirstFailure: boolean
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
type Infer = { [K in keyof S]: InferField<S[K]>; }
|
|
360
|
+
|
|
361
|
+
type InferFieldType = F['type'] extends 'string' ? string : F['type'] extends 'number' ? number : F['type'] extends 'boolean' ? boolean : F['type'] extends 'date' ? Date : F['type'] extends 'array' ? InferArrayType<F> : F['type'] extends 'object' ? InferObjectType<F> : unknown
|
|
362
|
+
|
|
363
|
+
type Schema = { [x: string]: FieldRuleDefinition;}
|
|
364
|
+
|
|
365
|
+
type StringRules = { [x: string]: string | string[];}
|
|
366
|
+
|
|
367
|
+
type ValidationErrors = { [x: string]: string;}
|
|
368
|
+
|
|
369
|
+
type ValidationOutcome = ValidationResult<T> | ValidationFailure
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerotal/validator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"maturity": "stable",
|
|
6
6
|
"private": false,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"CHANGELOG.md",
|
|
15
|
+
"api-surface.md",
|
|
15
16
|
"src",
|
|
16
17
|
"!src/**/*.test.ts",
|
|
17
18
|
"!src/**/*.test.tsx",
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
"typecheck": "tsc --noEmit"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@zerotal/core": "1.
|
|
33
|
+
"@zerotal/core": "1.7.2"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"typescript": "^5.8.0"
|
package/src/FormRequest.ts
CHANGED
|
@@ -113,6 +113,30 @@ export abstract class FormRequest {
|
|
|
113
113
|
throw new Error(`${this.constructor.name}.rules() is not implemented.`);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Normalise the raw body before the rules see it. Default: unchanged.
|
|
118
|
+
*
|
|
119
|
+
* This is where the shape of the *transport* gets reconciled with the shape of
|
|
120
|
+
* the rules. An HTML form has no way to say `null`: an unselected `<select>`
|
|
121
|
+
* posts `""`, so a `r.number().nullable()` field fails on the one input the
|
|
122
|
+
* browser can actually produce for "nothing". A client-side form can patch
|
|
123
|
+
* that up before it posts; a plain `<form method="post">` cannot, and a rule
|
|
124
|
+
* only reachable from a build with JavaScript is not a shared rule.
|
|
125
|
+
*
|
|
126
|
+
* Runs after `authorize()` and after the body is parsed, so it sees exactly
|
|
127
|
+
* what arrived. Return the object to validate — mutating and returning the
|
|
128
|
+
* argument is fine.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* override prepareForValidation(body: Record<string, unknown>) {
|
|
132
|
+
* if (body["assigneeId"] === "") body["assigneeId"] = null;
|
|
133
|
+
* return body;
|
|
134
|
+
* }
|
|
135
|
+
*/
|
|
136
|
+
prepareForValidation(body: Record<string, unknown>): Record<string, unknown> {
|
|
137
|
+
return body;
|
|
138
|
+
}
|
|
139
|
+
|
|
116
140
|
/**
|
|
117
141
|
* Attach a helper method to all FormRequest instances at runtime.
|
|
118
142
|
* Designed for framework packages (e.g. @zerotal/auth) that expose
|
|
@@ -193,6 +217,10 @@ export abstract class FormRequest {
|
|
|
193
217
|
schema[key] = rule._def;
|
|
194
218
|
}
|
|
195
219
|
|
|
220
|
+
// The transport's shape reconciled with the rules' shape, before the rules
|
|
221
|
+
// run. See `prepareForValidation` for why an HTML form needs this at all.
|
|
222
|
+
body = instance.prepareForValidation(body);
|
|
223
|
+
|
|
196
224
|
const result = await runValidationAsync(schema as Schema, body);
|
|
197
225
|
const session = (ctx as unknown as { session?: SessionLike }).session;
|
|
198
226
|
|
package/src/Validator.ts
CHANGED
|
@@ -135,7 +135,17 @@ export function runValidation<S extends Schema>(
|
|
|
135
135
|
continue;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
// Absent → skipped, which is what `optional()` means. An explicit `null` on
|
|
139
|
+
// a field that also declared `nullable()` is *not* absence: it is someone
|
|
140
|
+
// saying "clear this", and it falls through to the branch below so it
|
|
141
|
+
// reaches the output. Without this, `optional().nullable()` silently drops
|
|
142
|
+
// the null, `fill()` sees `undefined`, and "unassign" saves nothing while
|
|
143
|
+
// reporting success.
|
|
144
|
+
//
|
|
145
|
+
// `null` on a field that is optional but *not* nullable keeps the old
|
|
146
|
+
// behaviour — skipped rather than an error — so this widens what survives
|
|
147
|
+
// validation without newly rejecting anything.
|
|
148
|
+
if (!isRequired && (value === undefined || (value === null && !def.nullable))) {
|
|
139
149
|
continue;
|
|
140
150
|
}
|
|
141
151
|
|