schema-dsl 2.3.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 (109) 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 +35 -0
  11. package/CHANGELOG.md +633 -0
  12. package/CONTRIBUTING.md +368 -0
  13. package/LICENSE +21 -0
  14. package/README.md +1122 -0
  15. package/STATUS.md +273 -0
  16. package/docs/FEATURE-INDEX.md +521 -0
  17. package/docs/INDEX.md +224 -0
  18. package/docs/api-reference.md +1098 -0
  19. package/docs/best-practices.md +672 -0
  20. package/docs/cache-manager.md +336 -0
  21. package/docs/design-philosophy.md +602 -0
  22. package/docs/dsl-syntax.md +654 -0
  23. package/docs/dynamic-locale.md +552 -0
  24. package/docs/error-handling.md +703 -0
  25. package/docs/export-guide.md +459 -0
  26. package/docs/faq.md +576 -0
  27. package/docs/frontend-i18n-guide.md +290 -0
  28. package/docs/i18n-user-guide.md +488 -0
  29. package/docs/label-vs-description.md +262 -0
  30. package/docs/markdown-exporter.md +398 -0
  31. package/docs/mongodb-exporter.md +279 -0
  32. package/docs/multi-type-support.md +319 -0
  33. package/docs/mysql-exporter.md +257 -0
  34. package/docs/plugin-system.md +542 -0
  35. package/docs/postgresql-exporter.md +290 -0
  36. package/docs/quick-start.md +761 -0
  37. package/docs/schema-helper.md +340 -0
  38. package/docs/schema-utils.md +492 -0
  39. package/docs/string-extensions.md +480 -0
  40. package/docs/troubleshooting.md +471 -0
  41. package/docs/type-converter.md +319 -0
  42. package/docs/type-reference.md +219 -0
  43. package/docs/validate.md +486 -0
  44. package/docs/validation-guide.md +484 -0
  45. package/examples/array-dsl-example.js +227 -0
  46. package/examples/custom-extension.js +85 -0
  47. package/examples/dsl-match-example.js +74 -0
  48. package/examples/dsl-style.js +118 -0
  49. package/examples/dynamic-locale-configuration.js +348 -0
  50. package/examples/dynamic-locale-example.js +287 -0
  51. package/examples/export-demo.js +130 -0
  52. package/examples/i18n-full-demo.js +310 -0
  53. package/examples/i18n-memory-safety.examples.js +268 -0
  54. package/examples/markdown-export.js +71 -0
  55. package/examples/middleware-usage.js +93 -0
  56. package/examples/password-reset/README.md +153 -0
  57. package/examples/password-reset/schema.js +26 -0
  58. package/examples/password-reset/test.js +101 -0
  59. package/examples/plugin-system.examples.js +205 -0
  60. package/examples/simple-example.js +122 -0
  61. package/examples/string-extensions.js +297 -0
  62. package/examples/user-registration/README.md +156 -0
  63. package/examples/user-registration/routes.js +92 -0
  64. package/examples/user-registration/schema.js +150 -0
  65. package/examples/user-registration/server.js +74 -0
  66. package/index.d.ts +1999 -0
  67. package/index.js +270 -0
  68. package/index.mjs +30 -0
  69. package/lib/adapters/DslAdapter.js +653 -0
  70. package/lib/adapters/index.js +20 -0
  71. package/lib/config/constants.js +286 -0
  72. package/lib/config/patterns/creditCard.js +9 -0
  73. package/lib/config/patterns/idCard.js +9 -0
  74. package/lib/config/patterns/index.js +8 -0
  75. package/lib/config/patterns/licensePlate.js +4 -0
  76. package/lib/config/patterns/passport.js +4 -0
  77. package/lib/config/patterns/phone.js +9 -0
  78. package/lib/config/patterns/postalCode.js +5 -0
  79. package/lib/core/CacheManager.js +376 -0
  80. package/lib/core/DslBuilder.js +740 -0
  81. package/lib/core/ErrorCodes.js +233 -0
  82. package/lib/core/ErrorFormatter.js +342 -0
  83. package/lib/core/JSONSchemaCore.js +347 -0
  84. package/lib/core/Locale.js +119 -0
  85. package/lib/core/MessageTemplate.js +89 -0
  86. package/lib/core/PluginManager.js +448 -0
  87. package/lib/core/StringExtensions.js +209 -0
  88. package/lib/core/Validator.js +316 -0
  89. package/lib/exporters/MarkdownExporter.js +420 -0
  90. package/lib/exporters/MongoDBExporter.js +162 -0
  91. package/lib/exporters/MySQLExporter.js +212 -0
  92. package/lib/exporters/PostgreSQLExporter.js +289 -0
  93. package/lib/exporters/index.js +24 -0
  94. package/lib/locales/en-US.js +65 -0
  95. package/lib/locales/es-ES.js +66 -0
  96. package/lib/locales/fr-FR.js +66 -0
  97. package/lib/locales/index.js +8 -0
  98. package/lib/locales/ja-JP.js +66 -0
  99. package/lib/locales/zh-CN.js +93 -0
  100. package/lib/utils/LRUCache.js +174 -0
  101. package/lib/utils/SchemaHelper.js +240 -0
  102. package/lib/utils/SchemaUtils.js +313 -0
  103. package/lib/utils/TypeConverter.js +245 -0
  104. package/lib/utils/index.js +13 -0
  105. package/lib/validators/CustomKeywords.js +203 -0
  106. package/lib/validators/index.js +11 -0
  107. package/package.json +70 -0
  108. package/plugins/custom-format.js +101 -0
  109. package/plugins/custom-validator.js +200 -0
@@ -0,0 +1,316 @@
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
+ try {
120
+ // 如果schema是函数,说明已编译
121
+ let validate;
122
+ if (typeof schema === 'function') {
123
+ validate = schema;
124
+ } else {
125
+ // 生成缓存键
126
+ const cacheKey = this._generateCacheKey(schema);
127
+ validate = this.compile(schema, cacheKey);
128
+ }
129
+
130
+ // 执行验证
131
+ const valid = validate(data);
132
+
133
+ // 格式化错误
134
+ const errors = valid ? [] : (
135
+ shouldFormat
136
+ ? this.errorFormatter.format(validate.errors, locale)
137
+ : validate.errors
138
+ );
139
+
140
+ return {
141
+ valid,
142
+ errors,
143
+ data // 可能被useDefaults修改过
144
+ };
145
+ } catch (error) {
146
+ return {
147
+ valid: false,
148
+ errors: [{
149
+ message: `Validation error: ${error.message}`,
150
+ path: '',
151
+ keyword: 'error'
152
+ }],
153
+ data
154
+ };
155
+ } finally {
156
+ // 恢复原语言
157
+ if (originalLocale) {
158
+ Locale.setLocale(originalLocale);
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * 快速验证(不缓存)
165
+ * @param {Object} schema - JSON Schema对象
166
+ * @param {*} data - 待验证的数据
167
+ * @returns {boolean} 是否有效
168
+ */
169
+ quickValidate(schema, data) {
170
+ try {
171
+ return this.ajv.validate(schema, data);
172
+ } catch (error) {
173
+ return false;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * 批量验证
179
+ * @param {Object} schema - JSON Schema对象
180
+ * @param {Array} dataArray - 数据数组
181
+ * @returns {Array} 验证结果数组
182
+ */
183
+ validateBatch(schema, dataArray) {
184
+ if (!Array.isArray(dataArray)) {
185
+ throw new Error('Data must be an array');
186
+ }
187
+
188
+ // 编译一次,复用验证函数
189
+ const cacheKey = this._generateCacheKey(schema);
190
+ const validate = this.compile(schema, cacheKey);
191
+
192
+ return dataArray.map(data => this.validate(validate, data));
193
+ }
194
+
195
+ /**
196
+ * 添加自定义关键字
197
+ * @param {string} keyword - 关键字名称
198
+ * @param {Object} definition - 关键字定义
199
+ * @returns {Validator} 返回this支持链式调用
200
+ */
201
+ addKeyword(keyword, definition) {
202
+ try {
203
+ this.ajv.addKeyword(keyword, definition);
204
+ this.customKeywords.set(keyword, definition);
205
+ return this;
206
+ } catch (error) {
207
+ throw new Error(`Failed to add keyword '${keyword}': ${error.message}`);
208
+ }
209
+ }
210
+
211
+ /**
212
+ * 添加自定义格式
213
+ * @param {string} name - 格式名称
214
+ * @param {Function|RegExp} validator - 验证函数或正则表达式
215
+ * @returns {Validator} 返回this支持链式调用
216
+ */
217
+ addFormat(name, validator) {
218
+ try {
219
+ this.ajv.addFormat(name, validator);
220
+ return this;
221
+ } catch (error) {
222
+ throw new Error(`Failed to add format '${name}': ${error.message}`);
223
+ }
224
+ }
225
+
226
+ /**
227
+ * 添加Schema引用
228
+ * @param {string} uri - Schema URI
229
+ * @param {Object} schema - JSON Schema对象
230
+ * @returns {Validator} 返回this支持链式调用
231
+ */
232
+ addSchema(uri, schema) {
233
+ try {
234
+ this.ajv.addSchema(schema, uri);
235
+ return this;
236
+ } catch (error) {
237
+ throw new Error(`Failed to add schema '${uri}': ${error.message}`);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * 移除Schema引用
243
+ * @param {string} uri - Schema URI
244
+ * @returns {Validator} 返回this支持链式调用
245
+ */
246
+ removeSchema(uri) {
247
+ this.ajv.removeSchema(uri);
248
+ return this;
249
+ }
250
+
251
+ /**
252
+ * 获取ajv实例(高级用法)
253
+ * @returns {Ajv} ajv实例
254
+ */
255
+ getAjv() {
256
+ return this.ajv;
257
+ }
258
+
259
+ /**
260
+ * 清空缓存
261
+ */
262
+ clearCache() {
263
+ this.cache.clear();
264
+ }
265
+
266
+ /**
267
+ * 获取缓存统计
268
+ * @returns {Object} 缓存统计信息
269
+ */
270
+ getCacheStats() {
271
+ return this.cache.stats();
272
+ }
273
+
274
+ /**
275
+ * 生成Schema的缓存键
276
+ * @private
277
+ * @param {Object} schema - JSON Schema对象
278
+ * @returns {string} 缓存键
279
+ */
280
+ _generateCacheKey(schema) {
281
+ // 简单实现:使用JSON字符串的hash
282
+ // 生产环境建议使用更高效的hash算法
283
+ return JSON.stringify(schema);
284
+ }
285
+
286
+ /**
287
+ * 静态方法:创建验证器实例
288
+ * @static
289
+ * @param {Object} options - 配置选项
290
+ * @returns {Validator} Validator实例
291
+ */
292
+ static create(options) {
293
+ return new Validator(options);
294
+ }
295
+
296
+ /**
297
+ * 静态方法:快速验证(不创建实例)
298
+ * @static
299
+ * @param {Object} schema - JSON Schema对象
300
+ * @param {*} data - 待验证的数据
301
+ * @returns {boolean} 是否有效
302
+ */
303
+ static quickValidate(schema, data) {
304
+ const ajv = new Ajv();
305
+ return ajv.validate(schema, data);
306
+ }
307
+ }
308
+
309
+ // Support calling without new
310
+ const ValidatorProxy = new Proxy(Validator, {
311
+ apply: function(target, thisArg, argumentsList) {
312
+ return new target(...argumentsList);
313
+ }
314
+ });
315
+
316
+ module.exports = ValidatorProxy;