schema-dsl 1.2.5 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/CHANGELOG.md +130 -238
  2. package/LICENSE +21 -21
  3. package/README.md +628 -2486
  4. package/dist/DslBuilder-BIgQOAXp.d.ts +343 -0
  5. package/dist/DslBuilder-CjHTucNQ.d.cts +343 -0
  6. package/dist/Validator-CllRdrY0.d.ts +192 -0
  7. package/dist/Validator-D6okG9tr.d.cts +192 -0
  8. package/dist/index.cjs +6640 -0
  9. package/dist/index.d.cts +1151 -0
  10. package/dist/index.d.ts +1151 -0
  11. package/dist/index.js +6574 -0
  12. package/dist/plugin-CIKtTMtS.d.cts +246 -0
  13. package/dist/plugin-CIKtTMtS.d.ts +246 -0
  14. package/dist/plugins/custom-format.cjs +3818 -0
  15. package/dist/plugins/custom-format.d.cts +12 -0
  16. package/dist/plugins/custom-format.d.ts +12 -0
  17. package/dist/plugins/custom-format.js +3788 -0
  18. package/dist/plugins/custom-type-example.cjs +3811 -0
  19. package/dist/plugins/custom-type-example.d.cts +8 -0
  20. package/dist/plugins/custom-type-example.d.ts +8 -0
  21. package/dist/plugins/custom-type-example.js +3781 -0
  22. package/dist/plugins/custom-validator.cjs +144 -0
  23. package/dist/plugins/custom-validator.d.cts +10 -0
  24. package/dist/plugins/custom-validator.d.ts +10 -0
  25. package/dist/plugins/custom-validator.js +119 -0
  26. package/docs/FEATURE-INDEX.md +553 -519
  27. package/docs/add-custom-locale.md +496 -483
  28. package/docs/add-keyword.md +24 -0
  29. package/docs/api-reference.md +1047 -805
  30. package/docs/api.md +13 -0
  31. package/docs/best-practices-project-structure.md +417 -408
  32. package/docs/best-practices.md +712 -672
  33. package/docs/cache-manager.md +344 -336
  34. package/docs/compile.md +45 -0
  35. package/docs/conditional-api.md +1307 -1278
  36. package/docs/custom-extensions-guide.md +339 -411
  37. package/docs/design-philosophy.md +606 -601
  38. package/docs/doc-index.md +324 -0
  39. package/docs/dsl-syntax.md +714 -664
  40. package/docs/dynamic-locale.md +608 -598
  41. package/docs/enum.md +482 -475
  42. package/docs/error-handling.md +1975 -1966
  43. package/docs/export-guide.md +501 -462
  44. package/docs/export-limitations.md +567 -551
  45. package/docs/faq.md +596 -577
  46. package/docs/frontend-i18n-guide.md +307 -293
  47. package/docs/i18n-user-guide.md +487 -474
  48. package/docs/i18n.md +476 -457
  49. package/docs/index.md +48 -0
  50. package/docs/json-schema-basics.md +40 -0
  51. package/docs/label-vs-description.md +271 -262
  52. package/docs/markdown-exporter.md +406 -397
  53. package/docs/mongodb-exporter.md +302 -295
  54. package/docs/multi-language.md +26 -0
  55. package/docs/multi-type-support.md +322 -329
  56. package/docs/mysql-exporter.md +280 -273
  57. package/docs/number-operators.md +449 -442
  58. package/docs/optional-marker-guide.md +326 -321
  59. package/docs/performance-guide.md +49 -0
  60. package/docs/plugin-system.md +381 -542
  61. package/docs/plugin-type-registration.md +34 -0
  62. package/docs/postgresql-exporter.md +311 -304
  63. package/docs/public/favicon.svg +5 -0
  64. package/docs/quick-start.md +435 -761
  65. package/docs/runtime-locale-support.md +532 -521
  66. package/docs/schema-helper.md +345 -340
  67. package/docs/schema-utils-advanced-issues.md +23 -0
  68. package/docs/schema-utils-best-practices.md +20 -0
  69. package/docs/schema-utils-chaining.md +150 -143
  70. package/docs/schema-utils.md +524 -490
  71. package/docs/security-checklist.md +20 -0
  72. package/docs/string-extensions.md +488 -480
  73. package/docs/troubleshooting.md +486 -471
  74. package/docs/type-converter.md +310 -319
  75. package/docs/type-reference.md +242 -219
  76. package/docs/typescript-guide.md +584 -573
  77. package/docs/union-type-guide.md +157 -147
  78. package/docs/union-types.md +284 -277
  79. package/docs/validate-async.md +491 -480
  80. package/docs/validate-batch.md +49 -0
  81. package/docs/validate-dsl-object-support.md +578 -573
  82. package/docs/validate.md +506 -486
  83. package/docs/validation-guide.md +502 -484
  84. package/docs/validator.md +39 -0
  85. package/package.json +131 -73
  86. package/plugins/custom-format.cjs +8 -0
  87. package/plugins/custom-type-example.cjs +8 -0
  88. package/plugins/custom-validator.cjs +8 -0
  89. package/src/adapters/DslAdapter.ts +111 -0
  90. package/src/adapters/index.ts +1 -0
  91. package/src/config/constants.ts +83 -0
  92. package/src/config/index.ts +2 -0
  93. package/src/config/patterns.ts +77 -0
  94. package/src/core/CacheManager.ts +169 -0
  95. package/src/core/ConditionalBuilder.ts +382 -0
  96. package/src/core/ConditionalRuntime.ts +28 -0
  97. package/src/core/ConditionalValidator.ts +255 -0
  98. package/src/core/DslBuilder.ts +687 -0
  99. package/src/core/ErrorCodes.ts +38 -0
  100. package/src/core/ErrorFormatter.ts +271 -0
  101. package/src/core/JSONSchemaCore.ts +65 -0
  102. package/src/core/Locale.ts +187 -0
  103. package/src/core/MessageTemplate.ts +42 -0
  104. package/src/core/ObjectDslBuilder.ts +64 -0
  105. package/src/core/PluginManager.ts +326 -0
  106. package/src/core/StringExtensions.ts +140 -0
  107. package/src/core/TemplateEngine.ts +44 -0
  108. package/src/core/Validator.ts +448 -0
  109. package/src/errors/I18nError.ts +159 -0
  110. package/src/errors/ValidationError.ts +105 -0
  111. package/src/exporters/BaseExporter.ts +60 -0
  112. package/src/exporters/MarkdownExporter.ts +305 -0
  113. package/src/exporters/MongoDBExporter.ts +126 -0
  114. package/src/exporters/MySQLExporter.ts +156 -0
  115. package/src/exporters/PostgreSQLExporter.ts +222 -0
  116. package/src/exporters/index.ts +18 -0
  117. package/src/index.ts +651 -0
  118. package/{lib/locales/en-US.js → src/locales/en-US.ts} +160 -176
  119. package/{lib/locales/es-ES.js → src/locales/es-ES.ts} +160 -113
  120. package/{lib/locales/fr-FR.js → src/locales/fr-FR.ts} +160 -113
  121. package/src/locales/index.ts +103 -0
  122. package/{lib/locales/ja-JP.js → src/locales/ja-JP.ts} +160 -118
  123. package/src/locales/types.ts +156 -0
  124. package/{lib/locales/zh-CN.js → src/locales/zh-CN.ts} +160 -177
  125. package/src/parser/ConstraintParser.ts +101 -0
  126. package/src/parser/DslParser.ts +470 -0
  127. package/src/parser/SchemaCompiler.ts +66 -0
  128. package/src/parser/TypeRegistry.ts +250 -0
  129. package/src/parser/index.ts +6 -0
  130. package/src/plugins/custom-format.ts +124 -0
  131. package/src/plugins/custom-type-example.ts +106 -0
  132. package/src/plugins/custom-validator.ts +138 -0
  133. package/src/types/conditional.ts +28 -0
  134. package/src/types/config.ts +59 -0
  135. package/src/types/dsl.ts +131 -0
  136. package/src/types/error.ts +60 -0
  137. package/src/types/index.ts +17 -0
  138. package/src/types/infer.ts +128 -0
  139. package/src/types/plugin.ts +58 -0
  140. package/src/types/safe-regex.d.ts +9 -0
  141. package/src/types/schema.ts +66 -0
  142. package/src/types/validate.ts +71 -0
  143. package/src/utils/SchemaHelper.ts +196 -0
  144. package/src/utils/SchemaUtils.ts +365 -0
  145. package/src/utils/TypeConverter.ts +215 -0
  146. package/src/utils/index.ts +10 -0
  147. package/src/validators/CustomKeywords.ts +477 -0
  148. package/.eslintignore +0 -11
  149. package/.eslintrc.json +0 -27
  150. package/CONTRIBUTING.md +0 -368
  151. package/STATUS.md +0 -491
  152. package/changelogs/v1.0.0.md +0 -328
  153. package/changelogs/v1.0.9.md +0 -367
  154. package/changelogs/v1.1.0.md +0 -389
  155. package/changelogs/v1.1.1.md +0 -308
  156. package/changelogs/v1.1.2.md +0 -183
  157. package/changelogs/v1.1.3.md +0 -161
  158. package/changelogs/v1.1.4.md +0 -432
  159. package/changelogs/v1.1.5.md +0 -493
  160. package/changelogs/v1.1.6.md +0 -211
  161. package/changelogs/v1.1.8.md +0 -376
  162. package/changelogs/v1.2.3.md +0 -124
  163. package/docs/INDEX.md +0 -252
  164. package/docs/issues-resolved-summary.md +0 -196
  165. package/docs/performance-benchmark-report.md +0 -179
  166. package/docs/performance-quick-reference.md +0 -123
  167. package/docs/user-questions-answered.md +0 -353
  168. package/docs/validation-rules-v1.0.2.md +0 -1608
  169. package/examples/README.md +0 -81
  170. package/examples/array-dsl-example.js +0 -227
  171. package/examples/conditional-example.js +0 -288
  172. package/examples/conditional-non-object.js +0 -129
  173. package/examples/conditional-validate-example.js +0 -321
  174. package/examples/custom-extension.js +0 -85
  175. package/examples/dsl-match-example.js +0 -74
  176. package/examples/dsl-style.js +0 -118
  177. package/examples/dynamic-locale-configuration.js +0 -348
  178. package/examples/dynamic-locale-example.js +0 -287
  179. package/examples/enum.examples.js +0 -324
  180. package/examples/export-demo.js +0 -130
  181. package/examples/express-integration.js +0 -376
  182. package/examples/i18n-error-handling-complete.js +0 -381
  183. package/examples/i18n-error-handling-quickstart.md +0 -0
  184. package/examples/i18n-error.examples.js +0 -181
  185. package/examples/i18n-full-demo.js +0 -301
  186. package/examples/i18n-memory-safety.examples.js +0 -268
  187. package/examples/markdown-export.js +0 -71
  188. package/examples/middleware-usage.js +0 -93
  189. package/examples/new-features-comparison.js +0 -315
  190. package/examples/password-reset/README.md +0 -153
  191. package/examples/password-reset/schema.js +0 -26
  192. package/examples/password-reset/test.js +0 -101
  193. package/examples/plugin-system.examples.js +0 -205
  194. package/examples/schema-utils-chaining.examples.js +0 -250
  195. package/examples/simple-example.js +0 -122
  196. package/examples/slug.examples.js +0 -179
  197. package/examples/string-extensions.js +0 -297
  198. package/examples/union-type-example.js +0 -127
  199. package/examples/union-types-example.js +0 -77
  200. package/examples/user-registration/README.md +0 -156
  201. package/examples/user-registration/routes.js +0 -92
  202. package/examples/user-registration/schema.js +0 -150
  203. package/examples/user-registration/server.js +0 -74
  204. package/index.d.ts +0 -3658
  205. package/index.js +0 -475
  206. package/index.mjs +0 -60
  207. package/lib/adapters/DslAdapter.js +0 -995
  208. package/lib/adapters/index.js +0 -20
  209. package/lib/config/constants.js +0 -286
  210. package/lib/config/patterns/common.js +0 -47
  211. package/lib/config/patterns/creditCard.js +0 -9
  212. package/lib/config/patterns/idCard.js +0 -9
  213. package/lib/config/patterns/index.js +0 -9
  214. package/lib/config/patterns/licensePlate.js +0 -4
  215. package/lib/config/patterns/passport.js +0 -4
  216. package/lib/config/patterns/phone.js +0 -9
  217. package/lib/config/patterns/postalCode.js +0 -5
  218. package/lib/core/CacheManager.js +0 -376
  219. package/lib/core/ConditionalBuilder.js +0 -503
  220. package/lib/core/DslBuilder.js +0 -1589
  221. package/lib/core/ErrorCodes.js +0 -233
  222. package/lib/core/ErrorFormatter.js +0 -445
  223. package/lib/core/JSONSchemaCore.js +0 -347
  224. package/lib/core/Locale.js +0 -130
  225. package/lib/core/MessageTemplate.js +0 -98
  226. package/lib/core/PluginManager.js +0 -448
  227. package/lib/core/StringExtensions.js +0 -240
  228. package/lib/core/Validator.js +0 -654
  229. package/lib/errors/I18nError.js +0 -328
  230. package/lib/errors/ValidationError.js +0 -191
  231. package/lib/exporters/MarkdownExporter.js +0 -420
  232. package/lib/exporters/MongoDBExporter.js +0 -162
  233. package/lib/exporters/MySQLExporter.js +0 -212
  234. package/lib/exporters/PostgreSQLExporter.js +0 -289
  235. package/lib/exporters/index.js +0 -24
  236. package/lib/locales/index.js +0 -8
  237. package/lib/utils/LRUCache.js +0 -174
  238. package/lib/utils/SchemaHelper.js +0 -240
  239. package/lib/utils/SchemaUtils.js +0 -445
  240. package/lib/utils/TypeConverter.js +0 -245
  241. package/lib/utils/index.js +0 -13
  242. package/lib/validators/CustomKeywords.js +0 -616
  243. package/lib/validators/index.js +0 -11
@@ -1,672 +1,712 @@
1
- # schema-dsl 最佳实践
2
-
3
- > **用途**: 帮助你写出高质量、高性能的 Schema 代码
4
- > **更新**: 2025-12-26
5
-
6
- ---
7
-
8
- ## 📑 目录
9
-
10
- - [Schema 设计原则](#schema-设计原则)
11
- - [性能优化](#性能优化)
12
- - [安全性考虑](#安全性考虑)
13
- - [错误处理](#错误处理)
14
- - [代码组织](#代码组织)
15
- - [生产环境建议](#生产环境建议)
16
-
17
- ---
18
-
19
- ## Schema 设计原则
20
-
21
- ### 1. 简单字段用纯 DSL
22
-
23
- **推荐**:
24
- ```javascript
25
- const schema = dsl({
26
- username: 'string:3-32!',
27
- age: 'number:18-120',
28
- email: 'email!',
29
- role: 'admin|user|guest'
30
- });
31
- ```
32
-
33
- **不推荐**(过度复杂):
34
- ```javascript
35
- const schema = dsl({
36
- username: dsl('string').minLength(3).maxLength(32).required(),
37
- // 太冗长了!
38
- });
39
- ```
40
-
41
- **原则**: 能用 DSL 字符串表达的,就不要用链式调用。
42
-
43
- ---
44
-
45
- ### 2. 复杂验证用链式调用
46
-
47
- **适合链式调用的场景**:
48
- - 需要正则验证
49
- - 需要自定义错误消息
50
- - 需要自定义验证器
51
- - 需要标签(label)
52
-
53
- **示例**:
54
- ```javascript
55
- const schema = dsl({
56
- // 简单字段:纯 DSL
57
- age: 'number:18-120',
58
-
59
- // 复杂字段:链式调用
60
- username: 'string:3-32!'
61
- .pattern(/^[a-zA-Z0-9_]+$/)
62
- .label('用户名')
63
- .messages({
64
- 'pattern': '只能包含字母、数字和下划线',
65
- 'min': '至少3个字符',
66
- 'max': '最多32个字符'
67
- }),
68
-
69
- email: 'email!'
70
- .custom(async (value) => {
71
- const exists = await checkEmailExists(value);
72
- if (exists) return '邮箱已被占用';
73
- })
74
- .label('邮箱地址')
75
- });
76
- ```
77
-
78
- ---
79
-
80
- ### 3. 使用预设验证器
81
-
82
- SchemaI-DSL 提供了常用的预设验证器,开箱即用:
83
-
84
- ```javascript
85
- const schema = dsl({
86
- // 使用预设验证器(推荐)
87
- username: dsl('string!').username(), // 自动设置 3-32 长度 + 正则
88
- password: dsl('string!').password('strong'), // 强密码验证
89
- phone: dsl('string!').phone('cn'), // 中国手机号
90
-
91
- // ❌ 手动实现(不推荐)
92
- username: 'string:3-32!'
93
- .pattern(/^[a-zA-Z][a-zA-Z0-9_]*$/)
94
- });
95
- ```
96
-
97
- **可用预设**:
98
- - `username(preset?)` - 用户名验证
99
- - `password(strength?)` - 密码强度验证
100
- - `phone(country?)` - 手机号验证
101
- - `slug()` - URL slug 验证
102
-
103
- ---
104
-
105
- ### 4. 避免过深的嵌套
106
-
107
- **不推荐**(嵌套过深):
108
- ```javascript
109
- const schema = dsl({
110
- user: {
111
- profile: {
112
- personal: {
113
- address: {
114
- detail: {
115
- street: 'string' // 嵌套 5 层
116
- }
117
- }
118
- }
119
- }
120
- }
121
- });
122
- ```
123
-
124
- **推荐**(拆分或扁平化):
125
- ```javascript
126
- // 方案1: 拆分为多个 Schema
127
- const addressSchema = dsl({
128
- street: 'string!',
129
- city: 'string!',
130
- zipCode: 'string'
131
- });
132
-
133
- const userSchema = dsl({
134
- name: 'string!',
135
- email: 'email!',
136
- address: addressSchema
137
- });
138
-
139
- // 方案2: 扁平化
140
- const schema = dsl({
141
- 'user_name': 'string!',
142
- 'user_email': 'email!',
143
- 'address_street': 'string!',
144
- 'address_city': 'string!'
145
- });
146
- ```
147
-
148
- **原则**: 嵌套深度建议不超过 3-4 层。
149
-
150
- ---
151
-
152
- ## 性能优化
153
-
154
- ### 1. 预编译 Schema
155
-
156
- **不推荐**(每次都编译):
157
- ```javascript
158
- app.post('/api/user', (req, res) => {
159
- const schema = dsl({ username: 'string!' });
160
- const result = validate(schema, req.body); // 每次都编译
161
- });
162
- ```
163
-
164
- **推荐**(预编译):
165
- ```javascript
166
- // 在应用启动时编译一次
167
- const userSchema = dsl({ username: 'string!' });
168
- const validateUser = validator.compile(userSchema);
169
-
170
- app.post('/api/user', (req, res) => {
171
- const result = validateUser(req.body); // 直接使用
172
- });
173
- ```
174
-
175
- **性能提升**: 预编译可以提升 **10-100 倍** 的性能!
176
-
177
- ---
178
-
179
- ### 2. 启用缓存
180
-
181
- ```javascript
182
- const validator = new Validator({
183
- cache: true // 启用编译缓存
184
- });
185
-
186
- // 或者使用全局单例(默认启用缓存)
187
- const { validate } = require('schema-dsl');
188
- validate(schema, data); // 自动缓存
189
- ```
190
-
191
- ---
192
-
193
- ### 3. 批量验证
194
-
195
- **不推荐**(循环验证):
196
- ```javascript
197
- const errors = [];
198
- records.forEach(record => {
199
- const result = validate(schema, record);
200
- if (!result.valid) {
201
- errors.push(result.errors);
202
- }
203
- });
204
- ```
205
-
206
- **推荐**(批量验证):
207
- ```javascript
208
- const result = validator.validateBatch(schema, records);
209
- // 一次性验证所有记录,性能更好
210
- ```
211
-
212
- ---
213
-
214
- ### 4. 优化正则表达式
215
-
216
- **不推荐**(可能导致 ReDoS):
217
- ```javascript
218
- // 危险的正则:灾难性回溯
219
- .pattern(/^(a+)+$/)
220
- .pattern(/^(a*)*$/)
221
- .pattern(/^(a|a)*$/)
222
- ```
223
-
224
- **推荐**(安全高效):
225
- ```javascript
226
- // 简单明确的正则
227
- .pattern(/^[a-zA-Z0-9_]+$/)
228
- .pattern(/^[a-z]{3,10}$/)
229
- ```
230
-
231
- **工具**: 使用 [regexploit](https://www.npmjs.com/package/regexploit) 检测危险正则。
232
-
233
- ---
234
-
235
- ### 5. 避免在循环中创建 Schema
236
-
237
- **不推荐**:
238
- ```javascript
239
- records.forEach(record => {
240
- const schema = dsl({ name: 'string!' }); // 每次都创建
241
- validate(schema, record);
242
- });
243
- ```
244
-
245
- **推荐**:
246
- ```javascript
247
- const schema = dsl({ name: 'string!' }); // 创建一次
248
- records.forEach(record => {
249
- validate(schema, record); // 重复使用
250
- });
251
- ```
252
-
253
- ---
254
-
255
- ## 安全性考虑
256
-
257
- ### 1. 限制用户输入的正则
258
-
259
- **危险**:
260
- ```javascript
261
- // ❌ 用户控制的正则表达式
262
- app.post('/api/validate', (req, res) => {
263
- const pattern = req.body.pattern; // 用户输入
264
- const schema = dsl('string').pattern(new RegExp(pattern)); // 危险!
265
- });
266
- ```
267
-
268
- **原因**: 用户可能输入恶意正则导致 ReDoS 攻击。
269
-
270
- **安全做法**:
271
- ```javascript
272
- // ✅ 使用预定义的正则
273
- const ALLOWED_PATTERNS = {
274
- username: /^[a-zA-Z0-9_]+$/,
275
- email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
276
- };
277
-
278
- app.post('/api/validate', (req, res) => {
279
- const patternName = req.body.pattern;
280
- const pattern = ALLOWED_PATTERNS[patternName];
281
- if (!pattern) {
282
- return res.status(400).json({ error: 'Invalid pattern' });
283
- }
284
- const schema = dsl('string').pattern(pattern);
285
- });
286
- ```
287
-
288
- ---
289
-
290
- ### 2. 清理错误消息
291
-
292
- 生产环境不要暴露敏感信息:
293
-
294
- ```javascript
295
- // 开发环境
296
- if (process.env.NODE_ENV === 'development') {
297
- return res.status(400).json({
298
- valid: false,
299
- errors: result.errors // 详细错误
300
- });
301
- }
302
-
303
- // 生产环境
304
- return res.status(400).json({
305
- valid: false,
306
- message: '输入数据验证失败' // 简化消息
307
- });
308
- ```
309
-
310
- ---
311
-
312
- ### 3. 限制 Schema 复杂度
313
-
314
- ```javascript
315
- const validator = new Validator({
316
- maxNestingDepth: 10, // 限制嵌套深度
317
- maxSchemaSize: 10000 // 限制 Schema 大小(建议)
318
- });
319
-
320
- // validate 前检查
321
- DslBuilder.validateNestingDepth(schema, 10);
322
- ```
323
-
324
- ---
325
-
326
- ### 4. 防止原型污染
327
-
328
- ```javascript
329
- // 验证数据时避免原型污染
330
- const validator = new Validator({
331
- removeAdditional: true, // 移除额外属性
332
- useDefaults: false // 不自动填充默认值(如果不需要)
333
- });
334
- ```
335
-
336
- ---
337
-
338
- ## 错误处理
339
-
340
- ### 1. 统一错误格式
341
-
342
- **推荐的错误处理中间件**:
343
- ```javascript
344
- // Express 中间件
345
- function validateMiddleware(schema) {
346
- return (req, res, next) => {
347
- const result = validate(schema, req.body);
348
-
349
- if (!result.valid) {
350
- return res.status(400).json({
351
- code: 'VALIDATION_ERROR',
352
- message: '请求数据验证失败',
353
- errors: result.errors.map(err => ({
354
- field: err.path,
355
- message: err.message
356
- }))
357
- });
358
- }
359
-
360
- next();
361
- };
362
- }
363
-
364
- // 使用
365
- app.post('/api/user',
366
- validateMiddleware(userSchema),
367
- userController.create
368
- );
369
- ```
370
-
371
- ---
372
-
373
- ### 2. 友好的错误消息
374
-
375
- **使用 label 和自定义消息**:
376
- ```javascript
377
- const schema = dsl({
378
- username: 'string:3-32!'
379
- .label('用户名')
380
- .messages({
381
- 'required': '{{#label}}不能为空',
382
- 'min': '{{#label}}至少需要{{#limit}}个字符',
383
- 'max': '{{#label}}最多{{#limit}}个字符',
384
- 'pattern': '{{#label}}格式不正确'
385
- }),
386
-
387
- email: 'email!'
388
- .label('邮箱地址')
389
- .messages({
390
- 'required': '请填写{{#label}}',
391
- 'format': '{{#label}}格式不正确'
392
- })
393
- });
394
- ```
395
-
396
- **效果**:
397
- ```
398
- ❌ 用户名不能为空
399
- 用户名至少需要3个字符
400
- 清晰明了,用户友好
401
- ```
402
-
403
- ---
404
-
405
- ### 3. 处理异步验证错误
406
-
407
- ```javascript
408
- const schema = dsl({
409
- email: 'email!'.custom(async (value) => {
410
- try {
411
- const exists = await checkEmailExists(value);
412
- if (exists) return '邮箱已被占用';
413
- } catch (error) {
414
- // 记录错误但不阻止验证
415
- console.error('Email check failed:', error);
416
- // 可以选择跳过此验证或返回提示
417
- return; // 跳过
418
- }
419
- })
420
- });
421
- ```
422
-
423
- ---
424
-
425
- ## 代码组织
426
-
427
- ### 1. 集中管理 Schema
428
-
429
- **推荐的项目结构**:
430
- ```
431
- src/
432
- ├── schemas/
433
- │ ├── index.js # 导出所有 Schema
434
- │ ├── user.schema.js # 用户相关 Schema
435
- │ ├── post.schema.js # 文章相关 Schema
436
- │ └── common.schema.js # 通用 Schema
437
- ├── routes/
438
- │ ├── user.routes.js
439
- │ └── post.routes.js
440
- └── controllers/
441
- ```
442
-
443
- **schemas/user.schema.js**:
444
- ```javascript
445
- const { dsl } = require('schema-dsl');
446
-
447
- // 可复用的字段
448
- const commonFields = {
449
- username: dsl('string!').username().label('用户名'),
450
- email: 'email!',
451
- password: dsl('string!').password('strong').label('密码')
452
- };
453
-
454
- // 注册 Schema
455
- exports.registerSchema = dsl({
456
- ...commonFields,
457
- confirmPassword: 'string!',
458
- agreeTerms: 'boolean!'
459
- });
460
-
461
- // 登录 Schema
462
- exports.loginSchema = dsl({
463
- email: commonFields.email,
464
- password: commonFields.password
465
- });
466
-
467
- // 更新 Schema
468
- exports.updateSchema = dsl({
469
- username: commonFields.username,
470
- email: commonFields.email
471
- // 不包含密码
472
- });
473
- ```
474
-
475
- **schemas/index.js**:
476
- ```javascript
477
- const userSchemas = require('./user.schema');
478
- const postSchemas = require('./post.schema');
479
-
480
- module.exports = {
481
- user: userSchemas,
482
- post: postSchemas
483
- };
484
- ```
485
-
486
- **routes/user.routes.js**:
487
- ```javascript
488
- const schemas = require('../schemas');
489
- const { validate } = require('schema-dsl');
490
-
491
- router.post('/register', (req, res) => {
492
- const result = validate(schemas.user.registerSchema, req.body);
493
- // ...
494
- });
495
- ```
496
-
497
- ---
498
-
499
- ### 2. Schema 复用
500
-
501
- **使用 SchemaHelper**:
502
- ```javascript
503
- const { SchemaHelper } = require('schema-dsl');
504
-
505
- // 创建可复用字段库
506
- const fields = SchemaHelper.createLibrary({
507
- email: 'email!',
508
- phone: dsl('string!').phone('cn'),
509
- password: dsl('string!').password('strong')
510
- });
511
-
512
- // 在多个 Schema 中复用
513
- const registerSchema = dsl({
514
- ...fields.pick(['email', 'password']),
515
- username: 'string:3-32!'
516
- });
517
-
518
- const profileSchema = dsl({
519
- ...fields.pick(['email', 'phone']),
520
- bio: 'string:500'
521
- });
522
- ```
523
-
524
- ---
525
-
526
- ## 生产环境建议
527
-
528
- ### 1. 环境配置
529
-
530
- ```javascript
531
- // config/validator.js
532
- const { Validator } = require('schema-dsl');
533
-
534
- const config = {
535
- development: {
536
- verbose: true,
537
- allErrors: true,
538
- cache: false // 开发时不缓存,便于调试
539
- },
540
- production: {
541
- verbose: false,
542
- allErrors: false, // 只返回第一个错误
543
- cache: true // 生产环境启用缓存
544
- }
545
- };
546
-
547
- module.exports = new Validator(
548
- config[process.env.NODE_ENV || 'development']
549
- );
550
- ```
551
-
552
- ---
553
-
554
- ### 2. 监控和日志
555
-
556
- ```javascript
557
- const validator = new Validator();
558
-
559
- // 包装 validate 方法,添加监控
560
- const originalValidate = validator.validate.bind(validator);
561
- validator.validate = function(schema, data, options) {
562
- const startTime = Date.now();
563
- const result = originalValidate(schema, data, options);
564
- const duration = Date.now() - startTime;
565
-
566
- // 记录慢查询
567
- if (duration > 100) {
568
- console.warn(`Slow validation: ${duration}ms`);
569
- }
570
-
571
- // 记录验证失败
572
- if (!result.valid) {
573
- logger.info('Validation failed', {
574
- errors: result.errors.length,
575
- paths: result.errors.map(e => e.path)
576
- });
577
- }
578
-
579
- return result;
580
- };
581
- ```
582
-
583
- ---
584
-
585
- ### 3. 健康检查
586
-
587
- ```javascript
588
- // routes/health.js
589
- app.get('/health', (req, res) => {
590
- const { validator } = require('../config/validator');
591
-
592
- // 检查验证器是否正常
593
- try {
594
- const testSchema = dsl({ test: 'string!' });
595
- const result = validator.validate(testSchema, { test: 'ok' });
596
-
597
- if (!result.valid) {
598
- throw new Error('Validator test failed');
599
- }
600
-
601
- res.json({
602
- status: 'ok',
603
- validator: 'operational',
604
- cacheSize: validator.getCacheSize()
605
- });
606
- } catch (error) {
607
- res.status(500).json({
608
- status: 'error',
609
- message: error.message
610
- });
611
- }
612
- });
613
- ```
614
-
615
- ---
616
-
617
- ### 4. 定期维护
618
-
619
- ```javascript
620
- // 定期清理缓存
621
- const cron = require('node-cron');
622
-
623
- // 每天凌晨清理一次
624
- cron.schedule('0 0 * * *', () => {
625
- validator.clearCache();
626
- console.log('Validator cache cleared');
627
- });
628
-
629
- // 或者根据内存使用情况清理
630
- setInterval(() => {
631
- const memUsage = process.memoryUsage();
632
- if (memUsage.heapUsed > 500 * 1024 * 1024) { // 超过 500MB
633
- validator.clearCache();
634
- }
635
- }, 60000); // 每分钟检查一次
636
- ```
637
-
638
- ---
639
-
640
- ## 性能基准参考
641
-
642
- 基于 SchemaI-DSL 的性能测试:
643
-
644
- | 操作 | 性能指标 |
645
- |------|---------|
646
- | 简单验证(未缓存) | ~0.1ms |
647
- | 简单验证(已缓存) | ~0.01ms |
648
- | 复杂嵌套(未缓存) | ~1ms |
649
- | 复杂嵌套(已缓存) | ~0.1ms |
650
- | 批量验证(1000条) | ~100ms |
651
-
652
- **结论**: 合理使用缓存可以提升 **10-100倍** 性能。
653
-
654
- ---
655
-
656
- ## 总结
657
-
658
- 遵循这些最佳实践,你的 SchemaI-DSL 代码将具备:
659
-
660
- ✅ **高性能** - 通过预编译和缓存
661
- **高安全性** - 避免常见安全陷阱
662
- ✅ **高可维护性** - 清晰的代码组织
663
- ✅ **高可用性** - 完善的错误处理
664
-
665
- ---
666
-
667
- ## 延伸阅读
668
-
669
- - [性能优化指南](performance-guide.md)(待创建)
670
- - [安全检查清单](security-checklist.md)(待创建)
671
- - [故障排查指南](troubleshooting.md)
672
-
1
+ # schema-dsl 最佳实践
2
+
3
+ > **用途**: 帮助你写出高质量、高性能的 Schema 代码
4
+ > **更新**: 2026-05-08
5
+
6
+ ---
7
+
8
+ ## 📑 目录
9
+
10
+ - [Schema 设计原则](#schema-设计原则)
11
+ - [性能优化](#性能优化)
12
+ - [安全性考虑](#安全性考虑)
13
+ - [错误处理](#错误处理)
14
+ - [代码组织](#代码组织)
15
+ - [生产环境建议](#生产环境建议)
16
+
17
+ ---
18
+
19
+ ## Schema 设计原则
20
+
21
+ ### 1. 简单字段用纯 DSL
22
+
23
+ **推荐**:
24
+ ```javascript
25
+ const schema = dsl({
26
+ username: 'string:3-32!',
27
+ age: 'number:18-120',
28
+ email: 'email!',
29
+ role: 'admin|user|guest'
30
+ });
31
+ ```
32
+
33
+ **不推荐**(过度复杂):
34
+ ```javascript
35
+ const schema = dsl({
36
+ username: dsl('string').min(3).max(32).required(),
37
+ // 太冗长了!
38
+ });
39
+ ```
40
+
41
+ **原则**: 能用 DSL 字符串表达的,就不要用链式调用。
42
+
43
+ ---
44
+
45
+ ### 2. 复杂验证用链式调用
46
+
47
+ **适合链式调用的场景**:
48
+ - 需要正则验证
49
+ - 需要自定义错误消息
50
+ - 需要自定义验证器
51
+ - 需要标签(label)
52
+
53
+ **示例**:
54
+ ```javascript
55
+ const schema = dsl({
56
+ // 简单字段:纯 DSL
57
+ age: 'number:18-120',
58
+
59
+ // 复杂字段:链式调用
60
+ username: 'string:3-32!'
61
+ .pattern(/^[a-zA-Z0-9_]+$/)
62
+ .label('用户名')
63
+ .messages({
64
+ 'pattern': '只能包含字母、数字和下划线',
65
+ 'min': '至少3个字符',
66
+ 'max': '最多32个字符'
67
+ }),
68
+
69
+ email: 'email!'
70
+ .custom((value) => {
71
+ if (value.endsWith('@blocked.example')) return '该邮箱域名不允许注册';
72
+ })
73
+ .label('邮箱地址')
74
+ });
75
+ ```
76
+
77
+ ---
78
+
79
+ ### 3. 使用预设验证器
80
+
81
+ schema-dsl 提供了常用的预设验证器,开箱即用:
82
+
83
+ ```javascript
84
+ const schema = dsl({
85
+ // 使用预设验证器(推荐)
86
+ username: dsl('string!').username(), // 自动设置 3-32 长度 + 正则
87
+ password: dsl('string!').password('strong'), // 强密码验证
88
+ phone: dsl('string!').phone('cn'), // 中国手机号
89
+
90
+ // ❌ 手动实现(不推荐)
91
+ username: 'string:3-32!'
92
+ .pattern(/^[a-zA-Z][a-zA-Z0-9_]*$/)
93
+ });
94
+ ```
95
+
96
+ **可用预设**:
97
+ - `username(preset?)` - 用户名验证
98
+ - `password(strength?)` - 密码强度验证
99
+ - `phone(country?)` - 手机号验证
100
+ - `slug()` - URL slug 验证
101
+
102
+ ---
103
+
104
+ ### 4. 避免过深的嵌套
105
+
106
+ **不推荐**(嵌套过深):
107
+ ```javascript
108
+ const schema = dsl({
109
+ user: {
110
+ profile: {
111
+ personal: {
112
+ address: {
113
+ detail: {
114
+ street: 'string' // 嵌套 5 层
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ });
121
+ ```
122
+
123
+ **推荐**(拆分或扁平化):
124
+ ```javascript
125
+ // 方案1: 拆分为多个 Schema
126
+ const addressSchema = dsl({
127
+ street: 'string!',
128
+ city: 'string!',
129
+ zipCode: 'string'
130
+ });
131
+
132
+ const userSchema = dsl({
133
+ name: 'string!',
134
+ email: 'email!',
135
+ address: addressSchema
136
+ });
137
+
138
+ // 方案2: 扁平化
139
+ const schema = dsl({
140
+ 'user_name': 'string!',
141
+ 'user_email': 'email!',
142
+ 'address_street': 'string!',
143
+ 'address_city': 'string!'
144
+ });
145
+ ```
146
+
147
+ **原则**: 嵌套深度建议不超过 3-4 层。
148
+
149
+ ---
150
+
151
+ ## 性能优化
152
+
153
+ ### 1. 预编译 Schema
154
+
155
+ **不推荐**(每次都编译):
156
+ ```javascript
157
+ app.post('/api/user', (req, res) => {
158
+ const schema = dsl({ username: 'string!' });
159
+ const result = validate(schema, req.body); // 每次都编译
160
+ });
161
+ ```
162
+
163
+ **推荐**(预编译):
164
+ ```javascript
165
+ // 在应用启动时编译一次
166
+ const validator = new Validator();
167
+ const userSchema = dsl({ username: 'string!' });
168
+ const validateUser = validator.compile(userSchema);
169
+
170
+ app.post('/api/user', (req, res) => {
171
+ const result = validateUser(req.body); // 直接使用
172
+ });
173
+ ```
174
+
175
+ **收益**: 复用已编译结果可以显著减少重复编译成本,尤其适合热点路由和高频校验路径。
176
+
177
+ ---
178
+
179
+ ### 2. 启用缓存
180
+
181
+ ```javascript
182
+ const validator = new Validator({
183
+ cache: true // ✅ 简写:启用默认编译缓存配置
184
+ });
185
+
186
+ // 需要更细粒度时,使用对象配置
187
+ const tunedValidator = new Validator({
188
+ cache: {
189
+ enabled: true,
190
+ maxSize: 500,
191
+ ttl: 60 * 60 * 1000
192
+ }
193
+ });
194
+
195
+ // 或者使用全局单例(默认启用缓存)
196
+ const { validate } = require('schema-dsl');
197
+ validate(schema, data); // 自动缓存
198
+ ```
199
+
200
+ ---
201
+
202
+ ### 3. 批量验证
203
+
204
+ **不推荐**(循环验证):
205
+ ```javascript
206
+ const errors = [];
207
+ records.forEach(record => {
208
+ const result = validate(schema, record);
209
+ if (!result.valid) {
210
+ errors.push(result.errors);
211
+ }
212
+ });
213
+ ```
214
+
215
+ **推荐**(批量验证):
216
+ ```javascript
217
+ const { SchemaUtils, Validator } = require('schema-dsl');
218
+
219
+ const validator = new Validator();
220
+ const result = SchemaUtils.validateBatch(schema, records, validator.getAjv());
221
+ // 当你已经复用 Validator 底层 Ajv 实例时,这条路径适合批量校验
222
+ ```
223
+
224
+ > ℹ️ 如果你确实要直接传入自己创建的 Ajv 实例,请先确保它已经注册了与 schema-dsl 生成 schema 匹配的格式和关键字;对大多数项目来说,直接复用 `validator.getAjv()` 更稳妥。
225
+
226
+ ---
227
+
228
+ ### 4. 优化正则表达式
229
+
230
+ **不推荐**(可能导致 ReDoS):
231
+ ```javascript
232
+ // 危险的正则:灾难性回溯
233
+ .pattern(/^(a+)+$/)
234
+ .pattern(/^(a*)*$/)
235
+ .pattern(/^(a|a)*$/)
236
+ ```
237
+
238
+ **推荐**(安全高效):
239
+ ```javascript
240
+ // 简单明确的正则
241
+ .pattern(/^[a-zA-Z0-9_]+$/)
242
+ .pattern(/^[a-z]{3,10}$/)
243
+ ```
244
+
245
+ **工具**: 使用 [regexploit](https://www.npmjs.com/package/regexploit) 检测危险正则。
246
+
247
+ ---
248
+
249
+ ### 5. 避免在循环中创建 Schema
250
+
251
+ **不推荐**:
252
+ ```javascript
253
+ records.forEach(record => {
254
+ const schema = dsl({ name: 'string!' }); // 每次都创建
255
+ validate(schema, record);
256
+ });
257
+ ```
258
+
259
+ **推荐**:
260
+ ```javascript
261
+ const schema = dsl({ name: 'string!' }); // 创建一次
262
+ records.forEach(record => {
263
+ validate(schema, record); // 重复使用
264
+ });
265
+ ```
266
+
267
+ ---
268
+
269
+ ## 安全性考虑
270
+
271
+ ### 1. 限制用户输入的正则
272
+
273
+ **危险**:
274
+ ```javascript
275
+ // ❌ 用户控制的正则表达式
276
+ app.post('/api/validate', (req, res) => {
277
+ const pattern = req.body.pattern; // 用户输入
278
+ const schema = dsl('string').pattern(new RegExp(pattern)); // 危险!
279
+ });
280
+ ```
281
+
282
+ **原因**: 用户可能输入恶意正则导致 ReDoS 攻击。
283
+
284
+ **安全做法**:
285
+ ```javascript
286
+ // ✅ 使用预定义的正则
287
+ const ALLOWED_PATTERNS = {
288
+ username: /^[a-zA-Z0-9_]+$/,
289
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
290
+ };
291
+
292
+ app.post('/api/validate', (req, res) => {
293
+ const patternName = req.body.pattern;
294
+ const pattern = ALLOWED_PATTERNS[patternName];
295
+ if (!pattern) {
296
+ return res.status(400).json({ error: 'Invalid pattern' });
297
+ }
298
+ const schema = dsl('string').pattern(pattern);
299
+ });
300
+ ```
301
+
302
+ ---
303
+
304
+ ### 2. 清理错误消息
305
+
306
+ 生产环境不要暴露敏感信息:
307
+
308
+ ```javascript
309
+ // 开发环境
310
+ if (process.env.NODE_ENV === 'development') {
311
+ return res.status(400).json({
312
+ valid: false,
313
+ errors: result.errors // 详细错误
314
+ });
315
+ }
316
+
317
+ // 生产环境
318
+ return res.status(400).json({
319
+ valid: false,
320
+ message: '输入数据验证失败' // 简化消息
321
+ });
322
+ ```
323
+
324
+ ---
325
+
326
+ ### 3. 限制 Schema 复杂度
327
+
328
+ ```javascript
329
+ const MAX_SCHEMA_SIZE = 10000;
330
+
331
+ if (JSON.stringify(schema).length > MAX_SCHEMA_SIZE) {
332
+ throw new Error('Schema 体积过大,建议拆分');
333
+ }
334
+
335
+ // 在 validate 前检查
336
+ const depthCheck = DslBuilder.validateNestingDepth(schema, 10);
337
+ if (!depthCheck.valid) {
338
+ throw new Error(depthCheck.message);
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ### 4. 防止原型污染
345
+
346
+ ```javascript
347
+ // 验证数据时避免原型污染
348
+ const validator = new Validator({
349
+ removeAdditional: true, // 移除额外属性
350
+ useDefaults: false // 不自动填充默认值(如果不需要)
351
+ });
352
+ ```
353
+
354
+ ---
355
+
356
+ ## 错误处理
357
+
358
+ ### 1. 统一错误格式
359
+
360
+ **推荐的错误处理中间件**:
361
+ ```javascript
362
+ // Express 中间件
363
+ function validateMiddleware(schema) {
364
+ return (req, res, next) => {
365
+ const result = validate(schema, req.body);
366
+
367
+ if (!result.valid) {
368
+ return res.status(400).json({
369
+ code: 'VALIDATION_ERROR',
370
+ message: '请求数据验证失败',
371
+ errors: result.errors.map(err => ({
372
+ field: err.path,
373
+ message: err.message
374
+ }))
375
+ });
376
+ }
377
+
378
+ next();
379
+ };
380
+ }
381
+
382
+ // 使用
383
+ app.post('/api/user',
384
+ validateMiddleware(userSchema),
385
+ userController.create
386
+ );
387
+ ```
388
+
389
+ ---
390
+
391
+ ### 2. 友好的错误消息
392
+
393
+ **使用 label 和自定义消息**:
394
+ ```javascript
395
+ const schema = dsl({
396
+ username: 'string:3-32!'
397
+ .label('用户名')
398
+ .messages({
399
+ 'required': '{{#label}}不能为空',
400
+ 'min': '{{#label}}至少需要{{#limit}}个字符',
401
+ 'max': '{{#label}}最多{{#limit}}个字符',
402
+ 'pattern': '{{#label}}格式不正确'
403
+ }),
404
+
405
+ email: 'email!'
406
+ .label('邮箱地址')
407
+ .messages({
408
+ 'required': '请填写{{#label}}',
409
+ 'format': '{{#label}}格式不正确'
410
+ })
411
+ });
412
+ ```
413
+
414
+ **效果**:
415
+ ```text
416
+ 用户名不能为空
417
+ 用户名至少需要3个字符
418
+ ✅ 清晰明了,用户友好
419
+ ```
420
+
421
+ ---
422
+
423
+ ### 3. 处理外部异步校验错误
424
+
425
+ > `.custom()` 当前仅支持同步函数;涉及数据库、RPC、HTTP 等异步检查时,请在基础校验通过后于业务层单独执行。
426
+
427
+ ```javascript
428
+ const schema = dsl({
429
+ email: 'email!'.label('邮箱地址')
430
+ });
431
+
432
+ async function validateUser(data) {
433
+ const result = validate(schema, data);
434
+ if (!result.valid) return result;
435
+
436
+ try {
437
+ const exists = await checkEmailExists(data.email);
438
+ if (exists) {
439
+ return {
440
+ valid: false,
441
+ errors: [{ field: 'email', keyword: 'business', message: '邮箱已被占用' }]
442
+ };
443
+ }
444
+ } catch (error) {
445
+ console.error('Email check failed:', error);
446
+ }
447
+
448
+ return result;
449
+ }
450
+ ```
451
+
452
+ ---
453
+
454
+ ## 代码组织
455
+
456
+ ### 1. 集中管理 Schema
457
+
458
+ **推荐的项目结构**:
459
+ ```text
460
+ src/
461
+ ├── schemas/
462
+ │ ├── index.js # 导出所有 Schema
463
+ │ ├── user.schema.js # 用户相关 Schema
464
+ │ ├── post.schema.js # 文章相关 Schema
465
+ │ └── common.schema.js # 通用 Schema
466
+ ├── routes/
467
+ │ ├── user.routes.js
468
+ │ └── post.routes.js
469
+ └── controllers/
470
+ ```
471
+
472
+ **schemas/user.schema.js**:
473
+ ```javascript
474
+ const { dsl } = require('schema-dsl');
475
+
476
+ // 可复用的字段
477
+ const commonFields = {
478
+ username: dsl('string!').username().label('用户名'),
479
+ email: 'email!',
480
+ password: dsl('string!').password('strong').label('密码')
481
+ };
482
+
483
+ // 注册 Schema
484
+ exports.registerSchema = dsl({
485
+ ...commonFields,
486
+ confirmPassword: 'string!',
487
+ agreeTerms: 'boolean!'
488
+ });
489
+
490
+ // 登录 Schema
491
+ exports.loginSchema = dsl({
492
+ email: commonFields.email,
493
+ password: commonFields.password
494
+ });
495
+
496
+ // 更新 Schema
497
+ exports.updateSchema = dsl({
498
+ username: commonFields.username,
499
+ email: commonFields.email
500
+ // 不包含密码
501
+ });
502
+ ```
503
+
504
+ **schemas/index.js**:
505
+ ```javascript
506
+ const userSchemas = require('./user.schema');
507
+ const postSchemas = require('./post.schema');
508
+
509
+ module.exports = {
510
+ user: userSchemas,
511
+ post: postSchemas
512
+ };
513
+ ```
514
+
515
+ **routes/user.routes.js**:
516
+ ```javascript
517
+ const schemas = require('../schemas');
518
+ const { validate } = require('schema-dsl');
519
+
520
+ router.post('/register', (req, res) => {
521
+ const result = validate(schemas.user.registerSchema, req.body);
522
+ // ...
523
+ });
524
+ ```
525
+
526
+ ---
527
+
528
+ ### 2. Schema 复用
529
+
530
+ **使用 SchemaUtils**:
531
+ ```javascript
532
+ const { SchemaUtils, dsl } = require('schema-dsl');
533
+
534
+ // 创建可复用字段库
535
+ const fields = SchemaUtils.createLibrary({
536
+ email: () => 'email!',
537
+ phone: () => dsl('string!').phone('cn'),
538
+ password: () => dsl('string!').password('strong')
539
+ });
540
+
541
+ // 在多个 Schema 中复用
542
+ const registerSchema = dsl({
543
+ email: fields.email(),
544
+ password: fields.password(),
545
+ username: 'string:3-32!'
546
+ });
547
+
548
+ const profileSchema = dsl({
549
+ email: fields.email(),
550
+ phone: fields.phone(),
551
+ bio: 'string:500'
552
+ });
553
+ ```
554
+
555
+ ---
556
+
557
+ ## 生产环境建议
558
+
559
+ ### 1. 环境配置
560
+
561
+ ```javascript
562
+ // config/validator.js
563
+ const { Validator } = require('schema-dsl');
564
+
565
+ const config = {
566
+ development: {
567
+ verbose: true,
568
+ allErrors: true,
569
+ cache: false // ✅ 简写:关闭缓存,便于调试
570
+ },
571
+ production: {
572
+ verbose: false,
573
+ allErrors: false, // 只返回第一个错误
574
+ cache: {
575
+ enabled: true,
576
+ maxSize: 1000,
577
+ ttl: 60 * 60 * 1000
578
+ }
579
+ }
580
+ };
581
+
582
+ module.exports = new Validator(
583
+ config[process.env.NODE_ENV || 'development']
584
+ );
585
+ ```
586
+
587
+ ---
588
+
589
+ ### 2. 监控和日志
590
+
591
+ ```javascript
592
+ const validator = new Validator();
593
+
594
+ // 包装 validate 方法,添加监控
595
+ const originalValidate = validator.validate.bind(validator);
596
+ validator.validate = function(schema, data, options) {
597
+ const startTime = Date.now();
598
+ const result = originalValidate(schema, data, options);
599
+ const duration = Date.now() - startTime;
600
+
601
+ // 记录慢查询
602
+ if (duration > 100) {
603
+ console.warn(`Slow validation: ${duration}ms`);
604
+ }
605
+
606
+ // 记录验证失败
607
+ if (!result.valid) {
608
+ logger.info('Validation failed', {
609
+ errors: result.errors.length,
610
+ paths: result.errors.map(e => e.path)
611
+ });
612
+ }
613
+
614
+ return result;
615
+ };
616
+ ```
617
+
618
+ ---
619
+
620
+ ### 3. 健康检查
621
+
622
+ ```javascript
623
+ // routes/health.js
624
+ app.get('/health', (req, res) => {
625
+ const { validator } = require('../config/validator');
626
+
627
+ // 检查验证器是否正常
628
+ try {
629
+ const testSchema = dsl({ test: 'string!' });
630
+ const result = validator.validate(testSchema, { test: 'ok' });
631
+
632
+ if (!result.valid) {
633
+ throw new Error('Validator test failed');
634
+ }
635
+
636
+ res.json({
637
+ status: 'ok',
638
+ validator: 'operational',
639
+ cacheStats: validator.getCacheStats()
640
+ });
641
+ } catch (error) {
642
+ res.status(500).json({
643
+ status: 'error',
644
+ message: error.message
645
+ });
646
+ }
647
+ });
648
+ ```
649
+
650
+ ---
651
+
652
+ ### 4. 定期维护
653
+
654
+ ```javascript
655
+ // 定期清理缓存
656
+ const cron = require('node-cron');
657
+
658
+ // 每天凌晨清理一次
659
+ cron.schedule('0 0 * * *', () => {
660
+ validator.clearCache();
661
+ console.log('Validator cache cleared');
662
+ });
663
+
664
+ // 或者根据内存使用情况清理
665
+ setInterval(() => {
666
+ const memUsage = process.memoryUsage();
667
+ if (memUsage.heapUsed > 500 * 1024 * 1024) { // 超过 500MB
668
+ validator.clearCache();
669
+ }
670
+ }, 60000); // 每分钟检查一次
671
+ ```
672
+
673
+ ---
674
+
675
+ ## 性能基准参考
676
+
677
+ 缓存命中前后通常能显著降低重复编译开销,但绝对耗时会受到机器性能、Node 版本、schema 复杂度、数据规模和命中率影响,不建议把某组固定毫秒数当成通用基准。
678
+
679
+ **更稳定的结论**:
680
+
681
+ - 复用同一个 schema 对象或 `Validator` 实例,通常比每次请求都重新编译更快
682
+ - schema 越复杂、重复验证次数越多,缓存收益通常越明显
683
+ - 批量验证总耗时主要取决于单条 schema 复杂度和数据规模,不应使用固定毫秒数做容量承诺
684
+
685
+ 如需当前可复查的吞吐量对比,请以维护中的 benchmark 结果和 FAQ 中同步的性能数据为准。
686
+
687
+ ---
688
+
689
+ ## 总结
690
+
691
+ 遵循这些最佳实践,你的 schema-dsl 代码将具备:
692
+
693
+ ✅ **高性能** - 通过预编译和缓存
694
+ ✅ **高安全性** - 避免常见安全陷阱
695
+ ✅ **高可维护性** - 清晰的代码组织
696
+ ✅ **高可用性** - 完善的错误处理
697
+
698
+ ---
699
+
700
+ ## 延伸阅读
701
+
702
+ - [性能优化指南](performance-guide.md)
703
+ - [安全检查清单](security-checklist.md)
704
+ - [故障排查指南](troubleshooting.md)
705
+
706
+ ---
707
+
708
+ ## 对应示例文件
709
+
710
+ **示例入口**: [best-practices.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/best-practices.ts)
711
+ **说明**: 展示“简单字段用纯 DSL、复杂字段局部使用 Builder、字段库复用”的推荐组合,以及成功 / 失败两条验证路径。
712
+