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.
- package/CHANGELOG.md +87 -210
- package/README.md +391 -2249
- package/dist/DslBuilder-DQDN0ZxZ.d.cts +341 -0
- package/dist/DslBuilder-DkLaOo9Q.d.ts +341 -0
- package/dist/Validator-C7GsVQOH.d.cts +192 -0
- package/dist/Validator-hFWKGxir.d.ts +192 -0
- package/dist/index.cjs +6594 -0
- package/dist/index.d.cts +1145 -0
- package/dist/index.d.ts +1145 -0
- package/dist/index.js +6528 -0
- package/dist/plugin-CIKtTMtS.d.cts +246 -0
- package/dist/plugin-CIKtTMtS.d.ts +246 -0
- package/dist/plugins/custom-format.cjs +3802 -0
- package/dist/plugins/custom-format.d.cts +12 -0
- package/dist/plugins/custom-format.d.ts +12 -0
- package/dist/plugins/custom-format.js +3772 -0
- package/dist/plugins/custom-type-example.cjs +3795 -0
- package/dist/plugins/custom-type-example.d.cts +8 -0
- package/dist/plugins/custom-type-example.d.ts +8 -0
- package/dist/plugins/custom-type-example.js +3765 -0
- package/dist/plugins/custom-validator.cjs +146 -0
- package/dist/plugins/custom-validator.d.cts +10 -0
- package/dist/plugins/custom-validator.d.ts +10 -0
- package/dist/plugins/custom-validator.js +121 -0
- package/docs/FEATURE-INDEX.md +102 -68
- package/docs/add-custom-locale.md +48 -35
- package/docs/add-keyword.md +24 -0
- package/docs/api-reference.md +396 -154
- package/docs/api.md +13 -0
- package/docs/best-practices-project-structure.md +19 -10
- package/docs/best-practices.md +93 -53
- package/docs/cache-manager.md +23 -15
- package/docs/compile.md +45 -0
- package/docs/conditional-api.md +40 -11
- package/docs/custom-extensions-guide.md +80 -152
- package/docs/design-philosophy.md +76 -71
- package/docs/doc-index.md +324 -0
- package/docs/dsl-syntax.md +69 -19
- package/docs/dynamic-locale.md +24 -14
- package/docs/enum.md +12 -5
- package/docs/error-handling.md +53 -44
- package/docs/export-guide.md +47 -8
- package/docs/export-limitations.md +27 -11
- package/docs/faq.md +86 -67
- package/docs/frontend-i18n-guide.md +26 -12
- package/docs/i18n-user-guide.md +60 -47
- package/docs/i18n.md +51 -32
- package/docs/index.md +48 -0
- package/docs/json-schema-basics.md +40 -0
- package/docs/label-vs-description.md +12 -3
- package/docs/markdown-exporter.md +15 -6
- package/docs/mongodb-exporter.md +11 -4
- package/docs/multi-language.md +26 -0
- package/docs/multi-type-support.md +26 -33
- package/docs/mysql-exporter.md +9 -2
- package/docs/number-operators.md +12 -5
- package/docs/optional-marker-guide.md +28 -23
- package/docs/performance-guide.md +49 -0
- package/docs/plugin-system.md +205 -366
- package/docs/plugin-type-registration.md +34 -0
- package/docs/postgresql-exporter.md +9 -2
- package/docs/public/favicon.svg +5 -0
- package/docs/quick-start.md +37 -363
- package/docs/runtime-locale-support.md +20 -9
- package/docs/schema-helper.md +10 -5
- package/docs/schema-utils-advanced-issues.md +23 -0
- package/docs/schema-utils-best-practices.md +20 -0
- package/docs/schema-utils-chaining.md +7 -0
- package/docs/schema-utils.md +76 -42
- package/docs/security-checklist.md +20 -0
- package/docs/string-extensions.md +17 -9
- package/docs/troubleshooting.md +36 -21
- package/docs/type-converter.md +41 -50
- package/docs/type-reference.md +38 -15
- package/docs/typescript-guide.md +53 -42
- package/docs/union-type-guide.md +11 -1
- package/docs/union-types.md +10 -3
- package/docs/validate-async.md +36 -25
- package/docs/validate-batch.md +49 -0
- package/docs/validate-dsl-object-support.md +33 -28
- package/docs/validate.md +36 -16
- package/docs/validation-guide.md +25 -7
- package/docs/validator.md +39 -0
- package/package.json +85 -27
- package/plugins/custom-format.cjs +8 -0
- package/plugins/custom-type-example.cjs +8 -0
- package/plugins/custom-validator.cjs +8 -0
- package/src/adapters/DslAdapter.ts +111 -0
- package/src/adapters/index.ts +1 -0
- package/src/config/constants.ts +83 -0
- package/src/config/index.ts +2 -0
- package/src/config/patterns.ts +77 -0
- package/src/core/CacheManager.ts +159 -0
- package/src/core/ConditionalBuilder.ts +382 -0
- package/src/core/ConditionalRuntime.ts +28 -0
- package/src/core/ConditionalValidator.ts +255 -0
- package/src/core/DslBuilder.ts +677 -0
- package/src/core/ErrorCodes.ts +38 -0
- package/src/core/ErrorFormatter.ts +271 -0
- package/src/core/JSONSchemaCore.ts +65 -0
- package/src/core/Locale.ts +187 -0
- package/src/core/MessageTemplate.ts +42 -0
- package/src/core/ObjectDslBuilder.ts +64 -0
- package/src/core/PluginManager.ts +326 -0
- package/src/core/StringExtensions.ts +140 -0
- package/src/core/TemplateEngine.ts +44 -0
- package/src/core/Validator.ts +448 -0
- package/src/errors/I18nError.ts +159 -0
- package/src/errors/ValidationError.ts +105 -0
- package/src/exporters/BaseExporter.ts +60 -0
- package/src/exporters/MarkdownExporter.ts +305 -0
- package/src/exporters/MongoDBExporter.ts +126 -0
- package/src/exporters/MySQLExporter.ts +155 -0
- package/src/exporters/PostgreSQLExporter.ts +222 -0
- package/src/exporters/index.ts +18 -0
- package/src/index.ts +633 -0
- package/{lib/locales/en-US.js → src/locales/en-US.ts} +21 -37
- package/{lib/locales/es-ES.js → src/locales/es-ES.ts} +63 -16
- package/{lib/locales/fr-FR.js → src/locales/fr-FR.ts} +74 -27
- package/src/locales/index.ts +103 -0
- package/{lib/locales/ja-JP.js → src/locales/ja-JP.ts} +59 -17
- package/src/locales/types.ts +156 -0
- package/{lib/locales/zh-CN.js → src/locales/zh-CN.ts} +21 -38
- package/src/parser/ConstraintParser.ts +101 -0
- package/src/parser/DslParser.ts +470 -0
- package/src/parser/SchemaCompiler.ts +66 -0
- package/src/parser/TypeRegistry.ts +250 -0
- package/src/parser/index.ts +6 -0
- package/src/plugins/custom-format.ts +126 -0
- package/src/plugins/custom-type-example.ts +108 -0
- package/src/plugins/custom-validator.ts +140 -0
- package/src/types/conditional.ts +28 -0
- package/src/types/config.ts +59 -0
- package/src/types/dsl.ts +131 -0
- package/src/types/error.ts +60 -0
- package/src/types/index.ts +17 -0
- package/src/types/infer.ts +128 -0
- package/src/types/plugin.ts +58 -0
- package/src/types/safe-regex.d.ts +9 -0
- package/src/types/schema.ts +66 -0
- package/src/types/validate.ts +71 -0
- package/src/utils/SchemaHelper.ts +196 -0
- package/src/utils/SchemaUtils.ts +346 -0
- package/src/utils/TypeConverter.ts +215 -0
- package/src/utils/index.ts +10 -0
- package/src/validators/CustomKeywords.ts +477 -0
- package/.eslintignore +0 -11
- package/.eslintrc.json +0 -27
- package/CONTRIBUTING.md +0 -368
- package/STATUS.md +0 -491
- package/changelogs/v1.0.0.md +0 -328
- package/changelogs/v1.0.9.md +0 -367
- package/changelogs/v1.1.0.md +0 -389
- package/changelogs/v1.1.1.md +0 -308
- package/changelogs/v1.1.2.md +0 -183
- package/changelogs/v1.1.3.md +0 -161
- package/changelogs/v1.1.4.md +0 -432
- package/changelogs/v1.1.5.md +0 -493
- package/changelogs/v1.1.6.md +0 -211
- package/changelogs/v1.1.8.md +0 -376
- package/changelogs/v1.2.3.md +0 -124
- package/docs/INDEX.md +0 -252
- package/docs/issues-resolved-summary.md +0 -196
- package/docs/performance-benchmark-report.md +0 -179
- package/docs/performance-quick-reference.md +0 -123
- package/docs/user-questions-answered.md +0 -353
- package/docs/validation-rules-v1.0.2.md +0 -1608
- package/examples/README.md +0 -81
- package/examples/array-dsl-example.js +0 -227
- package/examples/conditional-example.js +0 -288
- package/examples/conditional-non-object.js +0 -129
- package/examples/conditional-validate-example.js +0 -321
- package/examples/custom-extension.js +0 -85
- package/examples/dsl-match-example.js +0 -74
- package/examples/dsl-style.js +0 -118
- package/examples/dynamic-locale-configuration.js +0 -348
- package/examples/dynamic-locale-example.js +0 -287
- package/examples/enum.examples.js +0 -324
- package/examples/export-demo.js +0 -130
- package/examples/express-integration.js +0 -376
- package/examples/i18n-error-handling-complete.js +0 -381
- package/examples/i18n-error-handling-quickstart.md +0 -0
- package/examples/i18n-error.examples.js +0 -181
- package/examples/i18n-full-demo.js +0 -301
- package/examples/i18n-memory-safety.examples.js +0 -268
- package/examples/markdown-export.js +0 -71
- package/examples/middleware-usage.js +0 -93
- package/examples/new-features-comparison.js +0 -315
- package/examples/password-reset/README.md +0 -153
- package/examples/password-reset/schema.js +0 -26
- package/examples/password-reset/test.js +0 -101
- package/examples/plugin-system.examples.js +0 -205
- package/examples/schema-utils-chaining.examples.js +0 -250
- package/examples/simple-example.js +0 -122
- package/examples/slug.examples.js +0 -179
- package/examples/string-extensions.js +0 -297
- package/examples/union-type-example.js +0 -127
- package/examples/union-types-example.js +0 -77
- package/examples/user-registration/README.md +0 -156
- package/examples/user-registration/routes.js +0 -92
- package/examples/user-registration/schema.js +0 -150
- package/examples/user-registration/server.js +0 -74
- package/index.d.ts +0 -3540
- package/index.js +0 -457
- package/index.mjs +0 -60
- package/lib/adapters/DslAdapter.js +0 -871
- package/lib/adapters/index.js +0 -20
- package/lib/config/constants.js +0 -286
- package/lib/config/patterns/common.js +0 -47
- package/lib/config/patterns/creditCard.js +0 -9
- package/lib/config/patterns/idCard.js +0 -9
- package/lib/config/patterns/index.js +0 -9
- package/lib/config/patterns/licensePlate.js +0 -4
- package/lib/config/patterns/passport.js +0 -4
- package/lib/config/patterns/phone.js +0 -9
- package/lib/config/patterns/postalCode.js +0 -5
- package/lib/core/CacheManager.js +0 -376
- package/lib/core/ConditionalBuilder.js +0 -503
- package/lib/core/DslBuilder.js +0 -1400
- package/lib/core/ErrorCodes.js +0 -233
- package/lib/core/ErrorFormatter.js +0 -445
- package/lib/core/JSONSchemaCore.js +0 -347
- package/lib/core/Locale.js +0 -130
- package/lib/core/MessageTemplate.js +0 -98
- package/lib/core/PluginManager.js +0 -448
- package/lib/core/StringExtensions.js +0 -240
- package/lib/core/Validator.js +0 -654
- package/lib/errors/I18nError.js +0 -328
- package/lib/errors/ValidationError.js +0 -191
- package/lib/exporters/MarkdownExporter.js +0 -420
- package/lib/exporters/MongoDBExporter.js +0 -162
- package/lib/exporters/MySQLExporter.js +0 -212
- package/lib/exporters/PostgreSQLExporter.js +0 -289
- package/lib/exporters/index.js +0 -24
- package/lib/locales/index.js +0 -8
- package/lib/utils/LRUCache.js +0 -174
- package/lib/utils/SchemaHelper.js +0 -240
- package/lib/utils/SchemaUtils.js +0 -445
- package/lib/utils/TypeConverter.js +0 -245
- package/lib/utils/index.js +0 -13
- package/lib/validators/CustomKeywords.js +0 -616
- package/lib/validators/index.js +0 -11
package/docs/faq.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 常见问题解答 (FAQ)
|
|
2
2
|
|
|
3
|
-
> **更新时间**:
|
|
3
|
+
> **更新时间**: 2026-05-08
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
---
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
|
|
21
21
|
## 基础问题
|
|
22
22
|
|
|
23
|
-
### Q:
|
|
23
|
+
### Q: schema-dsl 和 Joi、Yup 有什么区别?
|
|
24
24
|
|
|
25
|
-
**A**:
|
|
25
|
+
**A**: schema-dsl 采用 DSL 语法,更简洁:
|
|
26
26
|
|
|
27
27
|
```javascript
|
|
28
|
-
//
|
|
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: 如何安装
|
|
49
|
+
### Q: 如何安装 schema-dsl?
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
52
|
npm install schema-dsl
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
**Node.js
|
|
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
|
-
|
|
180
|
-
|
|
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:
|
|
234
|
+
### Q: schema-dsl 的性能怎么样?
|
|
219
235
|
|
|
220
|
-
**A**:
|
|
236
|
+
**A**: 性能不错,**S3 嵌套场景快于 Zod(28%),无效数据公平对比快 89x**:
|
|
221
237
|
|
|
222
|
-
|
|
|
223
|
-
|
|
224
|
-
|
|
|
225
|
-
|
|
|
226
|
-
|
|
|
227
|
-
|
|
|
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
|
-
- ✅
|
|
232
|
-
- ✅ 比
|
|
233
|
-
- ✅
|
|
246
|
+
- ✅ S3 嵌套场景快于 Zod(**28%**),S1 简单有效场景持平;无效数据公平对比快 **89x**
|
|
247
|
+
- ✅ 比 Joi 快约 **13x**(无效数据公平对比)
|
|
248
|
+
- ✅ 内置缓存确保热路径零解析开销
|
|
234
249
|
|
|
235
250
|
---
|
|
236
251
|
|
|
237
|
-
### Q:
|
|
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**:
|
|
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.
|
|
318
|
+
const stats = validator.getCacheStats();
|
|
314
319
|
console.log(stats);
|
|
315
320
|
// {
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
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
|
|
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.
|
|
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
|
-
-
|
|
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**:
|
|
478
|
+
**A**: 当前返回的是 slash path:
|
|
471
479
|
|
|
472
480
|
```javascript
|
|
473
|
-
'
|
|
474
|
-
'
|
|
475
|
-
'
|
|
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:
|
|
534
|
+
### Q: schema-dsl 支持 TypeScript 吗?
|
|
527
535
|
|
|
528
|
-
**A**:
|
|
536
|
+
**A**: 支持。当前更稳定的 TypeScript 写法是直接使用 `dsl('...')` Builder API,而不是依赖 String 原型扩展:
|
|
529
537
|
|
|
530
538
|
```typescript
|
|
531
|
-
import { dsl, validate,
|
|
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:
|
|
557
|
+
### Q: TypeScript 下如何写出更稳妥的链式提示?
|
|
547
558
|
|
|
548
|
-
**A**:
|
|
559
|
+
**A**: 推荐始终从 `dsl('...')` 开始链式调用;这样能和当前类型声明保持一致:
|
|
549
560
|
|
|
550
561
|
```typescript
|
|
551
|
-
// TypeScript 会识别这些方法
|
|
552
562
|
const schema = dsl({
|
|
553
|
-
email: 'email!'
|
|
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. 查看 [完整文档](
|
|
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**:
|
|
225
|
+
**A**: 实例创建本身很轻量,但**仍然建议复用同一个 `Validator` 实例**。原因不是构造函数慢,而是编译缓存挂在实例上;如果每个请求都 `new Validator()`,同一份 Schema 会反复出现首次编译 miss。
|
|
226
226
|
|
|
227
227
|
```javascript
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
//
|
|
254
|
-
import { dsl, validate } from 'schema-dsl
|
|
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.
|
|
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.
|
|
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
|
+
|
package/docs/i18n-user-guide.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 多语言支持用户指南
|
|
2
2
|
|
|
3
|
-
> **更新日期**:
|
|
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.
|
|
93
|
-
│ ├── en-US.
|
|
94
|
-
│ └── ja-JP.
|
|
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.
|
|
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.
|
|
212
|
-
req.
|
|
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.
|
|
350
|
-
│ ├── en-US.
|
|
351
|
-
│ └── ja-JP.
|
|
376
|
+
│ ├── zh-CN.cjs
|
|
377
|
+
│ ├── en-US.jsonc
|
|
378
|
+
│ └── ja-JP.json5
|
|
352
379
|
└── messages/ # 自定义消息(可选)
|
|
353
|
-
├── zh-CN.
|
|
354
|
-
└── en-US.
|
|
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
|
-
'
|
|
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.
|
|
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.
|
|
422
|
-
2.
|
|
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':
|
|
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
|
-
|
|
472
|
-
|
|
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
|
|