schema-dsl 1.2.4 → 2.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 (242) hide show
  1. package/CHANGELOG.md +87 -210
  2. package/README.md +391 -2249
  3. package/dist/DslBuilder-DQDN0ZxZ.d.cts +341 -0
  4. package/dist/DslBuilder-DkLaOo9Q.d.ts +341 -0
  5. package/dist/Validator-C7GsVQOH.d.cts +192 -0
  6. package/dist/Validator-hFWKGxir.d.ts +192 -0
  7. package/dist/index.cjs +6594 -0
  8. package/dist/index.d.cts +1145 -0
  9. package/dist/index.d.ts +1145 -0
  10. package/dist/index.js +6528 -0
  11. package/dist/plugin-CIKtTMtS.d.cts +246 -0
  12. package/dist/plugin-CIKtTMtS.d.ts +246 -0
  13. package/dist/plugins/custom-format.cjs +3802 -0
  14. package/dist/plugins/custom-format.d.cts +12 -0
  15. package/dist/plugins/custom-format.d.ts +12 -0
  16. package/dist/plugins/custom-format.js +3772 -0
  17. package/dist/plugins/custom-type-example.cjs +3795 -0
  18. package/dist/plugins/custom-type-example.d.cts +8 -0
  19. package/dist/plugins/custom-type-example.d.ts +8 -0
  20. package/dist/plugins/custom-type-example.js +3765 -0
  21. package/dist/plugins/custom-validator.cjs +146 -0
  22. package/dist/plugins/custom-validator.d.cts +10 -0
  23. package/dist/plugins/custom-validator.d.ts +10 -0
  24. package/dist/plugins/custom-validator.js +121 -0
  25. package/docs/FEATURE-INDEX.md +102 -68
  26. package/docs/add-custom-locale.md +48 -35
  27. package/docs/add-keyword.md +24 -0
  28. package/docs/api-reference.md +396 -154
  29. package/docs/api.md +13 -0
  30. package/docs/best-practices-project-structure.md +19 -10
  31. package/docs/best-practices.md +93 -53
  32. package/docs/cache-manager.md +23 -15
  33. package/docs/compile.md +45 -0
  34. package/docs/conditional-api.md +40 -11
  35. package/docs/custom-extensions-guide.md +80 -152
  36. package/docs/design-philosophy.md +76 -71
  37. package/docs/doc-index.md +324 -0
  38. package/docs/dsl-syntax.md +69 -19
  39. package/docs/dynamic-locale.md +24 -14
  40. package/docs/enum.md +12 -5
  41. package/docs/error-handling.md +53 -44
  42. package/docs/export-guide.md +47 -8
  43. package/docs/export-limitations.md +27 -11
  44. package/docs/faq.md +86 -67
  45. package/docs/frontend-i18n-guide.md +26 -12
  46. package/docs/i18n-user-guide.md +60 -47
  47. package/docs/i18n.md +51 -32
  48. package/docs/index.md +48 -0
  49. package/docs/json-schema-basics.md +40 -0
  50. package/docs/label-vs-description.md +12 -3
  51. package/docs/markdown-exporter.md +15 -6
  52. package/docs/mongodb-exporter.md +11 -4
  53. package/docs/multi-language.md +26 -0
  54. package/docs/multi-type-support.md +26 -33
  55. package/docs/mysql-exporter.md +9 -2
  56. package/docs/number-operators.md +12 -5
  57. package/docs/optional-marker-guide.md +28 -23
  58. package/docs/performance-guide.md +49 -0
  59. package/docs/plugin-system.md +205 -366
  60. package/docs/plugin-type-registration.md +34 -0
  61. package/docs/postgresql-exporter.md +9 -2
  62. package/docs/public/favicon.svg +5 -0
  63. package/docs/quick-start.md +37 -363
  64. package/docs/runtime-locale-support.md +20 -9
  65. package/docs/schema-helper.md +10 -5
  66. package/docs/schema-utils-advanced-issues.md +23 -0
  67. package/docs/schema-utils-best-practices.md +20 -0
  68. package/docs/schema-utils-chaining.md +7 -0
  69. package/docs/schema-utils.md +76 -42
  70. package/docs/security-checklist.md +20 -0
  71. package/docs/string-extensions.md +17 -9
  72. package/docs/troubleshooting.md +36 -21
  73. package/docs/type-converter.md +41 -50
  74. package/docs/type-reference.md +38 -15
  75. package/docs/typescript-guide.md +53 -42
  76. package/docs/union-type-guide.md +11 -1
  77. package/docs/union-types.md +10 -3
  78. package/docs/validate-async.md +36 -25
  79. package/docs/validate-batch.md +49 -0
  80. package/docs/validate-dsl-object-support.md +33 -28
  81. package/docs/validate.md +36 -16
  82. package/docs/validation-guide.md +25 -7
  83. package/docs/validator.md +39 -0
  84. package/package.json +85 -27
  85. package/plugins/custom-format.cjs +8 -0
  86. package/plugins/custom-type-example.cjs +8 -0
  87. package/plugins/custom-validator.cjs +8 -0
  88. package/src/adapters/DslAdapter.ts +111 -0
  89. package/src/adapters/index.ts +1 -0
  90. package/src/config/constants.ts +83 -0
  91. package/src/config/index.ts +2 -0
  92. package/src/config/patterns.ts +77 -0
  93. package/src/core/CacheManager.ts +159 -0
  94. package/src/core/ConditionalBuilder.ts +382 -0
  95. package/src/core/ConditionalRuntime.ts +28 -0
  96. package/src/core/ConditionalValidator.ts +255 -0
  97. package/src/core/DslBuilder.ts +677 -0
  98. package/src/core/ErrorCodes.ts +38 -0
  99. package/src/core/ErrorFormatter.ts +271 -0
  100. package/src/core/JSONSchemaCore.ts +65 -0
  101. package/src/core/Locale.ts +187 -0
  102. package/src/core/MessageTemplate.ts +42 -0
  103. package/src/core/ObjectDslBuilder.ts +64 -0
  104. package/src/core/PluginManager.ts +326 -0
  105. package/src/core/StringExtensions.ts +140 -0
  106. package/src/core/TemplateEngine.ts +44 -0
  107. package/src/core/Validator.ts +448 -0
  108. package/src/errors/I18nError.ts +159 -0
  109. package/src/errors/ValidationError.ts +105 -0
  110. package/src/exporters/BaseExporter.ts +60 -0
  111. package/src/exporters/MarkdownExporter.ts +305 -0
  112. package/src/exporters/MongoDBExporter.ts +126 -0
  113. package/src/exporters/MySQLExporter.ts +155 -0
  114. package/src/exporters/PostgreSQLExporter.ts +222 -0
  115. package/src/exporters/index.ts +18 -0
  116. package/src/index.ts +633 -0
  117. package/{lib/locales/en-US.js → src/locales/en-US.ts} +21 -37
  118. package/{lib/locales/es-ES.js → src/locales/es-ES.ts} +63 -16
  119. package/{lib/locales/fr-FR.js → src/locales/fr-FR.ts} +74 -27
  120. package/src/locales/index.ts +103 -0
  121. package/{lib/locales/ja-JP.js → src/locales/ja-JP.ts} +59 -17
  122. package/src/locales/types.ts +156 -0
  123. package/{lib/locales/zh-CN.js → src/locales/zh-CN.ts} +21 -38
  124. package/src/parser/ConstraintParser.ts +101 -0
  125. package/src/parser/DslParser.ts +470 -0
  126. package/src/parser/SchemaCompiler.ts +66 -0
  127. package/src/parser/TypeRegistry.ts +250 -0
  128. package/src/parser/index.ts +6 -0
  129. package/src/plugins/custom-format.ts +126 -0
  130. package/src/plugins/custom-type-example.ts +108 -0
  131. package/src/plugins/custom-validator.ts +140 -0
  132. package/src/types/conditional.ts +28 -0
  133. package/src/types/config.ts +59 -0
  134. package/src/types/dsl.ts +131 -0
  135. package/src/types/error.ts +60 -0
  136. package/src/types/index.ts +17 -0
  137. package/src/types/infer.ts +128 -0
  138. package/src/types/plugin.ts +58 -0
  139. package/src/types/safe-regex.d.ts +9 -0
  140. package/src/types/schema.ts +66 -0
  141. package/src/types/validate.ts +71 -0
  142. package/src/utils/SchemaHelper.ts +196 -0
  143. package/src/utils/SchemaUtils.ts +346 -0
  144. package/src/utils/TypeConverter.ts +215 -0
  145. package/src/utils/index.ts +10 -0
  146. package/src/validators/CustomKeywords.ts +477 -0
  147. package/.eslintignore +0 -11
  148. package/.eslintrc.json +0 -27
  149. package/CONTRIBUTING.md +0 -368
  150. package/STATUS.md +0 -491
  151. package/changelogs/v1.0.0.md +0 -328
  152. package/changelogs/v1.0.9.md +0 -367
  153. package/changelogs/v1.1.0.md +0 -389
  154. package/changelogs/v1.1.1.md +0 -308
  155. package/changelogs/v1.1.2.md +0 -183
  156. package/changelogs/v1.1.3.md +0 -161
  157. package/changelogs/v1.1.4.md +0 -432
  158. package/changelogs/v1.1.5.md +0 -493
  159. package/changelogs/v1.1.6.md +0 -211
  160. package/changelogs/v1.1.8.md +0 -376
  161. package/changelogs/v1.2.3.md +0 -124
  162. package/docs/INDEX.md +0 -252
  163. package/docs/issues-resolved-summary.md +0 -196
  164. package/docs/performance-benchmark-report.md +0 -179
  165. package/docs/performance-quick-reference.md +0 -123
  166. package/docs/user-questions-answered.md +0 -353
  167. package/docs/validation-rules-v1.0.2.md +0 -1608
  168. package/examples/README.md +0 -81
  169. package/examples/array-dsl-example.js +0 -227
  170. package/examples/conditional-example.js +0 -288
  171. package/examples/conditional-non-object.js +0 -129
  172. package/examples/conditional-validate-example.js +0 -321
  173. package/examples/custom-extension.js +0 -85
  174. package/examples/dsl-match-example.js +0 -74
  175. package/examples/dsl-style.js +0 -118
  176. package/examples/dynamic-locale-configuration.js +0 -348
  177. package/examples/dynamic-locale-example.js +0 -287
  178. package/examples/enum.examples.js +0 -324
  179. package/examples/export-demo.js +0 -130
  180. package/examples/express-integration.js +0 -376
  181. package/examples/i18n-error-handling-complete.js +0 -381
  182. package/examples/i18n-error-handling-quickstart.md +0 -0
  183. package/examples/i18n-error.examples.js +0 -181
  184. package/examples/i18n-full-demo.js +0 -301
  185. package/examples/i18n-memory-safety.examples.js +0 -268
  186. package/examples/markdown-export.js +0 -71
  187. package/examples/middleware-usage.js +0 -93
  188. package/examples/new-features-comparison.js +0 -315
  189. package/examples/password-reset/README.md +0 -153
  190. package/examples/password-reset/schema.js +0 -26
  191. package/examples/password-reset/test.js +0 -101
  192. package/examples/plugin-system.examples.js +0 -205
  193. package/examples/schema-utils-chaining.examples.js +0 -250
  194. package/examples/simple-example.js +0 -122
  195. package/examples/slug.examples.js +0 -179
  196. package/examples/string-extensions.js +0 -297
  197. package/examples/union-type-example.js +0 -127
  198. package/examples/union-types-example.js +0 -77
  199. package/examples/user-registration/README.md +0 -156
  200. package/examples/user-registration/routes.js +0 -92
  201. package/examples/user-registration/schema.js +0 -150
  202. package/examples/user-registration/server.js +0 -74
  203. package/index.d.ts +0 -3540
  204. package/index.js +0 -457
  205. package/index.mjs +0 -60
  206. package/lib/adapters/DslAdapter.js +0 -871
  207. package/lib/adapters/index.js +0 -20
  208. package/lib/config/constants.js +0 -286
  209. package/lib/config/patterns/common.js +0 -47
  210. package/lib/config/patterns/creditCard.js +0 -9
  211. package/lib/config/patterns/idCard.js +0 -9
  212. package/lib/config/patterns/index.js +0 -9
  213. package/lib/config/patterns/licensePlate.js +0 -4
  214. package/lib/config/patterns/passport.js +0 -4
  215. package/lib/config/patterns/phone.js +0 -9
  216. package/lib/config/patterns/postalCode.js +0 -5
  217. package/lib/core/CacheManager.js +0 -376
  218. package/lib/core/ConditionalBuilder.js +0 -503
  219. package/lib/core/DslBuilder.js +0 -1400
  220. package/lib/core/ErrorCodes.js +0 -233
  221. package/lib/core/ErrorFormatter.js +0 -445
  222. package/lib/core/JSONSchemaCore.js +0 -347
  223. package/lib/core/Locale.js +0 -130
  224. package/lib/core/MessageTemplate.js +0 -98
  225. package/lib/core/PluginManager.js +0 -448
  226. package/lib/core/StringExtensions.js +0 -240
  227. package/lib/core/Validator.js +0 -654
  228. package/lib/errors/I18nError.js +0 -328
  229. package/lib/errors/ValidationError.js +0 -191
  230. package/lib/exporters/MarkdownExporter.js +0 -420
  231. package/lib/exporters/MongoDBExporter.js +0 -162
  232. package/lib/exporters/MySQLExporter.js +0 -212
  233. package/lib/exporters/PostgreSQLExporter.js +0 -289
  234. package/lib/exporters/index.js +0 -24
  235. package/lib/locales/index.js +0 -8
  236. package/lib/utils/LRUCache.js +0 -174
  237. package/lib/utils/SchemaHelper.js +0 -240
  238. package/lib/utils/SchemaUtils.js +0 -445
  239. package/lib/utils/TypeConverter.js +0 -245
  240. package/lib/utils/index.js +0 -13
  241. package/lib/validators/CustomKeywords.js +0 -616
  242. package/lib/validators/index.js +0 -11
@@ -31,13 +31,18 @@ try {
31
31
  ---
32
32
 
33
33
  ## 基础用法
34
+
35
+ ```javascript
36
+ try {
37
+ await validateAsync(userSchema, {
34
38
  name: '',
35
39
  email: 'invalid'
36
40
  });
37
41
  } catch (error) {
38
42
  console.log(error instanceof ValidationError); // true
39
43
  console.log(error.getFieldErrors());
40
- // { name: '长度必须大于等于1', email: '邮箱格式错误', age: '字段必填' }
44
+ // { email: '邮箱格式错误' }
45
+ // `string!` 只表示字段必填;若要限制空字符串,需要叠加长度约束。
41
46
  }
42
47
  ```
43
48
 
@@ -51,7 +56,7 @@ try {
51
56
  class ValidationError extends Error {
52
57
  name: 'ValidationError' // 错误名称
53
58
  message: string // 友好的错误消息
54
- errors: Array<Object> // 原始错误列表
59
+ errors: Array<Object> // 格式化后的错误项列表(path/message/keyword/...)
55
60
  data: any // 原始输入数据
56
61
  statusCode: 400 // HTTP 状态码
57
62
  }
@@ -185,10 +190,9 @@ const fullUserSchema = dsl({
185
190
  updatedAt: 'date'
186
191
  });
187
192
 
188
- // POST /users - 创建用户(严格模式)
193
+ // POST /users - 创建用户(排除系统字段)
189
194
  const createSchema = SchemaUtils
190
- .omit(fullUserSchema, ['id', 'createdAt', 'updatedAt'])
191
- .strict();
195
+ .omit(fullUserSchema, ['id', 'createdAt', 'updatedAt']);
192
196
 
193
197
  app.post('/users', async (req, res, next) => {
194
198
  try {
@@ -204,17 +208,17 @@ app.post('/users', async (req, res, next) => {
204
208
  }
205
209
  });
206
210
 
207
- // GET /users/:id - 查询用户(移除敏感字段)
211
+ // GET /users/:id - 查询用户(业务层显式移除敏感字段)
208
212
  const publicSchema = SchemaUtils
209
- .omit(fullUserSchema, ['password'])
210
- .clean();
213
+ .pick(fullUserSchema, ['id', 'name', 'email', 'age', 'createdAt', 'updatedAt']);
211
214
 
212
215
  app.get('/users/:id', async (req, res, next) => {
213
216
  try {
214
217
  const user = await db.users.findById(req.params.id);
218
+ const { password, ...publicUser } = user;
215
219
  const { validate } = require('schema-dsl');
216
- const result = validate(publicSchema, user);
217
- res.json(result.data); // 自动移除 password
220
+ const result = validate(publicSchema, publicUser);
221
+ res.json(result.data);
218
222
  } catch (error) {
219
223
  next(error);
220
224
  }
@@ -223,8 +227,7 @@ app.get('/users/:id', async (req, res, next) => {
223
227
  // PATCH /users/:id - 更新用户(部分验证)
224
228
  const updateSchema = SchemaUtils
225
229
  .pick(fullUserSchema, ['name', 'age'])
226
- .partial()
227
- .loose();
230
+ .partial();
228
231
 
229
232
  app.patch('/users/:id', async (req, res, next) => {
230
233
  try {
@@ -239,10 +242,9 @@ app.patch('/users/:id', async (req, res, next) => {
239
242
  }
240
243
  });
241
244
 
242
- // PUT /users/:id - 替换用户(严格模式)
245
+ // PUT /users/:id - 替换用户(排除系统字段)
243
246
  const replaceSchema = SchemaUtils
244
- .omit(fullUserSchema, ['id', 'createdAt', 'updatedAt'])
245
- .strict();
247
+ .omit(fullUserSchema, ['id', 'createdAt', 'updatedAt']);
246
248
 
247
249
  app.put('/users/:id', async (req, res, next) => {
248
250
  try {
@@ -258,6 +260,8 @@ app.put('/users/:id', async (req, res, next) => {
258
260
  });
259
261
  ```
260
262
 
263
+ > ⚠️ `SchemaUtils` 当前只负责生成派生 Schema(如 `pick()` / `omit()` / `partial()` / `extend()`),不会自动删除运行时对象上的额外字段。返回公开响应时,请先在业务层显式投影数据,再用 `validate()` 校验投影结果。
264
+
261
265
  ---
262
266
 
263
267
  ## 错误处理
@@ -349,10 +353,9 @@ const baseUserSchema = dsl({
349
353
  updatedAt: 'date'
350
354
  });
351
355
 
352
- // 注册 Schema(排除系统字段,严格模式)
356
+ // 注册 Schema(排除系统字段)
353
357
  const registerSchema = SchemaUtils
354
- .omit(baseUserSchema, ['id', 'createdAt', 'updatedAt'])
355
- .strict();
358
+ .omit(baseUserSchema, ['id', 'createdAt', 'updatedAt']);
356
359
 
357
360
  // 注册接口
358
361
  app.post('/register', async (req, res, next) => {
@@ -380,13 +383,14 @@ app.post('/register', async (req, res, next) => {
380
383
  updatedAt: new Date()
381
384
  });
382
385
 
383
- // 5. 返回公开信息(移除密码)
386
+ // 5. 返回公开信息(先显式投影,再按公开 Schema 校验)
384
387
  const publicSchema = SchemaUtils
385
- .omit(baseUserSchema, ['password'])
386
- .clean();
388
+ .pick(baseUserSchema, ['id', 'username', 'email', 'age', 'createdAt', 'updatedAt']);
389
+
390
+ const { password, ...publicUser } = user;
387
391
 
388
392
  const { validate } = require('schema-dsl');
389
- const result = validate(publicSchema, user);
393
+ const result = validate(publicSchema, publicUser);
390
394
 
391
395
  res.status(201).json({
392
396
  success: true,
@@ -452,7 +456,7 @@ new ValidationError(errors, data)
452
456
  **属性**:
453
457
  - `name: 'ValidationError'`
454
458
  - `message: string` - 友好的错误消息
455
- - `errors: Array<Object>` - 原始错误列表
459
+ - `errors: Array<Object>` - 格式化后的错误项列表(如 `path`、`message`、`keyword`、`params`)
456
460
  - `data: any` - 原始输入数据
457
461
  - `statusCode: 400` - HTTP 状态码
458
462
 
@@ -470,11 +474,18 @@ new ValidationError(errors, data)
470
474
  - [SchemaUtils 链式调用](schema-utils-chaining.md) - Schema 复用简化方法
471
475
  - [validate.md](validate.md) - 传统同步验证方法
472
476
  - [error-handling.md](error-handling.md) - 错误处理指南
473
- - [Express 示例](../examples/express-integration.js) - 完整 Express 集成示例
477
+ - [validate-async 完整示例](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validate-async.ts) - 顶层 `validateAsync()`、`ValidationError` 与业务层异步检查示例
478
+
479
+ ---
480
+
481
+ ## 对应示例文件
482
+
483
+ **示例入口**: [validate-async.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validate-async.ts)
484
+ **说明**: 覆盖 `validateAsync()` 的成功路径、`ValidationError` 捕获,以及“结构校验通过后再做业务层异步检查”的推荐方式。
474
485
 
475
486
  ---
476
487
 
477
488
  **版本**: v1.0.4
478
489
  **更新日期**: 2025-12-29
479
- **作者**: SchemaI-DSL Team
490
+ **作者**: schema-dsl Team
480
491
 
@@ -0,0 +1,49 @@
1
+ # validateBatch 方法
2
+
3
+ `Validator.validateBatch(schema, dataArray)` 用同一个已编译 Schema 验证多条数据。
4
+
5
+ ## 方法签名
6
+
7
+ ```javascript
8
+ validator.validateBatch(schema, dataArray)
9
+ ```
10
+
11
+ ## 返回值
12
+
13
+ 返回一个数组,每一项都与单次 `validator.validate()` 的返回结构一致:
14
+
15
+ - `valid` - 是否通过
16
+ - `data` - 验证后的数据(通过时)
17
+ - `errors` - 错误列表(失败时)
18
+
19
+ ```javascript
20
+ [
21
+ { valid: true, data: { email: 'a@example.com' }, errors: [] },
22
+ { valid: false, data: { email: 'bad' }, errors: [/* ... */] }
23
+ ]
24
+ ```
25
+
26
+ ```javascript
27
+ const { Validator, dsl } = require('schema-dsl');
28
+ const validator = new Validator();
29
+ const schema = dsl({ email: 'email!' });
30
+ const results = validator.validateBatch(schema, [
31
+ { email: 'a@example.com' },
32
+ { email: 'bad' }
33
+ ]);
34
+ console.log(results);
35
+ ```
36
+
37
+ ## 适用场景
38
+
39
+ - 导入数据前的批量校验
40
+ - 同一 schema 下的大量记录预检查
41
+ - 希望只编译一次 schema,再对整批数据复用
42
+
43
+ ---
44
+
45
+ ## 对应示例文件
46
+
47
+ **示例入口**: [validate-batch.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validate-batch.ts)
48
+ **说明**: 覆盖 `Validator.validateBatch()` 的单次编译、多条数据复用,以及失败项错误输出。
49
+
@@ -6,13 +6,13 @@
6
6
 
7
7
  ## 答案
8
8
 
9
- **现在可以了!** 🎉 v1.1.7 开始,`validate()` 和 `validateAsync()` 都支持直接传入 DSL 对象。
9
+ **现在可以了!** 🎉 当前 TypeScript 重构版中,顶层 `validate()` 和 `validateAsync()` 都支持直接传入 DSL 对象。
10
10
 
11
11
  ---
12
12
 
13
13
  ## 支持的三种方式
14
14
 
15
- ### 方式1:传入 DSL 对象(✅ v1.1.7 新增)
15
+ ### 方式1:传入 DSL 对象(✅ 当前版本支持)
16
16
 
17
17
  ```javascript
18
18
  const { validate } = require('schema-dsl');
@@ -96,17 +96,13 @@ const result = validate(
96
96
 
97
97
  ### 自动检测逻辑
98
98
 
99
- `validate()` 函数会自动检测传入的 schema 类型:
99
+ 顶层 `validate()` / `validateAsync()` 会先归一化传入的 schema
100
100
 
101
101
  ```javascript
102
102
  function validate(schema, data, options = {}) {
103
- // 自动检测并转换 DSL 对象
104
- if (_isDslObject(schema)) {
105
- schema = DslAdapter.parseObject(schema);
106
- }
107
-
108
- const validator = new Validator(options);
109
- return validator.validate(schema, data, options);
103
+ const normalizedSchema = _normalizeSchemaInput(schema);
104
+ const validator = getDefaultValidator();
105
+ return validator.validate(normalizedSchema, data, options);
110
106
  }
111
107
  ```
112
108
 
@@ -128,9 +124,9 @@ function validate(schema, data, options = {}) {
128
124
 
129
125
  ## 为什么之前必须是 schema?
130
126
 
131
- ### 历史原因
127
+ ### 背景
132
128
 
133
- v1.1.7 之前,`validate()` 不会自动转换 DSL 对象:
129
+ 早期实现中,顶层 `validate()` 不会自动转换 DSL 对象:
134
130
 
135
131
  ```javascript
136
132
  // ❌ v1.1.6 及之前版本会失败
@@ -143,9 +139,9 @@ const result = validate(
143
139
 
144
140
  **原因**:`validate()` 会把 DSL 对象当作标准 JSON Schema,而 `"email!"` 不是有效的 JSON Schema 关键字。
145
141
 
146
- ### 解决方案
142
+ ### 当前方案
147
143
 
148
- v1.1.7 添加了自动检测和转换逻辑:
144
+ 当前 TypeScript 重构版已补齐自动检测和转换逻辑:
149
145
 
150
146
  1. **检测 DSL 对象**:识别对象中的 DSL 字符串
151
147
  2. **自动转换**:调用 `DslAdapter.parseObject()` 转换为 JSON Schema
@@ -191,7 +187,7 @@ module.exports = {
191
187
  .pattern(/^[a-zA-Z0-9_]+$/)
192
188
  .messages({ 'string.pattern': '只能包含字母、数字和下划线' }),
193
189
  email: 'email!',
194
- password: 'password:strong!',
190
+ password: dsl('string!').password('strong'),
195
191
  age: 'number:18-120'
196
192
  }),
197
193
 
@@ -326,13 +322,13 @@ const result = validate(
326
322
  ## 完整示例
327
323
 
328
324
  ```javascript
329
- const { validate, validateAsync } = require('schema-dsl');
325
+ const { dsl, validate, validateAsync } = require('schema-dsl');
330
326
 
331
327
  // 示例1:同步验证
332
328
  const result = validate(
333
329
  {
334
330
  email: 'email!',
335
- password: 'password:strong!',
331
+ password: dsl('string!').password('strong'),
336
332
  age: 'number:18-120',
337
333
  username: 'string:3-32!'
338
334
  },
@@ -372,7 +368,7 @@ if (result.valid) {
372
368
 
373
369
  **答:现在不必了!**
374
370
 
375
- - ✅ v1.1.7 开始支持直接传入 DSL 对象
371
+ - ✅ 当前版本支持直接传入 DSL 对象
376
372
  - ✅ 自动检测并转换,无需手动包裹
377
373
  - ✅ 完全向后兼容,不影响原有功能
378
374
  - ✅ 同时支持 JSON Schema、DslBuilder、DSL 对象三种方式
@@ -423,7 +419,7 @@ const result = validate(
423
419
  每次调用 `validate()` 时,DSL 对象都会被转换为 JSON Schema:
424
420
 
425
421
  ```javascript
426
- // ❌ 性能较差:每次请求都重复转换(~3.4秒/1000次)
422
+ // ❌ 性能较差:每次请求都重复转换
427
423
  app.post('/api/user', (req, res) => {
428
424
  const result = validate(
429
425
  { email: 'email!', age: 'number!' }, // ❌ 每次请求都会执行 DSL → JSON Schema 转换
@@ -431,7 +427,7 @@ app.post('/api/user', (req, res) => {
431
427
  );
432
428
  });
433
429
 
434
- // ✅ 性能最优:项目启动时转换一次,复用 schema(~3.3秒/1000次)
430
+ // ✅ 性能最优:项目启动时转换一次,复用 schema
435
431
  const userSchema = dsl({ email: 'email!', age: 'number!' }); // ✅ 启动时转换一次
436
432
 
437
433
  app.post('/api/user', (req, res) => {
@@ -439,6 +435,8 @@ app.post('/api/user', (req, res) => {
439
435
  });
440
436
  ```
441
437
 
438
+ > ℹ️ 具体耗时取决于机器性能、Node 版本、schema 复杂度和命中率;这里强调的是“预先转换后复用通常显著快于每次请求都重新转换”的相对结论,而不是固定秒数。
439
+
442
440
  **性能差异**:约 3-5%(对于简单 schema)
443
441
 
444
442
  **✅ 您的理解完全正确!**
@@ -456,7 +454,7 @@ const userSchemas = {
456
454
  .pattern(/^[a-zA-Z0-9_]+$/)
457
455
  .messages({ 'string.pattern': '只能包含字母、数字和下划线' }),
458
456
  email: 'email!',
459
- password: 'password:strong!',
457
+ password: dsl('string!').password('strong'),
460
458
  age: 'number:18-120'
461
459
  }),
462
460
 
@@ -498,7 +496,7 @@ app.post('/api/login', (req, res) => {
498
496
  | **原型开发** | ✅ 直接用 DSL 对象 | 快速迭代,无需在意性能 |
499
497
  | **测试代码** | ✅ 直接用 DSL 对象 | 简洁清晰,易于维护 |
500
498
 
501
- ### Q3: 为什么之前不这样设计?
499
+ ### Q3: 为什么复杂场景仍然建议先用 `dsl()` 转换?
502
500
 
503
501
  **历史原因**:
504
502
 
@@ -512,14 +510,14 @@ app.post('/api/login', (req, res) => {
512
510
  ```
513
511
  这种设计让每个步骤的职责更清晰。
514
512
 
515
- 2. **避免隐式转换**(最小惊喜原则)
513
+ 2. **避免在高频路径里滥用隐式转换**(最小惊喜原则)
516
514
  ```javascript
517
515
  // 用户传入什么,就是什么
518
516
  validate(jsonSchema, data); // JSON Schema
519
517
  validate(dslBuilder, data); // DslBuilder
520
518
 
521
- // 之前不支持隐式转换
522
- validate({ email: 'email!' }, data); // 会被当作 JSON Schema
519
+ // ⚠️ 当前虽然支持隐式转换,但高频场景仍建议预先转换后复用
520
+ validate({ email: 'email!' }, data);
523
521
  ```
524
522
 
525
523
  3. **类型安全考虑**(TypeScript)
@@ -545,7 +543,7 @@ app.post('/api/login', (req, res) => {
545
543
  }
546
544
  ```
547
545
 
548
- **为什么现在改变了?**
546
+ **为什么当前版本要补齐这个能力?**
549
547
 
550
548
  1. **用户反馈**:很多用户期望更简洁的 API
551
549
  2. **智能检测**:通过 `_isDslObject()` 准确区分 DSL 对象和 JSON Schema
@@ -557,8 +555,8 @@ app.post('/api/login', (req, res) => {
557
555
 
558
556
  | 设计方案 | 优点 | 缺点 |
559
557
  |---------|------|------|
560
- | **显式转换**(v1.1.6) | 职责清晰、类型安全、性能最优 | 代码冗长、学习成本高 |
561
- | **自动转换**(v1.1.7) | 简洁直观、学习成本低 | 隐式行为、可能误用 |
558
+ | **显式转换** | 职责清晰、类型安全、性能最优 | 代码稍长 |
559
+ | **自动转换**(当前顶层便捷函数) | 简洁直观、学习成本低 | 在高频路径里有额外转换开销 |
562
560
 
563
561
  **最终选择**:两者都支持,让用户自由选择!
564
562
 
@@ -571,3 +569,10 @@ const schema = dsl({ email: 'email!' });
571
569
  validate(schema, data);
572
570
  ```
573
571
 
572
+ ---
573
+
574
+ ## 对应示例文件
575
+
576
+ **示例入口**: [validate-dsl-object-support.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validate-dsl-object-support.ts)
577
+ **说明**: 覆盖直接传入 DSL 对象、混合使用 `DslBuilder` 与 DSL 字符串,以及顶层 `validate()` / `validateAsync()` 的真实支持边界。
578
+
package/docs/validate.md CHANGED
@@ -55,11 +55,13 @@ validator.validate(schema, data, options = {})
55
55
  ```javascript
56
56
  {
57
57
  valid: Boolean, // 是否有效
58
- errors: Array, // 错误列表
59
- data: Any // 验证后的数据(可能被 useDefaults 修改)
58
+ errors: Array, // 成功时为空数组,失败时为错误列表
59
+ data: Any // 当前实现会返回本次验证数据(可能被 useDefaults 修改)
60
60
  }
61
61
  ```
62
62
 
63
+ 当前实现中,`data` 与 `errors` 都会随结果一并返回:成功时 `errors` 为空数组,验证失败时仍会保留 `data` 以便排查输入。
64
+
63
65
  ---
64
66
 
65
67
  ## 参数详解
@@ -75,12 +77,25 @@ JSON Schema 对象,支持 JSON Schema Draft 7 标准。
75
77
 
76
78
  ### options 对象属性
77
79
 
80
+ `validator.validate(schema, data, options)` 当前按本次调用实际读取以下参数:
81
+
78
82
  | 参数 | 类型 | 必填 | 默认值 | 说明 |
79
83
  |------|------|------|--------|------|
80
84
  | `format` | Boolean | 否 | `true` | 是否格式化错误信息 |
85
+ | `locale` | String | 否 | — | 动态指定语言,如 `'zh-CN'`、`'en-US'`、`'ja-JP'` |
86
+ | `messages` | Object | 否 | — | 自定义错误消息覆盖 |
87
+
88
+ ### 相关配置入口
89
+
90
+ 以下能力**不属于** `validator.validate(schema, data, options)` 的逐次调用参数:
91
+
92
+ | 能力 | 正确入口 | 说明 |
93
+ |------|----------|------|
94
+ | `allErrors` / `useDefaults` / `coerceTypes` / `removeAdditional` / `cache` | `new Validator(options)` | 这些配置在创建 `Validator` 实例时注入到底层 AJV / 缓存层 |
95
+ | `strict` | Schema 本身 | 如果需要禁止额外字段,请在 schema 层使用 `DslBuilder.strict()` 或等价的 schema 级约束 |
96
+ | `coerce` | 顶层 `validate()` / `validateAsync()` 便捷函数 | 顶层 helper 默认会做字符串 → 数字 / 布尔值的便捷转换,传 `{ coerce: false }` 可关闭 |
81
97
 
82
- **图例说明**:
83
- - ✅ **标准功能**: 该参数来自 JSON Schema 或 ajv 标准
98
+ 如果你需要在**单次调用**中覆盖错误输出,请使用上表中的 `format`、`locale`、`messages`;如果你需要调整验证器行为,请优先在 `Validator` 构造阶段配置。
84
99
 
85
100
  ---
86
101
 
@@ -97,7 +112,7 @@ result.valid === false // 验证失败
97
112
 
98
113
  ### errors (Array)
99
114
 
100
- 验证错误列表,当 `valid` 为 `false` 时包含详细错误信息。
115
+ 验证错误列表。当前实现成功时返回空数组,验证失败时包含详细错误信息。
101
116
 
102
117
  **错误对象结构**:
103
118
  ```javascript
@@ -111,7 +126,7 @@ result.valid === false // 验证失败
111
126
 
112
127
  ### data (Any)
113
128
 
114
- 验证后的数据。如果 Validator 配置了 `useDefaults: true`,则会应用 Schema 中定义的默认值。
129
+ 验证后的数据。当前实现即使在验证失败时也会保留本次验证数据,便于排查输入;如果 Validator 配置了 `useDefaults: true`,则也可能反映 Schema 中应用后的默认值。
115
130
 
116
131
  ---
117
132
 
@@ -151,9 +166,10 @@ const result = validator.validate(schema, {
151
166
 
152
167
  console.log(result.valid); // false
153
168
  console.log(result.errors);
169
+ // 当前实现返回格式化后的错误列表:
154
170
  // [
155
- // { path: '', message: "must have required property 'name'" },
156
- // { path: 'age', message: 'must be number' }
171
+ // { path: 'name', message: 'name不能为空' },
172
+ // { path: 'age', message: 'age应该是 number 类型' }
157
173
  // ]
158
174
  ```
159
175
 
@@ -348,8 +364,7 @@ processData(result.data);
348
364
  const result = validator.validate(schema, data);
349
365
 
350
366
  if (!result.valid) {
351
- const error = new ValidationError('数据验证失败');
352
- error.errors = result.errors;
367
+ const error = new ValidationError(result.errors, data);
353
368
  throw error;
354
369
  }
355
370
  ```
@@ -394,18 +409,23 @@ app.post('/api/users', (req, res) => {
394
409
  ### 建议 3: 使用缓存
395
410
 
396
411
  ```javascript
397
- // 使用缓存键
398
- const result = validator.validate(
399
- schema,
400
- data,
401
- { cacheKey: 'user-schema' } // 自动缓存编译结果
402
- );
412
+ // 显式编译并复用缓存键
413
+ const validateUser = validator.compile(schema, 'user-schema');
414
+
415
+ console.log(validateUser(data));
403
416
  ```
404
417
 
405
418
  ---
406
419
 
407
420
  ## 常见问题
408
421
 
422
+ ---
423
+
424
+ ## 对应示例文件
425
+
426
+ **示例入口**: [validate.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validate.ts)
427
+ **说明**: 覆盖顶层 `validate()` 的成功/失败路径、默认类型转换,以及关闭 `coerce` 后的行为差异。
428
+
409
429
  ### Q1: 如何验证可选字段?
410
430
 
411
431
  不在 `required` 数组中的字段自动为可选:
@@ -158,9 +158,9 @@ const dataList = [
158
158
 
159
159
  const results = validator.validateBatch(schema, dataList);
160
160
  // [
161
- // { valid: true, errors: [] },
162
- // { valid: false, errors: [...] },
163
- // { valid: false, errors: [...] }
161
+ // { valid: true, data: {...}, errors: [] },
162
+ // { valid: false, data: {...}, errors: [...] },
163
+ // { valid: false, data: {...}, errors: [...] }
164
164
  // ]
165
165
  ```
166
166
 
@@ -275,8 +275,9 @@ const validator = new Validator({ allErrors: true });
275
275
  ### 4. 监控性能
276
276
 
277
277
  ```javascript
278
+ console.time('schema-dsl.validate');
278
279
  const result = validate(schema, data);
279
- console.log(`验证耗时: ${result.performance?.duration}ms`);
280
+ console.timeEnd('schema-dsl.validate');
280
281
  ```
281
282
 
282
283
  ---
@@ -437,18 +438,35 @@ const quickSchema = dsl({
437
438
  // 完整验证(详细)
438
439
  const fullSchema = dsl({
439
440
  username: 'string:3-32!'.pattern(/^[a-z]+$/),
440
- email: 'email!'.custom(async (v) => checkEmailUnique(v))
441
+ email: 'email!'
441
442
  });
442
443
 
443
444
  // 先快速验证,再完整验证
444
- function validateWithFallback(data) {
445
+ async function validateWithFallback(data) {
445
446
  const quick = validate(quickSchema, data);
446
447
  if (!quick.valid) return quick;
447
448
 
448
- return validate(fullSchema, data);
449
+ const full = validate(fullSchema, data);
450
+ if (!full.valid) return full;
451
+
452
+ if (await checkEmailUnique(data.email)) {
453
+ return {
454
+ valid: false,
455
+ errors: [{ field: 'email', keyword: 'business', message: '邮箱已被占用' }]
456
+ };
457
+ }
458
+
459
+ return full;
449
460
  }
450
461
  ```
451
462
 
463
+ ---
464
+
465
+ ## 对应示例文件
466
+
467
+ **示例入口**: [validation-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validation-guide.ts)
468
+ **说明**: 覆盖推荐的验证流程:定义可复用 schema、格式化错误、预编译复用以及批量验证。
469
+
452
470
  ### 5. 测试验证逻辑
453
471
 
454
472
  ```javascript
@@ -0,0 +1,39 @@
1
+ # Validator 类概述
2
+
3
+ `Validator` 是对 AJV 的封装,提供编译缓存、错误格式化、自定义关键字和批量验证能力。
4
+
5
+ ```javascript
6
+ const { Validator, dsl } = require('schema-dsl');
7
+ const validator = new Validator();
8
+ const schema = dsl({ email: 'email!' });
9
+ console.log(validator.validate(schema, { email: 'test@example.com' }));
10
+ ```
11
+
12
+ ## 缓存配置
13
+
14
+ `Validator` 构造器支持两种缓存写法:
15
+
16
+ ```javascript
17
+ new Validator({ cache: true }); // 使用默认缓存配置
18
+ new Validator({ cache: false }); // 关闭缓存
19
+
20
+ new Validator({
21
+ cache: {
22
+ enabled: true,
23
+ maxSize: 500,
24
+ ttl: 60 * 60 * 1000
25
+ }
26
+ });
27
+ ```
28
+
29
+ > 如果你希望直接传入 DSL 对象(例如 `validate({ email: 'email!' }, data)`),请使用顶层便捷函数 `validate()` / `validateAsync()`;`Validator` 实例方法仍建议接收标准 JSON Schema 或 `dsl({...})` 的转换结果。
30
+
31
+ 相关方法:`compile()`、`validate()`、`validateAsync()`、`validateBatch()`、`addKeyword()`、`addFormat()`。
32
+
33
+ ---
34
+
35
+ ## 对应示例文件
36
+
37
+ **示例入口**: [validator.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/validator.ts)
38
+ **说明**: 覆盖 `new Validator()` 的常见配置、单次验证、编译缓存命中和 `validateBatch()` 复用路径。
39
+