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
package/docs/faq.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 常见问题解答 (FAQ)
2
2
 
3
- > **更新时间**: 2025-12-25
3
+ > **更新时间**: 2026-05-08
4
4
 
5
5
 
6
6
  ---
@@ -20,12 +20,12 @@
20
20
 
21
21
  ## 基础问题
22
22
 
23
- ### Q: SchemaI-DSL 和 Joi、Yup 有什么区别?
23
+ ### Q: schema-dsl 和 Joi、Yup 有什么区别?
24
24
 
25
- **A**: SchemaI-DSL 采用 DSL 语法,更简洁:
25
+ **A**: schema-dsl 采用 DSL 语法,更简洁:
26
26
 
27
27
  ```javascript
28
- // SchemaI-DSL - 简洁
28
+ // schema-dsl - 简洁
29
29
  const schema = dsl({
30
30
  username: 'string:3-32!',
31
31
  email: 'email!'
@@ -46,13 +46,15 @@ const schema = Joi.object({
46
46
 
47
47
  ---
48
48
 
49
- ### Q: 如何安装 SchemaI-DSL
49
+ ### Q: 如何安装 schema-dsl
50
50
 
51
51
  ```bash
52
52
  npm install schema-dsl
53
53
  ```
54
54
 
55
- **Node.js 版本要求**:>= 12.0.0
55
+ **Node.js 版本要求**:`>=18.0.0`
56
+
57
+ 当前 TypeScript 重构版以 `Node.js >=18.0.0` 为唯一运行时基线,不再承诺旧 Node 版本兼容。
56
58
 
57
59
  ---
58
60
 
@@ -64,10 +66,25 @@ npm install schema-dsl
64
66
  // CommonJS
65
67
  const { dsl, validate } = require('schema-dsl');
66
68
 
67
- // ES Modules
69
+ // ES Modules(named import)
68
70
  import { dsl, validate } from 'schema-dsl';
71
+
72
+ // ES Modules(default import)
73
+ import dslDefault from 'schema-dsl';
69
74
  ```
70
75
 
76
+ ### Q: i18n 目录加载支持哪些语言包文件格式?
77
+
78
+ **A**: 在 **Node.js >= 18.0.0** 下,`dsl.config({ i18n: '/path/to/locales' })` 默认支持:
79
+
80
+ - `.js`(CommonJS 语言包)
81
+ - `.cjs`
82
+ - `.json`
83
+ - `.jsonc`
84
+ - `.json5`
85
+
86
+ **推荐**:如果你的项目是 `type: module` / ESM,优先使用 `.cjs`、`.json`、`.jsonc`、`.json5`,兼容性最稳定。
87
+
71
88
  ---
72
89
 
73
90
  ## DSL 语法问题
@@ -175,12 +192,9 @@ const result = validator.validate(schema, data);
175
192
 
176
193
  ```javascript
177
194
  {
178
- valid: true/false, // 是否通过
179
- errors: [], // 错误数组(如果有)
180
- data: {}, // 验证后的数据(可能包含默认值)
181
- performance: { // 性能信息
182
- duration: 1.5 // 验证耗时(毫秒)
183
- }
195
+ valid: true/false, // 是否通过
196
+ data: {}, // 当前实现会返回本次验证数据,失败时也便于定位输入
197
+ errors: [] // 成功时为空数组,失败时包含详细错误
184
198
  }
185
199
  ```
186
200
 
@@ -188,12 +202,14 @@ const result = validator.validate(schema, data);
188
202
 
189
203
  ### Q: 如何获取所有错误而不是只有第一个?
190
204
 
191
- **A**: 默认就是返回所有错误。如果只需要第一个:
205
+ **A**: 默认就会返回全部错误。如果你只想保留首条错误,可以显式关闭 `allErrors`:
192
206
 
193
207
  ```javascript
194
208
  const validator = new Validator({ allErrors: false });
195
209
  ```
196
210
 
211
+ `allErrors` 需要在创建 `Validator` 实例时配置,`validator.validate(schema, data, options)` 不能按次覆盖这个开关。
212
+
197
213
  ---
198
214
 
199
215
  ### Q: 如何使用默认值?
@@ -215,38 +231,27 @@ console.log(result.data);
215
231
 
216
232
  ## 性能问题
217
233
 
218
- ### Q: Schema-DSL 的性能怎么样?
234
+ ### Q: schema-dsl 的性能怎么样?
219
235
 
220
- **A**: 性能优秀,排名第3:
236
+ **A**: 性能不错,**S3 嵌套场景快于 Zod(28%),无效数据公平对比快 89x**:
221
237
 
222
- | 库名 | 每秒操作数 | 排名 |
223
- |------|-----------|------|
224
- | Ajv | 2,000,000 ops/s | 🥇 第1 |
225
- | Zod | 526,316 ops/s | 🥈 第2 |
226
- | **Schema-DSL** | **277,778 ops/s** | 🥉 **第3** |
227
- | Joi | 97,087 ops/s | 第4 |
228
- | Yup | 60,241 ops/s | 第5 |
238
+ | 场景 | Schema-DSL | Zod | 对比 |
239
+ |------|-----------|-----|------|
240
+ | S1 简单有效 | **1.301M ops/s** | 1.305M ops/s | ≈ 持平(差 <1%)|
241
+ | S2 无效(均无 i18n)| **1.205M ops/s** | 13.49K ops/s | **89x** |
242
+ | S3 嵌套有效 | **1.085M ops/s** | 847K ops/s | ✅ 快 **28%** |
243
+ | 底层 Ajv (raw) | ~4.7M ops/s | | 底层引擎 |
229
244
 
230
245
  **结论**:
231
- - ✅ Joi **2.86倍**
232
- - ✅ 比 Yup **4.61倍**
233
- - ✅ 对大多数应用足够(27万+ ops/s)
246
+ - ✅ S3 嵌套场景快于 Zod(**28%**),S1 简单有效场景持平;无效数据公平对比快 **89x**
247
+ - ✅ 比 Joi 快约 **13x**(无效数据公平对比)
248
+ - ✅ 内置缓存确保热路径零解析开销
234
249
 
235
250
  ---
236
251
 
237
- ### Q: 为什么比 Zod 慢?
238
-
239
- **A**: 因为 Schema-DSL 使用**运行时解析 DSL**,而 Zod 是**编译时构建**。
252
+ ### Q: 有效/无效数据场景性能差异为什么大?
240
253
 
241
- **权衡**:
242
- ```
243
- 损失: 比 Zod 慢 1.9倍
244
- 换来:
245
- ✅ 代码量减少 65%
246
- ✅ 完全动态的验证规则
247
- ✅ 多租户/配置驱动支持
248
- ✅ 前后端共享规则
249
- ```
254
+ **A**: 公平对比(S2,均不做 i18n 格式化)schema-dsl 显著快于 Zod(**89x**)。Zod 在无效数据场景极慢的根源是其错误收集使用异常驱动(`try/catch` 控制流),每个无效字段抛出一次 Error,4 个错误字段 = 4 次 Error 实例创建 + 4 次堆栈捕获。schema-dsl 基于 AJV 的无异常收集路径,无格式化时达 1.2M ops/s。
250
255
 
251
256
  ---
252
257
 
@@ -299,7 +304,7 @@ app.post('/api/users', (req, res) => {
299
304
 
300
305
  ### Q: 缓存如何工作?
301
306
 
302
- **A**: SchemaI-DSL 内置 LRU 缓存:
307
+ **A**: schema-dsl 当前通过 `CacheManager` 委托 `cache-hub` 的 `MemoryCache` 实现编译缓存:
303
308
 
304
309
  ```javascript
305
310
  const validator = new Validator({
@@ -310,13 +315,15 @@ const validator = new Validator({
310
315
  });
311
316
 
312
317
  // 缓存统计
313
- const stats = validator.cache.getStats();
318
+ const stats = validator.getCacheStats();
314
319
  console.log(stats);
315
320
  // {
316
- // size: 150, // 当前缓存数
317
- // hits: 8500, // 缓存命中次数
318
- // misses: 150, // 缓存未命中次数
319
- // evictions: 0 // 驱逐次数
321
+ // hits: 8500,
322
+ // misses: 150,
323
+ // hitRate: '98.27',
324
+ // size: 150,
325
+ // maxSize: 5000,
326
+ // enabled: true
320
327
  // }
321
328
  ```
322
329
 
@@ -324,11 +331,16 @@ console.log(stats);
324
331
 
325
332
  ### Q: 如何批量验证?
326
333
 
327
- **A**: 使用 `validateBatch()`:
334
+ **A**: 使用 `SchemaUtils.validateBatch()`:
328
335
 
329
336
  ```javascript
330
- const results = validator.validateBatch(schema, [data1, data2, data3]);
331
- // 返回结果数组
337
+ const { SchemaUtils, Validator } = require('schema-dsl');
338
+
339
+ const validator = new Validator();
340
+ const batch = SchemaUtils.validateBatch(schema, [data1, data2, data3], validator.getAjv());
341
+
342
+ console.log(batch.summary.valid);
343
+ console.log(batch.results[0].valid);
332
344
  ```
333
345
 
334
346
  ---
@@ -368,7 +380,7 @@ const results = validator.validateBatch(schema, [data1, data2, data3]);
368
380
  6. **前后端共享验证** - 一套规则,两端使用
369
381
 
370
382
  ⚠️ **不适合的场景**:
371
- 1. 极致性能要求(>50万 ops/s)→ 推荐 **Zod** **Ajv**
383
+ 1. 绝对吞吐量优先、且不需要 DSL 动态能力 推荐 **Ajv**
372
384
  2. TypeScript 强类型推断 → 推荐 **Zod**
373
385
  3. 静态验证规则 → 推荐 **Zod**
374
386
 
@@ -415,16 +427,12 @@ JSON.stringify({ username: 'string:3-32!' });
415
427
 
416
428
  **A**: Schema-DSL 的设计优先级:
417
429
 
418
- ```
430
+ ```text
419
431
  灵活性 > 易用性 > 性能
420
432
  ```
421
433
 
422
434
  **权衡结果**:
423
- - 损失:比 Zod 1.9倍
424
- - 换来:完全的动态性 + 代码量减少 65%
425
- - 结论:对大多数应用(<10万 ops/s)足够
426
-
427
- **如果需要极致性能**: 推荐使用 Zod(526k ops/s)或 Ajv(2M ops/s)
435
+ - 增益:S3 嵌套场景快于 Zod 28%,S1 持平;无效数据公平对比快 89x
428
436
 
429
437
  ---
430
438
 
@@ -467,12 +475,12 @@ validator.validate(schema, data, { locale: 'zh-CN' });
467
475
 
468
476
  ### Q: 错误路径格式是什么?
469
477
 
470
- **A**: JSON Pointer 格式:
478
+ **A**: 当前返回的是 slash path:
471
479
 
472
480
  ```javascript
473
- '/username' // 顶层字段
474
- '/user/name' // 嵌套字段
475
- '/items/0/name' // 数组元素
481
+ 'username' // 顶层字段
482
+ 'user/name' // 嵌套字段
483
+ 'items/0/name' // 数组元素
476
484
  ```
477
485
 
478
486
  ---
@@ -523,18 +531,21 @@ MySQL 会生成 `COMMENT`,PostgreSQL 会生成 `COMMENT ON COLUMN`。
523
531
 
524
532
  ## TypeScript 支持
525
533
 
526
- ### Q: SchemaI-DSL 支持 TypeScript 吗?
534
+ ### Q: schema-dsl 支持 TypeScript 吗?
527
535
 
528
- **A**: 支持,类型定义在 `index.d.ts`:
536
+ **A**: 支持。当前更稳定的 TypeScript 写法是直接使用 `dsl('...')` Builder API,而不是依赖 String 原型扩展:
529
537
 
530
538
  ```typescript
531
- import { dsl, validate, DslBuilder, Validator } from 'schema-dsl';
539
+ import { dsl, validate, Validator } from 'schema-dsl';
532
540
 
533
541
  const schema = dsl({
534
542
  username: 'string:3-32!',
535
- email: 'email!'
543
+ email: dsl('email!').label('邮箱地址').error({
544
+ required: '请输入邮箱地址'
545
+ })
536
546
  });
537
547
 
548
+ const validator = new Validator({ allErrors: true });
538
549
  const result = validate(schema, data);
539
550
  if (result.valid) {
540
551
  console.log(result.data);
@@ -543,14 +554,15 @@ if (result.valid) {
543
554
 
544
555
  ---
545
556
 
546
- ### Q: 如何获得 String 扩展的类型提示?
557
+ ### Q: TypeScript 下如何写出更稳妥的链式提示?
547
558
 
548
- **A**: 类型定义包含全局 String 扩展:
559
+ **A**: 推荐始终从 `dsl('...')` 开始链式调用;这样能和当前类型声明保持一致:
549
560
 
550
561
  ```typescript
551
- // TypeScript 会识别这些方法
552
562
  const schema = dsl({
553
- email: 'email!'.label('邮箱').messages({ ... })
563
+ email: dsl('email!')
564
+ .label('邮箱')
565
+ .error({ format: '请输入有效邮箱地址' })
554
566
  });
555
567
  ```
556
568
 
@@ -560,7 +572,7 @@ const schema = dsl({
560
572
 
561
573
  如果您有其他问题:
562
574
 
563
- 1. 查看 [完整文档](INDEX.md)
575
+ 1. 查看 [完整文档](doc-index.md)
564
576
  2. 查看 [DSL 语法指南](dsl-syntax.md)
565
577
  3. 查看 [API 参考](api-reference.md)
566
578
  4. 提交 [GitHub Issue](https://github.com/schema-dsl/schema-dsl/issues)
@@ -575,3 +587,10 @@ const schema = dsl({
575
587
  - [导出指南](export-guide.md)
576
588
  - [错误处理](error-handling.md)
577
589
 
590
+ ---
591
+
592
+ ## 对应示例文件
593
+
594
+ **示例入口**: [faq.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/faq.ts)
595
+ **说明**: 把 FAQ 里最常被复制的 4 类场景放在一个可运行示例中: 单次验证、多语言错误、批量验证、缓存统计。
596
+
@@ -60,7 +60,7 @@ app.post('/api/validate/:type', (req, res) => {
60
60
  }
61
61
 
62
62
  // 从请求头获取语言偏好
63
- const locale = req.headers['accept-language'] || 'en-US';
63
+ const locale = req.headers['accept-language']?.split(',')[0]?.trim() || 'en-US';
64
64
 
65
65
  // 验证(直接切换语言,无需重新加载)
66
66
  const result = validate(schema, req.body, { locale });
@@ -71,7 +71,7 @@ app.post('/api/validate/:type', (req, res) => {
71
71
  // 用户注册(带验证)
72
72
  app.post('/api/register', (req, res) => {
73
73
  // 从请求头获取语言偏好
74
- const locale = req.headers['accept-language'] || 'en-US';
74
+ const locale = req.headers['accept-language']?.split(',')[0]?.trim() || 'en-US';
75
75
 
76
76
  // 验证数据
77
77
  const result = validate(schemas.user, req.body, { locale });
@@ -222,13 +222,20 @@ app.post('/api/validate', (req, res) => {
222
222
 
223
223
  ### Q2: 每次请求创建 Validator 实例会影响性能吗?
224
224
 
225
- **A**: 不会。Validator 实例创建非常轻量,且验证器内部有编译缓存。
225
+ **A**: 实例创建本身很轻量,但**仍然建议复用同一个 `Validator` 实例**。原因不是构造函数慢,而是编译缓存挂在实例上;如果每个请求都 `new Validator()`,同一份 Schema 会反复出现首次编译 miss。
226
226
 
227
227
  ```javascript
228
- // 性能测试结果
229
- // 创建实例: ~0.001ms
230
- // 验证数据: ~0.1-1ms
231
- // 总计: 可忽略不计
228
+ const validator = new Validator();
229
+
230
+ app.post('/api/validate', (req, res) => {
231
+ const locale = resolveLocale(req);
232
+ const result = validator.validate(schema, req.body, { locale });
233
+ res.json(result);
234
+ });
235
+
236
+ // 说明:
237
+ // - 共享实例:同一 schema 的后续请求可以复用编译缓存
238
+ // - 语言仍通过 validate(..., { locale }) 按次传入,不要写进构造函数
232
239
  ```
233
240
 
234
241
  ### Q3: 如何支持更多语言?
@@ -250,8 +257,8 @@ Locale.addLocale('de-DE', {
250
257
  **A**: 后端返回错误消息已经是本地化的,前端无需处理。如果需要前端验证:
251
258
 
252
259
  ```javascript
253
- // 前端可以使用相同的 SchemaI-DSL(浏览器版)
254
- import { dsl, validate } from 'schema-dsl/browser';
260
+ // 前端可以复用同一套 schema-dsl 校验规则
261
+ import { dsl, validate } from 'schema-dsl';
255
262
 
256
263
  const schema = dsl({ /* ... */ });
257
264
  const result = validate(schema, formData, {
@@ -265,12 +272,12 @@ const result = validate(schema, formData, {
265
272
  // 中间件:优先级 Header > Cookie > Session > 默认
266
273
  app.use((req, res, next) => {
267
274
  const locale =
268
- req.headers['accept-language'] ||
275
+ req.headers['accept-language']?.split(',')[0]?.trim() ||
269
276
  req.cookies?.locale ||
270
277
  req.session?.locale ||
271
278
  'en-US';
272
279
 
273
- req.validator = new Validator({ locale });
280
+ req.locale = locale;
274
281
  next();
275
282
  });
276
283
  ```
@@ -281,7 +288,7 @@ app.use((req, res, next) => {
281
288
 
282
289
  ### ✅ 推荐做法
283
290
 
284
- 1. **使用实例级配置**:每个请求创建独立 Validator
291
+ 1. **复用共享 Validator 实例**:按次通过 `validate(..., { locale })` 传入语言
285
292
  2. **通过请求头传递语言**:符合 HTTP 标准
286
293
  3. **使用中间件统一处理**:提高代码复用性
287
294
 
@@ -291,3 +298,10 @@ app.use((req, res, next) => {
291
298
  - [API 参考](api-reference.md)
292
299
  - [最佳实践](best-practices.md)
293
300
 
301
+ ---
302
+
303
+ ## 对应示例文件
304
+
305
+ **示例入口**: [frontend-i18n-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/frontend-i18n-guide.ts)
306
+ **说明**: 覆盖前端常见的语言优先级解析、表单提交验证,以及把错误数组整理成字段级错误映射。
307
+
@@ -1,6 +1,6 @@
1
1
  # 多语言支持用户指南
2
2
 
3
- > **更新日期**: 2025-12-29
3
+ > **更新日期**: 2026-04-30
4
4
 
5
5
  ---
6
6
 
@@ -17,6 +17,11 @@
17
17
 
18
18
  ## 快速开始
19
19
 
20
+ > **Node.js 要求**:`>=18.0.0`
21
+ >
22
+ > **目录加载(Node >=18)默认支持的语言文件格式**:`.js`(CommonJS)、`.cjs`、`.json`、`.jsonc`、`.json5`。
23
+ > **推荐**:如果你的应用是 `type: module` / ESM 项目,优先使用 `.cjs`、`.json`、`.jsonc`、`.json5`。
24
+
20
25
  ### 5 分钟上手
21
26
 
22
27
  ```javascript
@@ -50,7 +55,12 @@ const result = validate(schema, data, { locale: 'zh-CN' });
50
55
 
51
56
  ## 配置方式
52
57
 
53
- ### 方式 1:直接传入对象(推荐小型项目)
58
+ ### 方式 1:传入对象配置(推荐小型项目)
59
+
60
+ `schema-dsl` 同时支持两种对象写法:
61
+
62
+ - 兼容包装层:`{ i18n: { locales: { ... } } }`
63
+ - 简写形式:`{ i18n: { 'zh-CN': { ... }, 'en-US': { ... } } }`
54
64
 
55
65
  ```javascript
56
66
  dsl.config({
@@ -71,6 +81,23 @@ dsl.config({
71
81
  });
72
82
  ```
73
83
 
84
+ **简写形式**:
85
+
86
+ ```javascript
87
+ dsl.config({
88
+ i18n: {
89
+ 'zh-CN': {
90
+ 'username': '用户名',
91
+ 'email': '邮箱地址'
92
+ },
93
+ 'en-US': {
94
+ 'username': 'Username',
95
+ 'email': 'Email Address'
96
+ }
97
+ }
98
+ });
99
+ ```
100
+
74
101
  **优点**:
75
102
  - ✅ 简单直接
76
103
  - ✅ 适合小型项目
@@ -85,13 +112,13 @@ dsl.config({
85
112
  ### 方式 2:从目录加载(推荐大型项目)
86
113
 
87
114
  **目录结构**:
88
- ```
115
+ ```text
89
116
  project/
90
117
  ├── i18n/
91
118
  │ └── labels/
92
- │ ├── zh-CN.js
93
- │ ├── en-US.js
94
- │ └── ja-JP.js
119
+ │ ├── zh-CN.cjs
120
+ │ ├── en-US.jsonc
121
+ │ └── ja-JP.json5
95
122
  ├── app.js
96
123
  └── routes/
97
124
  ```
@@ -107,7 +134,7 @@ dsl.config({
107
134
  });
108
135
  ```
109
136
 
110
- **语言包文件**(`i18n/labels/zh-CN.js`):
137
+ **语言包文件**(`i18n/labels/zh-CN.cjs`):
111
138
  ```javascript
112
139
  module.exports = {
113
140
  // 字段标签
@@ -148,7 +175,7 @@ dsl.config({
148
175
 
149
176
  | 项目规模 | maxSize | 说明 |
150
177
  |---------|---------|------|
151
- | 小型(< 100 Schema) | 1000(默认) | 够用 |
178
+ | 小型(< 100 Schema) | 1000 | 够用 |
152
179
  | 中型(100-1000) | 5000(默认) | 推荐 |
153
180
  | 大型(1000-5000) | 10000 | 推荐 |
154
181
  | 超大型(> 5000) | 20000 | 推荐 |
@@ -186,7 +213,7 @@ const addressSchema = dsl({
186
213
 
187
214
  **语言包**:
188
215
  ```javascript
189
- {
216
+ const labels = {
190
217
  'address.city': '城市',
191
218
  'address.street': '街道',
192
219
  'address.zipCode': '邮编'
@@ -206,10 +233,10 @@ const { validate } = require('schema-dsl');
206
233
  const app = express();
207
234
  app.use(express.json());
208
235
 
209
- // 中间件:提取语言参数
236
+ // 中间件:提取语言参数(简化版:query > Accept-Language > 默认)
210
237
  app.use((req, res, next) => {
211
- req.locale = req.headers['accept-language'] ||
212
- req.query.lang ||
238
+ req.locale = req.query.lang ||
239
+ req.headers['accept-language']?.split(',')[0]?.trim() ||
213
240
  'zh-CN';
214
241
  next();
215
242
  });
@@ -343,22 +370,22 @@ const handleSubmit = async () => {
343
370
  ### 1. 语言包组织
344
371
 
345
372
  **推荐结构**:
346
- ```
373
+ ```text
347
374
  i18n/
348
375
  ├── labels/ # 字段标签
349
- │ ├── zh-CN.js
350
- │ ├── en-US.js
351
- │ └── ja-JP.js
376
+ │ ├── zh-CN.cjs
377
+ │ ├── en-US.jsonc
378
+ │ └── ja-JP.json5
352
379
  └── messages/ # 自定义消息(可选)
353
- ├── zh-CN.js
354
- └── en-US.js
380
+ ├── zh-CN.cjs
381
+ └── en-US.json
355
382
  ```
356
383
 
357
384
  ### 2. 命名规范
358
385
 
359
386
  **字段标签**:
360
387
  ```javascript
361
- {
388
+ const fieldLabels = {
362
389
  'username': '用户名', // 简单字段
363
390
  'address.city': '城市', // 嵌套字段
364
391
  'order.items[0].name': '商品名称' // 数组字段
@@ -367,7 +394,7 @@ i18n/
367
394
 
368
395
  **自定义消息**:
369
396
  ```javascript
370
- {
397
+ const customMessages = {
371
398
  'custom.emailTaken': '邮箱已被注册',
372
399
  'custom.passwordWeak': '密码强度不够',
373
400
  'custom.orderExpired': '订单已过期'
@@ -381,8 +408,8 @@ i18n/
381
408
  const locale =
382
409
  req.query.lang || // 1. URL 参数(最高优先级)
383
410
  req.cookies.lang || // 2. Cookie
384
- req.headers['accept-language'] || // 3. Accept-Language
385
- 'zh-CN'; // 4. 默认语言
411
+ req.headers['accept-language']?.split(',')[0]?.trim() || // 3. Accept-Language 头(取首个语言标签)
412
+ 'en-US'; // 4. 默认语言
386
413
  ```
387
414
 
388
415
  ### 4. 语言持久化
@@ -405,7 +432,7 @@ const savedLang = localStorage.getItem('userLanguage') || 'zh-CN';
405
432
  **A**: 创建新的语言包文件并重启应用
406
433
 
407
434
  ```javascript
408
- // i18n/labels/ko-KR.js(韩语)
435
+ // i18n/labels/ko-KR.cjs(韩语)
409
436
  module.exports = {
410
437
  'username': '사용자 이름',
411
438
  'email': '이메일 주소'
@@ -416,10 +443,10 @@ module.exports = {
416
443
 
417
444
  **A**: 系统会自动回退
418
445
 
419
- ```
446
+ ```text
420
447
  查找顺序:
421
- 1. 用户语言包(i18n/labels/zh-CN.js)
422
- 2. 系统语言包(lib/locales/zh-CN.js)
448
+ 1. 用户语言包(例如 `i18n/labels/zh-CN.cjs` / `zh-CN.jsonc`)
449
+ 2. 内置语言包(包内预置的 `zh-CN` / `en-US` / `ja-JP` / `es-ES` / `fr-FR`)
423
450
  3. 使用 key 本身
424
451
  ```
425
452
 
@@ -427,7 +454,7 @@ module.exports = {
427
454
 
428
455
  **A**: 大型项目提升 3-10 倍
429
456
 
430
- ```
457
+ ```text
431
458
  场景:3000 个 Schema
432
459
  原配置(1000):33% 命中率
433
460
  优化后(5000):100% 命中率
@@ -440,35 +467,21 @@ module.exports = {
440
467
 
441
468
  ```javascript
442
469
  // 动态添加语言
470
+ const frFR = require('./i18n/fr-FR.cjs');
471
+
443
472
  dsl.config({
444
473
  i18n: {
445
474
  locales: {
446
- 'fr-FR': require('./i18n/fr-FR.js')
475
+ 'fr-FR': frFR
447
476
  }
448
477
  }
449
478
  });
450
479
  ```
451
480
 
452
- ### Q5: 如何与其他 i18n 库协同?
453
-
454
- **A**: 保持语言同步
455
-
456
- ```javascript
457
- import i18next from 'i18next';
458
- import { Locale } from 'schema-dsl';
459
-
460
- // 同时切换两个库的语言
461
- function changeLanguage(lang) {
462
- i18next.changeLanguage(lang);
463
- Locale.setLocale(lang);
464
- }
465
- ```
466
-
467
481
  ---
468
482
 
469
- ## 相关文档
483
+ ## 对应示例文件
470
484
 
471
- - [API 参考](./api-reference.md)
472
- - [完整示例](../examples/i18n-full-demo.js)
473
- - [动态缓存优化](./cache-manager.md)
485
+ **示例入口**: [i18n-user-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/i18n-user-guide.ts)
486
+ **说明**: 覆盖 `dsl.config({ i18n: { locales: ... } })` 的对象配置方式、已加载语言列表,以及不同 locale 的运行时切换。
474
487