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