@fluojs/validation 1.0.5 → 1.0.6
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.ko.md +26 -0
- package/README.md +26 -0
- package/dist/decorators.d.ts +67 -67
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +67 -130
- package/dist/internal/decorator-factories.d.ts +12 -0
- package/dist/internal/decorator-factories.d.ts.map +1 -0
- package/dist/internal/decorator-factories.js +32 -0
- package/dist/internal/decorator-metadata.d.ts +7 -0
- package/dist/internal/decorator-metadata.d.ts.map +1 -0
- package/dist/internal/decorator-metadata.js +38 -0
- package/dist/internal/dto-metadata-cache.d.ts +17 -0
- package/dist/internal/dto-metadata-cache.d.ts.map +1 -0
- package/dist/internal/dto-metadata-cache.js +48 -0
- package/dist/internal/object-utils.d.ts +4 -0
- package/dist/internal/object-utils.d.ts.map +1 -0
- package/dist/internal/object-utils.js +25 -0
- package/dist/internal/rule-handlers.d.ts +19 -0
- package/dist/internal/rule-handlers.d.ts.map +1 -0
- package/dist/internal/rule-handlers.js +209 -0
- package/dist/internal/validator-js-adapter.d.ts +5 -0
- package/dist/internal/validator-js-adapter.d.ts.map +1 -0
- package/dist/internal/validator-js-adapter.js +88 -0
- package/dist/mapped-types.d.ts +1 -1
- package/dist/mapped-types.d.ts.map +1 -1
- package/dist/mapped-types.js +1 -1
- package/dist/standard-schema.d.ts +1 -1
- package/dist/standard-schema.d.ts.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +9 -373
- package/package.json +2 -2
package/dist/validation.js
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import validator from 'validator';
|
|
2
|
-
import { getClassValidationRules, getDtoBindingSchema, getDtoValidationSchema } from '@fluojs/core/internal';
|
|
3
1
|
import { DtoValidationError } from './errors.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
function resolveNestedDto(dto) {
|
|
8
|
-
if (isClassConstructor(dto)) {
|
|
9
|
-
return dto;
|
|
10
|
-
}
|
|
11
|
-
return dto();
|
|
12
|
-
}
|
|
2
|
+
import { getCachedDtoMetadata, resolveNestedDto } from './internal/dto-metadata-cache.js';
|
|
3
|
+
import { assignSafeOwnEnumerableProperties, getIterableValues, isPlainObject } from './internal/object-utils.js';
|
|
4
|
+
import { getRuleHandler } from './internal/rule-handlers.js';
|
|
13
5
|
function toFieldName(propertyKey) {
|
|
14
6
|
return typeof propertyKey === 'string' ? propertyKey : String(propertyKey);
|
|
15
7
|
}
|
|
@@ -38,34 +30,6 @@ function normalizeResult(result, field, source, fallback) {
|
|
|
38
30
|
}
|
|
39
31
|
return [normalizeIssue(result, field, source)];
|
|
40
32
|
}
|
|
41
|
-
function getIterableValues(value) {
|
|
42
|
-
if (Array.isArray(value)) return value;
|
|
43
|
-
if (value instanceof Set) return Array.from(value.values());
|
|
44
|
-
if (value instanceof Map) return Array.from(value.values());
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
function isPlainObject(value) {
|
|
48
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
const prototype = Object.getPrototypeOf(value);
|
|
52
|
-
return prototype === Object.prototype || prototype === null;
|
|
53
|
-
}
|
|
54
|
-
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
55
|
-
function assignSafeOwnEnumerableProperties(target, source) {
|
|
56
|
-
for (const key of Reflect.ownKeys(source)) {
|
|
57
|
-
if (typeof key === 'string' && DANGEROUS_KEYS.has(key)) {
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (!Object.prototype.propertyIsEnumerable.call(source, key)) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
target[key] = source[key];
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function isEmptyValue(value) {
|
|
67
|
-
return value === '' || value === null || value === undefined;
|
|
68
|
-
}
|
|
69
33
|
function joinFieldPath(parent, child) {
|
|
70
34
|
if (!child) return parent;
|
|
71
35
|
return child.startsWith('[') ? `${parent}${child}` : `${parent}.${child}`;
|
|
@@ -93,246 +57,6 @@ function exitTraversal(value, context) {
|
|
|
93
57
|
}
|
|
94
58
|
context.active.delete(value);
|
|
95
59
|
}
|
|
96
|
-
const dtoMetadataCache = new WeakMap();
|
|
97
|
-
function collectNestedDtoTransforms(dtoValidationSchema) {
|
|
98
|
-
const nestedEntries = [];
|
|
99
|
-
for (const entry of dtoValidationSchema) {
|
|
100
|
-
const nestedRule = entry.rules.find(rule => rule.kind === 'nested');
|
|
101
|
-
if (!nestedRule) {
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
nestedEntries.push({
|
|
105
|
-
each: nestedRule.each === true,
|
|
106
|
-
propertyKey: entry.propertyKey,
|
|
107
|
-
target: resolveNestedDto(nestedRule.dto)
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
return nestedEntries;
|
|
111
|
-
}
|
|
112
|
-
function getCachedDtoMetadata(target) {
|
|
113
|
-
const cached = dtoMetadataCache.get(target);
|
|
114
|
-
if (cached) {
|
|
115
|
-
return cached;
|
|
116
|
-
}
|
|
117
|
-
const bindingMap = getDtoBindingMap(target);
|
|
118
|
-
const dtoValidationSchema = getDtoValidationSchema(target);
|
|
119
|
-
const classValidationRules = getClassValidationRules(target);
|
|
120
|
-
const mergedPropertyKeys = new Set([...bindingMap.keys(), ...dtoValidationSchema.map(entry => entry.propertyKey)]);
|
|
121
|
-
const nestedDtoTransforms = collectNestedDtoTransforms(dtoValidationSchema);
|
|
122
|
-
const next = {
|
|
123
|
-
bindingMap,
|
|
124
|
-
classValidationRules,
|
|
125
|
-
dtoValidationSchema,
|
|
126
|
-
mergedPropertyKeys,
|
|
127
|
-
nestedDtoTransforms
|
|
128
|
-
};
|
|
129
|
-
dtoMetadataCache.set(target, next);
|
|
130
|
-
return next;
|
|
131
|
-
}
|
|
132
|
-
function getRuleHandler(rule) {
|
|
133
|
-
return RULE_HANDLERS[rule.kind];
|
|
134
|
-
}
|
|
135
|
-
const RULE_HANDLERS = {
|
|
136
|
-
validateIf: {
|
|
137
|
-
defaultCode: 'VALIDATE_IF',
|
|
138
|
-
describe: field => `${field} is conditionally invalid.`,
|
|
139
|
-
validate: () => true
|
|
140
|
-
},
|
|
141
|
-
defined: {
|
|
142
|
-
defaultCode: 'REQUIRED',
|
|
143
|
-
describe: field => `${field} is required.`,
|
|
144
|
-
validate: (_rule, value) => value !== undefined && value !== null
|
|
145
|
-
},
|
|
146
|
-
optional: {
|
|
147
|
-
defaultCode: 'OPTIONAL',
|
|
148
|
-
describe: field => `${field} is optional.`,
|
|
149
|
-
validate: () => true
|
|
150
|
-
},
|
|
151
|
-
equals: {
|
|
152
|
-
defaultCode: 'EQUALS',
|
|
153
|
-
describe: (field, rule) => `${field} must equal ${String(rule.value)}.`,
|
|
154
|
-
validate: (rule, value) => value === rule.value
|
|
155
|
-
},
|
|
156
|
-
notEquals: {
|
|
157
|
-
defaultCode: 'NOT_EQUALS',
|
|
158
|
-
describe: (field, rule) => `${field} must not equal ${String(rule.value)}.`,
|
|
159
|
-
validate: (rule, value) => value !== rule.value
|
|
160
|
-
},
|
|
161
|
-
empty: {
|
|
162
|
-
defaultCode: 'EMPTY',
|
|
163
|
-
describe: field => `${field} must be empty.`,
|
|
164
|
-
validate: (_rule, value) => isEmptyValue(value)
|
|
165
|
-
},
|
|
166
|
-
notEmpty: {
|
|
167
|
-
defaultCode: 'NOT_EMPTY',
|
|
168
|
-
describe: field => `${field} should not be empty.`,
|
|
169
|
-
validate: (_rule, value) => !isEmptyValue(value)
|
|
170
|
-
},
|
|
171
|
-
in: {
|
|
172
|
-
defaultCode: 'IN',
|
|
173
|
-
describe: field => `${field} must be one of the allowed values.`,
|
|
174
|
-
validate: (rule, value) => rule.values.includes(value)
|
|
175
|
-
},
|
|
176
|
-
notIn: {
|
|
177
|
-
defaultCode: 'NOT_IN',
|
|
178
|
-
describe: field => `${field} contains a forbidden value.`,
|
|
179
|
-
validate: (rule, value) => !rule.values.includes(value)
|
|
180
|
-
},
|
|
181
|
-
string: {
|
|
182
|
-
defaultCode: 'INVALID_STRING',
|
|
183
|
-
describe: field => `${field} must be a string.`,
|
|
184
|
-
validate: (_rule, value) => typeof value === 'string'
|
|
185
|
-
},
|
|
186
|
-
number: {
|
|
187
|
-
defaultCode: 'INVALID_NUMBER',
|
|
188
|
-
describe: field => `${field} must be a number.`,
|
|
189
|
-
validate: (rule, value) => typeof value === 'number' && (rule.allowNaN || !Number.isNaN(value))
|
|
190
|
-
},
|
|
191
|
-
boolean: {
|
|
192
|
-
defaultCode: 'INVALID_BOOLEAN',
|
|
193
|
-
describe: field => `${field} must be a boolean.`,
|
|
194
|
-
validate: (_rule, value) => typeof value === 'boolean'
|
|
195
|
-
},
|
|
196
|
-
date: {
|
|
197
|
-
defaultCode: 'INVALID_DATE',
|
|
198
|
-
describe: field => `${field} must be a Date instance.`,
|
|
199
|
-
validate: (_rule, value) => value instanceof Date && !Number.isNaN(value.getTime())
|
|
200
|
-
},
|
|
201
|
-
array: {
|
|
202
|
-
defaultCode: 'INVALID_ARRAY',
|
|
203
|
-
describe: field => `${field} must be an array.`,
|
|
204
|
-
validate: (_rule, value) => Array.isArray(value)
|
|
205
|
-
},
|
|
206
|
-
object: {
|
|
207
|
-
defaultCode: 'INVALID_OBJECT',
|
|
208
|
-
describe: field => `${field} must be an object.`,
|
|
209
|
-
validate: (_rule, value) => isPlainObject(value)
|
|
210
|
-
},
|
|
211
|
-
enum: {
|
|
212
|
-
defaultCode: 'INVALID_ENUM',
|
|
213
|
-
describe: field => `${field} must be a supported enum value.`,
|
|
214
|
-
validate: (rule, value) => rule.values.includes(value)
|
|
215
|
-
},
|
|
216
|
-
int: {
|
|
217
|
-
defaultCode: 'INVALID_INT',
|
|
218
|
-
describe: field => `${field} must be an integer.`,
|
|
219
|
-
validate: (_rule, value) => typeof value === 'number' && Number.isInteger(value)
|
|
220
|
-
},
|
|
221
|
-
divisibleBy: {
|
|
222
|
-
defaultCode: 'DIVISIBLE_BY',
|
|
223
|
-
describe: (field, rule) => `${field} must be divisible by ${String(rule.value)}.`,
|
|
224
|
-
validate: (rule, value) => typeof value === 'number' && !Number.isNaN(value) && value % rule.value === 0
|
|
225
|
-
},
|
|
226
|
-
positive: {
|
|
227
|
-
defaultCode: 'POSITIVE',
|
|
228
|
-
describe: field => `${field} must be positive.`,
|
|
229
|
-
validate: (_rule, value) => typeof value === 'number' && value > 0
|
|
230
|
-
},
|
|
231
|
-
negative: {
|
|
232
|
-
defaultCode: 'NEGATIVE',
|
|
233
|
-
describe: field => `${field} must be negative.`,
|
|
234
|
-
validate: (_rule, value) => typeof value === 'number' && value < 0
|
|
235
|
-
},
|
|
236
|
-
min: {
|
|
237
|
-
defaultCode: 'MIN',
|
|
238
|
-
describe: (field, rule) => `${field} must be greater than or equal to ${String(rule.value)}.`,
|
|
239
|
-
validate: (rule, value) => typeof value === 'number' && !Number.isNaN(value) && value >= rule.value
|
|
240
|
-
},
|
|
241
|
-
max: {
|
|
242
|
-
defaultCode: 'MAX',
|
|
243
|
-
describe: (field, rule) => `${field} must be less than or equal to ${String(rule.value)}.`,
|
|
244
|
-
validate: (rule, value) => typeof value === 'number' && !Number.isNaN(value) && value <= rule.value
|
|
245
|
-
},
|
|
246
|
-
minDate: {
|
|
247
|
-
defaultCode: 'MIN_DATE',
|
|
248
|
-
describe: (field, rule) => `${field} must be on or after ${rule.value.toISOString()}.`,
|
|
249
|
-
validate: (rule, value) => value instanceof Date && !Number.isNaN(value.getTime()) && value.getTime() >= rule.value.getTime()
|
|
250
|
-
},
|
|
251
|
-
maxDate: {
|
|
252
|
-
defaultCode: 'MAX_DATE',
|
|
253
|
-
describe: (field, rule) => `${field} must be on or before ${rule.value.toISOString()}.`,
|
|
254
|
-
validate: (rule, value) => value instanceof Date && !Number.isNaN(value.getTime()) && value.getTime() <= rule.value.getTime()
|
|
255
|
-
},
|
|
256
|
-
contains: {
|
|
257
|
-
defaultCode: 'CONTAINS',
|
|
258
|
-
describe: (field, rule) => `${field} must contain ${rule.value}.`,
|
|
259
|
-
validate: (rule, value) => typeof value === 'string' && value.includes(rule.value)
|
|
260
|
-
},
|
|
261
|
-
notContains: {
|
|
262
|
-
defaultCode: 'NOT_CONTAINS',
|
|
263
|
-
describe: (field, rule) => `${field} must not contain ${rule.value}.`,
|
|
264
|
-
validate: (rule, value) => typeof value === 'string' && !value.includes(rule.value)
|
|
265
|
-
},
|
|
266
|
-
length: {
|
|
267
|
-
defaultCode: 'LENGTH',
|
|
268
|
-
describe: field => `${field} must have a valid length.`,
|
|
269
|
-
validate: (rule, value) => typeof value === 'string' && value.length >= rule.min && (rule.max === undefined || value.length <= rule.max)
|
|
270
|
-
},
|
|
271
|
-
minLength: {
|
|
272
|
-
defaultCode: 'MIN_LENGTH',
|
|
273
|
-
describe: (field, rule) => `${field} must have length at least ${String(rule.value)}.`,
|
|
274
|
-
validate: (rule, value) => typeof value === 'string' && value.length >= rule.value
|
|
275
|
-
},
|
|
276
|
-
maxLength: {
|
|
277
|
-
defaultCode: 'MAX_LENGTH',
|
|
278
|
-
describe: (field, rule) => `${field} must have length at most ${String(rule.value)}.`,
|
|
279
|
-
validate: (rule, value) => typeof value === 'string' && value.length <= rule.value
|
|
280
|
-
},
|
|
281
|
-
nested: {
|
|
282
|
-
defaultCode: 'INVALID_NESTED',
|
|
283
|
-
describe: field => `${field} contains invalid nested data.`,
|
|
284
|
-
validate: () => true
|
|
285
|
-
},
|
|
286
|
-
validatorjs: {
|
|
287
|
-
defaultCode: 'INVALID_FIELD',
|
|
288
|
-
describe: field => `${field} is invalid.`,
|
|
289
|
-
validate: (rule, value) => runValidatorJs(rule, value)
|
|
290
|
-
},
|
|
291
|
-
arrayContains: {
|
|
292
|
-
defaultCode: 'ARRAY_CONTAINS',
|
|
293
|
-
describe: field => `${field} must contain the required values.`,
|
|
294
|
-
validate: (rule, value) => Array.isArray(value) && rule.values.every(expected => value.includes(expected))
|
|
295
|
-
},
|
|
296
|
-
arrayNotContains: {
|
|
297
|
-
defaultCode: 'ARRAY_NOT_CONTAINS',
|
|
298
|
-
describe: field => `${field} contains forbidden values.`,
|
|
299
|
-
validate: (rule, value) => Array.isArray(value) && rule.values.every(expected => !value.includes(expected))
|
|
300
|
-
},
|
|
301
|
-
arrayNotEmpty: {
|
|
302
|
-
defaultCode: 'ARRAY_NOT_EMPTY',
|
|
303
|
-
describe: field => `${field} must not be an empty array.`,
|
|
304
|
-
validate: (_rule, value) => Array.isArray(value) && value.length > 0
|
|
305
|
-
},
|
|
306
|
-
arrayMinSize: {
|
|
307
|
-
defaultCode: 'ARRAY_MIN_SIZE',
|
|
308
|
-
describe: (field, rule) => `${field} must contain at least ${String(rule.value)} items.`,
|
|
309
|
-
validate: (rule, value) => Array.isArray(value) && value.length >= rule.value
|
|
310
|
-
},
|
|
311
|
-
arrayMaxSize: {
|
|
312
|
-
defaultCode: 'ARRAY_MAX_SIZE',
|
|
313
|
-
describe: (field, rule) => `${field} must contain at most ${String(rule.value)} items.`,
|
|
314
|
-
validate: (rule, value) => Array.isArray(value) && value.length <= rule.value
|
|
315
|
-
},
|
|
316
|
-
arrayUnique: {
|
|
317
|
-
defaultCode: 'ARRAY_UNIQUE',
|
|
318
|
-
describe: field => `${field} must contain unique values.`,
|
|
319
|
-
validate: (rule, value) => {
|
|
320
|
-
if (!Array.isArray(value)) return false;
|
|
321
|
-
const seen = new Set();
|
|
322
|
-
for (const entry of value) {
|
|
323
|
-
const key = rule.selector ? rule.selector(entry) : entry;
|
|
324
|
-
if (seen.has(key)) return false;
|
|
325
|
-
seen.add(key);
|
|
326
|
-
}
|
|
327
|
-
return true;
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
custom: {
|
|
331
|
-
defaultCode: 'INVALID_FIELD',
|
|
332
|
-
describe: field => `${field} is invalid.`,
|
|
333
|
-
validate: () => true
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
60
|
function createNestedDtoInstance(target, rawValue, context) {
|
|
337
61
|
if (rawValue instanceof target) {
|
|
338
62
|
return rawValue;
|
|
@@ -353,7 +77,7 @@ function createNestedDtoInstance(target, rawValue, context) {
|
|
|
353
77
|
if (currentValue === undefined || currentValue === null) {
|
|
354
78
|
continue;
|
|
355
79
|
}
|
|
356
|
-
instance[nestedEntry.propertyKey] =
|
|
80
|
+
instance[nestedEntry.propertyKey] = transformNestedCollectionValue(currentValue, nestedEntry.target, context);
|
|
357
81
|
}
|
|
358
82
|
return instance;
|
|
359
83
|
} finally {
|
|
@@ -369,9 +93,6 @@ function materializeNestedDtoValue(target, rawValue, context) {
|
|
|
369
93
|
}
|
|
370
94
|
return createNestedDtoInstance(target, rawValue, context);
|
|
371
95
|
}
|
|
372
|
-
function getDtoBindingMap(target) {
|
|
373
|
-
return new Map(getDtoBindingSchema(target).map(entry => [entry.propertyKey, entry.metadata]));
|
|
374
|
-
}
|
|
375
96
|
function applyBindingValues(instance, rawValue, keys, bindingMap) {
|
|
376
97
|
for (const propertyKey of keys) {
|
|
377
98
|
const sourceKey = bindingMap.get(propertyKey)?.key;
|
|
@@ -382,7 +103,7 @@ function applyBindingValues(instance, rawValue, keys, bindingMap) {
|
|
|
382
103
|
function transformNestedValue(value, target, context) {
|
|
383
104
|
return value === undefined || value === null ? value : materializeNestedDtoValue(target, value, context);
|
|
384
105
|
}
|
|
385
|
-
function
|
|
106
|
+
function transformNestedCollectionValue(value, target, context) {
|
|
386
107
|
if (Array.isArray(value)) {
|
|
387
108
|
return value.map(item => transformNestedValue(item, target, context));
|
|
388
109
|
}
|
|
@@ -401,93 +122,6 @@ function describeValidator(rule, field) {
|
|
|
401
122
|
message: rule.message ?? handler.describe(field, rule)
|
|
402
123
|
};
|
|
403
124
|
}
|
|
404
|
-
function runValidatorJs(rule, value) {
|
|
405
|
-
if (rule.validator === 'latitude') {
|
|
406
|
-
return typeof value === 'number' && Number.isFinite(value) && value >= -90 && value <= 90;
|
|
407
|
-
}
|
|
408
|
-
if (rule.validator === 'longitude') {
|
|
409
|
-
return typeof value === 'number' && Number.isFinite(value) && value >= -180 && value <= 180;
|
|
410
|
-
}
|
|
411
|
-
if (typeof value !== 'string') {
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
switch (rule.validator) {
|
|
415
|
-
case 'alpha':
|
|
416
|
-
return validator.isAlpha(value);
|
|
417
|
-
case 'alphanumeric':
|
|
418
|
-
return validator.isAlphanumeric(value);
|
|
419
|
-
case 'ascii':
|
|
420
|
-
return validator.isAscii(value);
|
|
421
|
-
case 'base64':
|
|
422
|
-
return validator.isBase64(value);
|
|
423
|
-
case 'booleanString':
|
|
424
|
-
return validator.isBoolean(value);
|
|
425
|
-
case 'currency':
|
|
426
|
-
return validator.isCurrency(value, rule.args?.[0]);
|
|
427
|
-
case 'dataURI':
|
|
428
|
-
return validator.isDataURI(value);
|
|
429
|
-
case 'dateString':
|
|
430
|
-
return validator.isISO8601(value);
|
|
431
|
-
case 'decimal':
|
|
432
|
-
return validator.isDecimal(value);
|
|
433
|
-
case 'email':
|
|
434
|
-
return validator.isEmail(value, rule.args?.[0]);
|
|
435
|
-
case 'fqdn':
|
|
436
|
-
return validator.isFQDN(value, rule.args?.[0]);
|
|
437
|
-
case 'hexColor':
|
|
438
|
-
return validator.isHexColor(value);
|
|
439
|
-
case 'hexadecimal':
|
|
440
|
-
return validator.isHexadecimal(value);
|
|
441
|
-
case 'ip':
|
|
442
|
-
return validator.isIP(value, rule.args?.[0]);
|
|
443
|
-
case 'isbn':
|
|
444
|
-
return validator.isISBN(value, rule.args?.[0]);
|
|
445
|
-
case 'issn':
|
|
446
|
-
return validator.isISSN(value);
|
|
447
|
-
case 'json':
|
|
448
|
-
return validator.isJSON(value);
|
|
449
|
-
case 'jwt':
|
|
450
|
-
return validator.isJWT(value);
|
|
451
|
-
case 'locale':
|
|
452
|
-
return validator.isLocale(value);
|
|
453
|
-
case 'lowercase':
|
|
454
|
-
return validator.isLowercase(value);
|
|
455
|
-
case 'magnetURI':
|
|
456
|
-
return validator.isMagnetURI(value);
|
|
457
|
-
case 'matches':
|
|
458
|
-
return validator.matches(value, rule.args?.[0], rule.args?.[1]);
|
|
459
|
-
case 'mimeType':
|
|
460
|
-
return validator.isMimeType(value);
|
|
461
|
-
case 'mobilePhone':
|
|
462
|
-
return validator.isMobilePhone(value, rule.args?.[0]);
|
|
463
|
-
case 'mongoId':
|
|
464
|
-
return validator.isMongoId(value);
|
|
465
|
-
case 'numberString':
|
|
466
|
-
return validator.isNumeric(value);
|
|
467
|
-
case 'port':
|
|
468
|
-
return validator.isPort(value);
|
|
469
|
-
case 'postalCode':
|
|
470
|
-
return validator.isPostalCode(value, rule.args?.[0] ?? 'any');
|
|
471
|
-
case 'rgbColor':
|
|
472
|
-
return validator.isRgbColor(value, rule.args?.[0]);
|
|
473
|
-
case 'rfc3339':
|
|
474
|
-
return validator.isRFC3339(value);
|
|
475
|
-
case 'semVer':
|
|
476
|
-
return validator.isSemVer(value);
|
|
477
|
-
case 'uppercase':
|
|
478
|
-
return validator.isUppercase(value);
|
|
479
|
-
case 'url':
|
|
480
|
-
return validator.isURL(value, rule.args?.[0]);
|
|
481
|
-
case 'uuid':
|
|
482
|
-
return validator.isUUID(value, rule.args?.[0]);
|
|
483
|
-
case 'iso8601':
|
|
484
|
-
return validator.isISO8601(value);
|
|
485
|
-
case 'latLong':
|
|
486
|
-
return validator.isLatLong(value);
|
|
487
|
-
default:
|
|
488
|
-
return false;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
125
|
function buildIssue(fallback, field, source) {
|
|
492
126
|
return {
|
|
493
127
|
code: fallback.code,
|
|
@@ -541,12 +175,14 @@ function runRulePredicate(rule, value) {
|
|
|
541
175
|
return getRuleHandler(rule).validate(rule, value);
|
|
542
176
|
}
|
|
543
177
|
async function validateNestedRule(rule, value, fieldPath, inheritedSource, context) {
|
|
544
|
-
const
|
|
178
|
+
const collectionValues = getIterableValues(value);
|
|
179
|
+
const values = collectionValues ?? [value];
|
|
180
|
+
const useIndexedPath = rule.each === true || collectionValues !== undefined;
|
|
545
181
|
const issues = [];
|
|
546
182
|
const resolvedDto = resolveNestedDto(rule.dto);
|
|
547
183
|
for (const [index, entry] of values.entries()) {
|
|
548
184
|
if (entry === undefined || entry === null) continue;
|
|
549
|
-
const nestedPath =
|
|
185
|
+
const nestedPath = useIndexedPath ? `${fieldPath}[${String(index)}]` : fieldPath;
|
|
550
186
|
const trackedEntry = typeof entry === 'object' && entry !== null ? entry : undefined;
|
|
551
187
|
if (!(entry instanceof resolvedDto) && !isPlainObject(entry)) {
|
|
552
188
|
issues.push(buildIssue(describeValidator(rule, nestedPath), nestedPath, inheritedSource));
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"decorators",
|
|
10
10
|
"schema"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.0.
|
|
12
|
+
"version": "1.0.6",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@standard-schema/spec": "^1.1.0",
|
|
44
44
|
"validator": "^13.15.26",
|
|
45
|
-
"@fluojs/core": "^1.0
|
|
45
|
+
"@fluojs/core": "^1.1.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/validator": "^13.15.10",
|