schema-dsl 1.0.9 → 1.1.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 +205 -2
- package/README.md +257 -0
- package/docs/FEATURE-INDEX.md +1 -1
- package/docs/best-practices.md +3 -3
- package/docs/cache-manager.md +1 -1
- package/docs/conditional-api.md +1032 -0
- package/docs/dsl-syntax.md +1 -1
- package/docs/dynamic-locale.md +2 -2
- package/docs/error-handling.md +2 -2
- package/docs/export-guide.md +2 -2
- package/docs/export-limitations.md +3 -3
- package/docs/faq.md +6 -6
- package/docs/frontend-i18n-guide.md +1 -1
- package/docs/mongodb-exporter.md +3 -3
- package/docs/multi-type-support.md +12 -2
- package/docs/mysql-exporter.md +1 -1
- package/docs/plugin-system.md +4 -4
- package/docs/postgresql-exporter.md +1 -1
- package/docs/quick-start.md +4 -4
- package/docs/troubleshooting.md +2 -2
- package/docs/type-reference.md +5 -5
- package/docs/typescript-guide.md +5 -6
- package/docs/union-type-guide.md +147 -0
- package/docs/union-types.md +277 -0
- package/docs/validate-async.md +1 -1
- package/examples/array-dsl-example.js +1 -1
- package/examples/conditional-example.js +288 -0
- package/examples/conditional-non-object.js +129 -0
- package/examples/conditional-validate-example.js +321 -0
- package/examples/union-type-example.js +127 -0
- package/examples/union-types-example.js +77 -0
- package/index.d.ts +332 -7
- package/index.js +19 -1
- package/lib/adapters/DslAdapter.js +14 -5
- package/lib/core/ConditionalBuilder.js +401 -0
- package/lib/core/DslBuilder.js +113 -0
- package/lib/core/Locale.js +13 -8
- package/lib/core/Validator.js +246 -2
- package/lib/locales/en-US.js +14 -0
- package/lib/locales/es-ES.js +4 -0
- package/lib/locales/fr-FR.js +4 -0
- package/lib/locales/ja-JP.js +9 -0
- package/lib/locales/zh-CN.js +14 -0
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,12 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
| 版本 | 日期 | 变更摘要 | 详细 |
|
|
13
13
|
|------------------|------|---------|------|
|
|
14
|
+
| [v1.1.0](#v110) | 2026-01-05 | 🎉 重大功能:跨类型联合验证 + 插件系统增强 | [查看详情](#v110) |
|
|
14
15
|
| [v1.0.9](#v109) | 2026-01-04 | 🎉 重大改进:多语言支持完善 + TypeScript 类型完整 | [查看详情](#v109) |
|
|
15
16
|
| [v1.0.8](#v108) | 2026-01-04 | 优化:错误消息过滤增强 | [查看详情](#v108) |
|
|
16
17
|
| [v1.0.7](#v107) | 2026-01-04 | 修复:dsl.match/dsl.if 嵌套支持 dsl() 包裹 | [查看详情](#v107) |
|
|
17
18
|
| [v1.0.6](#v106) | 2026-01-04 | 🚨 紧急修复:TypeScript 类型污染 | [查看详情](#v106) |
|
|
18
|
-
| [v1.0.7](#v107) | 2026-01-04 | 修复:dsl.match/dsl.if 嵌套支持 dsl() 包裹 | [查看详情](#v107) |
|
|
19
|
-
| [v1.0.6](#v106) | 2026-01-04 | 🚨 紧急修复:TypeScript 类型污染 | [查看详情](#v106) |
|
|
20
19
|
| [v1.0.5](#v105) | 2026-01-04 | 测试覆盖率提升至 97% | [查看详情](#v105) |
|
|
21
20
|
| [v1.0.4](#v104) | 2025-12-31 | TypeScript 完整支持、validateAsync、ValidationError | [查看详情](#v104) |
|
|
22
21
|
| [v1.0.3](#v103) | 2025-12-31 | ⚠️ 破坏性变更:单值语法修复 | [查看详情](#v103) |
|
|
@@ -26,6 +25,210 @@
|
|
|
26
25
|
|
|
27
26
|
---
|
|
28
27
|
|
|
28
|
+
## [v1.1.0] - 2026-01-05
|
|
29
|
+
|
|
30
|
+
### 🎉 新功能
|
|
31
|
+
|
|
32
|
+
#### 1. 跨类型联合验证 - `types:` 语法
|
|
33
|
+
|
|
34
|
+
**一个字段支持多种类型**
|
|
35
|
+
|
|
36
|
+
现在可以使用 `types:` 前缀定义跨类型联合验证,支持字段匹配多种不同的数据类型。
|
|
37
|
+
|
|
38
|
+
**基础用法**:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
const { dsl, validate } = require('schema-dsl');
|
|
42
|
+
|
|
43
|
+
// 字段可以是字符串或数字
|
|
44
|
+
const schema = dsl({
|
|
45
|
+
value: 'types:string|number'
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
validate(schema, { value: 'hello' }); // ✅ 通过
|
|
49
|
+
validate(schema, { value: 123 }); // ✅ 通过
|
|
50
|
+
validate(schema, { value: true }); // ❌ 失败
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**带约束的联合类型**:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
const schema = dsl({
|
|
57
|
+
contact: 'types:email|phone!', // 邮箱或手机号
|
|
58
|
+
price: 'types:number:0-|string:1-20', // 数字价格或"面议"
|
|
59
|
+
rating: 'types:integer:1-5|string:9' // 整数1-5或字符串"excellent"
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**实际应用场景**:
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
// 用户注册:灵活的联系方式
|
|
67
|
+
const registerSchema = dsl({
|
|
68
|
+
username: 'string:3-20!',
|
|
69
|
+
contact: 'types:email|phone!', // 支持邮箱或手机号注册
|
|
70
|
+
age: 'types:integer:1-150|null' // 年龄可选(null表示不填)
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**支持的类型**:
|
|
75
|
+
- ✅ 所有内置类型:`string`, `number`, `email`, `url`, `uuid` 等
|
|
76
|
+
- ✅ 插件注册的自定义类型(见下文)
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
#### 2. 插件系统增强 - DSL类型注册
|
|
81
|
+
|
|
82
|
+
**插件现在可以注册自定义DSL类型**
|
|
83
|
+
|
|
84
|
+
插件不再局限于注册ajv format/keyword,现在可以注册完整的DSL类型,让自定义类型在DSL语法中直接可用。
|
|
85
|
+
|
|
86
|
+
**改造前** (v1.0.x):
|
|
87
|
+
```javascript
|
|
88
|
+
// ❌ 只能注册ajv format(仅验证阶段生效)
|
|
89
|
+
install(schemaDsl, options) {
|
|
90
|
+
const ajv = validator.getAjv();
|
|
91
|
+
ajv.addFormat('phone-cn', { validate: /^1[3-9]\d{9}$/ });
|
|
92
|
+
|
|
93
|
+
// ❌ 无法在DSL语法中使用
|
|
94
|
+
// dsl({ phone: 'phone-cn!' }) // 报错:未知类型
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**改造后** (v1.1.0):
|
|
99
|
+
```javascript
|
|
100
|
+
// ✅ 同时注册DSL类型和ajv format
|
|
101
|
+
install(schemaDsl, options) {
|
|
102
|
+
const { DslBuilder } = schemaDsl;
|
|
103
|
+
|
|
104
|
+
// ✅ 注册到DSL解析器(解析阶段)
|
|
105
|
+
DslBuilder.registerType('phone-cn', {
|
|
106
|
+
type: 'string',
|
|
107
|
+
pattern: /^1[3-9]\d{9}$/.source,
|
|
108
|
+
minLength: 11,
|
|
109
|
+
maxLength: 11
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ✅ 注册到ajv(验证阶段)
|
|
113
|
+
const ajv = validator.getAjv();
|
|
114
|
+
ajv.addFormat('phone-cn', { validate: /^1[3-9]\d{9}$/ });
|
|
115
|
+
|
|
116
|
+
// ✅ 现在可以在DSL中直接使用
|
|
117
|
+
// dsl({ phone: 'phone-cn!' }) // ✅ 成功
|
|
118
|
+
// dsl({ contact: 'types:email|phone-cn' }) // ✅ 在联合类型中也能用
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**新增 API**:
|
|
123
|
+
|
|
124
|
+
| API | 说明 | 用途 |
|
|
125
|
+
|-----|------|------|
|
|
126
|
+
| `DslBuilder.registerType(name, schema)` | 注册自定义类型 | 插件注册新类型 |
|
|
127
|
+
| `DslBuilder.hasType(type)` | 检查类型是否存在 | 插件验证类型 |
|
|
128
|
+
| `DslBuilder.getCustomTypes()` | 获取所有自定义类型 | 调试和测试 |
|
|
129
|
+
| `DslBuilder.clearCustomTypes()` | 清除自定义类型 | 测试用 |
|
|
130
|
+
|
|
131
|
+
**插件示例**:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
// plugins/custom-format.js (v2.0.0)
|
|
135
|
+
module.exports = {
|
|
136
|
+
name: 'custom-format',
|
|
137
|
+
version: '2.0.0',
|
|
138
|
+
|
|
139
|
+
install(schemaDsl, options, context) {
|
|
140
|
+
const { DslBuilder } = schemaDsl;
|
|
141
|
+
const ajv = validator.getAjv();
|
|
142
|
+
|
|
143
|
+
// 定义自定义类型
|
|
144
|
+
const types = {
|
|
145
|
+
'phone-cn': {
|
|
146
|
+
pattern: /^1[3-9]\d{9}$/,
|
|
147
|
+
schema: { type: 'string', pattern: /^1[3-9]\d{9}$/.source, minLength: 11, maxLength: 11 }
|
|
148
|
+
},
|
|
149
|
+
'qq': {
|
|
150
|
+
pattern: /^[1-9][0-9]{4,10}$/,
|
|
151
|
+
schema: { type: 'string', pattern: /^[1-9][0-9]{4,10}$/.source, minLength: 5, maxLength: 11 }
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// 注册到DSL和ajv
|
|
156
|
+
Object.keys(types).forEach(name => {
|
|
157
|
+
const config = types[name];
|
|
158
|
+
DslBuilder.registerType(name, config.schema); // DSL解析
|
|
159
|
+
ajv.addFormat(name, { validate: config.pattern }); // ajv验证
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
#### 3. ConditionalBuilder 快捷验证方法
|
|
168
|
+
|
|
169
|
+
**一行代码完成条件验证**
|
|
170
|
+
|
|
171
|
+
新增4个快捷验证方法,让条件判断更简洁:
|
|
172
|
+
|
|
173
|
+
| 方法 | 返回值 | 抛错 | 异步 | 适用场景 |
|
|
174
|
+
|------|--------|------|------|---------|
|
|
175
|
+
| `.validate()` | `{ valid, errors, data }` | ❌ | ❌ | 需要错误详情 |
|
|
176
|
+
| `.validateAsync()` | `Promise<data>` | ✅ | ✅ | async/await、中间件 |
|
|
177
|
+
| `.assert()` | `data` | ✅ | ❌ | 快速失败、断言 |
|
|
178
|
+
| `.check()` | `boolean` | ❌ | ❌ | 只需判断真假 |
|
|
179
|
+
|
|
180
|
+
**使用示例**:
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
// 同步验证 - 返回详细结果
|
|
184
|
+
const result = dsl.if(d => d.age < 18)
|
|
185
|
+
.message('未成年用户不能注册')
|
|
186
|
+
.validate({ age: 16 });
|
|
187
|
+
|
|
188
|
+
// 异步验证 - 失败自动抛错
|
|
189
|
+
await dsl.if(d => d.age < 18)
|
|
190
|
+
.message('未成年用户不能注册')
|
|
191
|
+
.validateAsync({ age: 16 });
|
|
192
|
+
|
|
193
|
+
// 断言验证 - 同步抛错
|
|
194
|
+
dsl.if(d => d.age < 18).message('未成年').assert(userData);
|
|
195
|
+
|
|
196
|
+
// 快速判断 - 返回 boolean
|
|
197
|
+
const isValid = dsl.if(d => d.age < 18).message('未成年').check(data);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**优势**:
|
|
201
|
+
- ✅ 从2行代码减少到1行
|
|
202
|
+
- ✅ 支持 async/await
|
|
203
|
+
- ✅ 快速失败模式
|
|
204
|
+
- ✅ 完全向后兼容
|
|
205
|
+
|
|
206
|
+
**详细文档**:[ConditionalBuilder API](./docs/conditional-api.md)
|
|
207
|
+
|
|
208
|
+
### Changed
|
|
209
|
+
|
|
210
|
+
- 📖 优化 README.md,新增"条件验证 - 一行代码搞定"章节
|
|
211
|
+
- 📖 完善 API 文档和使用示例
|
|
212
|
+
- 📖 新增4个方法的 TypeScript 类型定义
|
|
213
|
+
|
|
214
|
+
### Tests
|
|
215
|
+
|
|
216
|
+
- ✅ 新增 45个测试用例(总计 807个测试)
|
|
217
|
+
- `.validate()` 方法: 9个
|
|
218
|
+
- `.validateAsync()` 方法: 6个
|
|
219
|
+
- `.assert()` 方法: 6个
|
|
220
|
+
- `.check()` 方法: 5个
|
|
221
|
+
- 边界情况: 12个
|
|
222
|
+
- 特殊场景: 7个
|
|
223
|
+
- ✅ 测试覆盖率 100%(807/807)
|
|
224
|
+
- ✅ 覆盖 null、undefined、空值、NaN、异常处理等所有边界情况
|
|
225
|
+
|
|
226
|
+
### 向后兼容
|
|
227
|
+
|
|
228
|
+
✅ **完全向后兼容**,所有现有代码无需修改
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
29
232
|
## [v1.0.9] - 2026-01-04
|
|
30
233
|
|
|
31
234
|
### 🎉 重大改进
|
package/README.md
CHANGED
|
@@ -181,12 +181,49 @@ if (result.valid) {
|
|
|
181
181
|
|------|-----------|------|
|
|
182
182
|
| **基本验证** | ✅ | string、number、boolean、date、email、url... |
|
|
183
183
|
| **高级验证** | ✅ | 正则、自定义、条件、嵌套、数组... |
|
|
184
|
+
| **🆕 跨类型联合** | ✅ | `types:string|number` 一个字段支持多种类型 (v1.1.0+) |
|
|
184
185
|
| **错误格式化** | ✅ | 自动多语言翻译 |
|
|
185
186
|
| **数据库导出** | ✅ | MongoDB、MySQL、PostgreSQL |
|
|
186
187
|
| **TypeScript** | ✅ | 完整类型定义 |
|
|
187
188
|
| **性能优化** | ✅ | WeakMap 缓存、智能编译 |
|
|
189
|
+
| **插件系统** | ✅ | 支持自定义类型注册 (v1.1.0+) |
|
|
188
190
|
| **文档生成** | ✅ | Markdown、HTML |
|
|
189
191
|
|
|
192
|
+
### 🆕 v1.1.0 新特性:跨类型联合验证
|
|
193
|
+
|
|
194
|
+
**一行代码支持多种类型**
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
const { dsl, validate } = require('schema-dsl');
|
|
198
|
+
|
|
199
|
+
// 字段可以是字符串或数字
|
|
200
|
+
const schema = dsl({
|
|
201
|
+
value: 'types:string|number'
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
validate(schema, { value: 'hello' }); // ✅ 通过
|
|
205
|
+
validate(schema, { value: 123 }); // ✅ 通过
|
|
206
|
+
validate(schema, { value: true }); // ❌ 失败
|
|
207
|
+
|
|
208
|
+
// 带约束的联合类型
|
|
209
|
+
const advancedSchema = dsl({
|
|
210
|
+
contact: 'types:email|phone!', // 邮箱或手机号
|
|
211
|
+
price: 'types:number:0-|string:1-20' // 数字价格或"面议"
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**实际场景示例**:
|
|
216
|
+
```javascript
|
|
217
|
+
// 用户注册:支持邮箱或手机号
|
|
218
|
+
const registerSchema = dsl({
|
|
219
|
+
username: 'string:3-20!',
|
|
220
|
+
contact: 'types:email|phone!', // 灵活的联系方式
|
|
221
|
+
age: 'types:integer:1-150|null' // 年龄可选
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
📖 [完整文档](./docs/union-types.md) | [插件开发指南](./docs/plugin-type-registration.md)
|
|
226
|
+
|
|
190
227
|
---
|
|
191
228
|
|
|
192
229
|
## 📦 安装
|
|
@@ -377,6 +414,146 @@ const registerSchema = SchemaUtils
|
|
|
377
414
|
// partial - 变为可选(用于更新接口)
|
|
378
415
|
```
|
|
379
416
|
|
|
417
|
+
### 条件验证 - 一行代码搞定
|
|
418
|
+
|
|
419
|
+
**问题场景**:不同情况需要不同的验证规则
|
|
420
|
+
|
|
421
|
+
```javascript
|
|
422
|
+
const { dsl } = require('schema-dsl');
|
|
423
|
+
|
|
424
|
+
// 场景1:年龄限制 - 未成年不能注册
|
|
425
|
+
// ❌ 传统做法:先验证,再判断,写两次
|
|
426
|
+
const result = validate(schema, userData);
|
|
427
|
+
if (!result.valid) return;
|
|
428
|
+
if (userData.age < 18) {
|
|
429
|
+
throw new Error('未成年用户不能注册');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ✅ 新做法:一行代码搞定
|
|
433
|
+
dsl.if(d => d.age < 18)
|
|
434
|
+
.message('未成年用户不能注册')
|
|
435
|
+
.assert(userData); // 失败自动抛错
|
|
436
|
+
|
|
437
|
+
// 场景2:权限检查 - 快速判断
|
|
438
|
+
// ❌ 传统做法:写 if 判断
|
|
439
|
+
if (user.role !== 'admin' && user.role !== 'moderator') {
|
|
440
|
+
return res.status(403).json({ error: '权限不足' });
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ✅ 新做法:一行搞定
|
|
444
|
+
if (!dsl.if(d => d.role === 'admin' || d.role === 'moderator')
|
|
445
|
+
.message('权限不足')
|
|
446
|
+
.check(user)) {
|
|
447
|
+
return res.status(403).json({ error: '权限不足' });
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// 场景3:批量过滤 - 筛选符合条件的数据
|
|
451
|
+
// ❌ 传统做法:写 filter 函数
|
|
452
|
+
const adults = users.filter(u => u.age >= 18);
|
|
453
|
+
|
|
454
|
+
// ✅ 新做法:语义更清晰
|
|
455
|
+
const adults = users.filter(u =>
|
|
456
|
+
!dsl.if(d => d.age < 18).message('未成年').check(u)
|
|
457
|
+
);
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
#### 四种方法,满足不同场景
|
|
461
|
+
|
|
462
|
+
| 方法 | 什么时候用 | 返回什么 | 示例 |
|
|
463
|
+
|------|-----------|---------|------|
|
|
464
|
+
| **`.validate()`** | 需要知道错误详情 | `{ valid, errors, data }` | 表单验证 |
|
|
465
|
+
| **`.validateAsync()`** | async/await 场景 | Promise(失败抛错) | Express 中间件 |
|
|
466
|
+
| **`.assert()`** | 快速失败,不想写 if | 失败直接抛错 | 函数入口检查 |
|
|
467
|
+
| **`.check()`** | 只需要判断真假 | `true/false` | 数据过滤 |
|
|
468
|
+
|
|
469
|
+
#### 实际例子
|
|
470
|
+
|
|
471
|
+
**表单验证 - 需要显示错误**
|
|
472
|
+
|
|
473
|
+
```javascript
|
|
474
|
+
// 使用 .validate() 获取错误详情
|
|
475
|
+
const result = dsl.if(d => d.age < 18)
|
|
476
|
+
.message('未成年用户不能注册')
|
|
477
|
+
.validate(formData);
|
|
478
|
+
|
|
479
|
+
if (!result.valid) {
|
|
480
|
+
showError(result.errors[0].message); // 显示给用户
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
**Express 中间件 - 异步验证**
|
|
485
|
+
|
|
486
|
+
```javascript
|
|
487
|
+
// 使用 .validateAsync() 失败自动抛错
|
|
488
|
+
app.post('/register', async (req, res, next) => {
|
|
489
|
+
try {
|
|
490
|
+
await dsl.if(d => d.age < 18)
|
|
491
|
+
.message('未成年用户不能注册')
|
|
492
|
+
.validateAsync(req.body);
|
|
493
|
+
|
|
494
|
+
// 验证通过,继续处理
|
|
495
|
+
const user = await createUser(req.body);
|
|
496
|
+
res.json(user);
|
|
497
|
+
} catch (error) {
|
|
498
|
+
next(error); // 自动传递给错误处理中间件
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**函数参数检查 - 快速断言**
|
|
504
|
+
|
|
505
|
+
```javascript
|
|
506
|
+
// 使用 .assert() 不满足直接抛错
|
|
507
|
+
function registerUser(userData) {
|
|
508
|
+
// 入口检查,不满足直接抛错,代码更清晰
|
|
509
|
+
dsl.if(d => d.age < 18).message('未成年不能注册').assert(userData);
|
|
510
|
+
dsl.if(d => !d.email).message('邮箱必填').assert(userData);
|
|
511
|
+
dsl.if(d => !d.phone).message('手机号必填').assert(userData);
|
|
512
|
+
|
|
513
|
+
// 检查通过,继续业务逻辑
|
|
514
|
+
return createUser(userData);
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
**批量数据处理 - 快速过滤**
|
|
519
|
+
|
|
520
|
+
```javascript
|
|
521
|
+
// 使用 .check() 只返回 true/false
|
|
522
|
+
const canRegister = dsl.if(d => d.age < 18)
|
|
523
|
+
.or(d => d.status === 'blocked')
|
|
524
|
+
.message('不允许注册');
|
|
525
|
+
|
|
526
|
+
// 过滤出可以注册的用户
|
|
527
|
+
const validUsers = users.filter(u => !canRegister.check(u));
|
|
528
|
+
|
|
529
|
+
// 统计未成年用户数量
|
|
530
|
+
const minorCount = users.filter(u =>
|
|
531
|
+
dsl.if(d => d.age < 18).message('未成年').check(u)
|
|
532
|
+
).length;
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**复用验证器**
|
|
536
|
+
|
|
537
|
+
```javascript
|
|
538
|
+
// 创建一次,到处使用
|
|
539
|
+
const ageValidator = dsl.if(d => d.age < 18)
|
|
540
|
+
.message('未成年用户不能注册');
|
|
541
|
+
|
|
542
|
+
// 不同场景使用不同方法
|
|
543
|
+
const r1 = ageValidator.validate({ age: 16 }); // 同步,返回详情
|
|
544
|
+
const r2 = await ageValidator.validateAsync(data); // 异步,失败抛错
|
|
545
|
+
const r3 = ageValidator.check({ age: 20 }); // 快速判断
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
#### 💡 选择建议
|
|
549
|
+
|
|
550
|
+
- 🎯 **表单验证**:用 `.validate()` - 需要显示错误给用户
|
|
551
|
+
- 🚀 **API 接口**:用 `.validateAsync()` - 配合 try/catch
|
|
552
|
+
- ⚡ **函数入口**:用 `.assert()` - 快速失败,代码简洁
|
|
553
|
+
- 🔍 **数据过滤**:用 `.check()` - 只需要判断真假
|
|
554
|
+
|
|
555
|
+
**完整文档**: [ConditionalBuilder API](./docs/conditional-api.md)
|
|
556
|
+
|
|
380
557
|
---
|
|
381
558
|
|
|
382
559
|
## 📖 DSL 语法速查
|
|
@@ -599,6 +776,86 @@ const vipSchema = dsl({
|
|
|
599
776
|
validate(vipSchema, { isVip: true, discount: 30 });
|
|
600
777
|
|
|
601
778
|
// ❌ 非 VIP 用户折扣超过 10
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### 🆕 链式条件判断 - dsl.if() (v1.1.0)
|
|
782
|
+
|
|
783
|
+
**运行时动态条件判断,类似 JavaScript if-else 语句**
|
|
784
|
+
|
|
785
|
+
```javascript
|
|
786
|
+
const { dsl, validate } = require('schema-dsl');
|
|
787
|
+
|
|
788
|
+
// 1. 简单条件 + 错误消息
|
|
789
|
+
const schema1 = dsl({
|
|
790
|
+
age: 'number!',
|
|
791
|
+
status: dsl.if((data) => data.age >= 18)
|
|
792
|
+
.message('未成年用户不能注册') // 不满足自动抛错
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
validate(schema1, { age: 16, status: 'active' });
|
|
796
|
+
// => { valid: false, errors: [{ message: '未成年用户不能注册' }] }
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
// 2. 条件 + then/else(动态Schema)
|
|
800
|
+
const schema2 = dsl({
|
|
801
|
+
userType: 'string!',
|
|
802
|
+
email: dsl.if((data) => data.userType === 'admin')
|
|
803
|
+
.then('email!') // 管理员必填
|
|
804
|
+
.else('email') // 普通用户可选
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
// 3. 多条件 AND
|
|
809
|
+
const schema3 = dsl({
|
|
810
|
+
age: 'number!',
|
|
811
|
+
userType: 'string!',
|
|
812
|
+
email: dsl.if((data) => data.age >= 18)
|
|
813
|
+
.and((data) => data.userType === 'admin')
|
|
814
|
+
.then('email!')
|
|
815
|
+
.else('email')
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
// 4. 多条件 OR
|
|
820
|
+
const schema4 = dsl({
|
|
821
|
+
age: 'number!',
|
|
822
|
+
status: 'string!',
|
|
823
|
+
reason: dsl.if((data) => data.age < 18)
|
|
824
|
+
.or((data) => data.status === 'blocked')
|
|
825
|
+
.message('不允许注册')
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
// 5. elseIf 多分支
|
|
830
|
+
const schema5 = dsl({
|
|
831
|
+
userType: 'string!',
|
|
832
|
+
permissions: dsl.if((data) => data.userType === 'admin')
|
|
833
|
+
.then('array<string>!')
|
|
834
|
+
.elseIf((data) => data.userType === 'vip')
|
|
835
|
+
.then('array<string>')
|
|
836
|
+
.else(null) // 游客不验证
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
// 6. else 可选(不写 else 就不验证)
|
|
841
|
+
const schema6 = dsl({
|
|
842
|
+
userType: 'string!',
|
|
843
|
+
vipLevel: dsl.if((data) => data.userType === 'vip')
|
|
844
|
+
.then('enum:gold|silver|bronze!')
|
|
845
|
+
// 不写 else,非 vip 用户不验证
|
|
846
|
+
});
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
**核心特性**:
|
|
850
|
+
- ✅ **运行时执行** - 在验证时根据实际数据判断(不是Schema定义时)
|
|
851
|
+
- ✅ **多条件组合** - 支持 and/or 逻辑组合
|
|
852
|
+
- ✅ **elseIf 分支** - 支持多层条件判断
|
|
853
|
+
- ✅ **else 可选** - 不写 else 就不验证
|
|
854
|
+
- ✅ **简化设计** - message 自动抛错,无需 throwError()
|
|
855
|
+
|
|
856
|
+
📖 [完整链式条件判断文档](./docs/conditional-api.md)
|
|
857
|
+
|
|
858
|
+
---
|
|
602
859
|
validate(vipSchema, { isVip: false, discount: 15 });
|
|
603
860
|
|
|
604
861
|
|
package/docs/FEATURE-INDEX.md
CHANGED
package/docs/best-practices.md
CHANGED
|
@@ -79,7 +79,7 @@ const schema = dsl({
|
|
|
79
79
|
|
|
80
80
|
### 3. 使用预设验证器
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
SchemaI-DSL 提供了常用的预设验证器,开箱即用:
|
|
83
83
|
|
|
84
84
|
```javascript
|
|
85
85
|
const schema = dsl({
|
|
@@ -639,7 +639,7 @@ setInterval(() => {
|
|
|
639
639
|
|
|
640
640
|
## 性能基准参考
|
|
641
641
|
|
|
642
|
-
基于
|
|
642
|
+
基于 SchemaI-DSL 的性能测试:
|
|
643
643
|
|
|
644
644
|
| 操作 | 性能指标 |
|
|
645
645
|
|------|---------|
|
|
@@ -655,7 +655,7 @@ setInterval(() => {
|
|
|
655
655
|
|
|
656
656
|
## 总结
|
|
657
657
|
|
|
658
|
-
遵循这些最佳实践,你的
|
|
658
|
+
遵循这些最佳实践,你的 SchemaI-DSL 代码将具备:
|
|
659
659
|
|
|
660
660
|
✅ **高性能** - 通过预编译和缓存
|
|
661
661
|
✅ **高安全性** - 避免常见安全陷阱
|