koatty_validation 1.6.0 → 1.6.1

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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @Author: richen
3
- * @Date: 2025-10-23 01:25:03
3
+ * @Date: 2025-10-23 20:54:39
4
4
  * @License: BSD (3-Clause)
5
5
  * @Copyright (c) - <richenlin(at)gmail.com>
6
6
  * @HomePage: https://koatty.org/
@@ -33,22 +33,22 @@ function _interopNamespaceDefault(e) {
33
33
  var helper__namespace = /*#__PURE__*/_interopNamespaceDefault(helper);
34
34
 
35
35
  /**
36
- * koatty_validation 类型定义
36
+ * koatty_validation type definitions
37
37
  * @author richen
38
38
  * @copyright Copyright (c) - <richenlin(at)gmail.com>
39
39
  * @license MIT
40
40
  */
41
41
  /**
42
- * 参数类型键常量
42
+ * Parameter type key constant
43
43
  */
44
44
  const PARAM_TYPE_KEY = 'PARAM_TYPE_KEY';
45
45
 
46
46
  /**
47
- * 性能缓存模块 - 提供多层次缓存和性能监控
47
+ * Performance cache module - Provides multi-level caching and performance monitoring
48
48
  * @author richen
49
49
  */
50
50
  /**
51
- * 元数据缓存
51
+ * Metadata cache
52
52
  */
53
53
  class MetadataCache {
54
54
  constructor() {
@@ -61,7 +61,7 @@ class MetadataCache {
61
61
  return MetadataCache.instance;
62
62
  }
63
63
  /**
64
- * 获取类的元数据缓存
64
+ * Get metadata cache for a class
65
65
  */
66
66
  getClassCache(target) {
67
67
  if (!this.cache.has(target)) {
@@ -70,28 +70,28 @@ class MetadataCache {
70
70
  return this.cache.get(target);
71
71
  }
72
72
  /**
73
- * 缓存元数据
73
+ * Cache metadata
74
74
  */
75
75
  setMetadata(target, key, value) {
76
76
  const classCache = this.getClassCache(target);
77
77
  classCache.set(key, value);
78
78
  }
79
79
  /**
80
- * 获取缓存的元数据
80
+ * Get cached metadata
81
81
  */
82
82
  getMetadata(target, key) {
83
83
  const classCache = this.getClassCache(target);
84
84
  return classCache.get(key);
85
85
  }
86
86
  /**
87
- * 检查是否已缓存
87
+ * Check if metadata is cached
88
88
  */
89
89
  hasMetadata(target, key) {
90
90
  const classCache = this.getClassCache(target);
91
91
  return classCache.has(key);
92
92
  }
93
93
  /**
94
- * 清空指定类的缓存
94
+ * Clear cache for a specific class
95
95
  */
96
96
  clearClassCache(target) {
97
97
  if (this.cache.has(target)) {
@@ -100,7 +100,7 @@ class MetadataCache {
100
100
  }
101
101
  }
102
102
  /**
103
- * 验证结果缓存
103
+ * Validation result cache
104
104
  */
105
105
  class ValidationCache {
106
106
  constructor(options) {
@@ -108,7 +108,7 @@ class ValidationCache {
108
108
  this.misses = 0;
109
109
  this.cache = new lruCache.LRUCache({
110
110
  max: (options === null || options === void 0 ? void 0 : options.max) || 5000,
111
- ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 10, // 10分钟
111
+ ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 10, // 10 minutes
112
112
  allowStale: (options === null || options === void 0 ? void 0 : options.allowStale) || false,
113
113
  updateAgeOnGet: (options === null || options === void 0 ? void 0 : options.updateAgeOnGet) || true,
114
114
  });
@@ -226,7 +226,7 @@ class RegexCache {
226
226
  constructor(options) {
227
227
  this.cache = new lruCache.LRUCache({
228
228
  max: (options === null || options === void 0 ? void 0 : options.max) || 200,
229
- ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 30, // 30分钟
229
+ ttl: (options === null || options === void 0 ? void 0 : options.ttl) || 1000 * 60 * 30, // 30 minutes
230
230
  allowStale: (options === null || options === void 0 ? void 0 : options.allowStale) || false,
231
231
  updateAgeOnGet: (options === null || options === void 0 ? void 0 : options.updateAgeOnGet) || true,
232
232
  });
@@ -410,7 +410,7 @@ function cached(validator, ttl) {
410
410
  try {
411
411
  const result = originalMethod.apply(this, args);
412
412
  validationCache.set(validator, value, result, ...additionalArgs);
413
- // 如果指定了TTL,设置过期时间
413
+ // If TTL is specified, set expiration time
414
414
  if (ttl && ttl > 0) {
415
415
  validationCache.setTTL(validator, value, ttl, ...additionalArgs);
416
416
  }
@@ -1135,24 +1135,24 @@ const FunctionValidator = {
1135
1135
  };
1136
1136
 
1137
1137
  /**
1138
- * 装饰器工厂 - 消除装饰器代码重复
1138
+ * Decorator Factory - Eliminate decorator code duplication
1139
1139
  * @author richen
1140
1140
  */
1141
1141
  /**
1142
- * 创建验证装饰器的工厂函数
1143
- * @param options 装饰器配置选项
1144
- * @returns 装饰器工厂函数
1142
+ * Factory function to create validation decorators
1143
+ * @param options Decorator configuration options
1144
+ * @returns Decorator factory function
1145
1145
  */
1146
1146
  function createValidationDecorator(options) {
1147
1147
  const { name, validator, defaultMessage, requiresValue = false } = options;
1148
1148
  return function decoratorFactory(...args) {
1149
- // 处理参数:最后一个参数是ValidationOptions,前面是验证函数的参数
1149
+ // Handle parameters: last parameter is ValidationOptions, previous ones are validator function parameters
1150
1150
  const validationOptions = args[args.length - 1];
1151
1151
  const validatorArgs = requiresValue ? args.slice(0, -1) : [];
1152
1152
  return function propertyDecorator(object, propertyName) {
1153
- // 设置属性为可导出
1153
+ // Set property as exportable
1154
1154
  setExpose(object, propertyName);
1155
- // 注册验证装饰器
1155
+ // Register validation decorator
1156
1156
  classValidator.registerDecorator({
1157
1157
  name,
1158
1158
  target: object.constructor,
@@ -1180,11 +1180,11 @@ function createValidationDecorator(options) {
1180
1180
  };
1181
1181
  }
1182
1182
  /**
1183
- * 创建简单验证装饰器(不需要额外参数)
1184
- * @param name 装饰器名称
1185
- * @param validator 验证函数
1186
- * @param defaultMessage 默认错误信息
1187
- * @returns 装饰器函数
1183
+ * Create simple validation decorator (no additional parameters required)
1184
+ * @param name Decorator name
1185
+ * @param validator Validation function
1186
+ * @param defaultMessage Default error message
1187
+ * @returns Decorator function
1188
1188
  */
1189
1189
  function createSimpleDecorator(name, validator, defaultMessage) {
1190
1190
  return createValidationDecorator({
@@ -1195,11 +1195,11 @@ function createSimpleDecorator(name, validator, defaultMessage) {
1195
1195
  });
1196
1196
  }
1197
1197
  /**
1198
- * 创建带参数的验证装饰器
1199
- * @param name 装饰器名称
1200
- * @param validator 验证函数
1201
- * @param defaultMessage 默认错误信息
1202
- * @returns 装饰器工厂函数
1198
+ * Create parameterized validation decorator
1199
+ * @param name Decorator name
1200
+ * @param validator Validation function
1201
+ * @param defaultMessage Default error message
1202
+ * @returns Decorator factory function
1203
1203
  */
1204
1204
  function createParameterizedDecorator(name, validator, defaultMessage) {
1205
1205
  return createValidationDecorator({
@@ -1211,21 +1211,21 @@ function createParameterizedDecorator(name, validator, defaultMessage) {
1211
1211
  }
1212
1212
 
1213
1213
  /**
1214
- * 改进的错误处理机制
1214
+ * Improved error handling mechanism
1215
1215
  * @author richen
1216
1216
  */
1217
1217
  /**
1218
- * 错误信息国际化
1218
+ * Error message internationalization
1219
1219
  */
1220
1220
  const ERROR_MESSAGES = {
1221
1221
  zh: {
1222
- // 中国本土化验证
1222
+ // Chinese localization validation
1223
1223
  IsCnName: '必须是有效的中文姓名',
1224
1224
  IsIdNumber: '必须是有效的身份证号码',
1225
1225
  IsZipCode: '必须是有效的邮政编码',
1226
1226
  IsMobile: '必须是有效的手机号码',
1227
1227
  IsPlateNumber: '必须是有效的车牌号码',
1228
- // 基础验证
1228
+ // Basic validation
1229
1229
  IsNotEmpty: '不能为空',
1230
1230
  IsDate: '必须是有效的日期',
1231
1231
  IsEmail: '必须是有效的邮箱地址',
@@ -1233,7 +1233,7 @@ const ERROR_MESSAGES = {
1233
1233
  IsPhoneNumber: '必须是有效的电话号码',
1234
1234
  IsUrl: '必须是有效的URL地址',
1235
1235
  IsHash: '必须是有效的哈希值',
1236
- // 比较验证
1236
+ // Comparison validation
1237
1237
  Equals: '必须等于 {comparison}',
1238
1238
  NotEquals: '不能等于 {comparison}',
1239
1239
  Contains: '必须包含 {seed}',
@@ -1243,7 +1243,7 @@ const ERROR_MESSAGES = {
1243
1243
  Gte: '必须大于或等于 {min}',
1244
1244
  Lt: '必须小于 {max}',
1245
1245
  Lte: '必须小于或等于 {max}',
1246
- // 通用错误
1246
+ // Common errors
1247
1247
  invalidParameter: '参数 {field} 无效',
1248
1248
  validationFailed: '验证失败',
1249
1249
  },
@@ -1278,7 +1278,7 @@ const ERROR_MESSAGES = {
1278
1278
  }
1279
1279
  };
1280
1280
  /**
1281
- * 增强的验证错误类
1281
+ * Enhanced validation error class
1282
1282
  */
1283
1283
  class KoattyValidationError extends Error {
1284
1284
  constructor(errors, message) {
@@ -1288,23 +1288,23 @@ class KoattyValidationError extends Error {
1288
1288
  this.errors = errors;
1289
1289
  this.statusCode = 400;
1290
1290
  this.timestamp = new Date();
1291
- // 确保正确的原型链
1291
+ // Ensure correct prototype chain
1292
1292
  Object.setPrototypeOf(this, KoattyValidationError.prototype);
1293
1293
  }
1294
1294
  /**
1295
- * 获取第一个错误信息
1295
+ * Get the first error message
1296
1296
  */
1297
1297
  getFirstError() {
1298
1298
  return this.errors[0];
1299
1299
  }
1300
1300
  /**
1301
- * 获取指定字段的错误
1301
+ * Get errors for a specific field
1302
1302
  */
1303
1303
  getFieldErrors(field) {
1304
1304
  return this.errors.filter(error => error.field === field);
1305
1305
  }
1306
1306
  /**
1307
- * 转换为JSON格式
1307
+ * Convert to JSON format
1308
1308
  */
1309
1309
  toJSON() {
1310
1310
  return {
@@ -1317,7 +1317,7 @@ class KoattyValidationError extends Error {
1317
1317
  }
1318
1318
  }
1319
1319
  /**
1320
- * 错误信息格式化器
1320
+ * Error message formatter
1321
1321
  */
1322
1322
  class ErrorMessageFormatter {
1323
1323
  constructor(language = 'zh') {
@@ -1325,33 +1325,33 @@ class ErrorMessageFormatter {
1325
1325
  this.language = language;
1326
1326
  }
1327
1327
  /**
1328
- * 设置语言
1328
+ * Set language
1329
1329
  */
1330
1330
  setLanguage(language) {
1331
1331
  this.language = language;
1332
1332
  }
1333
1333
  /**
1334
- * 格式化错误消息
1334
+ * Format error message
1335
1335
  */
1336
1336
  formatMessage(constraint, field, value, context) {
1337
1337
  const messages = ERROR_MESSAGES[this.language];
1338
1338
  let template = messages[constraint] || messages.invalidParameter;
1339
- // 替换占位符
1339
+ // Replace placeholders
1340
1340
  template = template.replace('{field}', field);
1341
- // 优先使用上下文中的值,然后是传入的value
1341
+ // Prioritize values from context, then use passed value
1342
1342
  if (context) {
1343
1343
  Object.entries(context).forEach(([key, val]) => {
1344
1344
  template = template.replace(`{${key}}`, this.formatValue(val));
1345
1345
  });
1346
1346
  }
1347
- // 如果还有{value}占位符且传入了value,则替换
1347
+ // If there's still a {value} placeholder and value was passed, replace it
1348
1348
  if (value !== undefined && template.includes('{value}')) {
1349
1349
  template = template.replace('{value}', this.formatValue(value));
1350
1350
  }
1351
1351
  return template;
1352
1352
  }
1353
1353
  /**
1354
- * 格式化值用于消息显示
1354
+ * Format value for message display
1355
1355
  * @private
1356
1356
  */
1357
1357
  formatValue(value) {
@@ -1370,7 +1370,7 @@ class ErrorMessageFormatter {
1370
1370
  return JSON.stringify(value);
1371
1371
  }
1372
1372
  catch {
1373
- // 处理循环引用
1373
+ // Handle circular references
1374
1374
  return '[Circular Reference]';
1375
1375
  }
1376
1376
  }
@@ -1378,17 +1378,17 @@ class ErrorMessageFormatter {
1378
1378
  }
1379
1379
  }
1380
1380
  /**
1381
- * 全局错误信息格式化器实例
1381
+ * Global error message formatter instance
1382
1382
  */
1383
1383
  const errorFormatter = new ErrorMessageFormatter();
1384
1384
  /**
1385
- * 设置全局语言
1385
+ * Set global language
1386
1386
  */
1387
1387
  function setValidationLanguage(language) {
1388
1388
  errorFormatter.setLanguage(language);
1389
1389
  }
1390
1390
  /**
1391
- * 创建验证错误
1391
+ * Create validation error
1392
1392
  */
1393
1393
  function createValidationError(field, value, constraint, customMessage, context) {
1394
1394
  const message = customMessage || errorFormatter.formatMessage(constraint, field, value, context);
@@ -1401,7 +1401,7 @@ function createValidationError(field, value, constraint, customMessage, context)
1401
1401
  };
1402
1402
  }
1403
1403
  /**
1404
- * 批量创建验证错误
1404
+ * Create validation errors in batch
1405
1405
  */
1406
1406
  function createValidationErrors(errors) {
1407
1407
  const validationErrors = errors.map(error => createValidationError(error.field, error.value, error.constraint, error.message, error.context));
@@ -1409,19 +1409,19 @@ function createValidationErrors(errors) {
1409
1409
  }
1410
1410
 
1411
1411
  /**
1412
- * 重构后的装饰器定义 - 使用工厂函数消除重复
1412
+ * Refactored decorator definitions - Using factory functions to eliminate duplication
1413
1413
  * @author richen
1414
1414
  */
1415
- // 中国本土化验证装饰器
1415
+ // Chinese localization validation decorators
1416
1416
  const IsCnName = createSimpleDecorator('IsCnName', (value) => helper__namespace.isString(value) && cnName(value), 'must be a valid Chinese name');
1417
1417
  const IsIdNumber = createSimpleDecorator('IsIdNumber', (value) => helper__namespace.isString(value) && idNumber(value), 'must be a valid ID number');
1418
1418
  const IsZipCode = createSimpleDecorator('IsZipCode', (value) => helper__namespace.isString(value) && zipCode(value), 'must be a valid zip code');
1419
1419
  const IsMobile = createSimpleDecorator('IsMobile', (value) => helper__namespace.isString(value) && mobile(value), 'must be a valid mobile number');
1420
1420
  const IsPlateNumber = createSimpleDecorator('IsPlateNumber', (value) => helper__namespace.isString(value) && plateNumber(value), 'must be a valid plate number');
1421
- // 基础验证装饰器
1421
+ // Basic validation decorators
1422
1422
  const IsNotEmpty = createSimpleDecorator('IsNotEmpty', (value) => !helper__namespace.isEmpty(value), 'should not be empty');
1423
1423
  const IsDate = createSimpleDecorator('IsDate', (value) => helper__namespace.isDate(value), 'must be a valid date');
1424
- // 带参数的验证装饰器
1424
+ // Parameterized validation decorators
1425
1425
  const Equals = createParameterizedDecorator('Equals', (value, comparison) => value === comparison, 'must equal to $constraint1');
1426
1426
  const NotEquals = createParameterizedDecorator('NotEquals', (value, comparison) => value !== comparison, 'should not equal to $constraint1');
1427
1427
  const Contains = createParameterizedDecorator('Contains', (value, seed) => helper__namespace.isString(value) && value.includes(seed), 'must contain $constraint1');
@@ -1448,9 +1448,9 @@ function IsUrl(options, validationOptions) {
1448
1448
  function IsHash(algorithm, validationOptions) {
1449
1449
  return createParameterizedDecorator('IsHash', (value) => classValidator.isHash(value, algorithm), 'must be a valid hash')(validationOptions);
1450
1450
  }
1451
- // 基础工具装饰器(从原始decorator.ts移植)
1451
+ // Basic utility decorators (migrated from original decorator.ts)
1452
1452
  /**
1453
- * 标记属性为可导出
1453
+ * Mark property as exportable
1454
1454
  */
1455
1455
  function Expose() {
1456
1456
  return function (object, propertyName) {
@@ -1458,7 +1458,7 @@ function Expose() {
1458
1458
  };
1459
1459
  }
1460
1460
  /**
1461
- * Expose的别名
1461
+ * Alias for Expose
1462
1462
  */
1463
1463
  function IsDefined() {
1464
1464
  return function (object, propertyName) {
@@ -1466,61 +1466,89 @@ function IsDefined() {
1466
1466
  };
1467
1467
  }
1468
1468
  /**
1469
- * 参数验证装饰器
1469
+ * Parameter validation decorator
1470
1470
  */
1471
1471
  function Valid(rule, options) {
1472
1472
  return function (object, propertyName, parameterIndex) {
1473
- // 这里保持与原始实现一致
1473
+ // Keep consistent with original implementation
1474
1474
  const existingRules = Reflect.getOwnMetadata("validate", object, propertyName) || {};
1475
1475
  existingRules[parameterIndex] = { rule, options };
1476
1476
  Reflect.defineMetadata("validate", existingRules, object, propertyName);
1477
1477
  };
1478
1478
  }
1479
1479
  /**
1480
- * 方法验证装饰器
1481
- * 自动验证方法参数中的 DTO 对象
1480
+ * Synchronous validation function - Executes the actual validation logic
1481
+ * @param args Method parameters
1482
+ * @param paramTypes Parameter type metadata
1483
+ * @returns Validated parameters and validation targets
1482
1484
  */
1483
- function Validated() {
1484
- return function (object, propertyName, descriptor) {
1485
- const originalMethod = descriptor.value;
1486
- descriptor.value = async function (...args) {
1487
- // 获取参数类型元数据
1488
- const paramTypes = Reflect.getMetadata('design:paramtypes', object, propertyName) || [];
1489
- // 验证每个参数
1490
- for (let i = 0; i < args.length; i++) {
1491
- const arg = args[i];
1492
- const paramType = paramTypes[i];
1493
- // 如果是类类型且不是基础类型,执行验证
1494
- if (paramType && typeof paramType === 'function' &&
1495
- paramType !== String && paramType !== Number &&
1496
- paramType !== Boolean && paramType !== Array &&
1497
- paramType !== Object && paramType !== Date) {
1498
- try {
1499
- // 如果参数不是目标类型的实例,转换为实例
1500
- let validationTarget = arg;
1501
- if (!(arg instanceof paramType)) {
1502
- validationTarget = Object.assign(new paramType(), arg);
1503
- }
1504
- const errors = await classValidator.validate(validationTarget);
1505
- if (errors.length > 0) {
1506
- throw createValidationErrors(errors.map(e => ({
1507
- field: e.property,
1508
- value: e.value,
1509
- constraint: Object.keys(e.constraints || {})[0] || 'unknown',
1510
- message: Object.values(e.constraints || {})[0] || 'Validation failed',
1511
- context: e.constraints
1512
- })));
1513
- }
1514
- }
1515
- catch (error) {
1516
- // 如果验证失败,重新抛出错误
1517
- throw error;
1518
- }
1485
+ async function checkValidated(args, paramTypes) {
1486
+ const validationTargets = [];
1487
+ // Validate each parameter
1488
+ for (let i = 0; i < args.length; i++) {
1489
+ const arg = args[i];
1490
+ const paramType = paramTypes[i];
1491
+ // If it's a class type and not a basic type, perform validation
1492
+ if (paramType && typeof paramType === 'function' &&
1493
+ paramType !== String && paramType !== Number &&
1494
+ paramType !== Boolean && paramType !== Array &&
1495
+ paramType !== Object && paramType !== Date) {
1496
+ try {
1497
+ // If parameter is not an instance of the target type, convert it to an instance
1498
+ let validationTarget = arg;
1499
+ if (!(arg instanceof paramType)) {
1500
+ validationTarget = Object.assign(new paramType(), arg);
1501
+ }
1502
+ const errors = await classValidator.validate(validationTarget);
1503
+ if (errors.length > 0) {
1504
+ throw createValidationErrors(errors.map(e => ({
1505
+ field: e.property,
1506
+ value: e.value,
1507
+ constraint: Object.keys(e.constraints || {})[0] || 'unknown',
1508
+ message: Object.values(e.constraints || {})[0] || 'Validation failed',
1509
+ context: e.constraints
1510
+ })));
1519
1511
  }
1512
+ validationTargets.push(validationTarget);
1520
1513
  }
1521
- // 执行原始方法
1522
- return originalMethod.apply(this, args);
1523
- };
1514
+ catch (error) {
1515
+ // If validation fails, rethrow the error
1516
+ throw error;
1517
+ }
1518
+ }
1519
+ else {
1520
+ validationTargets.push(arg);
1521
+ }
1522
+ }
1523
+ return { validatedArgs: args, validationTargets };
1524
+ }
1525
+ /**
1526
+ * Method validation decorator
1527
+ * Automatically validates DTO objects in method parameters
1528
+ * @param isAsync Whether to use async validation mode, default is true
1529
+ * - true: Async mode, validation is handled by IOC container in the framework (suitable for scenarios where parameter values need to be obtained asynchronously)
1530
+ * - false: Sync mode, validation is performed immediately when the method is called (suitable for scenarios where parameter values are already prepared)
1531
+ */
1532
+ function Validated(isAsync = true) {
1533
+ return function (target, propertyKey, descriptor) {
1534
+ if (isAsync) {
1535
+ // Async mode: Save metadata, validation will be performed by the framework after async parameter retrieval
1536
+ koatty_container.IOCContainer.savePropertyData(PARAM_CHECK_KEY, {
1537
+ dtoCheck: 1
1538
+ }, target, propertyKey);
1539
+ }
1540
+ else {
1541
+ // Sync mode: Perform validation immediately when the method is called
1542
+ const originalMethod = descriptor.value;
1543
+ descriptor.value = async function (...args) {
1544
+ // Get parameter type metadata
1545
+ const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyKey) || [];
1546
+ // Execute validation
1547
+ await checkValidated(args, paramTypes);
1548
+ // Execute original method
1549
+ return originalMethod.apply(this, args);
1550
+ };
1551
+ }
1524
1552
  return descriptor;
1525
1553
  };
1526
1554
  }
@@ -1562,6 +1590,7 @@ exports.ValidFuncs = ValidFuncs;
1562
1590
  exports.Validated = Validated;
1563
1591
  exports.cached = cached;
1564
1592
  exports.checkParamsType = checkParamsType;
1593
+ exports.checkValidated = checkValidated;
1565
1594
  exports.clearAllCaches = clearAllCaches;
1566
1595
  exports.configureCaches = configureCaches;
1567
1596
  exports.convertDtoParamsType = convertDtoParamsType;