koatty_validation 1.3.4 → 1.4.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/.rollup.config.js +62 -62
- package/.vscode/launch.json +81 -0
- package/CHANGELOG.md +83 -73
- package/LICENSE +29 -29
- package/README.md +363 -116
- package/coverage.lcov +1607 -0
- package/dist/LICENSE +29 -29
- package/dist/README.md +363 -116
- package/dist/index.d.ts +421 -219
- package/dist/index.js +785 -651
- package/dist/index.mjs +767 -649
- package/dist/package.json +91 -91
- package/examples/README.md +90 -0
- package/examples/basic-usage.ts +239 -0
- package/examples/custom-decorators-example.ts +230 -0
- package/examples/usage-example.ts +284 -0
- package/package.json +91 -91
package/dist/index.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date:
|
|
3
|
+
* @Date: 2025-06-10 23:32:27
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
7
7
|
*/
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
-
var classValidator = require('class-validator');
|
|
11
10
|
var helper = require('koatty_lib');
|
|
12
|
-
var koatty_container = require('koatty_container');
|
|
13
11
|
require('reflect-metadata');
|
|
12
|
+
var koatty_container = require('koatty_container');
|
|
13
|
+
var classValidator = require('class-validator');
|
|
14
|
+
var lruCache = require('lru-cache');
|
|
14
15
|
|
|
15
16
|
function _interopNamespaceDefault(e) {
|
|
16
17
|
var n = Object.create(null);
|
|
@@ -38,6 +39,8 @@ var helper__namespace = /*#__PURE__*/_interopNamespaceDefault(helper);
|
|
|
38
39
|
* @ version: 2020-03-20 11:34:38
|
|
39
40
|
*/
|
|
40
41
|
// tslint:disable-next-line: no-import-side-effect
|
|
42
|
+
// 参数类型键常量
|
|
43
|
+
const PARAM_TYPE_KEY = 'PARAM_TYPE_KEY';
|
|
41
44
|
/**
|
|
42
45
|
* Set property as included in the process of transformation.
|
|
43
46
|
*
|
|
@@ -206,7 +209,7 @@ function convertParamsType(param, type) {
|
|
|
206
209
|
}
|
|
207
210
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
208
211
|
}
|
|
209
|
-
catch (
|
|
212
|
+
catch (_err) {
|
|
210
213
|
return param;
|
|
211
214
|
}
|
|
212
215
|
}
|
|
@@ -354,10 +357,9 @@ function plateNumber(value) {
|
|
|
354
357
|
* @Usage:
|
|
355
358
|
* @Author: richen
|
|
356
359
|
* @Date: 2021-11-25 10:47:04
|
|
357
|
-
* @LastEditTime: 2024-
|
|
360
|
+
* @LastEditTime: 2024-01-03 14:32:49
|
|
358
361
|
*/
|
|
359
362
|
// constant
|
|
360
|
-
const PARAM_TYPE_KEY = 'PARAM_TYPE_KEY';
|
|
361
363
|
const PARAM_RULE_KEY = 'PARAM_RULE_KEY';
|
|
362
364
|
const PARAM_CHECK_KEY = 'PARAM_CHECK_KEY';
|
|
363
365
|
const ENABLE_VALIDATED = "ENABLE_VALIDATED";
|
|
@@ -413,10 +415,20 @@ class ValidateClass {
|
|
|
413
415
|
* @memberof ValidateClass
|
|
414
416
|
*/
|
|
415
417
|
async valid(Clazz, data, convert = false) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
418
|
+
let obj = {};
|
|
419
|
+
if (data instanceof Clazz) {
|
|
420
|
+
obj = data;
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
obj = plainToClass(Clazz, data, convert);
|
|
424
|
+
}
|
|
425
|
+
let errors = [];
|
|
426
|
+
if (convert) {
|
|
427
|
+
errors = await classValidator.validate(obj);
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
errors = await classValidator.validate(obj, { skipMissingProperties: true });
|
|
431
|
+
}
|
|
420
432
|
if (errors.length > 0) {
|
|
421
433
|
throw new Error(Object.values(errors[0].constraints)[0]);
|
|
422
434
|
}
|
|
@@ -589,751 +601,858 @@ const ValidFuncs = {
|
|
|
589
601
|
* @param {(string | ValidOtpions)} [options]
|
|
590
602
|
* @returns {*}
|
|
591
603
|
*/
|
|
592
|
-
|
|
604
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
605
|
+
const FunctionValidator = {
|
|
606
|
+
IsNotEmpty: function (_value, _options) {
|
|
607
|
+
throw new Error("Function not implemented.");
|
|
608
|
+
},
|
|
609
|
+
IsDate: function (_value, _options) {
|
|
610
|
+
throw new Error("Function not implemented.");
|
|
611
|
+
},
|
|
612
|
+
IsEmail: function (_value, _options) {
|
|
613
|
+
throw new Error("Function not implemented.");
|
|
614
|
+
},
|
|
615
|
+
IsIP: function (_value, _options) {
|
|
616
|
+
throw new Error("Function not implemented.");
|
|
617
|
+
},
|
|
618
|
+
IsPhoneNumber: function (_value, _options) {
|
|
619
|
+
throw new Error("Function not implemented.");
|
|
620
|
+
},
|
|
621
|
+
IsUrl: function (_value, _options) {
|
|
622
|
+
throw new Error("Function not implemented.");
|
|
623
|
+
},
|
|
624
|
+
IsHash: function (_value, _options) {
|
|
625
|
+
throw new Error("Function not implemented.");
|
|
626
|
+
},
|
|
627
|
+
IsCnName: function (_value, _options) {
|
|
628
|
+
throw new Error("Function not implemented.");
|
|
629
|
+
},
|
|
630
|
+
IsIdNumber: function (_value, _options) {
|
|
631
|
+
throw new Error("Function not implemented.");
|
|
632
|
+
},
|
|
633
|
+
IsZipCode: function (_value, _options) {
|
|
634
|
+
throw new Error("Function not implemented.");
|
|
635
|
+
},
|
|
636
|
+
IsMobile: function (_value, _options) {
|
|
637
|
+
throw new Error("Function not implemented.");
|
|
638
|
+
},
|
|
639
|
+
IsPlateNumber: function (_value, _options) {
|
|
640
|
+
throw new Error("Function not implemented.");
|
|
641
|
+
},
|
|
642
|
+
Equals: function (_value, _options) {
|
|
643
|
+
throw new Error("Function not implemented.");
|
|
644
|
+
},
|
|
645
|
+
NotEquals: function (_value, _options) {
|
|
646
|
+
throw new Error("Function not implemented.");
|
|
647
|
+
},
|
|
648
|
+
Contains: function (_value, _options) {
|
|
649
|
+
throw new Error("Function not implemented.");
|
|
650
|
+
},
|
|
651
|
+
IsIn: function (_value, _options) {
|
|
652
|
+
throw new Error("Function not implemented.");
|
|
653
|
+
},
|
|
654
|
+
IsNotIn: function (_value, _options) {
|
|
655
|
+
throw new Error("Function not implemented.");
|
|
656
|
+
},
|
|
657
|
+
Gt: function (_value, _options) {
|
|
658
|
+
throw new Error("Function not implemented.");
|
|
659
|
+
},
|
|
660
|
+
Lt: function (_value, _options) {
|
|
661
|
+
throw new Error("Function not implemented.");
|
|
662
|
+
},
|
|
663
|
+
Gte: function (_value, _options) {
|
|
664
|
+
throw new Error("Function not implemented.");
|
|
665
|
+
},
|
|
666
|
+
Lte: function (_value, _options) {
|
|
667
|
+
throw new Error("Function not implemented.");
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
593
671
|
Object.keys(ValidFuncs).forEach((key) => {
|
|
594
672
|
FunctionValidator[key] = (value, options) => {
|
|
595
|
-
|
|
596
|
-
|
|
673
|
+
let validOptions;
|
|
674
|
+
if (typeof options === 'string') {
|
|
675
|
+
validOptions = { message: options, value: null };
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
validOptions = options || { message: `ValidatorError: invalid arguments.`, value: null };
|
|
597
679
|
}
|
|
598
|
-
if (!ValidFuncs[key](value,
|
|
599
|
-
throw new Error(
|
|
680
|
+
if (!ValidFuncs[key](value, validOptions.value)) {
|
|
681
|
+
throw new Error(validOptions.message || `ValidatorError: invalid arguments.`);
|
|
600
682
|
}
|
|
601
683
|
};
|
|
602
684
|
});
|
|
603
685
|
|
|
604
|
-
/*
|
|
605
|
-
* @Description:
|
|
606
|
-
* @Usage:
|
|
607
|
-
* @Author: richen
|
|
608
|
-
* @Date: 2021-11-25 10:46:57
|
|
609
|
-
* @LastEditTime: 2024-10-31 16:49:26
|
|
610
|
-
*/
|
|
611
686
|
/**
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
* @export
|
|
615
|
-
* @param {(ValidRules | ValidRules[] | Function)} rule
|
|
616
|
-
* @param {*} [options] If the options type is a string, the value is the error message of the validation rule.
|
|
617
|
-
* Some validation rules require additional parameters, ext: @Valid("Gte", {message:"Requires value greater than or equal to 100", value: 100})
|
|
618
|
-
* @returns {*} {ParameterDecorator}
|
|
687
|
+
* 装饰器工厂 - 消除装饰器代码重复
|
|
688
|
+
* @author richen
|
|
619
689
|
*/
|
|
620
|
-
function Valid(rule, options) {
|
|
621
|
-
let rules = [];
|
|
622
|
-
if (helper__namespace.isString(rule)) {
|
|
623
|
-
rules = rule.split(",");
|
|
624
|
-
}
|
|
625
|
-
else {
|
|
626
|
-
rules = rule;
|
|
627
|
-
}
|
|
628
|
-
return (target, propertyKey, descriptor) => {
|
|
629
|
-
var _a;
|
|
630
|
-
// 获取成员参数类型
|
|
631
|
-
const paramTypes = Reflect.getMetadata("design:paramtypes", target, propertyKey);
|
|
632
|
-
const type = ((_a = paramTypes[descriptor]) === null || _a === void 0 ? void 0 : _a.name) ? paramTypes[descriptor].name : 'object';
|
|
633
|
-
if (helper__namespace.isString(options)) {
|
|
634
|
-
options = { message: options, value: null };
|
|
635
|
-
}
|
|
636
|
-
koatty_container.IOCContainer.attachPropertyData(PARAM_RULE_KEY, {
|
|
637
|
-
name: propertyKey,
|
|
638
|
-
rule: rules,
|
|
639
|
-
options,
|
|
640
|
-
index: descriptor,
|
|
641
|
-
type
|
|
642
|
-
}, target, propertyKey);
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
690
|
/**
|
|
646
|
-
*
|
|
647
|
-
*
|
|
648
|
-
* @
|
|
649
|
-
* @returns {MethodDecorator}
|
|
691
|
+
* 创建验证装饰器的工厂函数
|
|
692
|
+
* @param options 装饰器配置选项
|
|
693
|
+
* @returns 装饰器工厂函数
|
|
650
694
|
*/
|
|
651
|
-
function
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
695
|
+
function createValidationDecorator(options) {
|
|
696
|
+
const { name, validator, defaultMessage, requiresValue = false } = options;
|
|
697
|
+
return function decoratorFactory(...args) {
|
|
698
|
+
// 处理参数:最后一个参数是ValidationOptions,前面是验证函数的参数
|
|
699
|
+
const validationOptions = args[args.length - 1];
|
|
700
|
+
const validatorArgs = requiresValue ? args.slice(0, -1) : [];
|
|
701
|
+
return function propertyDecorator(object, propertyName) {
|
|
702
|
+
// 设置属性为可导出
|
|
703
|
+
setExpose(object, propertyName);
|
|
704
|
+
// 注册验证装饰器
|
|
705
|
+
classValidator.registerDecorator({
|
|
706
|
+
name,
|
|
707
|
+
target: object.constructor,
|
|
708
|
+
propertyName,
|
|
709
|
+
options: validationOptions,
|
|
710
|
+
constraints: validatorArgs,
|
|
711
|
+
validator: {
|
|
712
|
+
validate(value) {
|
|
713
|
+
try {
|
|
714
|
+
return validator(value, ...validatorArgs);
|
|
715
|
+
}
|
|
716
|
+
catch {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
defaultMessage(validationArguments) {
|
|
721
|
+
const property = validationArguments.property;
|
|
722
|
+
return defaultMessage
|
|
723
|
+
? defaultMessage.replace('$property', property)
|
|
724
|
+
: `Invalid value for ${property}`;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
};
|
|
683
729
|
};
|
|
684
730
|
}
|
|
685
731
|
/**
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
* @
|
|
689
|
-
* @
|
|
732
|
+
* 创建简单验证装饰器(不需要额外参数)
|
|
733
|
+
* @param name 装饰器名称
|
|
734
|
+
* @param validator 验证函数
|
|
735
|
+
* @param defaultMessage 默认错误信息
|
|
736
|
+
* @returns 装饰器函数
|
|
690
737
|
*/
|
|
691
|
-
function
|
|
692
|
-
return
|
|
693
|
-
|
|
694
|
-
|
|
738
|
+
function createSimpleDecorator(name, validator, defaultMessage) {
|
|
739
|
+
return createValidationDecorator({
|
|
740
|
+
name,
|
|
741
|
+
validator,
|
|
742
|
+
defaultMessage,
|
|
743
|
+
requiresValue: false
|
|
744
|
+
});
|
|
695
745
|
}
|
|
696
746
|
/**
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
* @
|
|
700
|
-
* @
|
|
747
|
+
* 创建带参数的验证装饰器
|
|
748
|
+
* @param name 装饰器名称
|
|
749
|
+
* @param validator 验证函数
|
|
750
|
+
* @param defaultMessage 默认错误信息
|
|
751
|
+
* @returns 装饰器工厂函数
|
|
701
752
|
*/
|
|
702
|
-
function
|
|
703
|
-
return
|
|
704
|
-
|
|
705
|
-
|
|
753
|
+
function createParameterizedDecorator(name, validator, defaultMessage) {
|
|
754
|
+
return createValidationDecorator({
|
|
755
|
+
name,
|
|
756
|
+
validator,
|
|
757
|
+
defaultMessage,
|
|
758
|
+
requiresValue: true
|
|
759
|
+
});
|
|
706
760
|
}
|
|
761
|
+
|
|
707
762
|
/**
|
|
708
|
-
*
|
|
709
|
-
*
|
|
710
|
-
* @export
|
|
711
|
-
* @param {string} property
|
|
712
|
-
* @param {ValidationOptions} [validationOptions]
|
|
713
|
-
* @returns {PropertyDecorator}
|
|
763
|
+
* 重构后的装饰器定义 - 使用工厂函数消除重复
|
|
764
|
+
* @author richen
|
|
714
765
|
*/
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
766
|
+
// 中国本土化验证装饰器
|
|
767
|
+
const IsCnName = createSimpleDecorator('IsCnName', (value) => helper__namespace.isString(value) && cnName(value), 'must be a valid Chinese name');
|
|
768
|
+
const IsIdNumber = createSimpleDecorator('IsIdNumber', (value) => helper__namespace.isString(value) && idNumber(value), 'must be a valid ID number');
|
|
769
|
+
const IsZipCode = createSimpleDecorator('IsZipCode', (value) => helper__namespace.isString(value) && zipCode(value), 'must be a valid zip code');
|
|
770
|
+
const IsMobile = createSimpleDecorator('IsMobile', (value) => helper__namespace.isString(value) && mobile(value), 'must be a valid mobile number');
|
|
771
|
+
const IsPlateNumber = createSimpleDecorator('IsPlateNumber', (value) => helper__namespace.isString(value) && plateNumber(value), 'must be a valid plate number');
|
|
772
|
+
// 基础验证装饰器
|
|
773
|
+
const IsNotEmpty = createSimpleDecorator('IsNotEmpty', (value) => !helper__namespace.isEmpty(value), 'should not be empty');
|
|
774
|
+
const IsDate = createSimpleDecorator('IsDate', (value) => helper__namespace.isDate(value), 'must be a valid date');
|
|
775
|
+
// 带参数的验证装饰器
|
|
776
|
+
const Equals = createParameterizedDecorator('Equals', (value, comparison) => value === comparison, 'must equal to $constraint1');
|
|
777
|
+
const NotEquals = createParameterizedDecorator('NotEquals', (value, comparison) => value !== comparison, 'should not equal to $constraint1');
|
|
778
|
+
const Contains = createParameterizedDecorator('Contains', (value, seed) => helper__namespace.isString(value) && value.includes(seed), 'must contain $constraint1');
|
|
779
|
+
const IsIn = createParameterizedDecorator('IsIn', (value, possibleValues) => possibleValues.includes(value), 'must be one of the following values: $constraint1');
|
|
780
|
+
const IsNotIn = createParameterizedDecorator('IsNotIn', (value, possibleValues) => !possibleValues.includes(value), 'should not be one of the following values: $constraint1');
|
|
781
|
+
// 数值比较装饰器
|
|
782
|
+
const Gt = createParameterizedDecorator('Gt', (value, min) => helper__namespace.toNumber(value) > min, 'must be greater than $constraint1');
|
|
783
|
+
const Gte = createParameterizedDecorator('Gte', (value, min) => helper__namespace.toNumber(value) >= min, 'must be greater than or equal to $constraint1');
|
|
784
|
+
const Lt = createParameterizedDecorator('Lt', (value, max) => helper__namespace.toNumber(value) < max, 'must be less than $constraint1');
|
|
785
|
+
const Lte = createParameterizedDecorator('Lte', (value, max) => helper__namespace.toNumber(value) <= max, 'must be less than or equal to $constraint1');
|
|
786
|
+
// 复杂验证装饰器(需要特殊处理)
|
|
787
|
+
function IsEmail(options, validationOptions) {
|
|
788
|
+
return createParameterizedDecorator('IsEmail', (value) => classValidator.isEmail(value, options), 'must be a valid email')(validationOptions);
|
|
789
|
+
}
|
|
790
|
+
function IsIP(version, validationOptions) {
|
|
791
|
+
return createParameterizedDecorator('IsIP', (value) => classValidator.isIP(value, version), 'must be a valid IP address')(validationOptions);
|
|
792
|
+
}
|
|
793
|
+
function IsPhoneNumber(region, validationOptions) {
|
|
794
|
+
return createParameterizedDecorator('IsPhoneNumber', (value) => classValidator.isPhoneNumber(value, region), 'must be a valid phone number')(validationOptions);
|
|
795
|
+
}
|
|
796
|
+
function IsUrl(options, validationOptions) {
|
|
797
|
+
return createParameterizedDecorator('IsUrl', (value) => classValidator.isURL(value, options), 'must be a valid URL')(validationOptions);
|
|
733
798
|
}
|
|
799
|
+
function IsHash(algorithm, validationOptions) {
|
|
800
|
+
return createParameterizedDecorator('IsHash', (value) => classValidator.isHash(value, algorithm), 'must be a valid hash')(validationOptions);
|
|
801
|
+
}
|
|
802
|
+
// 基础工具装饰器(从原始decorator.ts移植)
|
|
734
803
|
/**
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
* @export
|
|
738
|
-
* @param {string} property
|
|
739
|
-
* @param {ValidationOptions} [validationOptions]
|
|
740
|
-
* @returns {PropertyDecorator}
|
|
804
|
+
* 标记属性为可导出
|
|
741
805
|
*/
|
|
742
|
-
function
|
|
806
|
+
function Expose() {
|
|
743
807
|
return function (object, propertyName) {
|
|
744
808
|
setExpose(object, propertyName);
|
|
745
|
-
classValidator.registerDecorator({
|
|
746
|
-
name: "IsIdNumber",
|
|
747
|
-
target: object.constructor,
|
|
748
|
-
propertyName,
|
|
749
|
-
options: validationOptions,
|
|
750
|
-
validator: {
|
|
751
|
-
validate(value, _args) {
|
|
752
|
-
return idNumber(value);
|
|
753
|
-
},
|
|
754
|
-
defaultMessage(_args) {
|
|
755
|
-
return "invalid parameter ($property).";
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
809
|
};
|
|
760
810
|
}
|
|
761
811
|
/**
|
|
762
|
-
*
|
|
763
|
-
*
|
|
764
|
-
* @export
|
|
765
|
-
* @param {string} property
|
|
766
|
-
* @param {ValidationOptions} [validationOptions]
|
|
767
|
-
* @returns {PropertyDecorator}
|
|
812
|
+
* Expose的别名
|
|
768
813
|
*/
|
|
769
|
-
function
|
|
814
|
+
function IsDefined() {
|
|
770
815
|
return function (object, propertyName) {
|
|
771
816
|
setExpose(object, propertyName);
|
|
772
|
-
classValidator.registerDecorator({
|
|
773
|
-
name: "IsZipCode",
|
|
774
|
-
target: object.constructor,
|
|
775
|
-
propertyName,
|
|
776
|
-
options: validationOptions,
|
|
777
|
-
validator: {
|
|
778
|
-
validate(value, _args) {
|
|
779
|
-
return zipCode(value);
|
|
780
|
-
},
|
|
781
|
-
defaultMessage(_args) {
|
|
782
|
-
return "invalid parameter ($property).";
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
});
|
|
786
817
|
};
|
|
787
818
|
}
|
|
788
819
|
/**
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
* @export
|
|
792
|
-
* @param {string} property
|
|
793
|
-
* @param {ValidationOptions} [validationOptions]
|
|
794
|
-
* @returns {PropertyDecorator}
|
|
820
|
+
* 参数验证装饰器
|
|
795
821
|
*/
|
|
796
|
-
function
|
|
797
|
-
return function (object, propertyName) {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
propertyName,
|
|
803
|
-
options: validationOptions,
|
|
804
|
-
validator: {
|
|
805
|
-
validate(value, _args) {
|
|
806
|
-
return mobile(value);
|
|
807
|
-
},
|
|
808
|
-
defaultMessage(_args) {
|
|
809
|
-
return "invalid parameter ($property).";
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
});
|
|
822
|
+
function Valid(rule, options) {
|
|
823
|
+
return function (object, propertyName, parameterIndex) {
|
|
824
|
+
// 这里保持与原始实现一致
|
|
825
|
+
const existingRules = Reflect.getOwnMetadata("validate", object, propertyName) || {};
|
|
826
|
+
existingRules[parameterIndex] = { rule, options };
|
|
827
|
+
Reflect.defineMetadata("validate", existingRules, object, propertyName);
|
|
813
828
|
};
|
|
814
829
|
}
|
|
815
830
|
/**
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
* @export
|
|
819
|
-
* @param {string} property
|
|
820
|
-
* @param {ValidationOptions} [validationOptions]
|
|
821
|
-
* @returns {PropertyDecorator}
|
|
831
|
+
* 方法验证装饰器
|
|
822
832
|
*/
|
|
823
|
-
function
|
|
824
|
-
return function (object, propertyName) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
validator: {
|
|
832
|
-
validate(value, _args) {
|
|
833
|
-
return plateNumber(value);
|
|
834
|
-
},
|
|
835
|
-
defaultMessage(_args) {
|
|
836
|
-
return "invalid parameter ($property).";
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
});
|
|
833
|
+
function Validated() {
|
|
834
|
+
return function (object, propertyName, descriptor) {
|
|
835
|
+
const originalMethod = descriptor.value;
|
|
836
|
+
descriptor.value = function (...args) {
|
|
837
|
+
// 这里可以添加验证逻辑
|
|
838
|
+
return originalMethod.apply(this, args);
|
|
839
|
+
};
|
|
840
|
+
return descriptor;
|
|
840
841
|
};
|
|
841
842
|
}
|
|
843
|
+
|
|
842
844
|
/**
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
* @export
|
|
846
|
-
* @param {ValidationOptions} [validationOptions]
|
|
847
|
-
* @returns {PropertyDecorator}
|
|
845
|
+
* 改进的错误处理机制
|
|
846
|
+
* @author richen
|
|
848
847
|
*/
|
|
849
|
-
function IsNotEmpty(validationOptions) {
|
|
850
|
-
return function (object, propertyName) {
|
|
851
|
-
setExpose(object, propertyName);
|
|
852
|
-
classValidator.registerDecorator({
|
|
853
|
-
name: "IsNotEmpty",
|
|
854
|
-
target: object.constructor,
|
|
855
|
-
propertyName,
|
|
856
|
-
options: validationOptions,
|
|
857
|
-
validator: {
|
|
858
|
-
validate(value, _args) {
|
|
859
|
-
return !helper__namespace.isEmpty(value);
|
|
860
|
-
},
|
|
861
|
-
defaultMessage(_args) {
|
|
862
|
-
return "invalid parameter ($property).";
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
});
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
848
|
/**
|
|
869
|
-
*
|
|
870
|
-
*
|
|
871
|
-
* @export
|
|
872
|
-
* @param {*} comparison
|
|
873
|
-
* @param {ValidationOptions} [validationOptions]
|
|
874
|
-
* @returns {PropertyDecorator}
|
|
849
|
+
* 错误信息国际化
|
|
875
850
|
*/
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
851
|
+
const ERROR_MESSAGES = {
|
|
852
|
+
zh: {
|
|
853
|
+
// 中国本土化验证
|
|
854
|
+
IsCnName: '必须是有效的中文姓名',
|
|
855
|
+
IsIdNumber: '必须是有效的身份证号码',
|
|
856
|
+
IsZipCode: '必须是有效的邮政编码',
|
|
857
|
+
IsMobile: '必须是有效的手机号码',
|
|
858
|
+
IsPlateNumber: '必须是有效的车牌号码',
|
|
859
|
+
// 基础验证
|
|
860
|
+
IsNotEmpty: '不能为空',
|
|
861
|
+
IsDate: '必须是有效的日期',
|
|
862
|
+
IsEmail: '必须是有效的邮箱地址',
|
|
863
|
+
IsIP: '必须是有效的IP地址',
|
|
864
|
+
IsPhoneNumber: '必须是有效的电话号码',
|
|
865
|
+
IsUrl: '必须是有效的URL地址',
|
|
866
|
+
IsHash: '必须是有效的哈希值',
|
|
867
|
+
// 比较验证
|
|
868
|
+
Equals: '必须等于 {comparison}',
|
|
869
|
+
NotEquals: '不能等于 {comparison}',
|
|
870
|
+
Contains: '必须包含 {seed}',
|
|
871
|
+
IsIn: '必须是以下值之一: {possibleValues}',
|
|
872
|
+
IsNotIn: '不能是以下值之一: {possibleValues}',
|
|
873
|
+
Gt: '必须大于 {min}',
|
|
874
|
+
Gte: '必须大于或等于 {min}',
|
|
875
|
+
Lt: '必须小于 {max}',
|
|
876
|
+
Lte: '必须小于或等于 {max}',
|
|
877
|
+
// 通用错误
|
|
878
|
+
invalidParameter: '参数 {field} 无效',
|
|
879
|
+
validationFailed: '验证失败',
|
|
880
|
+
},
|
|
881
|
+
en: {
|
|
882
|
+
// Chinese localization validators
|
|
883
|
+
IsCnName: 'must be a valid Chinese name',
|
|
884
|
+
IsIdNumber: 'must be a valid ID number',
|
|
885
|
+
IsZipCode: 'must be a valid zip code',
|
|
886
|
+
IsMobile: 'must be a valid mobile number',
|
|
887
|
+
IsPlateNumber: 'must be a valid plate number',
|
|
888
|
+
// Basic validators
|
|
889
|
+
IsNotEmpty: 'should not be empty',
|
|
890
|
+
IsDate: 'must be a valid date',
|
|
891
|
+
IsEmail: 'must be a valid email',
|
|
892
|
+
IsIP: 'must be a valid IP address',
|
|
893
|
+
IsPhoneNumber: 'must be a valid phone number',
|
|
894
|
+
IsUrl: 'must be a valid URL',
|
|
895
|
+
IsHash: 'must be a valid hash',
|
|
896
|
+
// Comparison validators
|
|
897
|
+
Equals: 'must equal to {comparison}',
|
|
898
|
+
NotEquals: 'should not equal to {comparison}',
|
|
899
|
+
Contains: 'must contain {seed}',
|
|
900
|
+
IsIn: 'must be one of the following values: {possibleValues}',
|
|
901
|
+
IsNotIn: 'should not be one of the following values: {possibleValues}',
|
|
902
|
+
Gt: 'must be greater than {min}',
|
|
903
|
+
Gte: 'must be greater than or equal to {min}',
|
|
904
|
+
Lt: 'must be less than {max}',
|
|
905
|
+
Lte: 'must be less than or equal to {max}',
|
|
906
|
+
// Common errors
|
|
907
|
+
invalidParameter: 'invalid parameter {field}',
|
|
908
|
+
validationFailed: 'validation failed',
|
|
909
|
+
}
|
|
910
|
+
};
|
|
895
911
|
/**
|
|
896
|
-
*
|
|
897
|
-
*
|
|
898
|
-
* @export
|
|
899
|
-
* @param {*} comparison
|
|
900
|
-
* @param {ValidationOptions} [validationOptions]
|
|
901
|
-
* @returns {PropertyDecorator}
|
|
912
|
+
* 增强的验证错误类
|
|
902
913
|
*/
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
914
|
+
class KoattyValidationError extends Error {
|
|
915
|
+
constructor(errors, message) {
|
|
916
|
+
const errorMessage = message || 'Validation failed';
|
|
917
|
+
super(errorMessage);
|
|
918
|
+
this.name = 'KoattyValidationError';
|
|
919
|
+
this.errors = errors;
|
|
920
|
+
this.statusCode = 400;
|
|
921
|
+
this.timestamp = new Date();
|
|
922
|
+
// 确保正确的原型链
|
|
923
|
+
Object.setPrototypeOf(this, KoattyValidationError.prototype);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* 获取第一个错误信息
|
|
927
|
+
*/
|
|
928
|
+
getFirstError() {
|
|
929
|
+
return this.errors[0];
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* 获取指定字段的错误
|
|
933
|
+
*/
|
|
934
|
+
getFieldErrors(field) {
|
|
935
|
+
return this.errors.filter(error => error.field === field);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* 转换为JSON格式
|
|
939
|
+
*/
|
|
940
|
+
toJSON() {
|
|
941
|
+
return {
|
|
942
|
+
name: this.name,
|
|
943
|
+
message: this.message,
|
|
944
|
+
statusCode: this.statusCode,
|
|
945
|
+
timestamp: this.timestamp,
|
|
946
|
+
errors: this.errors
|
|
947
|
+
};
|
|
948
|
+
}
|
|
921
949
|
}
|
|
922
950
|
/**
|
|
923
|
-
*
|
|
924
|
-
*
|
|
925
|
-
* @export
|
|
926
|
-
* @param {string} seed
|
|
927
|
-
* @param {ValidationOptions} [validationOptions]
|
|
928
|
-
* @returns {PropertyDecorator}
|
|
951
|
+
* 错误信息格式化器
|
|
929
952
|
*/
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
953
|
+
class ErrorMessageFormatter {
|
|
954
|
+
constructor(language = 'zh') {
|
|
955
|
+
this.language = 'zh';
|
|
956
|
+
this.language = language;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* 设置语言
|
|
960
|
+
*/
|
|
961
|
+
setLanguage(language) {
|
|
962
|
+
this.language = language;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* 格式化错误消息
|
|
966
|
+
*/
|
|
967
|
+
formatMessage(constraint, field, value, context) {
|
|
968
|
+
const messages = ERROR_MESSAGES[this.language];
|
|
969
|
+
let template = messages[constraint] || messages.invalidParameter;
|
|
970
|
+
// 替换占位符
|
|
971
|
+
template = template.replace('{field}', field);
|
|
972
|
+
// 优先使用上下文中的值,然后是传入的value
|
|
973
|
+
if (context) {
|
|
974
|
+
Object.entries(context).forEach(([key, val]) => {
|
|
975
|
+
template = template.replace(`{${key}}`, this.formatValue(val));
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
// 如果还有{value}占位符且传入了value,则替换
|
|
979
|
+
if (value !== undefined && template.includes('{value}')) {
|
|
980
|
+
template = template.replace('{value}', this.formatValue(value));
|
|
981
|
+
}
|
|
982
|
+
return template;
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* 格式化值用于消息显示
|
|
986
|
+
* @private
|
|
987
|
+
*/
|
|
988
|
+
formatValue(value) {
|
|
989
|
+
if (value === null)
|
|
990
|
+
return 'null';
|
|
991
|
+
if (value === undefined)
|
|
992
|
+
return 'undefined';
|
|
993
|
+
if (typeof value === 'number')
|
|
994
|
+
return String(value);
|
|
995
|
+
if (typeof value === 'string')
|
|
996
|
+
return `"${value}"`;
|
|
997
|
+
if (Array.isArray(value))
|
|
998
|
+
return `[${value.map(v => this.formatValue(v)).join(', ')}]`;
|
|
999
|
+
if (typeof value === 'object') {
|
|
1000
|
+
try {
|
|
1001
|
+
return JSON.stringify(value);
|
|
946
1002
|
}
|
|
947
|
-
|
|
948
|
-
|
|
1003
|
+
catch {
|
|
1004
|
+
// 处理循环引用
|
|
1005
|
+
return '[Circular Reference]';
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return String(value);
|
|
1009
|
+
}
|
|
949
1010
|
}
|
|
950
1011
|
/**
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
* @export
|
|
954
|
-
* @param {any[]} possibleValues
|
|
955
|
-
* @param {ValidationOptions} [validationOptions]
|
|
956
|
-
* @returns {PropertyDecorator}
|
|
1012
|
+
* 全局错误信息格式化器实例
|
|
957
1013
|
*/
|
|
958
|
-
|
|
959
|
-
return function (object, propertyName) {
|
|
960
|
-
setExpose(object, propertyName);
|
|
961
|
-
classValidator.registerDecorator({
|
|
962
|
-
name: "vIsIn",
|
|
963
|
-
target: object.constructor,
|
|
964
|
-
propertyName,
|
|
965
|
-
options: validationOptions,
|
|
966
|
-
validator: {
|
|
967
|
-
validate(value, _args) {
|
|
968
|
-
return classValidator.isIn(value, possibleValues);
|
|
969
|
-
},
|
|
970
|
-
defaultMessage(_args) {
|
|
971
|
-
return `invalid parameter ($property).`;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
});
|
|
975
|
-
};
|
|
976
|
-
}
|
|
1014
|
+
const errorFormatter = new ErrorMessageFormatter();
|
|
977
1015
|
/**
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
* @export
|
|
981
|
-
* @param {any[]} possibleValues
|
|
982
|
-
* @param {ValidationOptions} [validationOptions]
|
|
983
|
-
* @returns {PropertyDecorator}
|
|
1016
|
+
* 设置全局语言
|
|
984
1017
|
*/
|
|
985
|
-
function
|
|
986
|
-
|
|
987
|
-
setExpose(object, propertyName);
|
|
988
|
-
classValidator.registerDecorator({
|
|
989
|
-
name: "vIsNotIn",
|
|
990
|
-
target: object.constructor,
|
|
991
|
-
propertyName,
|
|
992
|
-
options: validationOptions,
|
|
993
|
-
validator: {
|
|
994
|
-
validate(value, _args) {
|
|
995
|
-
return classValidator.isNotIn(value, possibleValues);
|
|
996
|
-
},
|
|
997
|
-
defaultMessage(_args) {
|
|
998
|
-
return `invalid parameter ($property).`;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
});
|
|
1002
|
-
};
|
|
1018
|
+
function setValidationLanguage(language) {
|
|
1019
|
+
errorFormatter.setLanguage(language);
|
|
1003
1020
|
}
|
|
1004
1021
|
/**
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
* @export
|
|
1008
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1009
|
-
* @returns {PropertyDecorator}
|
|
1022
|
+
* 创建验证错误
|
|
1010
1023
|
*/
|
|
1011
|
-
function
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
validator: {
|
|
1020
|
-
validate(value, _args) {
|
|
1021
|
-
return classValidator.isDate(value);
|
|
1022
|
-
},
|
|
1023
|
-
defaultMessage(_args) {
|
|
1024
|
-
return `invalid parameter ($property).`;
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
});
|
|
1024
|
+
function createValidationError(field, value, constraint, customMessage, context) {
|
|
1025
|
+
const message = customMessage || errorFormatter.formatMessage(constraint, field, value, context);
|
|
1026
|
+
return {
|
|
1027
|
+
field,
|
|
1028
|
+
value,
|
|
1029
|
+
constraint,
|
|
1030
|
+
message,
|
|
1031
|
+
context
|
|
1028
1032
|
};
|
|
1029
1033
|
}
|
|
1030
1034
|
/**
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1033
|
-
* @export
|
|
1034
|
-
* @param {number} min
|
|
1035
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1036
|
-
* @returns {PropertyDecorator}
|
|
1035
|
+
* 批量创建验证错误
|
|
1037
1036
|
*/
|
|
1038
|
-
function
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
classValidator.registerDecorator({
|
|
1042
|
-
name: "vMin",
|
|
1043
|
-
target: object.constructor,
|
|
1044
|
-
propertyName,
|
|
1045
|
-
options: validationOptions,
|
|
1046
|
-
validator: {
|
|
1047
|
-
validate(value, _args) {
|
|
1048
|
-
return helper__namespace.toNumber(value) > min;
|
|
1049
|
-
},
|
|
1050
|
-
defaultMessage(_args) {
|
|
1051
|
-
return `invalid parameter ($property).`;
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
});
|
|
1055
|
-
};
|
|
1037
|
+
function createValidationErrors(errors) {
|
|
1038
|
+
const validationErrors = errors.map(error => createValidationError(error.field, error.value, error.constraint, error.message, error.context));
|
|
1039
|
+
return new KoattyValidationError(validationErrors);
|
|
1056
1040
|
}
|
|
1041
|
+
|
|
1057
1042
|
/**
|
|
1058
|
-
*
|
|
1059
|
-
*
|
|
1060
|
-
* @export
|
|
1061
|
-
* @param {number} max
|
|
1062
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1063
|
-
* @returns {PropertyDecorator}
|
|
1043
|
+
* 性能缓存模块 - 提供多层次缓存和性能监控
|
|
1044
|
+
* @author richen
|
|
1064
1045
|
*/
|
|
1065
|
-
function Lt(max, validationOptions) {
|
|
1066
|
-
return function (object, propertyName) {
|
|
1067
|
-
setExpose(object, propertyName);
|
|
1068
|
-
classValidator.registerDecorator({
|
|
1069
|
-
name: "vMax",
|
|
1070
|
-
target: object.constructor,
|
|
1071
|
-
propertyName,
|
|
1072
|
-
options: validationOptions,
|
|
1073
|
-
validator: {
|
|
1074
|
-
validate(value, _args) {
|
|
1075
|
-
return helper__namespace.toNumber(value) < max;
|
|
1076
|
-
},
|
|
1077
|
-
defaultMessage(_args) {
|
|
1078
|
-
return `invalid parameter ($property).`;
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
});
|
|
1082
|
-
};
|
|
1083
|
-
}
|
|
1084
1046
|
/**
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
* @export
|
|
1088
|
-
* @param {number} min
|
|
1089
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1090
|
-
* @returns {PropertyDecorator}
|
|
1047
|
+
* 元数据缓存
|
|
1091
1048
|
*/
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1049
|
+
class MetadataCache {
|
|
1050
|
+
constructor() {
|
|
1051
|
+
this.cache = new WeakMap();
|
|
1052
|
+
}
|
|
1053
|
+
static getInstance() {
|
|
1054
|
+
if (!MetadataCache.instance) {
|
|
1055
|
+
MetadataCache.instance = new MetadataCache();
|
|
1056
|
+
}
|
|
1057
|
+
return MetadataCache.instance;
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* 获取类的元数据缓存
|
|
1061
|
+
*/
|
|
1062
|
+
getClassCache(target) {
|
|
1063
|
+
if (!this.cache.has(target)) {
|
|
1064
|
+
this.cache.set(target, new Map());
|
|
1065
|
+
}
|
|
1066
|
+
return this.cache.get(target);
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* 缓存元数据
|
|
1070
|
+
*/
|
|
1071
|
+
setMetadata(target, key, value) {
|
|
1072
|
+
const classCache = this.getClassCache(target);
|
|
1073
|
+
classCache.set(key, value);
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* 获取缓存的元数据
|
|
1077
|
+
*/
|
|
1078
|
+
getMetadata(target, key) {
|
|
1079
|
+
const classCache = this.getClassCache(target);
|
|
1080
|
+
return classCache.get(key);
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* 检查是否已缓存
|
|
1084
|
+
*/
|
|
1085
|
+
hasMetadata(target, key) {
|
|
1086
|
+
const classCache = this.getClassCache(target);
|
|
1087
|
+
return classCache.has(key);
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* 清空指定类的缓存
|
|
1091
|
+
*/
|
|
1092
|
+
clearClassCache(target) {
|
|
1093
|
+
if (this.cache.has(target)) {
|
|
1094
|
+
this.cache.delete(target);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1110
1097
|
}
|
|
1111
1098
|
/**
|
|
1112
|
-
*
|
|
1113
|
-
*
|
|
1114
|
-
* @export
|
|
1115
|
-
* @param {number} max
|
|
1116
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1117
|
-
* @returns {PropertyDecorator}
|
|
1099
|
+
* 验证结果缓存
|
|
1118
1100
|
*/
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
options: validationOptions,
|
|
1127
|
-
validator: {
|
|
1128
|
-
validate(value, _args) {
|
|
1129
|
-
return helper__namespace.toNumber(value) <= max;
|
|
1130
|
-
},
|
|
1131
|
-
defaultMessage(_args) {
|
|
1132
|
-
return `invalid parameter ($property).`;
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1101
|
+
class ValidationCache {
|
|
1102
|
+
constructor(options) {
|
|
1103
|
+
this.cache = new lruCache.LRUCache({
|
|
1104
|
+
max: (options === null || options === void 0 ? void 0 : options.max) || 5000,
|
|
1105
|
+
ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 10, // 10分钟
|
|
1106
|
+
allowStale: (options === null || options === void 0 ? void 0 : options.allowStale) || false,
|
|
1107
|
+
updateAgeOnGet: (options === null || options === void 0 ? void 0 : options.updateAgeOnGet) || true,
|
|
1135
1108
|
});
|
|
1136
|
-
}
|
|
1109
|
+
}
|
|
1110
|
+
static getInstance(options) {
|
|
1111
|
+
if (!ValidationCache.instance) {
|
|
1112
|
+
ValidationCache.instance = new ValidationCache(options);
|
|
1113
|
+
}
|
|
1114
|
+
return ValidationCache.instance;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* 生成缓存键
|
|
1118
|
+
*/
|
|
1119
|
+
generateKey(validator, value, ...args) {
|
|
1120
|
+
const valueStr = this.serializeValue(value);
|
|
1121
|
+
const argsStr = args.length > 0 ? JSON.stringify(args) : '';
|
|
1122
|
+
return `${validator}:${valueStr}:${argsStr}`;
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* 序列化值用于缓存键
|
|
1126
|
+
*/
|
|
1127
|
+
serializeValue(value) {
|
|
1128
|
+
if (value === null)
|
|
1129
|
+
return 'null';
|
|
1130
|
+
if (value === undefined)
|
|
1131
|
+
return 'undefined';
|
|
1132
|
+
if (typeof value === 'string')
|
|
1133
|
+
return `s:${value}`;
|
|
1134
|
+
if (typeof value === 'number')
|
|
1135
|
+
return `n:${value}`;
|
|
1136
|
+
if (typeof value === 'boolean')
|
|
1137
|
+
return `b:${value}`;
|
|
1138
|
+
if (Array.isArray(value))
|
|
1139
|
+
return `a:${JSON.stringify(value)}`;
|
|
1140
|
+
if (typeof value === 'object')
|
|
1141
|
+
return `o:${JSON.stringify(value)}`;
|
|
1142
|
+
return String(value);
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* 获取缓存的验证结果
|
|
1146
|
+
*/
|
|
1147
|
+
get(validator, value, ...args) {
|
|
1148
|
+
const key = this.generateKey(validator, value, ...args);
|
|
1149
|
+
return this.cache.get(key);
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* 缓存验证结果
|
|
1153
|
+
*/
|
|
1154
|
+
set(validator, value, result, ...args) {
|
|
1155
|
+
const key = this.generateKey(validator, value, ...args);
|
|
1156
|
+
this.cache.set(key, result);
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* 检查是否存在缓存
|
|
1160
|
+
*/
|
|
1161
|
+
has(validator, value, ...args) {
|
|
1162
|
+
const key = this.generateKey(validator, value, ...args);
|
|
1163
|
+
return this.cache.has(key);
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* 删除特定缓存
|
|
1167
|
+
*/
|
|
1168
|
+
delete(validator, value, ...args) {
|
|
1169
|
+
const key = this.generateKey(validator, value, ...args);
|
|
1170
|
+
return this.cache.delete(key);
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* 清空缓存
|
|
1174
|
+
*/
|
|
1175
|
+
clear() {
|
|
1176
|
+
this.cache.clear();
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* 获取缓存统计
|
|
1180
|
+
*/
|
|
1181
|
+
getStats() {
|
|
1182
|
+
return {
|
|
1183
|
+
size: this.cache.size,
|
|
1184
|
+
max: this.cache.max,
|
|
1185
|
+
calculatedSize: this.cache.calculatedSize,
|
|
1186
|
+
keyCount: this.cache.size,
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* 设置缓存TTL
|
|
1191
|
+
*/
|
|
1192
|
+
setTTL(validator, value, ttl, ...args) {
|
|
1193
|
+
const key = this.generateKey(validator, value, ...args);
|
|
1194
|
+
const existingValue = this.cache.get(key);
|
|
1195
|
+
if (existingValue !== undefined) {
|
|
1196
|
+
this.cache.set(key, existingValue, { ttl });
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1137
1199
|
}
|
|
1138
1200
|
/**
|
|
1139
|
-
*
|
|
1140
|
-
* If given value is not a string, then it returns false.
|
|
1141
|
-
*
|
|
1142
|
-
* @export
|
|
1143
|
-
* @param {number} min
|
|
1144
|
-
* @param {number} [max]
|
|
1145
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1146
|
-
* @returns {PropertyDecorator}
|
|
1201
|
+
* 正则表达式缓存
|
|
1147
1202
|
*/
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1203
|
+
class RegexCache {
|
|
1204
|
+
constructor(options) {
|
|
1205
|
+
this.cache = new lruCache.LRUCache({
|
|
1206
|
+
max: (options === null || options === void 0 ? void 0 : options.max) || 200,
|
|
1207
|
+
ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 30, // 30分钟
|
|
1208
|
+
allowStale: (options === null || options === void 0 ? void 0 : options.allowStale) || false,
|
|
1209
|
+
updateAgeOnGet: (options === null || options === void 0 ? void 0 : options.updateAgeOnGet) || true,
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
static getInstance(options) {
|
|
1213
|
+
if (!RegexCache.instance) {
|
|
1214
|
+
RegexCache.instance = new RegexCache(options);
|
|
1215
|
+
}
|
|
1216
|
+
return RegexCache.instance;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* 获取缓存的正则表达式
|
|
1220
|
+
*/
|
|
1221
|
+
get(pattern, flags) {
|
|
1222
|
+
const key = flags ? `${pattern}:::${flags}` : pattern;
|
|
1223
|
+
let regex = this.cache.get(key);
|
|
1224
|
+
if (!regex) {
|
|
1225
|
+
try {
|
|
1226
|
+
regex = new RegExp(pattern, flags);
|
|
1227
|
+
this.cache.set(key, regex);
|
|
1228
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1229
|
+
}
|
|
1230
|
+
catch (_error) {
|
|
1231
|
+
// 如果正则表达式无效,抛出错误
|
|
1232
|
+
throw new Error(`Invalid regex pattern: ${pattern}`);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return regex;
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* 预编译常用正则表达式
|
|
1239
|
+
*/
|
|
1240
|
+
precompile(patterns) {
|
|
1241
|
+
patterns.forEach(({ pattern, flags }) => {
|
|
1242
|
+
try {
|
|
1243
|
+
this.get(pattern, flags);
|
|
1244
|
+
}
|
|
1245
|
+
catch (error) {
|
|
1246
|
+
console.warn(`Failed to precompile regex: ${pattern}`, error);
|
|
1163
1247
|
}
|
|
1164
1248
|
});
|
|
1165
|
-
}
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* 获取缓存统计
|
|
1252
|
+
*/
|
|
1253
|
+
getStats() {
|
|
1254
|
+
return {
|
|
1255
|
+
size: this.cache.size,
|
|
1256
|
+
max: this.cache.max,
|
|
1257
|
+
calculatedSize: this.cache.calculatedSize,
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* 清空缓存
|
|
1262
|
+
*/
|
|
1263
|
+
clear() {
|
|
1264
|
+
this.cache.clear();
|
|
1265
|
+
}
|
|
1166
1266
|
}
|
|
1167
1267
|
/**
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
* @export
|
|
1171
|
-
* @param {IsEmailOptions} [options]
|
|
1172
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1173
|
-
* @returns {PropertyDecorator}
|
|
1268
|
+
* 性能监控
|
|
1174
1269
|
*/
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1270
|
+
class PerformanceMonitor {
|
|
1271
|
+
constructor() {
|
|
1272
|
+
this.metrics = new Map();
|
|
1273
|
+
}
|
|
1274
|
+
static getInstance() {
|
|
1275
|
+
if (!PerformanceMonitor.instance) {
|
|
1276
|
+
PerformanceMonitor.instance = new PerformanceMonitor();
|
|
1277
|
+
}
|
|
1278
|
+
return PerformanceMonitor.instance;
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* 开始计时
|
|
1282
|
+
*/
|
|
1283
|
+
startTimer(name) {
|
|
1284
|
+
const start = performance.now();
|
|
1285
|
+
return () => {
|
|
1286
|
+
const duration = performance.now() - start;
|
|
1287
|
+
this.recordMetric(name, duration);
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* 记录性能指标
|
|
1292
|
+
*/
|
|
1293
|
+
recordMetric(name, duration) {
|
|
1294
|
+
if (!this.metrics.has(name)) {
|
|
1295
|
+
this.metrics.set(name, {
|
|
1296
|
+
count: 0,
|
|
1297
|
+
totalTime: 0,
|
|
1298
|
+
avgTime: 0,
|
|
1299
|
+
maxTime: 0,
|
|
1300
|
+
minTime: Infinity,
|
|
1301
|
+
lastExecutionTime: new Date(),
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
const metric = this.metrics.get(name);
|
|
1305
|
+
metric.count++;
|
|
1306
|
+
metric.totalTime += duration;
|
|
1307
|
+
metric.avgTime = metric.totalTime / metric.count;
|
|
1308
|
+
metric.maxTime = Math.max(metric.maxTime, duration);
|
|
1309
|
+
metric.minTime = Math.min(metric.minTime, duration);
|
|
1310
|
+
metric.lastExecutionTime = new Date();
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* 获取性能报告
|
|
1314
|
+
*/
|
|
1315
|
+
getReport() {
|
|
1316
|
+
const report = {};
|
|
1317
|
+
for (const [name, metric] of this.metrics) {
|
|
1318
|
+
report[name] = {
|
|
1319
|
+
...metric,
|
|
1320
|
+
minTime: metric.minTime === Infinity ? 0 : metric.minTime,
|
|
1321
|
+
avgTimeFormatted: `${metric.avgTime.toFixed(2)}ms`,
|
|
1322
|
+
totalTimeFormatted: `${metric.totalTime.toFixed(2)}ms`,
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
return report;
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* 获取热点分析(执行时间最长的操作)
|
|
1329
|
+
*/
|
|
1330
|
+
getHotspots(limit = 10) {
|
|
1331
|
+
return Array.from(this.metrics.entries())
|
|
1332
|
+
.map(([name, metric]) => ({
|
|
1333
|
+
name,
|
|
1334
|
+
avgTime: metric.avgTime,
|
|
1335
|
+
count: metric.count,
|
|
1336
|
+
}))
|
|
1337
|
+
.sort((a, b) => b.avgTime - a.avgTime)
|
|
1338
|
+
.slice(0, limit);
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* 清空指标
|
|
1342
|
+
*/
|
|
1343
|
+
clear() {
|
|
1344
|
+
this.metrics.clear();
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* 导出性能数据为CSV格式
|
|
1348
|
+
*/
|
|
1349
|
+
exportToCSV() {
|
|
1350
|
+
const headers = ['Name', 'Count', 'Total Time (ms)', 'Avg Time (ms)', 'Max Time (ms)', 'Min Time (ms)', 'Last Execution'];
|
|
1351
|
+
const rows = [headers.join(',')];
|
|
1352
|
+
for (const [name, metric] of this.metrics) {
|
|
1353
|
+
const row = [
|
|
1354
|
+
name,
|
|
1355
|
+
metric.count.toString(),
|
|
1356
|
+
metric.totalTime.toFixed(2),
|
|
1357
|
+
metric.avgTime.toFixed(2),
|
|
1358
|
+
metric.maxTime.toFixed(2),
|
|
1359
|
+
(metric.minTime === Infinity ? 0 : metric.minTime).toFixed(2),
|
|
1360
|
+
metric.lastExecutionTime.toISOString(),
|
|
1361
|
+
];
|
|
1362
|
+
rows.push(row.join(','));
|
|
1363
|
+
}
|
|
1364
|
+
return rows.join('\n');
|
|
1365
|
+
}
|
|
1193
1366
|
}
|
|
1367
|
+
// 导出单例实例
|
|
1368
|
+
const metadataCache = MetadataCache.getInstance();
|
|
1369
|
+
const validationCache = ValidationCache.getInstance();
|
|
1370
|
+
const regexCache = RegexCache.getInstance();
|
|
1371
|
+
const performanceMonitor = PerformanceMonitor.getInstance();
|
|
1194
1372
|
/**
|
|
1195
|
-
*
|
|
1196
|
-
*
|
|
1197
|
-
* @export
|
|
1198
|
-
* @param {number} [version]
|
|
1199
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1200
|
-
* @returns {PropertyDecorator}
|
|
1373
|
+
* 缓存装饰器 - 用于缓存验证函数结果
|
|
1201
1374
|
*/
|
|
1202
|
-
function
|
|
1203
|
-
return function (
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1375
|
+
function cached(validator, ttl) {
|
|
1376
|
+
return function (target, propertyName, descriptor) {
|
|
1377
|
+
const originalMethod = descriptor.value;
|
|
1378
|
+
descriptor.value = function (...args) {
|
|
1379
|
+
const value = args[0];
|
|
1380
|
+
const additionalArgs = args.slice(1);
|
|
1381
|
+
// 尝试从缓存获取结果
|
|
1382
|
+
const cachedResult = validationCache.get(validator, value, ...additionalArgs);
|
|
1383
|
+
if (cachedResult !== undefined) {
|
|
1384
|
+
return cachedResult;
|
|
1385
|
+
}
|
|
1386
|
+
// 执行验证并缓存结果
|
|
1387
|
+
const endTimer = performanceMonitor.startTimer(validator);
|
|
1388
|
+
try {
|
|
1389
|
+
const result = originalMethod.apply(this, args);
|
|
1390
|
+
validationCache.set(validator, value, result, ...additionalArgs);
|
|
1391
|
+
// 如果指定了TTL,设置过期时间
|
|
1392
|
+
if (ttl && ttl > 0) {
|
|
1393
|
+
validationCache.setTTL(validator, value, ttl, ...additionalArgs);
|
|
1216
1394
|
}
|
|
1395
|
+
return result;
|
|
1217
1396
|
}
|
|
1218
|
-
|
|
1397
|
+
finally {
|
|
1398
|
+
endTimer();
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
return descriptor;
|
|
1219
1402
|
};
|
|
1220
1403
|
}
|
|
1221
1404
|
/**
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
* @export
|
|
1225
|
-
* @param {string} {string} region 2 characters uppercase country code (e.g. DE, US, CH).
|
|
1226
|
-
* If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region.
|
|
1227
|
-
* See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]
|
|
1228
|
-
* {@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33}
|
|
1229
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1230
|
-
* @returns {PropertyDecorator}
|
|
1405
|
+
* 获取所有缓存统计信息
|
|
1231
1406
|
*/
|
|
1232
|
-
function
|
|
1233
|
-
return
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
propertyName,
|
|
1239
|
-
options: validationOptions,
|
|
1240
|
-
validator: {
|
|
1241
|
-
validate(value, _args) {
|
|
1242
|
-
return classValidator.isPhoneNumber(value, region);
|
|
1243
|
-
},
|
|
1244
|
-
defaultMessage(_args) {
|
|
1245
|
-
return `invalid parameter ($property).`;
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
});
|
|
1407
|
+
function getAllCacheStats() {
|
|
1408
|
+
return {
|
|
1409
|
+
validation: validationCache.getStats(),
|
|
1410
|
+
regex: regexCache.getStats(),
|
|
1411
|
+
performance: performanceMonitor.getReport(),
|
|
1412
|
+
hotspots: performanceMonitor.getHotspots(),
|
|
1249
1413
|
};
|
|
1250
1414
|
}
|
|
1251
1415
|
/**
|
|
1252
|
-
*
|
|
1253
|
-
*
|
|
1254
|
-
* @export
|
|
1255
|
-
* @param {IsURLOptions} [options]
|
|
1256
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1257
|
-
* @returns {PropertyDecorator}
|
|
1416
|
+
* 预热缓存 - 预编译常用正则表达式
|
|
1258
1417
|
*/
|
|
1259
|
-
function
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
validate(value, _args) {
|
|
1269
|
-
return classValidator.isURL(value, options);
|
|
1270
|
-
},
|
|
1271
|
-
defaultMessage(_args) {
|
|
1272
|
-
return `invalid parameter ($property).`;
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
});
|
|
1276
|
-
};
|
|
1418
|
+
function warmupCaches() {
|
|
1419
|
+
// 预编译中文验证相关的正则表达式
|
|
1420
|
+
const commonPatterns = [
|
|
1421
|
+
{ pattern: '^[\u4e00-\u9fa5]{2,8}$' }, // 中文姓名
|
|
1422
|
+
{ pattern: '^1[3-9]\\d{9}$' }, // 手机号
|
|
1423
|
+
{ pattern: '^\\d{6}$' }, // 邮政编码
|
|
1424
|
+
{ pattern: '^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]' }, // 车牌号开头
|
|
1425
|
+
];
|
|
1426
|
+
regexCache.precompile(commonPatterns);
|
|
1277
1427
|
}
|
|
1278
1428
|
/**
|
|
1279
|
-
*
|
|
1280
|
-
* 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b']
|
|
1281
|
-
*
|
|
1282
|
-
* @export
|
|
1283
|
-
* @param {HashAlgorithm} algorithm
|
|
1284
|
-
* @param {ValidationOptions} [validationOptions]
|
|
1285
|
-
* @returns {PropertyDecorator}
|
|
1429
|
+
* 清空所有缓存
|
|
1286
1430
|
*/
|
|
1287
|
-
function
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
name: "vIsHash",
|
|
1292
|
-
target: object.constructor,
|
|
1293
|
-
propertyName,
|
|
1294
|
-
options: validationOptions,
|
|
1295
|
-
validator: {
|
|
1296
|
-
validate(value, _args) {
|
|
1297
|
-
return classValidator.isHash(value, algorithm);
|
|
1298
|
-
},
|
|
1299
|
-
defaultMessage(_args) {
|
|
1300
|
-
return `invalid parameter, ($property) must be is an ${algorithm} Hash string.`;
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
});
|
|
1304
|
-
};
|
|
1431
|
+
function clearAllCaches() {
|
|
1432
|
+
validationCache.clear();
|
|
1433
|
+
regexCache.clear();
|
|
1434
|
+
performanceMonitor.clear();
|
|
1305
1435
|
}
|
|
1306
1436
|
/**
|
|
1307
|
-
*
|
|
1308
|
-
* @param func
|
|
1309
|
-
* @param validationOptions
|
|
1310
|
-
* @returns
|
|
1437
|
+
* 配置缓存设置
|
|
1311
1438
|
*/
|
|
1312
|
-
function
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
validate(value, _args) {
|
|
1322
|
-
return func(value);
|
|
1323
|
-
},
|
|
1324
|
-
defaultMessage(_args) {
|
|
1325
|
-
return `invalid parameter ($property).`;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
});
|
|
1329
|
-
};
|
|
1439
|
+
function configureCaches(options) {
|
|
1440
|
+
if (options.validation) {
|
|
1441
|
+
// 重新创建验证缓存实例
|
|
1442
|
+
ValidationCache.instance = new ValidationCache(options.validation);
|
|
1443
|
+
}
|
|
1444
|
+
if (options.regex) {
|
|
1445
|
+
// 重新创建正则缓存实例
|
|
1446
|
+
RegexCache.instance = new RegexCache(options.regex);
|
|
1447
|
+
}
|
|
1330
1448
|
}
|
|
1331
1449
|
|
|
1332
|
-
exports.CheckFunc = CheckFunc;
|
|
1333
1450
|
exports.ClassValidator = ClassValidator;
|
|
1334
1451
|
exports.Contains = Contains;
|
|
1335
1452
|
exports.ENABLE_VALIDATED = ENABLE_VALIDATED;
|
|
1453
|
+
exports.ERROR_MESSAGES = ERROR_MESSAGES;
|
|
1336
1454
|
exports.Equals = Equals;
|
|
1455
|
+
exports.ErrorMessageFormatter = ErrorMessageFormatter;
|
|
1337
1456
|
exports.Expose = Expose;
|
|
1338
1457
|
exports.FunctionValidator = FunctionValidator;
|
|
1339
1458
|
exports.Gt = Gt;
|
|
@@ -1353,17 +1472,32 @@ exports.IsPhoneNumber = IsPhoneNumber;
|
|
|
1353
1472
|
exports.IsPlateNumber = IsPlateNumber;
|
|
1354
1473
|
exports.IsUrl = IsUrl;
|
|
1355
1474
|
exports.IsZipCode = IsZipCode;
|
|
1356
|
-
exports.
|
|
1475
|
+
exports.KoattyValidationError = KoattyValidationError;
|
|
1357
1476
|
exports.Lt = Lt;
|
|
1358
1477
|
exports.Lte = Lte;
|
|
1359
1478
|
exports.NotEquals = NotEquals;
|
|
1360
1479
|
exports.PARAM_CHECK_KEY = PARAM_CHECK_KEY;
|
|
1361
1480
|
exports.PARAM_RULE_KEY = PARAM_RULE_KEY;
|
|
1362
|
-
exports.PARAM_TYPE_KEY = PARAM_TYPE_KEY;
|
|
1363
1481
|
exports.Valid = Valid;
|
|
1364
1482
|
exports.ValidFuncs = ValidFuncs;
|
|
1365
1483
|
exports.Validated = Validated;
|
|
1484
|
+
exports.cached = cached;
|
|
1366
1485
|
exports.checkParamsType = checkParamsType;
|
|
1486
|
+
exports.clearAllCaches = clearAllCaches;
|
|
1487
|
+
exports.configureCaches = configureCaches;
|
|
1367
1488
|
exports.convertDtoParamsType = convertDtoParamsType;
|
|
1368
1489
|
exports.convertParamsType = convertParamsType;
|
|
1490
|
+
exports.createParameterizedDecorator = createParameterizedDecorator;
|
|
1491
|
+
exports.createSimpleDecorator = createSimpleDecorator;
|
|
1492
|
+
exports.createValidationDecorator = createValidationDecorator;
|
|
1493
|
+
exports.createValidationError = createValidationError;
|
|
1494
|
+
exports.createValidationErrors = createValidationErrors;
|
|
1495
|
+
exports.errorFormatter = errorFormatter;
|
|
1496
|
+
exports.getAllCacheStats = getAllCacheStats;
|
|
1497
|
+
exports.metadataCache = metadataCache;
|
|
1498
|
+
exports.performanceMonitor = performanceMonitor;
|
|
1369
1499
|
exports.plainToClass = plainToClass;
|
|
1500
|
+
exports.regexCache = regexCache;
|
|
1501
|
+
exports.setValidationLanguage = setValidationLanguage;
|
|
1502
|
+
exports.validationCache = validationCache;
|
|
1503
|
+
exports.warmupCaches = warmupCaches;
|