schema-dsl 2.0.0 → 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 (145) hide show
  1. package/CHANGELOG.md +130 -113
  2. package/LICENSE +21 -21
  3. package/README.md +628 -628
  4. package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
  5. package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
  6. package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
  7. package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
  8. package/dist/index.cjs +75 -29
  9. package/dist/index.d.cts +10 -4
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.js +75 -29
  12. package/dist/plugins/custom-format.cjs +33 -17
  13. package/dist/plugins/custom-format.d.cts +1 -1
  14. package/dist/plugins/custom-format.d.ts +1 -1
  15. package/dist/plugins/custom-format.js +33 -17
  16. package/dist/plugins/custom-type-example.cjs +33 -17
  17. package/dist/plugins/custom-type-example.d.cts +1 -1
  18. package/dist/plugins/custom-type-example.d.ts +1 -1
  19. package/dist/plugins/custom-type-example.js +33 -17
  20. package/dist/plugins/custom-validator.cjs +0 -2
  21. package/dist/plugins/custom-validator.d.cts +1 -1
  22. package/dist/plugins/custom-validator.d.ts +1 -1
  23. package/dist/plugins/custom-validator.js +0 -2
  24. package/docs/FEATURE-INDEX.md +553 -553
  25. package/docs/add-custom-locale.md +496 -496
  26. package/docs/add-keyword.md +24 -24
  27. package/docs/api-reference.md +1047 -1047
  28. package/docs/api.md +13 -13
  29. package/docs/best-practices-project-structure.md +417 -417
  30. package/docs/best-practices.md +712 -712
  31. package/docs/cache-manager.md +344 -344
  32. package/docs/compile.md +45 -45
  33. package/docs/conditional-api.md +1307 -1307
  34. package/docs/custom-extensions-guide.md +339 -339
  35. package/docs/design-philosophy.md +606 -606
  36. package/docs/doc-index.md +324 -324
  37. package/docs/dsl-syntax.md +714 -714
  38. package/docs/dynamic-locale.md +608 -608
  39. package/docs/enum.md +482 -482
  40. package/docs/error-handling.md +1975 -1975
  41. package/docs/export-guide.md +501 -501
  42. package/docs/export-limitations.md +567 -567
  43. package/docs/faq.md +596 -596
  44. package/docs/frontend-i18n-guide.md +307 -307
  45. package/docs/i18n-user-guide.md +487 -487
  46. package/docs/i18n.md +476 -476
  47. package/docs/index.md +48 -48
  48. package/docs/json-schema-basics.md +40 -40
  49. package/docs/label-vs-description.md +271 -271
  50. package/docs/markdown-exporter.md +406 -406
  51. package/docs/mongodb-exporter.md +302 -302
  52. package/docs/multi-language.md +26 -26
  53. package/docs/multi-type-support.md +322 -322
  54. package/docs/mysql-exporter.md +280 -280
  55. package/docs/number-operators.md +449 -449
  56. package/docs/optional-marker-guide.md +326 -326
  57. package/docs/performance-guide.md +49 -49
  58. package/docs/plugin-system.md +381 -381
  59. package/docs/plugin-type-registration.md +34 -34
  60. package/docs/postgresql-exporter.md +311 -311
  61. package/docs/public/favicon.svg +4 -4
  62. package/docs/quick-start.md +435 -435
  63. package/docs/runtime-locale-support.md +532 -532
  64. package/docs/schema-helper.md +345 -345
  65. package/docs/schema-utils-advanced-issues.md +23 -23
  66. package/docs/schema-utils-best-practices.md +20 -20
  67. package/docs/schema-utils-chaining.md +150 -150
  68. package/docs/schema-utils.md +524 -524
  69. package/docs/security-checklist.md +20 -20
  70. package/docs/string-extensions.md +488 -488
  71. package/docs/troubleshooting.md +486 -486
  72. package/docs/type-converter.md +310 -310
  73. package/docs/type-reference.md +242 -242
  74. package/docs/typescript-guide.md +584 -584
  75. package/docs/union-type-guide.md +157 -157
  76. package/docs/union-types.md +284 -284
  77. package/docs/validate-async.md +491 -491
  78. package/docs/validate-batch.md +49 -49
  79. package/docs/validate-dsl-object-support.md +578 -578
  80. package/docs/validate.md +506 -506
  81. package/docs/validation-guide.md +502 -502
  82. package/docs/validator.md +39 -39
  83. package/package.json +131 -131
  84. package/plugins/custom-format.cjs +8 -8
  85. package/plugins/custom-type-example.cjs +8 -8
  86. package/plugins/custom-validator.cjs +8 -8
  87. package/src/adapters/DslAdapter.ts +111 -111
  88. package/src/adapters/index.ts +1 -1
  89. package/src/config/constants.ts +83 -83
  90. package/src/config/index.ts +2 -2
  91. package/src/config/patterns.ts +77 -77
  92. package/src/core/CacheManager.ts +169 -159
  93. package/src/core/ConditionalBuilder.ts +382 -382
  94. package/src/core/ConditionalRuntime.ts +27 -27
  95. package/src/core/ConditionalValidator.ts +254 -254
  96. package/src/core/DslBuilder.ts +687 -677
  97. package/src/core/ErrorCodes.ts +38 -38
  98. package/src/core/ErrorFormatter.ts +271 -271
  99. package/src/core/JSONSchemaCore.ts +65 -65
  100. package/src/core/Locale.ts +187 -187
  101. package/src/core/MessageTemplate.ts +42 -42
  102. package/src/core/ObjectDslBuilder.ts +64 -64
  103. package/src/core/PluginManager.ts +326 -326
  104. package/src/core/StringExtensions.ts +140 -140
  105. package/src/core/TemplateEngine.ts +44 -44
  106. package/src/core/Validator.ts +448 -448
  107. package/src/errors/I18nError.ts +159 -159
  108. package/src/errors/ValidationError.ts +105 -105
  109. package/src/exporters/BaseExporter.ts +60 -60
  110. package/src/exporters/MarkdownExporter.ts +305 -305
  111. package/src/exporters/MongoDBExporter.ts +126 -126
  112. package/src/exporters/MySQLExporter.ts +156 -155
  113. package/src/exporters/PostgreSQLExporter.ts +222 -222
  114. package/src/exporters/index.ts +18 -18
  115. package/src/index.ts +651 -633
  116. package/src/locales/en-US.ts +160 -160
  117. package/src/locales/es-ES.ts +160 -160
  118. package/src/locales/fr-FR.ts +160 -160
  119. package/src/locales/index.ts +103 -103
  120. package/src/locales/ja-JP.ts +160 -160
  121. package/src/locales/types.ts +156 -156
  122. package/src/locales/zh-CN.ts +160 -160
  123. package/src/parser/ConstraintParser.ts +101 -101
  124. package/src/parser/DslParser.ts +470 -470
  125. package/src/parser/SchemaCompiler.ts +66 -66
  126. package/src/parser/TypeRegistry.ts +250 -250
  127. package/src/parser/index.ts +6 -6
  128. package/src/plugins/custom-format.ts +124 -126
  129. package/src/plugins/custom-type-example.ts +106 -108
  130. package/src/plugins/custom-validator.ts +138 -140
  131. package/src/types/conditional.ts +28 -28
  132. package/src/types/config.ts +59 -59
  133. package/src/types/dsl.ts +131 -131
  134. package/src/types/error.ts +60 -60
  135. package/src/types/index.ts +17 -17
  136. package/src/types/infer.ts +127 -127
  137. package/src/types/plugin.ts +58 -58
  138. package/src/types/safe-regex.d.ts +9 -9
  139. package/src/types/schema.ts +66 -66
  140. package/src/types/validate.ts +71 -71
  141. package/src/utils/SchemaHelper.ts +196 -196
  142. package/src/utils/SchemaUtils.ts +365 -346
  143. package/src/utils/TypeConverter.ts +215 -215
  144. package/src/utils/index.ts +10 -10
  145. package/src/validators/CustomKeywords.ts +477 -477
package/docs/faq.md CHANGED
@@ -1,596 +1,596 @@
1
- # 常见问题解答 (FAQ)
2
-
3
- > **更新时间**: 2026-05-08
4
-
5
-
6
- ---
7
-
8
- ## 📑 目录
9
-
10
- - [基础问题](#基础问题)
11
- - [DSL 语法问题](#dsl-语法问题)
12
- - [验证问题](#验证问题)
13
- - [性能问题](#性能问题)
14
- - [设计理念](#设计理念)
15
- - [错误处理](#错误处理)
16
- - [数据库导出](#数据库导出)
17
- - [TypeScript 支持](#typescript-支持)
18
-
19
- ---
20
-
21
- ## 基础问题
22
-
23
- ### Q: schema-dsl 和 Joi、Yup 有什么区别?
24
-
25
- **A**: schema-dsl 采用 DSL 语法,更简洁:
26
-
27
- ```javascript
28
- // schema-dsl - 简洁
29
- const schema = dsl({
30
- username: 'string:3-32!',
31
- email: 'email!'
32
- });
33
-
34
- // Joi - 繁琐
35
- const schema = Joi.object({
36
- username: Joi.string().min(3).max(32).required(),
37
- email: Joi.string().email().required()
38
- });
39
- ```
40
-
41
- **主要区别**:
42
- - 更简洁的 DSL 语法
43
- - 支持数据库 Schema 导出
44
- - 内置常见验证器(username、password、phone)
45
- - 基于 JSON Schema 标准
46
-
47
- ---
48
-
49
- ### Q: 如何安装 schema-dsl?
50
-
51
- ```bash
52
- npm install schema-dsl
53
- ```
54
-
55
- **Node.js 版本要求**:`>=18.0.0`
56
-
57
- 当前 TypeScript 重构版以 `Node.js >=18.0.0` 为唯一运行时基线,不再承诺旧 Node 版本兼容。
58
-
59
- ---
60
-
61
- ### Q: 支持 ES Modules 吗?
62
-
63
- **A**: 支持。
64
-
65
- ```javascript
66
- // CommonJS
67
- const { dsl, validate } = require('schema-dsl');
68
-
69
- // ES Modules(named import)
70
- import { dsl, validate } from 'schema-dsl';
71
-
72
- // ES Modules(default import)
73
- import dslDefault from 'schema-dsl';
74
- ```
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
-
88
- ---
89
-
90
- ## DSL 语法问题
91
-
92
- ### Q: `'string:3-32!'` 是什么意思?
93
-
94
- **A**: 这是 DSL 语法:
95
- - `string` - 类型
96
- - `3-32` - 长度范围(最小3,最大32)
97
- - `!` - 必填
98
-
99
- 更多示例:
100
- ```javascript
101
- 'string:10' // 最大长度10
102
- 'string:3-' // 最小长度3
103
- 'number:0-100' // 数值范围0-100
104
- 'email!' // 必填邮箱
105
- 'a|b|c' // 枚举值
106
- ```
107
-
108
- ---
109
-
110
- ### Q: 如何定义数组?
111
-
112
- **A**: 使用 `array` 类型:
113
-
114
- ```javascript
115
- // 简单数组
116
- tags: 'array'
117
-
118
- // 带长度约束
119
- tags: 'array:1-10' // 1-10个元素
120
- tags: 'array!1-10' // 必填,1-10个元素
121
-
122
- // 带元素类型
123
- tags: 'array<string>' // 字符串数组
124
- tags: 'array<number>' // 数字数组
125
- tags: 'array<string:1-20>' // 带约束的字符串数组
126
- ```
127
-
128
- ---
129
-
130
- ### Q: 如何定义嵌套对象?
131
-
132
- **A**: 直接嵌套即可:
133
-
134
- ```javascript
135
- const schema = dsl({
136
- user: {
137
- name: 'string!',
138
- address: {
139
- city: 'string!',
140
- zip: 'string:5-10!'
141
- }
142
- }
143
- });
144
- ```
145
-
146
- ---
147
-
148
- ### Q: 如何使用 String 扩展?
149
-
150
- **A**: 字符串可以直接链式调用方法:
151
-
152
- ```javascript
153
- const schema = dsl({
154
- email: 'email!'
155
- .label('邮箱地址')
156
- .messages({
157
- 'required': '{{#label}}不能为空',
158
- 'format': '请输入有效的{{#label}}'
159
- }),
160
-
161
- username: 'string:3-32!'
162
- .pattern(/^[a-z0-9_]+$/)
163
- .label('用户名')
164
- .username('medium')
165
- });
166
- ```
167
-
168
- ---
169
-
170
- ## 验证问题
171
-
172
- ### Q: 如何验证数据?
173
-
174
- **A**: 使用 `validate()` 函数或 `Validator` 类:
175
-
176
- ```javascript
177
- // 方式1:便捷函数
178
- const { dsl, validate } = require('schema-dsl');
179
- const result = validate(schema, data);
180
-
181
- // 方式2:Validator 实例
182
- const { Validator } = require('schema-dsl');
183
- const validator = new Validator();
184
- const result = validator.validate(schema, data);
185
- ```
186
-
187
- ---
188
-
189
- ### Q: 验证结果的格式是什么?
190
-
191
- **A**: 返回对象包含:
192
-
193
- ```javascript
194
- {
195
- valid: true/false, // 是否通过
196
- data: {}, // 当前实现会返回本次验证数据,失败时也便于定位输入
197
- errors: [] // 成功时为空数组,失败时包含详细错误
198
- }
199
- ```
200
-
201
- ---
202
-
203
- ### Q: 如何获取所有错误而不是只有第一个?
204
-
205
- **A**: 默认就会返回全部错误。如果你只想保留首条错误,可以显式关闭 `allErrors`:
206
-
207
- ```javascript
208
- const validator = new Validator({ allErrors: false });
209
- ```
210
-
211
- `allErrors` 需要在创建 `Validator` 实例时配置,`validator.validate(schema, data, options)` 不能按次覆盖这个开关。
212
-
213
- ---
214
-
215
- ### Q: 如何使用默认值?
216
-
217
- **A**: 使用 `.default()` 方法:
218
-
219
- ```javascript
220
- const schema = dsl({
221
- status: 'string'.default('active'),
222
- count: 'integer'.default(0)
223
- });
224
-
225
- const result = validate(schema, {});
226
- console.log(result.data);
227
- // { status: 'active', count: 0 }
228
- ```
229
-
230
- ---
231
-
232
- ## 性能问题
233
-
234
- ### Q: schema-dsl 的性能怎么样?
235
-
236
- **A**: 性能不错,**S3 嵌套场景快于 Zod(28%),无效数据公平对比快 89x**:
237
-
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 | — | 底层引擎 |
244
-
245
- **结论**:
246
- - ✅ S3 嵌套场景快于 Zod(**28%**),S1 简单有效场景持平;无效数据公平对比快 **89x**
247
- - ✅ 比 Joi 快约 **13x**(无效数据公平对比)
248
- - ✅ 内置缓存确保热路径零解析开销
249
-
250
- ---
251
-
252
- ### Q: 有效/无效数据场景性能差异为什么大?
253
-
254
- **A**: 公平对比(S2,均不做 i18n 格式化)schema-dsl 显著快于 Zod(**89x**)。Zod 在无效数据场景极慢的根源是其错误收集使用异常驱动(`try/catch` 控制流),每个无效字段抛出一次 Error,4 个错误字段 = 4 次 Error 实例创建 + 4 次堆栈捕获。schema-dsl 基于 AJV 的无异常收集路径,无格式化时达 1.2M ops/s。
255
-
256
- ---
257
-
258
- ### Q: 什么时候性能会成为瓶颈?
259
-
260
- **A**: 以下场景才可能成为瓶颈:
261
-
262
- 1. **API 网关**(每秒 >50万次验证)
263
- 2. **高并发服务**(每秒 >50万次请求)
264
- 3. **实时数据处理**(毫秒级延迟要求)
265
-
266
- **大多数应用**(每秒 <10万次验证)不会遇到性能瓶颈。
267
-
268
- ---
269
-
270
- ### Q: 验证速度慢怎么办?
271
-
272
- **A**: 使用预编译和缓存:
273
-
274
- ```javascript
275
- // 1. 使用预编译
276
- const validator = new Validator();
277
- const validateUser = validator.compile(userSchema);
278
-
279
- // 2. 启用缓存
280
-
281
- const validator = new Validator({
282
- cache: {
283
- maxSize: 5000, // 缓存5000个Schema
284
- ttl: 3600000 // 1小时过期
285
- }
286
- });
287
-
288
- // 3. 复用 Validator 实例
289
- // ❌ 错误:每次都创建新实例
290
- app.post('/api/users', (req, res) => {
291
- const validator = new Validator(); // 慢
292
- // ...
293
- });
294
-
295
- // ✅ 正确:复用实例
296
- const validator = new Validator();
297
- app.post('/api/users', (req, res) => {
298
- const result = validator.validate(schema, req.body); // 快
299
- // ...
300
- });
301
- ```
302
-
303
- ---
304
-
305
- ### Q: 缓存如何工作?
306
-
307
- **A**: schema-dsl 当前通过 `CacheManager` 委托 `cache-hub` 的 `MemoryCache` 实现编译缓存:
308
-
309
- ```javascript
310
- const validator = new Validator({
311
- cache: {
312
- maxSize: 5000, // 最大缓存5000条
313
- ttl: 3600000 // 1小时过期
314
- }
315
- });
316
-
317
- // 缓存统计
318
- const stats = validator.getCacheStats();
319
- console.log(stats);
320
- // {
321
- // hits: 8500,
322
- // misses: 150,
323
- // hitRate: '98.27',
324
- // size: 150,
325
- // maxSize: 5000,
326
- // enabled: true
327
- // }
328
- ```
329
-
330
- ---
331
-
332
- ### Q: 如何批量验证?
333
-
334
- **A**: 使用 `SchemaUtils.validateBatch()`:
335
-
336
- ```javascript
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);
344
- ```
345
-
346
- ---
347
-
348
- ## 设计理念
349
-
350
- ### Q: 为什么选择运行时解析而不是编译时构建?
351
-
352
- **A**: 这是有意的设计选择,优先考虑**灵活性**而非**极致性能**。
353
-
354
- **运行时解析的优势**:
355
- 1. ✅ **完全动态** - 可从配置/数据库动态生成规则
356
- 2. ✅ **多租户支持** - 每个租户不同规则,零代码修改
357
- 3. ✅ **可序列化** - 可存储、传输、共享
358
- 4. ✅ **前后端共享** - 一套规则,两端使用
359
- 5. ✅ **低代码基础** - 可视化配置表单验证
360
-
361
- **编译时构建的限制**:
362
- - ❌ Schema 固定,无法动态调整
363
- - ❌ 无法序列化和传输
364
- - ❌ 多租户困难
365
- - ❌ 无法从数据库读取规则
366
-
367
- **详细说明**: [设计理念文档](design-philosophy.md)
368
-
369
- ---
370
-
371
- ### Q: Schema-DSL 适合什么场景?
372
-
373
- **A**: ✅ **最适合的场景**:
374
-
375
- 1. **多租户 SaaS 系统** - 每个租户不同验证规则
376
- 2. **后台管理系统** - 管理员配置表单验证
377
- 3. **配置驱动开发** - 验证规则存储在配置/数据库
378
- 4. **低代码/无代码平台** - 可视化表单构建器
379
- 5. **快速原型开发** - 5分钟上手,代码量最少
380
- 6. **前后端共享验证** - 一套规则,两端使用
381
-
382
- ⚠️ **不适合的场景**:
383
- 1. 绝对吞吐量优先、且不需要 DSL 动态能力 → 推荐 **Ajv**
384
- 2. TypeScript 强类型推断 → 推荐 **Zod**
385
- 3. 静态验证规则 → 推荐 **Zod**
386
-
387
- ---
388
-
389
- ### Q: 为什么不做成像 Zod 那样的编译时库?
390
-
391
- **A**: 因为会失去核心价值:
392
-
393
- **失去的能力**:
394
- ```javascript
395
- // ❌ 无法从数据库读取规则
396
- const rules = await db.findOne({ entity: 'user' });
397
- const schema = dsl(rules);
398
-
399
- // ❌ 无法多租户动态规则
400
- function getTenantSchema(tenantId) {
401
- return dsl(tenantConfig[tenantId]);
402
- }
403
-
404
- // ❌ 无法通过 API 传输
405
- res.json({ validationRules: rules });
406
-
407
- // ❌ 无法后台配置表单验证
408
- ```
409
-
410
- **保留的能力**:
411
- ```javascript
412
- // ✅ 完全动态
413
- const schema = dsl({
414
- username: `string:${config.min}-${config.max}!`
415
- });
416
-
417
- // ✅ 可序列化
418
- JSON.stringify({ username: 'string:3-32!' });
419
-
420
- // ✅ 前后端共享
421
- // 后端定义 → API传输 → 前端使用
422
- ```
423
-
424
- ---
425
-
426
- ### Q: 性能和灵活性如何平衡?
427
-
428
- **A**: Schema-DSL 的设计优先级:
429
-
430
- ```text
431
- 灵活性 > 易用性 > 性能
432
- ```
433
-
434
- **权衡结果**:
435
- - 增益:S3 嵌套场景快于 Zod 28%,S1 持平;无效数据公平对比快 89x
436
-
437
- ---
438
-
439
- ## 错误处理
440
-
441
- ### Q: 如何自定义错误消息?
442
-
443
- **A**: 使用 `.messages()` 方法:
444
-
445
- ```javascript
446
- username: 'string:3-32!'
447
- .label('用户名')
448
- .messages({
449
- 'min': '{{#label}}太短了',
450
- 'max': '{{#label}}太长了',
451
- 'required': '请输入{{#label}}'
452
- })
453
- ```
454
-
455
- ---
456
-
457
- ### Q: 如何支持多语言?
458
-
459
- **A**: 使用 `Locale` 类:
460
-
461
- ```javascript
462
- const { Locale } = require('schema-dsl');
463
-
464
- // 添加语言包
465
- Locale.addLocale('zh-CN', {
466
- 'required': '{{#label}}不能为空',
467
- 'min': '{{#label}}长度不能少于{{#limit}}'
468
- });
469
-
470
- // 验证时指定语言
471
- validator.validate(schema, data, { locale: 'zh-CN' });
472
- ```
473
-
474
- ---
475
-
476
- ### Q: 错误路径格式是什么?
477
-
478
- **A**: 当前返回的是 slash path:
479
-
480
- ```javascript
481
- 'username' // 顶层字段
482
- 'user/name' // 嵌套字段
483
- 'items/0/name' // 数组元素
484
- ```
485
-
486
- ---
487
-
488
- ## 数据库导出
489
-
490
- ### Q: 如何导出为 MongoDB Schema?
491
-
492
- ```javascript
493
- const { exporters } = require('schema-dsl');
494
-
495
- const exporter = new exporters.MongoDBExporter();
496
- const mongoSchema = exporter.export(schema);
497
- ```
498
-
499
- ---
500
-
501
- ### Q: 如何导出为 MySQL DDL?
502
-
503
- ```javascript
504
- const exporter = new exporters.MySQLExporter();
505
- const ddl = exporter.export('table_name', schema);
506
- ```
507
-
508
- ---
509
-
510
- ### Q: 如何导出为 PostgreSQL DDL?
511
-
512
- ```javascript
513
- const exporter = new exporters.PostgreSQLExporter({ schema: 'public' });
514
- const ddl = exporter.export('table_name', schema);
515
- ```
516
-
517
- ---
518
-
519
- ### Q: 导出时如何添加注释?
520
-
521
- **A**: 使用 `.description()`:
522
-
523
- ```javascript
524
- username: 'string:3-32!'
525
- .description('用户登录名,只能包含字母数字')
526
- ```
527
-
528
- MySQL 会生成 `COMMENT`,PostgreSQL 会生成 `COMMENT ON COLUMN`。
529
-
530
- ---
531
-
532
- ## TypeScript 支持
533
-
534
- ### Q: schema-dsl 支持 TypeScript 吗?
535
-
536
- **A**: 支持。当前更稳定的 TypeScript 写法是直接使用 `dsl('...')` Builder API,而不是依赖 String 原型扩展:
537
-
538
- ```typescript
539
- import { dsl, validate, Validator } from 'schema-dsl';
540
-
541
- const schema = dsl({
542
- username: 'string:3-32!',
543
- email: dsl('email!').label('邮箱地址').error({
544
- required: '请输入邮箱地址'
545
- })
546
- });
547
-
548
- const validator = new Validator({ allErrors: true });
549
- const result = validate(schema, data);
550
- if (result.valid) {
551
- console.log(result.data);
552
- }
553
- ```
554
-
555
- ---
556
-
557
- ### Q: TypeScript 下如何写出更稳妥的链式提示?
558
-
559
- **A**: 推荐始终从 `dsl('...')` 开始链式调用;这样能和当前类型声明保持一致:
560
-
561
- ```typescript
562
- const schema = dsl({
563
- email: dsl('email!')
564
- .label('邮箱')
565
- .error({ format: '请输入有效邮箱地址' })
566
- });
567
- ```
568
-
569
- ---
570
-
571
- ## 更多问题
572
-
573
- 如果您有其他问题:
574
-
575
- 1. 查看 [完整文档](doc-index.md)
576
- 2. 查看 [DSL 语法指南](dsl-syntax.md)
577
- 3. 查看 [API 参考](api-reference.md)
578
- 4. 提交 [GitHub Issue](https://github.com/schema-dsl/schema-dsl/issues)
579
-
580
- ---
581
-
582
- ## 相关文档
583
-
584
- - [快速开始](quick-start.md)
585
- - [DSL 语法](dsl-syntax.md)
586
- - [验证指南](validation-guide.md)
587
- - [导出指南](export-guide.md)
588
- - [错误处理](error-handling.md)
589
-
590
- ---
591
-
592
- ## 对应示例文件
593
-
594
- **示例入口**: [faq.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/faq.ts)
595
- **说明**: 把 FAQ 里最常被复制的 4 类场景放在一个可运行示例中: 单次验证、多语言错误、批量验证、缓存统计。
596
-
1
+ # 常见问题解答 (FAQ)
2
+
3
+ > **更新时间**: 2026-05-08
4
+
5
+
6
+ ---
7
+
8
+ ## 📑 目录
9
+
10
+ - [基础问题](#基础问题)
11
+ - [DSL 语法问题](#dsl-语法问题)
12
+ - [验证问题](#验证问题)
13
+ - [性能问题](#性能问题)
14
+ - [设计理念](#设计理念)
15
+ - [错误处理](#错误处理)
16
+ - [数据库导出](#数据库导出)
17
+ - [TypeScript 支持](#typescript-支持)
18
+
19
+ ---
20
+
21
+ ## 基础问题
22
+
23
+ ### Q: schema-dsl 和 Joi、Yup 有什么区别?
24
+
25
+ **A**: schema-dsl 采用 DSL 语法,更简洁:
26
+
27
+ ```javascript
28
+ // schema-dsl - 简洁
29
+ const schema = dsl({
30
+ username: 'string:3-32!',
31
+ email: 'email!'
32
+ });
33
+
34
+ // Joi - 繁琐
35
+ const schema = Joi.object({
36
+ username: Joi.string().min(3).max(32).required(),
37
+ email: Joi.string().email().required()
38
+ });
39
+ ```
40
+
41
+ **主要区别**:
42
+ - 更简洁的 DSL 语法
43
+ - 支持数据库 Schema 导出
44
+ - 内置常见验证器(username、password、phone)
45
+ - 基于 JSON Schema 标准
46
+
47
+ ---
48
+
49
+ ### Q: 如何安装 schema-dsl?
50
+
51
+ ```bash
52
+ npm install schema-dsl
53
+ ```
54
+
55
+ **Node.js 版本要求**:`>=18.0.0`
56
+
57
+ 当前 TypeScript 重构版以 `Node.js >=18.0.0` 为唯一运行时基线,不再承诺旧 Node 版本兼容。
58
+
59
+ ---
60
+
61
+ ### Q: 支持 ES Modules 吗?
62
+
63
+ **A**: 支持。
64
+
65
+ ```javascript
66
+ // CommonJS
67
+ const { dsl, validate } = require('schema-dsl');
68
+
69
+ // ES Modules(named import)
70
+ import { dsl, validate } from 'schema-dsl';
71
+
72
+ // ES Modules(default import)
73
+ import dslDefault from 'schema-dsl';
74
+ ```
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
+
88
+ ---
89
+
90
+ ## DSL 语法问题
91
+
92
+ ### Q: `'string:3-32!'` 是什么意思?
93
+
94
+ **A**: 这是 DSL 语法:
95
+ - `string` - 类型
96
+ - `3-32` - 长度范围(最小3,最大32)
97
+ - `!` - 必填
98
+
99
+ 更多示例:
100
+ ```javascript
101
+ 'string:10' // 最大长度10
102
+ 'string:3-' // 最小长度3
103
+ 'number:0-100' // 数值范围0-100
104
+ 'email!' // 必填邮箱
105
+ 'a|b|c' // 枚举值
106
+ ```
107
+
108
+ ---
109
+
110
+ ### Q: 如何定义数组?
111
+
112
+ **A**: 使用 `array` 类型:
113
+
114
+ ```javascript
115
+ // 简单数组
116
+ tags: 'array'
117
+
118
+ // 带长度约束
119
+ tags: 'array:1-10' // 1-10个元素
120
+ tags: 'array!1-10' // 必填,1-10个元素
121
+
122
+ // 带元素类型
123
+ tags: 'array<string>' // 字符串数组
124
+ tags: 'array<number>' // 数字数组
125
+ tags: 'array<string:1-20>' // 带约束的字符串数组
126
+ ```
127
+
128
+ ---
129
+
130
+ ### Q: 如何定义嵌套对象?
131
+
132
+ **A**: 直接嵌套即可:
133
+
134
+ ```javascript
135
+ const schema = dsl({
136
+ user: {
137
+ name: 'string!',
138
+ address: {
139
+ city: 'string!',
140
+ zip: 'string:5-10!'
141
+ }
142
+ }
143
+ });
144
+ ```
145
+
146
+ ---
147
+
148
+ ### Q: 如何使用 String 扩展?
149
+
150
+ **A**: 字符串可以直接链式调用方法:
151
+
152
+ ```javascript
153
+ const schema = dsl({
154
+ email: 'email!'
155
+ .label('邮箱地址')
156
+ .messages({
157
+ 'required': '{{#label}}不能为空',
158
+ 'format': '请输入有效的{{#label}}'
159
+ }),
160
+
161
+ username: 'string:3-32!'
162
+ .pattern(/^[a-z0-9_]+$/)
163
+ .label('用户名')
164
+ .username('medium')
165
+ });
166
+ ```
167
+
168
+ ---
169
+
170
+ ## 验证问题
171
+
172
+ ### Q: 如何验证数据?
173
+
174
+ **A**: 使用 `validate()` 函数或 `Validator` 类:
175
+
176
+ ```javascript
177
+ // 方式1:便捷函数
178
+ const { dsl, validate } = require('schema-dsl');
179
+ const result = validate(schema, data);
180
+
181
+ // 方式2:Validator 实例
182
+ const { Validator } = require('schema-dsl');
183
+ const validator = new Validator();
184
+ const result = validator.validate(schema, data);
185
+ ```
186
+
187
+ ---
188
+
189
+ ### Q: 验证结果的格式是什么?
190
+
191
+ **A**: 返回对象包含:
192
+
193
+ ```javascript
194
+ {
195
+ valid: true/false, // 是否通过
196
+ data: {}, // 当前实现会返回本次验证数据,失败时也便于定位输入
197
+ errors: [] // 成功时为空数组,失败时包含详细错误
198
+ }
199
+ ```
200
+
201
+ ---
202
+
203
+ ### Q: 如何获取所有错误而不是只有第一个?
204
+
205
+ **A**: 默认就会返回全部错误。如果你只想保留首条错误,可以显式关闭 `allErrors`:
206
+
207
+ ```javascript
208
+ const validator = new Validator({ allErrors: false });
209
+ ```
210
+
211
+ `allErrors` 需要在创建 `Validator` 实例时配置,`validator.validate(schema, data, options)` 不能按次覆盖这个开关。
212
+
213
+ ---
214
+
215
+ ### Q: 如何使用默认值?
216
+
217
+ **A**: 使用 `.default()` 方法:
218
+
219
+ ```javascript
220
+ const schema = dsl({
221
+ status: 'string'.default('active'),
222
+ count: 'integer'.default(0)
223
+ });
224
+
225
+ const result = validate(schema, {});
226
+ console.log(result.data);
227
+ // { status: 'active', count: 0 }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## 性能问题
233
+
234
+ ### Q: schema-dsl 的性能怎么样?
235
+
236
+ **A**: 性能不错,**S3 嵌套场景快于 Zod(28%),无效数据公平对比快 89x**:
237
+
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 | — | 底层引擎 |
244
+
245
+ **结论**:
246
+ - ✅ S3 嵌套场景快于 Zod(**28%**),S1 简单有效场景持平;无效数据公平对比快 **89x**
247
+ - ✅ 比 Joi 快约 **13x**(无效数据公平对比)
248
+ - ✅ 内置缓存确保热路径零解析开销
249
+
250
+ ---
251
+
252
+ ### Q: 有效/无效数据场景性能差异为什么大?
253
+
254
+ **A**: 公平对比(S2,均不做 i18n 格式化)schema-dsl 显著快于 Zod(**89x**)。Zod 在无效数据场景极慢的根源是其错误收集使用异常驱动(`try/catch` 控制流),每个无效字段抛出一次 Error,4 个错误字段 = 4 次 Error 实例创建 + 4 次堆栈捕获。schema-dsl 基于 AJV 的无异常收集路径,无格式化时达 1.2M ops/s。
255
+
256
+ ---
257
+
258
+ ### Q: 什么时候性能会成为瓶颈?
259
+
260
+ **A**: 以下场景才可能成为瓶颈:
261
+
262
+ 1. **API 网关**(每秒 >50万次验证)
263
+ 2. **高并发服务**(每秒 >50万次请求)
264
+ 3. **实时数据处理**(毫秒级延迟要求)
265
+
266
+ **大多数应用**(每秒 <10万次验证)不会遇到性能瓶颈。
267
+
268
+ ---
269
+
270
+ ### Q: 验证速度慢怎么办?
271
+
272
+ **A**: 使用预编译和缓存:
273
+
274
+ ```javascript
275
+ // 1. 使用预编译
276
+ const validator = new Validator();
277
+ const validateUser = validator.compile(userSchema);
278
+
279
+ // 2. 启用缓存
280
+
281
+ const validator = new Validator({
282
+ cache: {
283
+ maxSize: 5000, // 缓存5000个Schema
284
+ ttl: 3600000 // 1小时过期
285
+ }
286
+ });
287
+
288
+ // 3. 复用 Validator 实例
289
+ // ❌ 错误:每次都创建新实例
290
+ app.post('/api/users', (req, res) => {
291
+ const validator = new Validator(); // 慢
292
+ // ...
293
+ });
294
+
295
+ // ✅ 正确:复用实例
296
+ const validator = new Validator();
297
+ app.post('/api/users', (req, res) => {
298
+ const result = validator.validate(schema, req.body); // 快
299
+ // ...
300
+ });
301
+ ```
302
+
303
+ ---
304
+
305
+ ### Q: 缓存如何工作?
306
+
307
+ **A**: schema-dsl 当前通过 `CacheManager` 委托 `cache-hub` 的 `MemoryCache` 实现编译缓存:
308
+
309
+ ```javascript
310
+ const validator = new Validator({
311
+ cache: {
312
+ maxSize: 5000, // 最大缓存5000条
313
+ ttl: 3600000 // 1小时过期
314
+ }
315
+ });
316
+
317
+ // 缓存统计
318
+ const stats = validator.getCacheStats();
319
+ console.log(stats);
320
+ // {
321
+ // hits: 8500,
322
+ // misses: 150,
323
+ // hitRate: '98.27',
324
+ // size: 150,
325
+ // maxSize: 5000,
326
+ // enabled: true
327
+ // }
328
+ ```
329
+
330
+ ---
331
+
332
+ ### Q: 如何批量验证?
333
+
334
+ **A**: 使用 `SchemaUtils.validateBatch()`:
335
+
336
+ ```javascript
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);
344
+ ```
345
+
346
+ ---
347
+
348
+ ## 设计理念
349
+
350
+ ### Q: 为什么选择运行时解析而不是编译时构建?
351
+
352
+ **A**: 这是有意的设计选择,优先考虑**灵活性**而非**极致性能**。
353
+
354
+ **运行时解析的优势**:
355
+ 1. ✅ **完全动态** - 可从配置/数据库动态生成规则
356
+ 2. ✅ **多租户支持** - 每个租户不同规则,零代码修改
357
+ 3. ✅ **可序列化** - 可存储、传输、共享
358
+ 4. ✅ **前后端共享** - 一套规则,两端使用
359
+ 5. ✅ **低代码基础** - 可视化配置表单验证
360
+
361
+ **编译时构建的限制**:
362
+ - ❌ Schema 固定,无法动态调整
363
+ - ❌ 无法序列化和传输
364
+ - ❌ 多租户困难
365
+ - ❌ 无法从数据库读取规则
366
+
367
+ **详细说明**: [设计理念文档](design-philosophy.md)
368
+
369
+ ---
370
+
371
+ ### Q: Schema-DSL 适合什么场景?
372
+
373
+ **A**: ✅ **最适合的场景**:
374
+
375
+ 1. **多租户 SaaS 系统** - 每个租户不同验证规则
376
+ 2. **后台管理系统** - 管理员配置表单验证
377
+ 3. **配置驱动开发** - 验证规则存储在配置/数据库
378
+ 4. **低代码/无代码平台** - 可视化表单构建器
379
+ 5. **快速原型开发** - 5分钟上手,代码量最少
380
+ 6. **前后端共享验证** - 一套规则,两端使用
381
+
382
+ ⚠️ **不适合的场景**:
383
+ 1. 绝对吞吐量优先、且不需要 DSL 动态能力 → 推荐 **Ajv**
384
+ 2. TypeScript 强类型推断 → 推荐 **Zod**
385
+ 3. 静态验证规则 → 推荐 **Zod**
386
+
387
+ ---
388
+
389
+ ### Q: 为什么不做成像 Zod 那样的编译时库?
390
+
391
+ **A**: 因为会失去核心价值:
392
+
393
+ **失去的能力**:
394
+ ```javascript
395
+ // ❌ 无法从数据库读取规则
396
+ const rules = await db.findOne({ entity: 'user' });
397
+ const schema = dsl(rules);
398
+
399
+ // ❌ 无法多租户动态规则
400
+ function getTenantSchema(tenantId) {
401
+ return dsl(tenantConfig[tenantId]);
402
+ }
403
+
404
+ // ❌ 无法通过 API 传输
405
+ res.json({ validationRules: rules });
406
+
407
+ // ❌ 无法后台配置表单验证
408
+ ```
409
+
410
+ **保留的能力**:
411
+ ```javascript
412
+ // ✅ 完全动态
413
+ const schema = dsl({
414
+ username: `string:${config.min}-${config.max}!`
415
+ });
416
+
417
+ // ✅ 可序列化
418
+ JSON.stringify({ username: 'string:3-32!' });
419
+
420
+ // ✅ 前后端共享
421
+ // 后端定义 → API传输 → 前端使用
422
+ ```
423
+
424
+ ---
425
+
426
+ ### Q: 性能和灵活性如何平衡?
427
+
428
+ **A**: Schema-DSL 的设计优先级:
429
+
430
+ ```text
431
+ 灵活性 > 易用性 > 性能
432
+ ```
433
+
434
+ **权衡结果**:
435
+ - 增益:S3 嵌套场景快于 Zod 28%,S1 持平;无效数据公平对比快 89x
436
+
437
+ ---
438
+
439
+ ## 错误处理
440
+
441
+ ### Q: 如何自定义错误消息?
442
+
443
+ **A**: 使用 `.messages()` 方法:
444
+
445
+ ```javascript
446
+ username: 'string:3-32!'
447
+ .label('用户名')
448
+ .messages({
449
+ 'min': '{{#label}}太短了',
450
+ 'max': '{{#label}}太长了',
451
+ 'required': '请输入{{#label}}'
452
+ })
453
+ ```
454
+
455
+ ---
456
+
457
+ ### Q: 如何支持多语言?
458
+
459
+ **A**: 使用 `Locale` 类:
460
+
461
+ ```javascript
462
+ const { Locale } = require('schema-dsl');
463
+
464
+ // 添加语言包
465
+ Locale.addLocale('zh-CN', {
466
+ 'required': '{{#label}}不能为空',
467
+ 'min': '{{#label}}长度不能少于{{#limit}}'
468
+ });
469
+
470
+ // 验证时指定语言
471
+ validator.validate(schema, data, { locale: 'zh-CN' });
472
+ ```
473
+
474
+ ---
475
+
476
+ ### Q: 错误路径格式是什么?
477
+
478
+ **A**: 当前返回的是 slash path:
479
+
480
+ ```javascript
481
+ 'username' // 顶层字段
482
+ 'user/name' // 嵌套字段
483
+ 'items/0/name' // 数组元素
484
+ ```
485
+
486
+ ---
487
+
488
+ ## 数据库导出
489
+
490
+ ### Q: 如何导出为 MongoDB Schema?
491
+
492
+ ```javascript
493
+ const { exporters } = require('schema-dsl');
494
+
495
+ const exporter = new exporters.MongoDBExporter();
496
+ const mongoSchema = exporter.export(schema);
497
+ ```
498
+
499
+ ---
500
+
501
+ ### Q: 如何导出为 MySQL DDL?
502
+
503
+ ```javascript
504
+ const exporter = new exporters.MySQLExporter();
505
+ const ddl = exporter.export('table_name', schema);
506
+ ```
507
+
508
+ ---
509
+
510
+ ### Q: 如何导出为 PostgreSQL DDL?
511
+
512
+ ```javascript
513
+ const exporter = new exporters.PostgreSQLExporter({ schema: 'public' });
514
+ const ddl = exporter.export('table_name', schema);
515
+ ```
516
+
517
+ ---
518
+
519
+ ### Q: 导出时如何添加注释?
520
+
521
+ **A**: 使用 `.description()`:
522
+
523
+ ```javascript
524
+ username: 'string:3-32!'
525
+ .description('用户登录名,只能包含字母数字')
526
+ ```
527
+
528
+ MySQL 会生成 `COMMENT`,PostgreSQL 会生成 `COMMENT ON COLUMN`。
529
+
530
+ ---
531
+
532
+ ## TypeScript 支持
533
+
534
+ ### Q: schema-dsl 支持 TypeScript 吗?
535
+
536
+ **A**: 支持。当前更稳定的 TypeScript 写法是直接使用 `dsl('...')` Builder API,而不是依赖 String 原型扩展:
537
+
538
+ ```typescript
539
+ import { dsl, validate, Validator } from 'schema-dsl';
540
+
541
+ const schema = dsl({
542
+ username: 'string:3-32!',
543
+ email: dsl('email!').label('邮箱地址').error({
544
+ required: '请输入邮箱地址'
545
+ })
546
+ });
547
+
548
+ const validator = new Validator({ allErrors: true });
549
+ const result = validate(schema, data);
550
+ if (result.valid) {
551
+ console.log(result.data);
552
+ }
553
+ ```
554
+
555
+ ---
556
+
557
+ ### Q: TypeScript 下如何写出更稳妥的链式提示?
558
+
559
+ **A**: 推荐始终从 `dsl('...')` 开始链式调用;这样能和当前类型声明保持一致:
560
+
561
+ ```typescript
562
+ const schema = dsl({
563
+ email: dsl('email!')
564
+ .label('邮箱')
565
+ .error({ format: '请输入有效邮箱地址' })
566
+ });
567
+ ```
568
+
569
+ ---
570
+
571
+ ## 更多问题
572
+
573
+ 如果您有其他问题:
574
+
575
+ 1. 查看 [完整文档](doc-index.md)
576
+ 2. 查看 [DSL 语法指南](dsl-syntax.md)
577
+ 3. 查看 [API 参考](api-reference.md)
578
+ 4. 提交 [GitHub Issue](https://github.com/schema-dsl/schema-dsl/issues)
579
+
580
+ ---
581
+
582
+ ## 相关文档
583
+
584
+ - [快速开始](quick-start.md)
585
+ - [DSL 语法](dsl-syntax.md)
586
+ - [验证指南](validation-guide.md)
587
+ - [导出指南](export-guide.md)
588
+ - [错误处理](error-handling.md)
589
+
590
+ ---
591
+
592
+ ## 对应示例文件
593
+
594
+ **示例入口**: [faq.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/faq.ts)
595
+ **说明**: 把 FAQ 里最常被复制的 4 类场景放在一个可运行示例中: 单次验证、多语言错误、批量验证、缓存统计。
596
+