schema-dsl 1.0.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.
Files changed (116) hide show
  1. package/.eslintignore +10 -0
  2. package/.eslintrc.json +27 -0
  3. package/.github/CODE_OF_CONDUCT.md +45 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +57 -0
  5. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +45 -0
  7. package/.github/ISSUE_TEMPLATE/question.md +31 -0
  8. package/.github/PULL_REQUEST_TEMPLATE.md +70 -0
  9. package/.github/SECURITY.md +184 -0
  10. package/.github/workflows/ci.yml +33 -0
  11. package/CHANGELOG.md +633 -0
  12. package/CONTRIBUTING.md +368 -0
  13. package/LICENSE +21 -0
  14. package/README.md +1184 -0
  15. package/STATUS.md +101 -0
  16. package/docs/FEATURE-INDEX.md +519 -0
  17. package/docs/INDEX.md +253 -0
  18. package/docs/api-reference.md +1096 -0
  19. package/docs/best-practices.md +672 -0
  20. package/docs/cache-manager.md +336 -0
  21. package/docs/design-philosophy.md +601 -0
  22. package/docs/dsl-syntax.md +653 -0
  23. package/docs/dynamic-locale.md +552 -0
  24. package/docs/error-handling.md +703 -0
  25. package/docs/export-guide.md +462 -0
  26. package/docs/export-limitations.md +551 -0
  27. package/docs/faq.md +577 -0
  28. package/docs/frontend-i18n-guide.md +290 -0
  29. package/docs/i18n-user-guide.md +476 -0
  30. package/docs/label-vs-description.md +262 -0
  31. package/docs/markdown-exporter.md +397 -0
  32. package/docs/mongodb-exporter.md +295 -0
  33. package/docs/multi-type-support.md +319 -0
  34. package/docs/mysql-exporter.md +273 -0
  35. package/docs/plugin-system.md +542 -0
  36. package/docs/postgresql-exporter.md +304 -0
  37. package/docs/quick-start.md +761 -0
  38. package/docs/schema-helper.md +340 -0
  39. package/docs/schema-utils-chaining.md +143 -0
  40. package/docs/schema-utils.md +490 -0
  41. package/docs/string-extensions.md +480 -0
  42. package/docs/troubleshooting.md +471 -0
  43. package/docs/type-converter.md +319 -0
  44. package/docs/type-reference.md +219 -0
  45. package/docs/validate-async.md +480 -0
  46. package/docs/validate.md +486 -0
  47. package/docs/validation-guide.md +484 -0
  48. package/examples/array-dsl-example.js +227 -0
  49. package/examples/custom-extension.js +85 -0
  50. package/examples/dsl-match-example.js +74 -0
  51. package/examples/dsl-style.js +118 -0
  52. package/examples/dynamic-locale-configuration.js +348 -0
  53. package/examples/dynamic-locale-example.js +287 -0
  54. package/examples/export-demo.js +130 -0
  55. package/examples/express-integration.js +376 -0
  56. package/examples/i18n-full-demo.js +310 -0
  57. package/examples/i18n-memory-safety.examples.js +268 -0
  58. package/examples/markdown-export.js +71 -0
  59. package/examples/middleware-usage.js +93 -0
  60. package/examples/new-features-comparison.js +315 -0
  61. package/examples/password-reset/README.md +153 -0
  62. package/examples/password-reset/schema.js +26 -0
  63. package/examples/password-reset/test.js +101 -0
  64. package/examples/plugin-system.examples.js +205 -0
  65. package/examples/schema-utils-chaining.examples.js +250 -0
  66. package/examples/simple-example.js +122 -0
  67. package/examples/string-extensions.js +297 -0
  68. package/examples/user-registration/README.md +156 -0
  69. package/examples/user-registration/routes.js +92 -0
  70. package/examples/user-registration/schema.js +150 -0
  71. package/examples/user-registration/server.js +74 -0
  72. package/index.d.ts +1999 -0
  73. package/index.js +282 -0
  74. package/index.mjs +30 -0
  75. package/lib/adapters/DslAdapter.js +699 -0
  76. package/lib/adapters/index.js +20 -0
  77. package/lib/config/constants.js +286 -0
  78. package/lib/config/patterns/creditCard.js +9 -0
  79. package/lib/config/patterns/idCard.js +9 -0
  80. package/lib/config/patterns/index.js +8 -0
  81. package/lib/config/patterns/licensePlate.js +4 -0
  82. package/lib/config/patterns/passport.js +4 -0
  83. package/lib/config/patterns/phone.js +9 -0
  84. package/lib/config/patterns/postalCode.js +5 -0
  85. package/lib/core/CacheManager.js +376 -0
  86. package/lib/core/DslBuilder.js +740 -0
  87. package/lib/core/ErrorCodes.js +233 -0
  88. package/lib/core/ErrorFormatter.js +342 -0
  89. package/lib/core/JSONSchemaCore.js +347 -0
  90. package/lib/core/Locale.js +119 -0
  91. package/lib/core/MessageTemplate.js +89 -0
  92. package/lib/core/PluginManager.js +448 -0
  93. package/lib/core/StringExtensions.js +209 -0
  94. package/lib/core/Validator.js +376 -0
  95. package/lib/errors/ValidationError.js +191 -0
  96. package/lib/exporters/MarkdownExporter.js +420 -0
  97. package/lib/exporters/MongoDBExporter.js +162 -0
  98. package/lib/exporters/MySQLExporter.js +212 -0
  99. package/lib/exporters/PostgreSQLExporter.js +289 -0
  100. package/lib/exporters/index.js +24 -0
  101. package/lib/locales/en-US.js +65 -0
  102. package/lib/locales/es-ES.js +66 -0
  103. package/lib/locales/fr-FR.js +66 -0
  104. package/lib/locales/index.js +8 -0
  105. package/lib/locales/ja-JP.js +66 -0
  106. package/lib/locales/zh-CN.js +93 -0
  107. package/lib/utils/LRUCache.js +174 -0
  108. package/lib/utils/SchemaHelper.js +240 -0
  109. package/lib/utils/SchemaUtils.js +445 -0
  110. package/lib/utils/TypeConverter.js +245 -0
  111. package/lib/utils/index.js +13 -0
  112. package/lib/validators/CustomKeywords.js +203 -0
  113. package/lib/validators/index.js +11 -0
  114. package/package.json +70 -0
  115. package/plugins/custom-format.js +101 -0
  116. package/plugins/custom-validator.js +200 -0
@@ -0,0 +1,376 @@
1
+ /**
2
+ * Validator - JSON Schema验证器
3
+ *
4
+ * 基于ajv实现,支持JSON Schema Draft 7标准
5
+ *
6
+ * @module lib/core/Validator
7
+ * @version 1.0.0
8
+ */
9
+
10
+ const Ajv = require('ajv');
11
+ const addFormats = require('ajv-formats');
12
+ const CacheManager = require('./CacheManager');
13
+ const ErrorFormatter = require('./ErrorFormatter');
14
+ const CustomKeywords = require('../validators/CustomKeywords');
15
+ const Locale = require('./Locale');
16
+
17
+ /**
18
+ * 验证器类
19
+ *
20
+ * @class Validator
21
+ * @description 封装ajv,提供统一的验证接口
22
+ */
23
+ class Validator {
24
+ /**
25
+ * 构造函数
26
+ * @param {Object} options - ajv配置选项
27
+ * @param {boolean} options.allErrors - 是否返回所有错误(默认true)
28
+ * @param {boolean} options.useDefaults - 是否使用默认值(默认true)
29
+ * @param {boolean} options.coerceTypes - 是否自动类型转换(默认false)
30
+ * @param {boolean} options.removeAdditional - 是否移除额外属性(默认false)
31
+ */
32
+ constructor(options = {}) {
33
+ // ajv配置
34
+ this.ajvOptions = {
35
+ allErrors: options.allErrors !== false,
36
+ useDefaults: options.useDefaults !== false,
37
+ coerceTypes: options.coerceTypes || false,
38
+ removeAdditional: options.removeAdditional || false,
39
+ verbose: true, // 启用详细模式,以便访问 parentSchema
40
+ ...options
41
+ };
42
+
43
+ // 创建ajv实例
44
+ this.ajv = new Ajv(this.ajvOptions);
45
+
46
+ // 添加格式支持(email、uri、date-time等)
47
+ addFormats(this.ajv);
48
+
49
+ // 注册自定义关键字
50
+ CustomKeywords.registerAll(this.ajv);
51
+
52
+ // 编译缓存
53
+ this.cache = new CacheManager({
54
+ maxSize: 100,
55
+ ttl: 3600000 // 1小时
56
+ });
57
+
58
+ // 错误格式化器
59
+ this.errorFormatter = new ErrorFormatter();
60
+
61
+ // 自定义关键字注册表
62
+ this.customKeywords = new Map();
63
+ }
64
+
65
+ /**
66
+ * 编译Schema
67
+ * @param {Object} schema - JSON Schema对象
68
+ * @param {string} cacheKey - 缓存键(可选)
69
+ * @returns {Function} ajv验证函数
70
+ */
71
+ compile(schema, cacheKey = null) {
72
+ // 尝试从缓存获取
73
+ if (cacheKey) {
74
+ const cached = this.cache.get(cacheKey);
75
+ if (cached) {
76
+ return cached;
77
+ }
78
+ }
79
+
80
+ try {
81
+ // 编译Schema
82
+ const validate = this.ajv.compile(schema);
83
+
84
+ // 缓存编译结果
85
+ if (cacheKey) {
86
+ this.cache.set(cacheKey, validate);
87
+ }
88
+
89
+ return validate;
90
+ } catch (error) {
91
+ throw new Error(`Schema compilation failed: ${error.message}`);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * 验证数据
97
+ * @param {Object} schema - JSON Schema对象或已编译的验证函数
98
+ * @param {*} data - 待验证的数据
99
+ * @param {Object} options - 验证选项
100
+ * @param {boolean} options.format - 是否格式化错误(默认true)
101
+ * @param {string} options.locale - 动态指定语言(如 'zh-CN', 'en-US')
102
+ * @returns {Object} 验证结果 { valid: boolean, errors: Array, data: * }
103
+ */
104
+ validate(schema, data, options = {}) {
105
+ const shouldFormat = options.format !== false;
106
+ const locale = options.locale || Locale.getLocale();
107
+
108
+ // 如果指定了语言,临时切换
109
+ const originalLocale = options.locale ? Locale.getLocale() : null;
110
+ if (options.locale) {
111
+ Locale.setLocale(options.locale);
112
+ }
113
+
114
+ // 如果schema是DslBuilder实例,转换为JSON Schema
115
+ if (schema && typeof schema.toSchema === 'function') {
116
+ schema = schema.toSchema();
117
+ }
118
+
119
+ // 检查是否需要移除额外字段 (clean 模式)
120
+ if (schema && schema._removeAdditional) {
121
+ // 创建新的 Validator 实例,启用 removeAdditional
122
+ const tempValidator = new Validator({
123
+ ...this.ajvOptions,
124
+ removeAdditional: true
125
+ });
126
+
127
+ // 移除标记字段并深拷贝,避免修改原 schema
128
+ const cleanSchema = JSON.parse(JSON.stringify(schema));
129
+ delete cleanSchema._removeAdditional;
130
+
131
+ return tempValidator.validate(cleanSchema, data, options);
132
+ }
133
+
134
+ try {
135
+ // 如果schema是函数,说明已编译
136
+ let validate;
137
+ if (typeof schema === 'function') {
138
+ validate = schema;
139
+ } else {
140
+ // 生成缓存键
141
+ const cacheKey = this._generateCacheKey(schema);
142
+ validate = this.compile(schema, cacheKey);
143
+ }
144
+
145
+ // 执行验证
146
+ const valid = validate(data);
147
+
148
+ // 格式化错误
149
+ const errors = valid ? [] : (
150
+ shouldFormat
151
+ ? this.errorFormatter.format(validate.errors, locale)
152
+ : validate.errors
153
+ );
154
+
155
+ return {
156
+ valid,
157
+ errors,
158
+ data // 可能被useDefaults修改过
159
+ };
160
+ } catch (error) {
161
+ return {
162
+ valid: false,
163
+ errors: [{
164
+ message: `Validation error: ${error.message}`,
165
+ path: '',
166
+ keyword: 'error'
167
+ }],
168
+ data
169
+ };
170
+ } finally {
171
+ // 恢复原语言
172
+ if (originalLocale) {
173
+ Locale.setLocale(originalLocale);
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ * 快速验证(不缓存)
180
+ * @param {Object} schema - JSON Schema对象
181
+ * @param {*} data - 待验证的数据
182
+ * @returns {boolean} 是否有效
183
+ */
184
+ quickValidate(schema, data) {
185
+ try {
186
+ return this.ajv.validate(schema, data);
187
+ } catch (error) {
188
+ return false;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * 异步验证方法(验证失败自动抛出异常)
194
+ *
195
+ * @param {Object|Function} schema - JSON Schema对象或DslBuilder实例
196
+ * @param {*} data - 待验证的数据
197
+ * @param {Object} options - 验证选项(可选)
198
+ * @param {boolean} options.format - 是否格式化错误(默认true)
199
+ * @param {string} options.locale - 语言环境(如 'zh-CN', 'en-US')
200
+ * @returns {Promise<*>} 验证通过返回处理后的数据
201
+ * @throws {ValidationError} 验证失败抛出异常
202
+ *
203
+ * @example
204
+ * // 基础使用
205
+ * try {
206
+ * const data = await validator.validateAsync(userSchema, inputData);
207
+ * console.log('验证通过:', data);
208
+ * } catch (error) {
209
+ * if (error instanceof ValidationError) {
210
+ * console.error('验证失败:', error.errors);
211
+ * }
212
+ * }
213
+ *
214
+ * @example
215
+ * // Express 中使用
216
+ * app.post('/users', async (req, res, next) => {
217
+ * try {
218
+ * const data = await validator.validateAsync(userSchema, req.body);
219
+ * const user = await db.users.insert(data);
220
+ * res.json(user);
221
+ * } catch (error) {
222
+ * next(error);
223
+ * }
224
+ * });
225
+ */
226
+ async validateAsync(schema, data, options = {}) {
227
+ const result = this.validate(schema, data, options);
228
+
229
+ if (!result.valid) {
230
+ const ValidationError = require('../errors/ValidationError');
231
+ throw new ValidationError(result.errors, data);
232
+ }
233
+
234
+ return result.data;
235
+ }
236
+
237
+ /**
238
+ * 批量验证
239
+ * @param {Object} schema - JSON Schema对象
240
+ * @param {Array} dataArray - 数据数组
241
+ * @returns {Array} 验证结果数组
242
+ */
243
+ validateBatch(schema, dataArray) {
244
+ if (!Array.isArray(dataArray)) {
245
+ throw new Error('Data must be an array');
246
+ }
247
+
248
+ // 编译一次,复用验证函数
249
+ const cacheKey = this._generateCacheKey(schema);
250
+ const validate = this.compile(schema, cacheKey);
251
+
252
+ return dataArray.map(data => this.validate(validate, data));
253
+ }
254
+
255
+ /**
256
+ * 添加自定义关键字
257
+ * @param {string} keyword - 关键字名称
258
+ * @param {Object} definition - 关键字定义
259
+ * @returns {Validator} 返回this支持链式调用
260
+ */
261
+ addKeyword(keyword, definition) {
262
+ try {
263
+ this.ajv.addKeyword(keyword, definition);
264
+ this.customKeywords.set(keyword, definition);
265
+ return this;
266
+ } catch (error) {
267
+ throw new Error(`Failed to add keyword '${keyword}': ${error.message}`);
268
+ }
269
+ }
270
+
271
+ /**
272
+ * 添加自定义格式
273
+ * @param {string} name - 格式名称
274
+ * @param {Function|RegExp} validator - 验证函数或正则表达式
275
+ * @returns {Validator} 返回this支持链式调用
276
+ */
277
+ addFormat(name, validator) {
278
+ try {
279
+ this.ajv.addFormat(name, validator);
280
+ return this;
281
+ } catch (error) {
282
+ throw new Error(`Failed to add format '${name}': ${error.message}`);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * 添加Schema引用
288
+ * @param {string} uri - Schema URI
289
+ * @param {Object} schema - JSON Schema对象
290
+ * @returns {Validator} 返回this支持链式调用
291
+ */
292
+ addSchema(uri, schema) {
293
+ try {
294
+ this.ajv.addSchema(schema, uri);
295
+ return this;
296
+ } catch (error) {
297
+ throw new Error(`Failed to add schema '${uri}': ${error.message}`);
298
+ }
299
+ }
300
+
301
+ /**
302
+ * 移除Schema引用
303
+ * @param {string} uri - Schema URI
304
+ * @returns {Validator} 返回this支持链式调用
305
+ */
306
+ removeSchema(uri) {
307
+ this.ajv.removeSchema(uri);
308
+ return this;
309
+ }
310
+
311
+ /**
312
+ * 获取ajv实例(高级用法)
313
+ * @returns {Ajv} ajv实例
314
+ */
315
+ getAjv() {
316
+ return this.ajv;
317
+ }
318
+
319
+ /**
320
+ * 清空缓存
321
+ */
322
+ clearCache() {
323
+ this.cache.clear();
324
+ }
325
+
326
+ /**
327
+ * 获取缓存统计
328
+ * @returns {Object} 缓存统计信息
329
+ */
330
+ getCacheStats() {
331
+ return this.cache.stats();
332
+ }
333
+
334
+ /**
335
+ * 生成Schema的缓存键
336
+ * @private
337
+ * @param {Object} schema - JSON Schema对象
338
+ * @returns {string} 缓存键
339
+ */
340
+ _generateCacheKey(schema) {
341
+ // 简单实现:使用JSON字符串的hash
342
+ // 生产环境建议使用更高效的hash算法
343
+ return JSON.stringify(schema);
344
+ }
345
+
346
+ /**
347
+ * 静态方法:创建验证器实例
348
+ * @static
349
+ * @param {Object} options - 配置选项
350
+ * @returns {Validator} Validator实例
351
+ */
352
+ static create(options) {
353
+ return new Validator(options);
354
+ }
355
+
356
+ /**
357
+ * 静态方法:快速验证(不创建实例)
358
+ * @static
359
+ * @param {Object} schema - JSON Schema对象
360
+ * @param {*} data - 待验证的数据
361
+ * @returns {boolean} 是否有效
362
+ */
363
+ static quickValidate(schema, data) {
364
+ const ajv = new Ajv();
365
+ return ajv.validate(schema, data);
366
+ }
367
+ }
368
+
369
+ // Support calling without new
370
+ const ValidatorProxy = new Proxy(Validator, {
371
+ apply: function(target, thisArg, argumentsList) {
372
+ return new target(...argumentsList);
373
+ }
374
+ });
375
+
376
+ module.exports = ValidatorProxy;
@@ -0,0 +1,191 @@
1
+ /**
2
+ * ValidationError - 验证错误类
3
+ *
4
+ * 用于 validateAsync() 方法,验证失败时自动抛出
5
+ *
6
+ * @module lib/errors/ValidationError
7
+ * @version 2.1.0
8
+ */
9
+
10
+ /**
11
+ * 验证错误类
12
+ *
13
+ * @class ValidationError
14
+ * @extends Error
15
+ *
16
+ * @property {string} name - 错误名称(固定为 'ValidationError')
17
+ * @property {string} message - 错误消息(所有错误的汇总)
18
+ * @property {Array<Object>} errors - 详细错误列表
19
+ * @property {*} data - 原始验证数据
20
+ * @property {number} statusCode - HTTP 状态码(默认 400)
21
+ *
22
+ * @example
23
+ * // 创建错误
24
+ * const errors = [
25
+ * { path: '/name', message: '字段必填', keyword: 'required' }
26
+ * ];
27
+ * const error = new ValidationError(errors, inputData);
28
+ *
29
+ * @example
30
+ * // 在 Express 中使用
31
+ * app.use((error, req, res, next) => {
32
+ * if (error instanceof ValidationError) {
33
+ * return res.status(error.statusCode).json(error.toJSON());
34
+ * }
35
+ * next(error);
36
+ * });
37
+ */
38
+ class ValidationError extends Error {
39
+ /**
40
+ * 构造函数
41
+ * @param {Array<Object>} errors - 错误列表
42
+ * @param {string} errors[].path - 字段路径(如 '/name')
43
+ * @param {string} errors[].message - 错误消息
44
+ * @param {string} errors[].keyword - 验证关键字(如 'required', 'format')
45
+ * @param {Object} errors[].params - 错误参数
46
+ * @param {*} data - 原始数据
47
+ */
48
+ constructor(errors, data) {
49
+ // 生成友好的错误消息
50
+ const messages = errors.map(e => {
51
+ if (e.path) {
52
+ const field = e.path.replace(/^\//, '');
53
+ // 只有字段名非空时才添加前缀
54
+ return field ? `${field}: ${e.message}` : e.message;
55
+ }
56
+ return e.message;
57
+ }).join('; ');
58
+
59
+ // 检查是否所有错误都完全没有 path 属性(而不是空路径)
60
+ const hasNoPath = errors.every(e => e.path === undefined || e.path === null);
61
+
62
+ // 如果都是无 path 属性的错误,使用简单格式;否则使用标准格式
63
+ super(hasNoPath ? `Validation failed - ${messages}` : `Validation failed: ${messages}`);
64
+
65
+ this.name = 'ValidationError';
66
+ this.errors = errors;
67
+ this.data = data;
68
+ this.statusCode = 400;
69
+
70
+ // 保持堆栈跟踪
71
+ if (Error.captureStackTrace) {
72
+ Error.captureStackTrace(this, ValidationError);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * 转换为 JSON 格式(用于 API 响应)
78
+ *
79
+ * @returns {Object} JSON 对象
80
+ * @returns {string} return.error - 错误名称
81
+ * @returns {string} return.message - 错误消息
82
+ * @returns {number} return.statusCode - 状态码
83
+ * @returns {Array<Object>} return.details - 详细错误列表
84
+ *
85
+ * @example
86
+ * const json = error.toJSON();
87
+ * res.status(400).json(json);
88
+ * // {
89
+ * // error: 'ValidationError',
90
+ * // message: 'Validation failed: name: 字段必填',
91
+ * // statusCode: 400,
92
+ * // details: [...]
93
+ * // }
94
+ */
95
+ toJSON() {
96
+ return {
97
+ error: this.name,
98
+ message: this.message,
99
+ statusCode: this.statusCode,
100
+ details: this.errors.map(e => ({
101
+ field: e.path ? e.path.replace(/^\//, '') : null,
102
+ message: e.message,
103
+ keyword: e.keyword,
104
+ params: e.params
105
+ }))
106
+ };
107
+ }
108
+
109
+ /**
110
+ * 获取指定字段的错误
111
+ *
112
+ * @param {string} field - 字段名称(如 'name' 或 '/name')
113
+ * @returns {Object|null} 错误对象或 null
114
+ *
115
+ * @example
116
+ * const nameError = error.getFieldError('name');
117
+ * if (nameError) {
118
+ * console.log('姓名错误:', nameError.message);
119
+ * }
120
+ */
121
+ getFieldError(field) {
122
+ // 规范化字段名(移除前导斜杠)
123
+ const normalizedField = field.replace(/^\//, '');
124
+
125
+ // 查找匹配的错误(支持多种路径格式)
126
+ return this.errors.find(e => {
127
+ if (!e.path) return false;
128
+ const errorField = e.path.replace(/^\//, '');
129
+ return errorField === normalizedField;
130
+ }) || null;
131
+ }
132
+
133
+ /**
134
+ * 获取所有字段的错误映射
135
+ *
136
+ * @returns {Object} 字段错误映射 { fieldName: errorMessage }
137
+ *
138
+ * @example
139
+ * const fieldErrors = error.getFieldErrors();
140
+ * // { name: '字段必填', email: '邮箱格式错误' }
141
+ */
142
+ getFieldErrors() {
143
+ const result = {};
144
+ this.errors.forEach(e => {
145
+ if (e.path) {
146
+ const field = e.path.replace(/^\//, '');
147
+ if (field) {
148
+ result[field] = e.message;
149
+ }
150
+ }
151
+ });
152
+ return result;
153
+ }
154
+
155
+ /**
156
+ * 检查是否包含指定字段的错误
157
+ *
158
+ * @param {string} field - 字段名称
159
+ * @returns {boolean} 是否包含错误
160
+ *
161
+ * @example
162
+ * if (error.hasFieldError('name')) {
163
+ * console.log('姓名字段有错误');
164
+ * }
165
+ */
166
+ hasFieldError(field) {
167
+ return this.getFieldError(field) !== null;
168
+ }
169
+
170
+ /**
171
+ * 获取错误数量
172
+ *
173
+ * @returns {number} 错误数量
174
+ *
175
+ * @example
176
+ * console.log(`共 ${error.getErrorCount()} 个错误`);
177
+ */
178
+ getErrorCount() {
179
+ return this.errors.length;
180
+ }
181
+ }
182
+
183
+ // Support calling without new
184
+ const ValidationErrorProxy = new Proxy(ValidationError, {
185
+ apply: function(target, thisArg, argumentsList) {
186
+ return new target(...argumentsList);
187
+ }
188
+ });
189
+
190
+ module.exports = ValidationErrorProxy;
191
+