schema-dsl 1.1.2 → 1.1.4
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 +75 -1236
- package/README.md +221 -2
- package/STATUS.md +67 -2
- package/changelogs/v1.0.0.md +328 -0
- package/changelogs/v1.0.9.md +367 -0
- package/changelogs/v1.1.0.md +389 -0
- package/changelogs/v1.1.1.md +308 -0
- package/changelogs/v1.1.2.md +183 -0
- package/changelogs/v1.1.3.md +161 -0
- package/changelogs/v1.1.4.md +432 -0
- package/docs/dsl-syntax.md +14 -3
- package/docs/optional-marker-guide.md +321 -0
- package/docs/runtime-locale-support.md +443 -0
- package/index.d.ts +126 -10
- package/index.js +6 -3
- package/index.mjs +2 -2
- package/lib/core/DslBuilder.js +11 -2
- package/lib/core/ErrorFormatter.js +6 -1
- package/lib/errors/I18nError.js +21 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,1282 +1,121 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 变更日志 (CHANGELOG)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
|
6
|
-
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
3
|
+
> **说明**: 版本概览摘要,详细变更见 [changelogs/](./changelogs/) 目录
|
|
4
|
+
> **最后更新**: 2026-01-13
|
|
7
5
|
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
## 版本概览
|
|
11
9
|
|
|
12
|
-
| 版本
|
|
13
|
-
|
|
14
|
-
| [v1.1.
|
|
15
|
-
| [v1.1.
|
|
16
|
-
| [v1.1.
|
|
17
|
-
| [v1.
|
|
18
|
-
| [v1.0
|
|
19
|
-
| [v1.0.
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## [v1.1.2] - 2026-01-06
|
|
31
|
-
|
|
32
|
-
### 🎉 新功能
|
|
33
|
-
|
|
34
|
-
#### 数字比较运算符 - 更直观的数值验证
|
|
35
|
-
|
|
36
|
-
**新增 5 种比较运算符,让数值验证更语义化**
|
|
37
|
-
|
|
38
|
-
现在 `number` 和 `integer` 类型支持 5 种比较运算符:`>`, `>=`, `<`, `<=`, `=`
|
|
39
|
-
|
|
40
|
-
**基础用法**:
|
|
41
|
-
|
|
42
|
-
```javascript
|
|
43
|
-
const { dsl, validate } = require('schema-dsl');
|
|
44
|
-
|
|
45
|
-
// ✅ 大于(不包括边界)
|
|
46
|
-
const schema1 = dsl({ value: 'number:>0' });
|
|
47
|
-
validate(schema1, { value: 1 }); // ✅ true
|
|
48
|
-
validate(schema1, { value: 0 }); // ❌ false (0不满足>0)
|
|
49
|
-
|
|
50
|
-
// ✅ 大于等于
|
|
51
|
-
const schema2 = dsl({ age: 'number:>=18' });
|
|
52
|
-
validate(schema2, { age: 18 }); // ✅ true (包括18)
|
|
53
|
-
validate(schema2, { age: 17 }); // ❌ false
|
|
54
|
-
|
|
55
|
-
// ✅ 小于(不包括边界)
|
|
56
|
-
const schema3 = dsl({ temp: 'number:<100' });
|
|
57
|
-
validate(schema3, { temp: 99.9 }); // ✅ true
|
|
58
|
-
validate(schema3, { temp: 100 }); // ❌ false (100不满足<100)
|
|
59
|
-
|
|
60
|
-
// ✅ 小于等于
|
|
61
|
-
const schema4 = dsl({ score: 'number:<=100' });
|
|
62
|
-
validate(schema4, { score: 100 }); // ✅ true (包括100)
|
|
63
|
-
|
|
64
|
-
// ✅ 等于
|
|
65
|
-
const schema5 = dsl({ level: 'number:=5' });
|
|
66
|
-
validate(schema5, { level: 5 }); // ✅ true
|
|
67
|
-
validate(schema5, { level: 4 }); // ❌ false
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**支持小数和负数**:
|
|
71
|
-
|
|
72
|
-
```javascript
|
|
73
|
-
// 小数
|
|
74
|
-
dsl({ value: 'number:>0.5' }) // 大于0.5
|
|
75
|
-
dsl({ price: 'number:<=99.99' }) // 小于等于99.99
|
|
76
|
-
|
|
77
|
-
// 负数
|
|
78
|
-
dsl({ value: 'number:>-10' }) // 大于-10
|
|
79
|
-
dsl({ temp: 'number:<-5' }) // 小于-5
|
|
80
|
-
|
|
81
|
-
// 配合必填标记
|
|
82
|
-
dsl({ age: 'number:>=18!' }) // 必填且大于等于18
|
|
83
|
-
dsl({ price: 'number:>0!' }) // 必填且大于0
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**对比:比较运算符 vs 范围语法**:
|
|
87
|
-
|
|
88
|
-
| 需求 | 范围语法 | 比较运算符 | 推荐 |
|
|
89
|
-
|------|---------|-----------|------|
|
|
90
|
-
| x ≥ 18 | `number:18-` | `number:>=18` | **比较运算符**(更清晰) |
|
|
91
|
-
| x > 0(不包括0) | ❌ 无法表达 | `number:>0` | **比较运算符**(唯一方法) |
|
|
92
|
-
| x < 100(不包括100) | ❌ 无法表达 | `number:<100` | **比较运算符**(唯一方法) |
|
|
93
|
-
| x = 100 | `number:100`(实际≤100) | `number:=100` | **比较运算符**(精确匹配) |
|
|
94
|
-
|
|
95
|
-
**实际应用场景**:
|
|
96
|
-
|
|
97
|
-
```javascript
|
|
98
|
-
// 场景1:用户注册 - 年龄限制
|
|
99
|
-
const schema = dsl({
|
|
100
|
-
username: 'string:3-32!',
|
|
101
|
-
email: 'email!',
|
|
102
|
-
age: 'number:>=18!', // 必须年满18岁
|
|
103
|
-
password: 'string:8-!'
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// 场景2:电商系统 - 价格验证
|
|
107
|
-
const schema = dsl({
|
|
108
|
-
productName: 'string:1-100!',
|
|
109
|
-
price: 'number:>0!', // 价格必须大于0
|
|
110
|
-
discount: 'number:0-100' // 折扣 0-100
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// 场景3:温度监控
|
|
114
|
-
const schema = dsl({
|
|
115
|
-
deviceId: 'string!',
|
|
116
|
-
temperature: 'number:>0', // 温度 > 0
|
|
117
|
-
humidity: 'number:<=100' // 湿度 ≤ 100
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**JSON Schema 映射**:
|
|
122
|
-
|
|
123
|
-
```javascript
|
|
124
|
-
'number:>0' → { exclusiveMinimum: 0 }
|
|
125
|
-
'number:>=18' → { minimum: 18 }
|
|
126
|
-
'number:<100' → { exclusiveMaximum: 100 }
|
|
127
|
-
'number:<=100' → { maximum: 100 }
|
|
128
|
-
'number:=100' → { enum: [100] }
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
**特性**:
|
|
132
|
-
- ✅ 支持 5 种比较运算符
|
|
133
|
-
- ✅ 支持小数(如 `number:>0.5`)
|
|
134
|
-
- ✅ 支持负数(如 `number:>-10`)
|
|
135
|
-
- ✅ 支持必填标记(如 `number:>=18!`)
|
|
136
|
-
- ✅ 适用于 `number` 和 `integer` 类型
|
|
137
|
-
- ✅ 完全向后兼容原有范围语法
|
|
138
|
-
- ✅ 完整的 TypeScript 类型支持
|
|
139
|
-
|
|
140
|
-
**文档链接**:
|
|
141
|
-
- [数字比较运算符完整文档](./docs/number-operators.md)
|
|
142
|
-
- [README 语法速查](./README.md#-dsl-语法速查)
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
### 🐛 Bug 修复
|
|
10
|
+
| 版本 | 日期 | 变更摘要 | 详细 |
|
|
11
|
+
|------|------|---------|------|
|
|
12
|
+
| [v1.1.4](./changelogs/v1.1.4.md) | 2026-01-13 | 🔧 TypeScript类型修复:移除重复函数签名 + 多语言文档完善 | [查看](./changelogs/v1.1.4.md) |
|
|
13
|
+
| [v1.1.3](./changelogs/v1.1.3.md) | 2026-01-09 | 🐛 Bug修复:类型错误消息模板变量未替换 | [查看](./changelogs/v1.1.3.md) |
|
|
14
|
+
| [v1.1.2](./changelogs/v1.1.2.md) | 2026-01-06 | 🎉 新功能:数字比较运算符 + Bug修复 | [查看](./changelogs/v1.1.2.md) |
|
|
15
|
+
| [v1.1.1](./changelogs/v1.1.1.md) | 2026-01-06 | 🎉 新功能:ConditionalBuilder 独立消息支持 | [查看](./changelogs/v1.1.1.md) |
|
|
16
|
+
| [v1.1.0](./changelogs/v1.1.0.md) | 2026-01-05 | 🎉 重大功能:跨类型联合验证 + 插件系统增强 | [查看](./changelogs/v1.1.0.md) |
|
|
17
|
+
| [v1.0.9](./changelogs/v1.0.9.md) | 2026-01-04 | 🎉 重大改进:多语言支持完善 + TypeScript 类型完整 | [查看](./changelogs/v1.0.9.md) |
|
|
18
|
+
| v1.0.8 | 2026-01-04 | 优化:错误消息过滤增强 | - |
|
|
19
|
+
| v1.0.7 | 2026-01-04 | 修复:dsl.match/dsl.if 嵌套支持 dsl() 包裹 | - |
|
|
20
|
+
| v1.0.6 | 2026-01-04 | 🚨 紧急修复:TypeScript 类型污染 | - |
|
|
21
|
+
| v1.0.5 | 2026-01-04 | 测试覆盖率提升至 97% | - |
|
|
22
|
+
| v1.0.4 | 2025-12-31 | TypeScript 完整支持、validateAsync、ValidationError | - |
|
|
23
|
+
| v1.0.3 | 2025-12-31 | ⚠️ 破坏性变更:单值语法修复 | - |
|
|
24
|
+
| v1.0.2 | 2025-12-31 | 15个新增验证器、完整文档、75个测试 | - |
|
|
25
|
+
| v1.0.1 | 2025-12-31 | 枚举功能、自动类型识别、统一错误消息 | - |
|
|
26
|
+
| [v1.0.0](./changelogs/v1.0.0.md) | 2025-12-29 | 初始发布版本 | [查看](./changelogs/v1.0.0.md) |
|
|
147
27
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
在 v1.1.1 及之前版本,使用 `number:>0` 等语法时会被错误解析为字符串枚举 `enum: ['>0']`,导致验证结果完全相反。
|
|
152
|
-
|
|
153
|
-
**修复前**:
|
|
154
|
-
```javascript
|
|
155
|
-
const schema = dsl({ value: 'number:>0' });
|
|
156
|
-
validate(schema, { value: -5 }); // ❌ 错误:返回 true(应该false)
|
|
157
|
-
validate(schema, { value: 5 }); // ❌ 错误:返回 false(应该true)
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
**修复后**:
|
|
161
|
-
```javascript
|
|
162
|
-
const schema = dsl({ value: 'number:>0' });
|
|
163
|
-
validate(schema, { value: -5 }); // ✅ 正确:返回 false
|
|
164
|
-
validate(schema, { value: 5 }); // ✅ 正确:返回 true
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
**影响范围**:
|
|
168
|
-
- 仅影响使用 `>`, `<`, `>=`, `<=`, `=` 符号的 number 约束
|
|
169
|
-
- 原有范围语法(如 `number:0-100`)不受影响
|
|
170
|
-
- 完全向后兼容
|
|
28
|
+
> 💡 **提示**: 重要版本的详细变更记录在 [changelogs/](./changelogs/) 目录中。
|
|
29
|
+
> 当前已有详细文档的版本:v1.1.4, v1.1.3, v1.1.2, v1.1.1, v1.1.0, v1.0.9, v1.0.0
|
|
30
|
+
> 其他版本的详细变更信息请参考项目提交历史或联系维护者。
|
|
171
31
|
|
|
172
32
|
---
|
|
173
33
|
|
|
174
|
-
|
|
34
|
+
## 变更统计
|
|
175
35
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
36
|
+
| 版本系列 | 版本数 | 主要改进方向 |
|
|
37
|
+
|---------|-------|------------|
|
|
38
|
+
| v1.1.x | 5 | TypeScript类型完善、多语言支持、数字运算符、联合类型、类型错误修复 |
|
|
39
|
+
| v1.0.x | 10 | 核心功能、验证器、测试覆盖、文档完善 |
|
|
180
40
|
|
|
181
41
|
---
|
|
182
42
|
|
|
183
|
-
|
|
43
|
+
## 里程碑版本
|
|
184
44
|
|
|
185
|
-
-
|
|
186
|
-
- **测试总数**:949 个(921 + 28)
|
|
187
|
-
- **测试覆盖**:100% 通过
|
|
188
|
-
- **测试文件**:`test/unit/number-operators.test.js`
|
|
45
|
+
### v1.1.4 - TypeScript类型修复与文档完善 🔧
|
|
189
46
|
|
|
190
|
-
|
|
47
|
+
**发布日期**: 2026-01-13
|
|
48
|
+
**重要性**: ⭐⭐⭐
|
|
191
49
|
|
|
192
|
-
|
|
50
|
+
**核心改进**:
|
|
51
|
+
- ✅ 修复 index.d.ts 中2处重复函数签名(46个类型错误→0个错误)
|
|
52
|
+
- ✅ 修复 `dsl.error.assert` 重复签名(L1336-1343)
|
|
53
|
+
- ✅ 修复 `I18nError.assert` 重复签名(L1805-1821)
|
|
54
|
+
- ✅ 完善多语言运行时支持文档(docs/runtime-locale-support.md)
|
|
55
|
+
- ✅ README.md 添加运行时语言指定示例
|
|
56
|
+
- ✅ 验证 string? 可选语法完整支持
|
|
193
57
|
|
|
194
|
-
|
|
195
|
-
- **Bug 修复**:1 个(比较运算符误解析)
|
|
196
|
-
- **文档更新**:3 个(README, docs/number-operators.md, index.d.ts)
|
|
197
|
-
- **测试新增**:28 个
|
|
198
|
-
- **代码变更**:2 个文件(lib/core/DslBuilder.js, lib/adapters/DslAdapter.js)
|
|
58
|
+
**详细信息**: [查看 changelogs/v1.1.4.md](./changelogs/v1.1.4.md)
|
|
199
59
|
|
|
200
60
|
---
|
|
201
61
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
### 🎉 新功能
|
|
205
|
-
|
|
206
|
-
#### ConditionalBuilder 独立消息支持 - `.and()/.or()` 后可调用 `.message()`
|
|
207
|
-
|
|
208
|
-
**每个条件都可以有自己的错误消息**
|
|
62
|
+
### v1.1.3 - 类型错误消息模板修复 🐛
|
|
209
63
|
|
|
210
|
-
|
|
64
|
+
**发布日期**: 2026-01-09
|
|
65
|
+
**重要性**: ⭐⭐⭐
|
|
211
66
|
|
|
212
|
-
|
|
67
|
+
**核心改进**:
|
|
68
|
+
- ✅ 修复类型错误消息中 `{{#actual}}` 模板变量未替换问题
|
|
69
|
+
- ✅ 错误消息正确显示实际数据类型
|
|
70
|
+
- ✅ 向后兼容,无破坏性变更
|
|
213
71
|
|
|
214
|
-
|
|
215
|
-
const { dsl } = require('schema-dsl');
|
|
216
|
-
|
|
217
|
-
// ✅ v1.1.1 新功能:每个条件独立消息
|
|
218
|
-
dsl.if(d => !d)
|
|
219
|
-
.message('ACCOUNT_NOT_FOUND')
|
|
220
|
-
.and(d => d.tradable_credits < amount)
|
|
221
|
-
.message('INSUFFICIENT_TRADABLE_CREDITS')
|
|
222
|
-
.assert(account);
|
|
223
|
-
|
|
224
|
-
// 工作原理:
|
|
225
|
-
// - 第一个条件失败 → 返回 'ACCOUNT_NOT_FOUND'
|
|
226
|
-
// - 第二个条件失败 → 返回 'INSUFFICIENT_TRADABLE_CREDITS'
|
|
227
|
-
// - 所有条件通过 → 验证成功
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
**特性**:
|
|
231
|
-
- ✅ 支持多个 `.and()` 条件各有独立消息
|
|
232
|
-
- ✅ 支持 `.or()` 条件独立消息
|
|
233
|
-
- ✅ 自动检测启用链式检查模式
|
|
234
|
-
- ✅ 100% 向后兼容,不影响现有代码
|
|
235
|
-
- ✅ 完整的 TypeScript 类型支持
|
|
236
|
-
|
|
237
|
-
**实际应用示例**:
|
|
238
|
-
|
|
239
|
-
```javascript
|
|
240
|
-
// 多层验证,每层都有清晰的错误消息
|
|
241
|
-
dsl.if(d => !d)
|
|
242
|
-
.message('ACCOUNT_NOT_FOUND')
|
|
243
|
-
.and(d => d.status !== 'active')
|
|
244
|
-
.message('ACCOUNT_INACTIVE')
|
|
245
|
-
.and(d => d.tradable_credits < amount)
|
|
246
|
-
.message('INSUFFICIENT_TRADABLE_CREDITS')
|
|
247
|
-
.assert(account);
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
**文档链接**:
|
|
251
|
-
- [条件 API 文档](./docs/conditional-api.md)
|
|
252
|
-
- [README FAQ Q7](./README.md#q7-如何合并多个-dslif-验证)
|
|
72
|
+
**详细信息**: [查看 changelogs/v1.1.3.md](./changelogs/v1.1.3.md)
|
|
253
73
|
|
|
254
74
|
---
|
|
255
75
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
**统一的多语言错误抛出**
|
|
259
|
-
|
|
260
|
-
新增 `I18nError` 类和 `dsl.error` 快捷方法,提供统一的多语言错误抛出机制。
|
|
76
|
+
### v1.1.0 - 跨类型联合验证 🎉
|
|
261
77
|
|
|
262
|
-
|
|
78
|
+
**发布日期**: 2026-01-05
|
|
79
|
+
**重要性**: ⭐⭐⭐⭐⭐
|
|
263
80
|
|
|
264
|
-
|
|
265
|
-
|
|
81
|
+
**核心特性**:
|
|
82
|
+
- ✅ 跨类型联合验证(email|phone 可以混合不同类型)
|
|
83
|
+
- ✅ 运行时多语言支持(dsl.error.create 可指定 locale 参数)
|
|
84
|
+
- ✅ 插件系统增强
|
|
266
85
|
|
|
267
|
-
|
|
268
|
-
I18nError.throw('account.notFound');
|
|
269
|
-
// 中文: "账户不存在"
|
|
270
|
-
// 英文: "Account not found"
|
|
271
|
-
|
|
272
|
-
// 方式2:带参数插值
|
|
273
|
-
I18nError.throw('account.insufficientBalance', {
|
|
274
|
-
balance: 50,
|
|
275
|
-
required: 100
|
|
276
|
-
});
|
|
277
|
-
// 输出: "余额不足,当前余额50,需要100"
|
|
278
|
-
|
|
279
|
-
// 方式3:断言风格(推荐)
|
|
280
|
-
I18nError.assert(account, 'account.notFound');
|
|
281
|
-
I18nError.assert(
|
|
282
|
-
account.balance >= 100,
|
|
283
|
-
'account.insufficientBalance',
|
|
284
|
-
{ balance: account.balance, required: 100 }
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
// 方式4:快捷方法
|
|
288
|
-
dsl.error.throw('user.noPermission');
|
|
289
|
-
dsl.error.assert(user.role === 'admin', 'user.noPermission');
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
**特性**:
|
|
293
|
-
- ✅ 统一的错误代码格式:`{模块}.{错误类型}`
|
|
294
|
-
- ✅ 自动多语言翻译(支持中英文)
|
|
295
|
-
- ✅ 参数插值支持
|
|
296
|
-
- ✅ Express/Koa 集成(toJSON 方法)
|
|
297
|
-
- ✅ 与 `.message()` 和 `.label()` 使用相同的多语言机制
|
|
298
|
-
|
|
299
|
-
**内置错误代码**:
|
|
300
|
-
- 通用: `error.notFound`, `error.forbidden`, `error.unauthorized`
|
|
301
|
-
- 账户: `account.notFound`, `account.insufficientBalance`, `account.insufficientCredits`
|
|
302
|
-
- 用户: `user.notFound`, `user.noPermission`, `user.notVerified`
|
|
303
|
-
- 订单: `order.notPaid`, `order.paymentMissing`, `order.addressMissing`
|
|
304
|
-
|
|
305
|
-
**Express/Koa 集成**:
|
|
306
|
-
|
|
307
|
-
```javascript
|
|
308
|
-
app.use((error, req, res, next) => {
|
|
309
|
-
if (error instanceof I18nError) {
|
|
310
|
-
return res.status(error.statusCode).json(error.toJSON());
|
|
311
|
-
}
|
|
312
|
-
next(error);
|
|
313
|
-
});
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
**文档链接**:
|
|
317
|
-
- [使用示例](./examples/i18n-error.examples.js)
|
|
318
|
-
- [README FAQ Q8](./README.md#q8-如何统一抛出多语言错误v111)
|
|
86
|
+
**详细信息**: [查看 changelogs/v1.1.0.md](./changelogs/v1.1.0.md)
|
|
319
87
|
|
|
320
88
|
---
|
|
321
89
|
|
|
322
|
-
###
|
|
90
|
+
### v1.0.9 - 多语言支持完善 🌍
|
|
323
91
|
|
|
324
|
-
-
|
|
325
|
-
|
|
92
|
+
**发布日期**: 2026-01-04
|
|
93
|
+
**重要性**: ⭐⭐⭐⭐
|
|
326
94
|
|
|
327
|
-
|
|
95
|
+
**核心特性**:
|
|
96
|
+
- ✅ 完整的多语言支持(5种语言)
|
|
97
|
+
- ✅ TypeScript 类型定义完整
|
|
98
|
+
- ✅ I18nError 多语言错误类
|
|
328
99
|
|
|
329
|
-
|
|
330
|
-
- **新增**: examples/i18n-error.examples.js I18nError 使用示例
|
|
331
|
-
- **更新**: README.md FAQ Q7/Q8 添加新功能说明
|
|
332
|
-
- **更新**: index.d.ts TypeScript 类型注释和示例(I18nError + dsl.error)
|
|
100
|
+
**详细信息**: [查看 changelogs/v1.0.9.md](./changelogs/v1.0.9.md)
|
|
333
101
|
|
|
334
102
|
---
|
|
335
103
|
|
|
336
|
-
|
|
104
|
+
### v1.0.0 - 正式发布 🎉
|
|
337
105
|
|
|
338
|
-
|
|
106
|
+
**发布日期**: 2025-12-29
|
|
107
|
+
**重要性**: ⭐⭐⭐⭐⭐
|
|
339
108
|
|
|
340
|
-
|
|
109
|
+
**核心成就**:
|
|
110
|
+
- ✅ 初始发布
|
|
111
|
+
- ✅ 简洁的DSL语法
|
|
112
|
+
- ✅ 完整的验证功能
|
|
341
113
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
...existing content...
|
|
345
|
-
|
|
346
|
-
**带约束的联合类型**:
|
|
347
|
-
|
|
348
|
-
```javascript
|
|
349
|
-
const schema = dsl({
|
|
350
|
-
contact: 'types:email|phone!', // 邮箱或手机号
|
|
351
|
-
price: 'types:number:0-|string:1-20', // 数字价格或"面议"
|
|
352
|
-
rating: 'types:integer:1-5|string:9' // 整数1-5或字符串"excellent"
|
|
353
|
-
});
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
**实际应用场景**:
|
|
357
|
-
|
|
358
|
-
```javascript
|
|
359
|
-
// 用户注册:灵活的联系方式
|
|
360
|
-
const registerSchema = dsl({
|
|
361
|
-
username: 'string:3-20!',
|
|
362
|
-
contact: 'types:email|phone!', // 支持邮箱或手机号注册
|
|
363
|
-
age: 'types:integer:1-150|null' // 年龄可选(null表示不填)
|
|
364
|
-
});
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
**支持的类型**:
|
|
368
|
-
- ✅ 所有内置类型:`string`, `number`, `email`, `url`, `uuid` 等
|
|
369
|
-
- ✅ 插件注册的自定义类型(见下文)
|
|
114
|
+
**详细信息**: [查看 changelogs/v1.0.0.md](./changelogs/v1.0.0.md)
|
|
370
115
|
|
|
371
116
|
---
|
|
372
117
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
插件不再局限于注册ajv format/keyword,现在可以注册完整的DSL类型,让自定义类型在DSL语法中直接可用。
|
|
378
|
-
|
|
379
|
-
**改造前** (v1.0.x):
|
|
380
|
-
```javascript
|
|
381
|
-
// ❌ 只能注册ajv format(仅验证阶段生效)
|
|
382
|
-
install(schemaDsl, options) {
|
|
383
|
-
const ajv = validator.getAjv();
|
|
384
|
-
ajv.addFormat('phone-cn', { validate: /^1[3-9]\d{9}$/ });
|
|
385
|
-
|
|
386
|
-
// ❌ 无法在DSL语法中使用
|
|
387
|
-
// dsl({ phone: 'phone-cn!' }) // 报错:未知类型
|
|
388
|
-
}
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
**改造后** (v1.1.0):
|
|
392
|
-
```javascript
|
|
393
|
-
// ✅ 同时注册DSL类型和ajv format
|
|
394
|
-
install(schemaDsl, options) {
|
|
395
|
-
const { DslBuilder } = schemaDsl;
|
|
396
|
-
|
|
397
|
-
// ✅ 注册到DSL解析器(解析阶段)
|
|
398
|
-
DslBuilder.registerType('phone-cn', {
|
|
399
|
-
type: 'string',
|
|
400
|
-
pattern: /^1[3-9]\d{9}$/.source,
|
|
401
|
-
minLength: 11,
|
|
402
|
-
maxLength: 11
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
// ✅ 注册到ajv(验证阶段)
|
|
406
|
-
const ajv = validator.getAjv();
|
|
407
|
-
ajv.addFormat('phone-cn', { validate: /^1[3-9]\d{9}$/ });
|
|
408
|
-
|
|
409
|
-
// ✅ 现在可以在DSL中直接使用
|
|
410
|
-
// dsl({ phone: 'phone-cn!' }) // ✅ 成功
|
|
411
|
-
// dsl({ contact: 'types:email|phone-cn' }) // ✅ 在联合类型中也能用
|
|
412
|
-
}
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
**新增 API**:
|
|
416
|
-
|
|
417
|
-
| API | 说明 | 用途 |
|
|
418
|
-
|-----|------|------|
|
|
419
|
-
| `DslBuilder.registerType(name, schema)` | 注册自定义类型 | 插件注册新类型 |
|
|
420
|
-
| `DslBuilder.hasType(type)` | 检查类型是否存在 | 插件验证类型 |
|
|
421
|
-
| `DslBuilder.getCustomTypes()` | 获取所有自定义类型 | 调试和测试 |
|
|
422
|
-
| `DslBuilder.clearCustomTypes()` | 清除自定义类型 | 测试用 |
|
|
423
|
-
|
|
424
|
-
**插件示例**:
|
|
425
|
-
|
|
426
|
-
```javascript
|
|
427
|
-
// plugins/custom-format.js (v2.0.0)
|
|
428
|
-
module.exports = {
|
|
429
|
-
name: 'custom-format',
|
|
430
|
-
version: '2.0.0',
|
|
431
|
-
|
|
432
|
-
install(schemaDsl, options, context) {
|
|
433
|
-
const { DslBuilder } = schemaDsl;
|
|
434
|
-
const ajv = validator.getAjv();
|
|
435
|
-
|
|
436
|
-
// 定义自定义类型
|
|
437
|
-
const types = {
|
|
438
|
-
'phone-cn': {
|
|
439
|
-
pattern: /^1[3-9]\d{9}$/,
|
|
440
|
-
schema: { type: 'string', pattern: /^1[3-9]\d{9}$/.source, minLength: 11, maxLength: 11 }
|
|
441
|
-
},
|
|
442
|
-
'qq': {
|
|
443
|
-
pattern: /^[1-9][0-9]{4,10}$/,
|
|
444
|
-
schema: { type: 'string', pattern: /^[1-9][0-9]{4,10}$/.source, minLength: 5, maxLength: 11 }
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
// 注册到DSL和ajv
|
|
449
|
-
Object.keys(types).forEach(name => {
|
|
450
|
-
const config = types[name];
|
|
451
|
-
DslBuilder.registerType(name, config.schema); // DSL解析
|
|
452
|
-
ajv.addFormat(name, { validate: config.pattern }); // ajv验证
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
---
|
|
459
|
-
|
|
460
|
-
#### 3. ConditionalBuilder 快捷验证方法
|
|
461
|
-
|
|
462
|
-
**一行代码完成条件验证**
|
|
463
|
-
|
|
464
|
-
新增4个快捷验证方法,让条件判断更简洁:
|
|
465
|
-
|
|
466
|
-
| 方法 | 返回值 | 抛错 | 异步 | 适用场景 |
|
|
467
|
-
|------|--------|------|------|---------|
|
|
468
|
-
| `.validate()` | `{ valid, errors, data }` | ❌ | ❌ | 需要错误详情 |
|
|
469
|
-
| `.validateAsync()` | `Promise<data>` | ✅ | ✅ | async/await、中间件 |
|
|
470
|
-
| `.assert()` | `data` | ✅ | ❌ | 快速失败、断言 |
|
|
471
|
-
| `.check()` | `boolean` | ❌ | ❌ | 只需判断真假 |
|
|
472
|
-
|
|
473
|
-
**使用示例**:
|
|
474
|
-
|
|
475
|
-
```javascript
|
|
476
|
-
// 同步验证 - 返回详细结果
|
|
477
|
-
const result = dsl.if(d => d.age < 18)
|
|
478
|
-
.message('未成年用户不能注册')
|
|
479
|
-
.validate({ age: 16 });
|
|
480
|
-
|
|
481
|
-
// 异步验证 - 失败自动抛错
|
|
482
|
-
await dsl.if(d => d.age < 18)
|
|
483
|
-
.message('未成年用户不能注册')
|
|
484
|
-
.validateAsync({ age: 16 });
|
|
485
|
-
|
|
486
|
-
// 断言验证 - 同步抛错
|
|
487
|
-
dsl.if(d => d.age < 18).message('未成年').assert(userData);
|
|
488
|
-
|
|
489
|
-
// 快速判断 - 返回 boolean
|
|
490
|
-
const isValid = dsl.if(d => d.age < 18).message('未成年').check(data);
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
**优势**:
|
|
494
|
-
- ✅ 从2行代码减少到1行
|
|
495
|
-
- ✅ 支持 async/await
|
|
496
|
-
- ✅ 快速失败模式
|
|
497
|
-
- ✅ 完全向后兼容
|
|
498
|
-
|
|
499
|
-
**详细文档**:[ConditionalBuilder API](./docs/conditional-api.md)
|
|
500
|
-
|
|
501
|
-
### Changed
|
|
502
|
-
|
|
503
|
-
- 📖 优化 README.md,新增"条件验证 - 一行代码搞定"章节
|
|
504
|
-
- 📖 完善 API 文档和使用示例
|
|
505
|
-
- 📖 新增4个方法的 TypeScript 类型定义
|
|
506
|
-
|
|
507
|
-
### Tests
|
|
508
|
-
|
|
509
|
-
- ✅ 新增 45个测试用例(总计 807个测试)
|
|
510
|
-
- `.validate()` 方法: 9个
|
|
511
|
-
- `.validateAsync()` 方法: 6个
|
|
512
|
-
- `.assert()` 方法: 6个
|
|
513
|
-
- `.check()` 方法: 5个
|
|
514
|
-
- 边界情况: 12个
|
|
515
|
-
- 特殊场景: 7个
|
|
516
|
-
- ✅ 测试覆盖率 100%(807/807)
|
|
517
|
-
- ✅ 覆盖 null、undefined、空值、NaN、异常处理等所有边界情况
|
|
518
|
-
|
|
519
|
-
### 向后兼容
|
|
520
|
-
|
|
521
|
-
✅ **完全向后兼容**,所有现有代码无需修改
|
|
522
|
-
|
|
523
|
-
---
|
|
524
|
-
|
|
525
|
-
## [v1.0.9] - 2026-01-04
|
|
526
|
-
|
|
527
|
-
### 🎉 重大改进
|
|
528
|
-
|
|
529
|
-
#### 1. 多语言文档完善 ⭐⭐⭐
|
|
530
|
-
|
|
531
|
-
**优化内容**:
|
|
532
|
-
|
|
533
|
-
1. ✅ **统一文档描述为"首次加载,运行时切换"**
|
|
534
|
-
- 修复 `i18n-user-guide.md` 配置示例错误
|
|
535
|
-
- 更新 `add-custom-locale.md`、`dynamic-locale.md`、`frontend-i18n-guide.md`
|
|
536
|
-
- 添加完整的配置和使用示例
|
|
537
|
-
- 添加错误示例对比(运行时单个加载 vs 首次加载)
|
|
538
|
-
|
|
539
|
-
2. ✅ **添加文档导航**
|
|
540
|
-
- 在 `i18n.md` 添加多语言文档导航
|
|
541
|
-
- 帮助用户快速找到所需文档
|
|
542
|
-
|
|
543
|
-
3. ✅ **性能数据更新**
|
|
544
|
-
- 更新性能对比数据:**2,879,606 ops/s**(提升3.19倍)
|
|
545
|
-
- 比 Zod 快 1.58倍
|
|
546
|
-
- 比 Joi 快 9.61倍
|
|
547
|
-
- 比 Yup 快 27.07倍
|
|
548
|
-
|
|
549
|
-
#### 2. 多语言支持完善(v1.0.9 早期)⭐⭐⭐
|
|
550
|
-
|
|
551
|
-
**问题描述**:
|
|
552
|
-
- TypeScript 类型定义缺少 `options` 参数
|
|
553
|
-
- 多语言切换需要修改全局状态,并发场景不安全
|
|
554
|
-
- 自定义错误消息查找逻辑有问题
|
|
555
|
-
|
|
556
|
-
**修复内容**:
|
|
557
|
-
|
|
558
|
-
1. ✅ **TypeScript 类型定义完善**
|
|
559
|
-
```typescript
|
|
560
|
-
// 新增 ValidateOptions 接口
|
|
561
|
-
interface ValidateOptions {
|
|
562
|
-
format?: boolean; // 是否格式化错误
|
|
563
|
-
locale?: string; // 动态指定语言(如 'zh-CN', 'en-US')
|
|
564
|
-
messages?: ErrorMessages; // 自定义错误消息
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// 更新函数签名
|
|
568
|
-
function validate(
|
|
569
|
-
schema: JSONSchema | DslBuilder,
|
|
570
|
-
data: any,
|
|
571
|
-
options?: ValidateOptions // ← 新增
|
|
572
|
-
): ValidationResult;
|
|
573
|
-
|
|
574
|
-
// 更新 Validator.validate 方法签名
|
|
575
|
-
validate(
|
|
576
|
-
schema: JSONSchema | DslBuilder | Function,
|
|
577
|
-
data: any,
|
|
578
|
-
options?: ValidateOptions // ← 新增
|
|
579
|
-
): ValidationResult;
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
2. ✅ **参数化语言切换(无需全局状态)**
|
|
583
|
-
```javascript
|
|
584
|
-
// 旧方式(需要修改全局状态)
|
|
585
|
-
Locale.setLocale('zh-CN');
|
|
586
|
-
const result = validate(schema, data);
|
|
587
|
-
|
|
588
|
-
// 新方式(参数化,支持并发)
|
|
589
|
-
const result = validate(schema, data, { locale: 'zh-CN' });
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
3. ✅ **修复自定义错误消息查找逻辑**
|
|
593
|
-
- **Bug 1**: 优先使用通用模板而非 schema 自定义消息
|
|
594
|
-
- **Bug 2**: 无法正确处理三种自定义消息格式:
|
|
595
|
-
- 键引用:`_customMessages.pattern = "pattern.objectId"`
|
|
596
|
-
- 模板字符串:`_customMessages.min = "{{#label}}必须大于{{#limit}}"`
|
|
597
|
-
- 最终消息:`_customMessages.pattern = "手机号格式不正确"`
|
|
598
|
-
|
|
599
|
-
**修复效果**:
|
|
600
|
-
|
|
601
|
-
```javascript
|
|
602
|
-
// 1. 并发调用不同语言
|
|
603
|
-
const [resultZh, resultEn] = await Promise.all([
|
|
604
|
-
validate(schema, data, { locale: 'zh-CN' }),
|
|
605
|
-
validate(schema, data, { locale: 'en-US' })
|
|
606
|
-
]);
|
|
607
|
-
// ✅ 两次调用互不影响
|
|
608
|
-
|
|
609
|
-
// 2. 自定义消息(键引用)
|
|
610
|
-
const schema = dsl('objectId!');
|
|
611
|
-
const result = validate(schema, 'invalid', { locale: 'zh-CN' });
|
|
612
|
-
// message: "无效的 ObjectId" ✅
|
|
613
|
-
|
|
614
|
-
// 3. 自定义消息(模板字符串)
|
|
615
|
-
const schema = dsl({
|
|
616
|
-
age: 'number:18-!'.label('年龄')
|
|
617
|
-
.messages({ 'min': '{{#label}}必须大于{{#limit}}' })
|
|
618
|
-
});
|
|
619
|
-
const result = validate(schema, { age: 10 });
|
|
620
|
-
// message: "年龄必须大于18" ✅
|
|
621
|
-
|
|
622
|
-
// 4. 自定义消息(最终消息)
|
|
623
|
-
const schema = dsl({
|
|
624
|
-
phone: 'string!'.pattern(/^1[3-9]\d{9}$/)
|
|
625
|
-
.messages({ 'pattern': '手机号格式不正确' })
|
|
626
|
-
});
|
|
627
|
-
const result = validate(schema, { phone: 'invalid' });
|
|
628
|
-
// message: "手机号格式不正确" ✅
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
**技术细节**:
|
|
632
|
-
- 修改 `index.js` 中的 `validate()` 函数,传递 `options` 参数
|
|
633
|
-
- 修改 `Validator.js` 中的 `validate()` 方法,直接传递 `locale` 到 ErrorFormatter
|
|
634
|
-
- 修改 `ErrorFormatter.js`:
|
|
635
|
-
- `format()` 方法支持对象参数 `{ locale, messages }`
|
|
636
|
-
- `formatDetailed()` 方法新增 `customMessages` 参数
|
|
637
|
-
- 优化自定义消息查找逻辑:优先检查 schema 中的 `_customMessages`
|
|
638
|
-
|
|
639
|
-
**测试结果**:
|
|
640
|
-
- ✅ 725 个测试全部通过(从 51 个失败修复到 0 个失败)
|
|
641
|
-
- ✅ 完全向后兼容,不破坏现有 API
|
|
642
|
-
|
|
643
|
-
### Changed (变更)
|
|
644
|
-
|
|
645
|
-
- 📝 **API**: `validate()` 和 `Validator.validate()` 新增 `options` 参数(向后兼容)
|
|
646
|
-
- 🔧 **内部**: `ErrorFormatter.formatDetailed()` 新增 `customMessages` 参数
|
|
647
|
-
|
|
648
|
-
### Fixed (修复)
|
|
649
|
-
|
|
650
|
-
- 🐛 修复 TypeScript 类型定义缺少 `options` 参数
|
|
651
|
-
- 🐛 修复自定义错误消息查找逻辑(优先级问题)
|
|
652
|
-
- 🐛 修复自定义消息无法区分键引用/模板字符串/最终消息
|
|
653
|
-
|
|
654
|
-
---
|
|
655
|
-
|
|
656
|
-
## [v1.0.8] - 2026-01-04
|
|
657
|
-
|
|
658
|
-
### Improved (优化)
|
|
659
|
-
|
|
660
|
-
#### 错误消息过滤增强 ⭐
|
|
661
|
-
|
|
662
|
-
**问题描述**:
|
|
663
|
-
用户报告错误消息存在冗余问题:
|
|
664
|
-
- 在嵌套 If/Match 结构中,会显示重复的 "must match then schema" 包装错误
|
|
665
|
-
- 当已有具体字段错误时(如"Credit price is required"),还会额外显示2-3个通用包装错误
|
|
666
|
-
|
|
667
|
-
**修复内容**:
|
|
668
|
-
- ✅ 过滤 `if` 关键字的包装错误
|
|
669
|
-
- ✅ 过滤 `anyOf` 关键字的包装错误
|
|
670
|
-
- ✅ 过滤 `oneOf` 关键字的包装错误
|
|
671
|
-
- ✅ 当存在具体字段错误时,自动移除所有包装错误
|
|
672
|
-
|
|
673
|
-
**修复效果**:
|
|
674
|
-
|
|
675
|
-
修复前:
|
|
676
|
-
```javascript
|
|
677
|
-
const result = validate(schema, data);
|
|
678
|
-
// errors: [
|
|
679
|
-
// { message: "Credit price is required" },
|
|
680
|
-
// { message: "must match then schema" }, // 冗余
|
|
681
|
-
// { message: "must match then schema" } // 冗余
|
|
682
|
-
// ]
|
|
683
|
-
```
|
|
684
|
-
|
|
685
|
-
修复后:
|
|
686
|
-
```javascript
|
|
687
|
-
const result = validate(schema, data);
|
|
688
|
-
// errors: [
|
|
689
|
-
// { message: "Credit price is required" } // 只显示具体错误
|
|
690
|
-
// ]
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
**技术细节**:
|
|
694
|
-
- 修改文件:[lib/core/ErrorFormatter.js](lib/core/ErrorFormatter.js#L84-L101)
|
|
695
|
-
- 过滤逻辑:检测到具体错误(type, required, pattern等)时,自动过滤包装错误(if, anyOf, oneOf)
|
|
696
|
-
- 适用场景:所有使用 `dsl.if()`、`dsl.match()` 以及 JSON Schema 的 anyOf/oneOf 的验证
|
|
697
|
-
|
|
698
|
-
**测试覆盖**:
|
|
699
|
-
- 测试文件:[test/unit/error-message-filter.test.js](test/unit/error-message-filter.test.js)
|
|
700
|
-
- 新增测试:5个错误过滤场景
|
|
701
|
-
- 测试总数:725 (+5)
|
|
702
|
-
- 所有测试通过 ✅
|
|
703
|
-
|
|
704
|
-
---
|
|
705
|
-
|
|
706
|
-
## [v1.0.7] - 2026-01-04
|
|
707
|
-
|
|
708
|
-
### Fixed (修复)
|
|
709
|
-
|
|
710
|
-
#### 全面支持 dsl.match/dsl.if 所有嵌套组合 ⭐⭐⭐
|
|
711
|
-
|
|
712
|
-
**问题描述**:
|
|
713
|
-
在 v1.0.6 中,TypeScript 用户被要求使用 `dsl()` 包裹字符串,但在 `dsl.match()` 和 `dsl.if()` 中使用嵌套结构时会失败。用户报告了复杂嵌套场景无法正常工作。
|
|
714
|
-
|
|
715
|
-
**修复内容**:
|
|
716
|
-
- ✅ **Match 嵌套 Match** - 多级条件分支
|
|
717
|
-
- ✅ **Match 嵌套 If** - 在分支中使用条件
|
|
718
|
-
- ✅ **If 嵌套 Match** - 条件中使用多分支(用户报告的场景)
|
|
719
|
-
- ✅ **If 嵌套 If** - 多层条件嵌套
|
|
720
|
-
- ✅ **_default 中嵌套** - 默认规则支持 Match/If
|
|
721
|
-
- ✅ **三层嵌套** - 支持任意深度嵌套
|
|
722
|
-
|
|
723
|
-
**修复前问题**:
|
|
724
|
-
```javascript
|
|
725
|
-
// ❌ v1.0.6 中所有嵌套都会失败
|
|
726
|
-
credit_price: dsl.if('enabled',
|
|
727
|
-
dsl.match('payment_type', {
|
|
728
|
-
'credit': dsl('integer:1-10000!').label('价格'),
|
|
729
|
-
'_default': 'integer:1-10000'
|
|
730
|
-
}),
|
|
731
|
-
'integer:1-10000'
|
|
732
|
-
)
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
**修复后**:
|
|
736
|
-
```javascript
|
|
737
|
-
// ✅ v1.0.7 完全支持所有嵌套组合
|
|
738
|
-
|
|
739
|
-
// 1. If 嵌套 Match(用户场景)
|
|
740
|
-
credit_price: dsl.if('enabled',
|
|
741
|
-
dsl.match('payment_type', {
|
|
742
|
-
'credit': dsl('integer:1-10000!').label('credit_price'),
|
|
743
|
-
'_default': 'integer:1-10000'
|
|
744
|
-
}),
|
|
745
|
-
'integer:1-10000'
|
|
746
|
-
)
|
|
747
|
-
|
|
748
|
-
// 2. Match 嵌套 Match
|
|
749
|
-
value: dsl.match('category', {
|
|
750
|
-
'contact': dsl.match('type', {
|
|
751
|
-
'email': dsl('email!').label('邮箱'),
|
|
752
|
-
'phone': dsl('string:11!').label('手机号')
|
|
753
|
-
}),
|
|
754
|
-
'payment': dsl.match('type', {
|
|
755
|
-
'credit': dsl('integer:1-10000!'),
|
|
756
|
-
'cash': dsl('number:0.01-10000!')
|
|
757
|
-
})
|
|
758
|
-
})
|
|
759
|
-
|
|
760
|
-
// 3. Match 嵌套 If
|
|
761
|
-
discount: dsl.match('user_type', {
|
|
762
|
-
'member': dsl.if('is_vip',
|
|
763
|
-
dsl('number:10-50!').label('VIP会员折扣'),
|
|
764
|
-
dsl('number:5-20!').label('普通会员折扣')
|
|
765
|
-
),
|
|
766
|
-
'guest': dsl('number:0-10').label('访客折扣')
|
|
767
|
-
})
|
|
768
|
-
|
|
769
|
-
// 4. If 嵌套 If
|
|
770
|
-
price: dsl.if('is_member',
|
|
771
|
-
dsl.if('is_premium',
|
|
772
|
-
dsl('number:100-500!').label('高级会员价'),
|
|
773
|
-
dsl('number:200-800!').label('普通会员价')
|
|
774
|
-
),
|
|
775
|
-
dsl('number:500-1000!').label('非会员价')
|
|
776
|
-
)
|
|
777
|
-
|
|
778
|
-
// 5. 三层嵌套
|
|
779
|
-
value: dsl.match('level1', {
|
|
780
|
-
'A': dsl.match('level2', {
|
|
781
|
-
'A1': dsl.if('level3',
|
|
782
|
-
dsl('integer:1-100!'),
|
|
783
|
-
dsl('integer:1-50!')
|
|
784
|
-
)
|
|
785
|
-
})
|
|
786
|
-
})
|
|
787
|
-
```
|
|
788
|
-
|
|
789
|
-
**技术细节**:
|
|
790
|
-
- 修复了 `DslAdapter._buildMatchSchema()` 方法(第476-489行)
|
|
791
|
-
- 修复了 `DslAdapter._buildIfSchema()` 方法
|
|
792
|
-
- 递归处理所有 `_isMatch` 和 `_isIf` 结构
|
|
793
|
-
- 新增 null/undefined 分支值处理逻辑
|
|
794
|
-
- 支持任意深度嵌套(已测试至5层)
|
|
795
|
-
|
|
796
|
-
**已知限制**:
|
|
797
|
-
1. **自定义验证器传递限制** - `.custom()` 验证器在嵌套 Match/If 中可能不会完全传递
|
|
798
|
-
```javascript
|
|
799
|
-
// .custom() 的自定义消息可能在深层嵌套中丢失
|
|
800
|
-
value: dsl.match('type', {
|
|
801
|
-
'email': dsl('string!').custom((v) => v.includes('@'))
|
|
802
|
-
})
|
|
803
|
-
// 基础验证(必填、类型)会生效,但 custom 验证可能不完全传递
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
2. **嵌套字段路径限制** - `dsl.match(field)` 的 field 参数不支持嵌套路径
|
|
807
|
-
```javascript
|
|
808
|
-
// ❌ 不支持
|
|
809
|
-
dsl.match('config.engine', {...})
|
|
810
|
-
|
|
811
|
-
// ✅ 支持 - 使用扁平化字段
|
|
812
|
-
dsl.match('config_engine', {...})
|
|
813
|
-
```
|
|
814
|
-
|
|
815
|
-
3. **自定义消息传递** - 在多层嵌套中,自定义错误消息可能不会完全保留
|
|
816
|
-
|
|
817
|
-
**测试覆盖**:
|
|
818
|
-
- 测试总数:**686 → 720**(**+34 个全面测试**)
|
|
819
|
-
- **v1.0.7 新增测试场景**:
|
|
820
|
-
|
|
821
|
-
**基础嵌套组合**(6个测试):
|
|
822
|
-
- Match 嵌套 Match - 多级条件分支
|
|
823
|
-
- Match 嵌套 If - 在分支中使用条件
|
|
824
|
-
- If 嵌套 Match - 条件中使用多分支
|
|
825
|
-
- If 嵌套 If - 多层条件嵌套
|
|
826
|
-
- _default 中使用嵌套 - 默认规则支持 Match/If
|
|
827
|
-
- 三层嵌套 - 任意深度嵌套验证
|
|
828
|
-
|
|
829
|
-
**参数验证和错误处理**(7个测试):
|
|
830
|
-
- 空 map 处理 - Match 中空映射表的行为
|
|
831
|
-
- null/undefined 分支值 - 空值分支的处理
|
|
832
|
-
- 参数验证 - match/if 参数的有效性检查
|
|
833
|
-
- 对象/数组作为条件值 - 复杂类型作为条件的验证
|
|
834
|
-
- 循环依赖检测 - 防止无限递归
|
|
835
|
-
- 同一字段多规则引用 - 字段重复使用场景
|
|
836
|
-
- undefined else 分支 - If 中省略 else 的行为
|
|
837
|
-
|
|
838
|
-
**深度嵌套和高级场景**(6个测试):
|
|
839
|
-
- 4层嵌套 - Match-Match-Match-If 四层组合
|
|
840
|
-
- 大量分支(10+)- 单个 Match 包含15个分支
|
|
841
|
-
- .custom() 验证器在嵌套中 - 自定义验证器的传递(已知限制)
|
|
842
|
-
- 复杂对象规则 - 嵌套中包含复杂对象 schema
|
|
843
|
-
- 混合嵌套 - Match 中 If,If 中 Match,再嵌套对象
|
|
844
|
-
- 5层超深嵌套 - 极端深度测试
|
|
845
|
-
|
|
846
|
-
**覆盖率提升测试**(14个测试):
|
|
847
|
-
- 错误处理和边界情况(6个)
|
|
848
|
-
- 特殊DSL语法覆盖(4个)
|
|
849
|
-
- 极端和性能测试(4个)
|
|
850
|
-
|
|
851
|
-
**代码修复**:
|
|
852
|
-
- 修复 `lib/adapters/DslAdapter.js` 中 null/undefined 分支处理
|
|
853
|
-
- 新增代码行:476-489(处理空值分支)
|
|
854
|
-
|
|
855
|
-
- **测试覆盖率**:73.12% → 73.17%(语句覆盖率)
|
|
856
|
-
- **Match/If核心功能覆盖率**:~100%(所有嵌套组合和边界情况)
|
|
857
|
-
- 所有测试通过 ✅
|
|
858
|
-
|
|
859
|
-
**迁移指南**:
|
|
860
|
-
无需任何代码修改,所有嵌套场景现在都自动支持。
|
|
861
|
-
|
|
862
|
-
---
|
|
863
|
-
|
|
864
|
-
## [v1.0.6] - 2026-01-04
|
|
865
|
-
|
|
866
|
-
### 🚨 Fixed (紧急修复)
|
|
867
|
-
|
|
868
|
-
#### TypeScript 类型污染问题 ⭐⭐⭐
|
|
869
|
-
|
|
870
|
-
**问题描述**:
|
|
871
|
-
- v1.0.5 及更早版本中,`index.d.ts` 包含了全局 `interface String` 扩展(第 816-1065 行,共 209 行代码)
|
|
872
|
-
- 这导致 TypeScript 全局类型系统污染,原生 `String.prototype` 方法的类型被错误推断
|
|
873
|
-
- **严重问题**:`String.prototype.trim()` 返回类型从 `string` 被错误推断为 `DslBuilder`
|
|
874
|
-
- 影响所有使用 TypeScript 的项目,导致类型安全问题
|
|
875
|
-
|
|
876
|
-
**修复内容**:
|
|
877
|
-
- ✅ **完全删除**全局 `interface String` 扩展(移除 209 行类型污染代码)
|
|
878
|
-
- ✅ 添加详细的 TypeScript 使用说明文档
|
|
879
|
-
- ✅ 保证原生 String 方法的类型推断正确(`trim()` 正确返回 `string`)
|
|
880
|
-
|
|
881
|
-
**对用户的影响**:
|
|
882
|
-
|
|
883
|
-
1. **JavaScript 用户** ✅ **完全不受影响**
|
|
884
|
-
```javascript
|
|
885
|
-
// 仍然可以正常使用 String 扩展
|
|
886
|
-
const schema = dsl({ email: 'email!'.label('邮箱') });
|
|
887
|
-
```
|
|
888
|
-
|
|
889
|
-
2. **TypeScript 用户** ⚠️ **需要调整用法**
|
|
890
|
-
```typescript
|
|
891
|
-
// ❌ v1.0.5 及之前(有类型污染 bug)
|
|
892
|
-
const schema = dsl({ email: 'email!'.label('邮箱') });
|
|
893
|
-
|
|
894
|
-
// ✅ v1.0.6 推荐写法(获得正确类型提示)
|
|
895
|
-
const schema = dsl({
|
|
896
|
-
email: dsl('email!').label('邮箱')
|
|
897
|
-
});
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
**技术细节**:
|
|
901
|
-
- 文件变化:`index.d.ts` 从 2958 行减少到 2749 行
|
|
902
|
-
- 测试状态:所有 677 个测试通过 ✅
|
|
903
|
-
- 类型验证:原生 `String.prototype.trim()` 现在正确返回 `string` 类型
|
|
904
|
-
|
|
905
|
-
**迁移指南**:
|
|
906
|
-
- JavaScript 项目:无需任何修改
|
|
907
|
-
- TypeScript 项目:使用 `dsl()` 函数包裹字符串字面量获得类型提示
|
|
908
|
-
- 详见:[TypeScript 使用指南](./docs/typescript-guide.md)
|
|
909
|
-
|
|
910
|
-
---
|
|
911
|
-
|
|
912
|
-
## [v1.0.5] - 2026-01-04
|
|
913
|
-
|
|
914
|
-
### Added (新增功能)
|
|
915
|
-
|
|
916
|
-
#### 测试覆盖率大幅提升 ⭐
|
|
917
|
-
|
|
918
|
-
- ✅ **新增 5 个核心类的完整测试**
|
|
919
|
-
- `CacheManager.test.js` - 24 个测试(缓存管理)
|
|
920
|
-
- `ErrorFormatter.test.js` - 9 个测试(错误格式化)
|
|
921
|
-
- `JSONSchemaCore.test.js` - 10 个测试(JSON Schema 核心)
|
|
922
|
-
- `MarkdownExporter.test.js` - 3 个测试(Markdown 导出)
|
|
923
|
-
- `ErrorCodes.test.js` - 4 个测试(错误代码)
|
|
924
|
-
|
|
925
|
-
- ✅ **测试统计**
|
|
926
|
-
- 总测试数:651 → 677(+26 个测试)
|
|
927
|
-
- 测试覆盖率:92% → 97%(+5%)
|
|
928
|
-
- 所有核心类现在都有完整测试覆盖
|
|
929
|
-
|
|
930
|
-
### Fixed (修复)
|
|
931
|
-
|
|
932
|
-
- ✅ 修复缓存配置支持(4 个参数完整支持)
|
|
933
|
-
- ✅ 简化 i18n 配置(从 3 个方法简化为 2 个)
|
|
934
|
-
|
|
935
|
-
---
|
|
936
|
-
|
|
937
|
-
## [v1.0.4] - 2025-12-31
|
|
938
|
-
|
|
939
|
-
### Added (新增功能)
|
|
940
|
-
|
|
941
|
-
#### TypeScript 完整支持 ⭐
|
|
942
|
-
|
|
943
|
-
- ✅ **完整的类型定义**
|
|
944
|
-
- 新增 `validateAsync` 函数类型定义
|
|
945
|
-
- 新增 `ValidationError` 类完整类型(包含所有方法)
|
|
946
|
-
- 优化 String 扩展的 TypeScript 说明
|
|
947
|
-
|
|
948
|
-
- ✅ **TypeScript 使用指南**
|
|
949
|
-
- 创建完整的 TypeScript 使用文档 (`docs/typescript-guide.md`)
|
|
950
|
-
- 1000+ 行详细说明,涵盖从基础到高级所有场景
|
|
951
|
-
- 3个完整实战案例(用户注册、API验证、字段复用)
|
|
952
|
-
- 5个常见问题解答
|
|
953
|
-
|
|
954
|
-
- ✅ **TypeScript 链式调用最佳实践**
|
|
955
|
-
```typescript
|
|
956
|
-
// ✅ 推荐:使用 dsl() 包裹获得完整类型推导
|
|
957
|
-
const schema = dsl({
|
|
958
|
-
email: dsl('email!').label('邮箱').pattern(/custom/)
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
// ❌ 不推荐:可能缺少类型提示
|
|
962
|
-
const schema = dsl({
|
|
963
|
-
email: 'email!'.label('邮箱')
|
|
964
|
-
});
|
|
965
|
-
```
|
|
966
|
-
|
|
967
|
-
### Improved (改进)
|
|
968
|
-
|
|
969
|
-
- 📝 **README 更新**
|
|
970
|
-
- 添加 "1.5 TypeScript 用法" 快速开始章节
|
|
971
|
-
- 添加 TypeScript 使用指南链接
|
|
972
|
-
- 清晰说明 TypeScript 和 JavaScript 的不同用法
|
|
973
|
-
|
|
974
|
-
- 🔧 **类型定义优化**
|
|
975
|
-
- 修复 `dsl.config` 的 `i18n` 参数类型错误
|
|
976
|
-
- 统一 `DslConfigOptions` 和 `dsl.config` 的类型定义
|
|
977
|
-
- 标记 String 扩展方法为 `@deprecated` for TypeScript
|
|
978
|
-
|
|
979
|
-
### Documentation (文档)
|
|
980
|
-
|
|
981
|
-
- 📚 新增文档
|
|
982
|
-
- `docs/typescript-guide.md` - TypeScript 使用指南(1000+ 行)
|
|
983
|
-
- `reports/schema-dsl/implementation/dollar-method-implementation-v1.0.4.md` - 实施报告
|
|
984
|
-
- `reports/schema-dsl/summary/typescript-support-completion-v1.0.4.md` - 完成总结
|
|
985
|
-
|
|
986
|
-
### Note (重要说明)
|
|
987
|
-
|
|
988
|
-
- ✅ **100% 向后兼容** - JavaScript 用户无需任何改变
|
|
989
|
-
- ✅ **TypeScript 用户推荐使用 `dsl()` 包裹字符串** 以获得完整类型推导
|
|
990
|
-
- ✅ **所有 API 都有完整的 TypeScript 类型定义**
|
|
991
|
-
|
|
992
|
-
---
|
|
993
|
-
|
|
994
|
-
## [v1.0.3] - 2025-12-31
|
|
995
|
-
|
|
996
|
-
### ⚠️ 破坏性变更 (Breaking Changes)
|
|
997
|
-
|
|
998
|
-
#### String 单值语法含义变更
|
|
999
|
-
|
|
1000
|
-
**变更内容**: `'string:N'` 的含义从"最大长度"改为"精确长度"
|
|
1001
|
-
|
|
1002
|
-
**变更原因**:
|
|
1003
|
-
1. ✅ 更符合直觉 - 验证码、国家代码等常用场景都是精确长度
|
|
1004
|
-
2. ✅ 语义更清晰 - 看到 `'string:6'` 就知道是6位
|
|
1005
|
-
3. ✅ 有替代方案 - 最大长度可用 `'string:-N'`
|
|
1006
|
-
|
|
1007
|
-
**影响范围**:
|
|
1008
|
-
```javascript
|
|
1009
|
-
// ❌ v1.0.2 及之前
|
|
1010
|
-
'string:10' → maxLength: 10(最大长度)
|
|
1011
|
-
|
|
1012
|
-
// ✅ v1.0.3 及之后
|
|
1013
|
-
'string:10' → exactLength: 10(精确长度)
|
|
1014
|
-
'string:-10' → maxLength: 10(最大长度,新语法)
|
|
1015
|
-
```
|
|
1016
|
-
|
|
1017
|
-
**迁移指南**:
|
|
1018
|
-
|
|
1019
|
-
1. **如果你的代码使用 `'string:N'` 表示最大长度**:
|
|
1020
|
-
```javascript
|
|
1021
|
-
// 旧代码
|
|
1022
|
-
bio: 'string:500'
|
|
1023
|
-
|
|
1024
|
-
// 新代码(添加 - 前缀)
|
|
1025
|
-
bio: 'string:-500'
|
|
1026
|
-
```
|
|
1027
|
-
|
|
1028
|
-
2. **如果你的代码本意就是精确长度**:
|
|
1029
|
-
```javascript
|
|
1030
|
-
// 旧代码(行为不符合预期)
|
|
1031
|
-
code: 'string:6' // 之前会解析为 maxLength: 6(错误)
|
|
1032
|
-
|
|
1033
|
-
// 新代码(现在正确工作)
|
|
1034
|
-
code: 'string:6' // 现在正确解析为 exactLength: 6
|
|
1035
|
-
```
|
|
1036
|
-
|
|
1037
|
-
**检查方法**:
|
|
1038
|
-
```bash
|
|
1039
|
-
# 在项目中搜索所有使用单值语法的地方
|
|
1040
|
-
grep -rn "'string:[0-9]\\+['\"]" .
|
|
1041
|
-
grep -rn '"string:[0-9]' .
|
|
1042
|
-
```
|
|
1043
|
-
|
|
1044
|
-
**受益场景统计**:
|
|
1045
|
-
- ✅ 60% 场景更简洁直观(验证码、国家码、邮编等)
|
|
1046
|
-
- ⚠️ 20% 场景需要迁移(简介、描述等)
|
|
1047
|
-
- ✅ 20% 场景无影响(范围语法)
|
|
1048
|
-
|
|
1049
|
-
### 🔧 修复 (Fixes)
|
|
1050
|
-
|
|
1051
|
-
- 修复 String 单值约束语义不直观的问题
|
|
1052
|
-
- 统一约束语法规则
|
|
1053
|
-
- 完善文档说明
|
|
1054
|
-
|
|
1055
|
-
### 📝 文档 (Documentation)
|
|
1056
|
-
|
|
1057
|
-
- 新增约束语法规则说明
|
|
1058
|
-
- 新增迁移指南
|
|
1059
|
-
- 更新所有示例代码
|
|
1060
|
-
|
|
1061
|
-
### 🆕 新增功能 (Added)
|
|
1062
|
-
|
|
1063
|
-
- 新增 `dateGreater()` 链式方法 - 日期大于验证
|
|
1064
|
-
- 新增 `dateLess()` 链式方法 - 日期小于验证
|
|
1065
|
-
|
|
1066
|
-
---
|
|
1067
|
-
|
|
1068
|
-
## [v1.0.2] - 2025-12-31
|
|
1069
|
-
|
|
1070
|
-
### 🎉 新增功能 (Added)
|
|
1071
|
-
|
|
1072
|
-
#### 验证器扩展 (15 个新增验证器)
|
|
1073
|
-
|
|
1074
|
-
**String 验证器** (6 个):
|
|
1075
|
-
- ✅ **exactLength**: 精确长度验证 - 验证字符串长度必须等于指定值
|
|
1076
|
-
- ✅ **alphanum**: 只能包含字母和数字 - 用于用户名、编码等场景
|
|
1077
|
-
- ✅ **trim**: 不能包含前后空格 - 用于搜索关键词、API密钥等
|
|
1078
|
-
- ✅ **lowercase**: 必须是小写 - 用于邮箱、URL slug等
|
|
1079
|
-
- ✅ **uppercase**: 必须是大写 - 用于国家代码、货币代码等
|
|
1080
|
-
- ✅ **jsonString**: JSON 字符串验证 - 验证有效的 JSON 格式
|
|
1081
|
-
|
|
1082
|
-
**Number 验证器** (2 个):
|
|
1083
|
-
- ✅ **precision**: 小数位数限制 - 用于价格、百分比等高精度场景
|
|
1084
|
-
- ✅ **port**: 端口号验证 (1-65535) - 用于服务器配置
|
|
1085
|
-
|
|
1086
|
-
**Object 验证器** (2 个):
|
|
1087
|
-
- ✅ **requiredAll**: 要求所有定义的属性都存在 - 用于完整性检查
|
|
1088
|
-
- ✅ **strictSchema**: 严格模式,不允许额外属性 - 用于API请求验证
|
|
1089
|
-
|
|
1090
|
-
**Array 验证器** (2 个):
|
|
1091
|
-
- ✅ **noSparse**: 不允许稀疏数组 - 用于批量处理数据
|
|
1092
|
-
- ✅ **includesRequired**: 必须包含指定的元素 - 用于权限、标签验证
|
|
1093
|
-
|
|
1094
|
-
**Date 验证器** (3 个):
|
|
1095
|
-
- ✅ **dateFormat**: 自定义日期格式验证 (支持5种格式: YYYY-MM-DD, YYYY/MM/DD, DD-MM-YYYY, DD/MM/YYYY, ISO8601)
|
|
1096
|
-
- ✅ **dateGreater**: 日期必须大于指定日期 - 用于活动时间范围
|
|
1097
|
-
- ✅ **dateLess**: 日期必须小于指定日期 - 用于历史数据验证
|
|
1098
|
-
|
|
1099
|
-
**使用示例**:
|
|
1100
|
-
```javascript
|
|
1101
|
-
const { dsl, validate } = require('schema-dsl');
|
|
1102
|
-
|
|
1103
|
-
// String 验证器
|
|
1104
|
-
const schema = {
|
|
1105
|
-
code: { type: 'string', exactLength: 6 }, // 验证码
|
|
1106
|
-
username: { type: 'string', alphanum: true }, // 用户名
|
|
1107
|
-
keyword: { type: 'string', trim: true }, // 搜索关键词
|
|
1108
|
-
email: { type: 'string', lowercase: true }, // 邮箱
|
|
1109
|
-
countryCode: { type: 'string', uppercase: true }, // 国家代码
|
|
1110
|
-
config: { type: 'string', jsonString: true }, // JSON配置
|
|
1111
|
-
|
|
1112
|
-
// Number 验证器
|
|
1113
|
-
price: { type: 'number', precision: 2 }, // 价格(2位小数)
|
|
1114
|
-
port: { type: 'integer', port: true }, // 端口号
|
|
1115
|
-
|
|
1116
|
-
// Date 验证器
|
|
1117
|
-
birthDate: { type: 'string', dateFormat: 'YYYY-MM-DD' }, // 生日
|
|
1118
|
-
endDate: { type: 'string', dateGreater: '2025-01-01' }, // 结束日期
|
|
1119
|
-
startDate: { type: 'string', dateLess: '2025-12-31' } // 开始日期
|
|
1120
|
-
};
|
|
1121
|
-
```
|
|
1122
|
-
|
|
1123
|
-
#### 多语言支持
|
|
1124
|
-
- ✅ **中文消息**: 新增 19 个验证消息
|
|
1125
|
-
- ✅ **英文消息**: 新增 19 个验证消息
|
|
1126
|
-
- ✅ **错误消息键**: 统一使用 string.length, number.precision 等键名
|
|
1127
|
-
|
|
1128
|
-
### 📝 文档 (Documentation)
|
|
1129
|
-
- ✅ 新增 `docs/validation-rules-v1.0.2.md` - 15个验证器详细文档(1200行)
|
|
1130
|
-
- ✅ 每个验证器包含:用途、参数、错误消息、使用方法、应用场景、最佳实践
|
|
1131
|
-
- ✅ 完整的代码示例和验证演示
|
|
1132
|
-
- ✅ 组合使用技巧和国际化支持说明
|
|
1133
|
-
|
|
1134
|
-
### 🧪 测试 (Tests)
|
|
1135
|
-
- ✅ 新增 `test/unit/validators/CustomKeywords-v1.0.2.test.js`
|
|
1136
|
-
- ✅ 75 个单元测试用例,覆盖所有新增验证器
|
|
1137
|
-
- ✅ 每个验证器至少 5 个测试用例(正常、边界、错误情况)
|
|
1138
|
-
- ✅ 测试覆盖率: 100%
|
|
1139
|
-
- ✅ 所有测试通过: 602 passing
|
|
1140
|
-
|
|
1141
|
-
### 📦 示例 (Examples)
|
|
1142
|
-
- ⏳ 待补充 `examples/validation-rules-v1.0.2.examples.js`
|
|
1143
|
-
|
|
1144
|
-
### 🔧 技术细节 (Technical Details)
|
|
1145
|
-
|
|
1146
|
-
**实现位置**: `lib/validators/CustomKeywords.js`
|
|
1147
|
-
- String 验证器: L210-334
|
|
1148
|
-
- Number 验证器: L342-389
|
|
1149
|
-
- Object 验证器: L397-441
|
|
1150
|
-
- Array 验证器: L449-499
|
|
1151
|
-
- Date 验证器: L507-608
|
|
1152
|
-
|
|
1153
|
-
**错误消息位置**:
|
|
1154
|
-
- `lib/locales/zh-CN.js` (126 行)
|
|
1155
|
-
- `lib/locales/en-US.js` (126 行)
|
|
1156
|
-
|
|
1157
|
-
---
|
|
1158
|
-
|
|
1159
|
-
## [v1.0.1] - 2025-12-31
|
|
1160
|
-
|
|
1161
|
-
### 🎉 新增功能 (Added)
|
|
1162
|
-
|
|
1163
|
-
#### 枚举功能
|
|
1164
|
-
- ✅ **字符串枚举**: `'active|inactive|pending'` 简写语法
|
|
1165
|
-
- ✅ **布尔值枚举**: `'true|false'` 自动识别为布尔值
|
|
1166
|
-
- ✅ **数字枚举**: `'1|2|3'` 自动识别为数字
|
|
1167
|
-
- ✅ **整数枚举**: `'enum:integer:1|2|3'` 显式指定整数类型
|
|
1168
|
-
- ✅ **小数枚举**: `'1.0|1.5|2.0'` 支持小数值
|
|
1169
|
-
- ✅ **必填枚举**: `'active|inactive!'` 支持必填标记
|
|
1170
|
-
- ✅ **显式类型**: `'enum:type:values'` 显式指定枚举类型
|
|
1171
|
-
|
|
1172
|
-
**使用示例**:
|
|
1173
|
-
```javascript
|
|
1174
|
-
const { dsl, validate } = require('schema-dsl');
|
|
1175
|
-
|
|
1176
|
-
const schema = dsl({
|
|
1177
|
-
// 字符串枚举(自动识别)
|
|
1178
|
-
status: 'active|inactive|pending',
|
|
1179
|
-
|
|
1180
|
-
// 布尔值枚举(自动识别)
|
|
1181
|
-
isPublic: 'true|false',
|
|
1182
|
-
|
|
1183
|
-
// 数字枚举(自动识别)
|
|
1184
|
-
priority: '1|2|3',
|
|
1185
|
-
|
|
1186
|
-
// 整数枚举(显式指定)
|
|
1187
|
-
level: 'enum:integer:1|2|3',
|
|
1188
|
-
|
|
1189
|
-
// 必填枚举
|
|
1190
|
-
role: 'admin|user|guest!'
|
|
1191
|
-
});
|
|
1192
|
-
|
|
1193
|
-
// 验证
|
|
1194
|
-
const result = validate(schema, {
|
|
1195
|
-
status: 'active',
|
|
1196
|
-
isPublic: true,
|
|
1197
|
-
priority: 1,
|
|
1198
|
-
level: 2,
|
|
1199
|
-
role: 'admin'
|
|
1200
|
-
});
|
|
1201
|
-
```
|
|
1202
|
-
|
|
1203
|
-
#### 统一错误消息
|
|
1204
|
-
- ✅ **统一 'enum' 键**: 所有枚举类型统一使用 `'enum'` 定义错误消息
|
|
1205
|
-
- ✅ **简化配置**: 不需要记忆不同类型的错误消息键名
|
|
1206
|
-
- ✅ **高级用法**: 支持 `'type.enum'` 格式按类型定制消息(可选)
|
|
1207
|
-
|
|
1208
|
-
**使用示例**:
|
|
1209
|
-
```javascript
|
|
1210
|
-
const schema = dsl({
|
|
1211
|
-
status: dsl('active|inactive').messages({
|
|
1212
|
-
'enum': '状态必须是 active 或 inactive' // 统一使用 enum
|
|
1213
|
-
})
|
|
1214
|
-
});
|
|
1215
|
-
```
|
|
1216
|
-
|
|
1217
|
-
### 📝 文档 (Documentation)
|
|
1218
|
-
|
|
1219
|
-
- ✅ **新增 docs/enum.md**: 完整的枚举功能文档(476行)
|
|
1220
|
-
- ✅ **更新 README.md**: 添加枚举语法说明
|
|
1221
|
-
- ✅ **新增示例**: examples/enum.examples.js(325行,10个示例)
|
|
1222
|
-
|
|
1223
|
-
### ✅ 测试 (Tests)
|
|
1224
|
-
|
|
1225
|
-
- ✅ **新增 test/unit/enum.test.js**: 30个枚举测试用例
|
|
1226
|
-
- ✅ **测试覆盖**: 字符串/布尔值/数字/整数枚举全覆盖
|
|
1227
|
-
- ✅ **错误处理测试**: 无效枚举值、类型不匹配测试
|
|
1228
|
-
|
|
1229
|
-
### 📊 变更统计
|
|
1230
|
-
|
|
1231
|
-
- **新增代码**: ~500 行
|
|
1232
|
-
- **新增测试**: 30 个
|
|
1233
|
-
- **新增文档**: 476 行
|
|
1234
|
-
- **新增示例**: 325 行
|
|
1235
|
-
|
|
1236
|
-
---
|
|
1237
|
-
|
|
1238
|
-
## [v1.0.0] - 2025-12-29
|
|
1239
|
-
|
|
1240
|
-
### 🎉 初始发布
|
|
1241
|
-
|
|
1242
|
-
#### 核心功能
|
|
1243
|
-
- ✅ **DSL 语法**: 简洁的字符串 DSL 定义 Schema
|
|
1244
|
-
- `'string:3-32!'` - 字符串长度 3-32,必填
|
|
1245
|
-
- `'number:0-100'` - 数字范围 0-100
|
|
1246
|
-
- `'email!'` - 邮箱格式,必填
|
|
1247
|
-
|
|
1248
|
-
- ✅ **基础类型**: string, number, integer, boolean, object, array, null
|
|
1249
|
-
|
|
1250
|
-
- ✅ **格式类型**: email, url, uuid, date, datetime, time, ipv4, ipv6, binary
|
|
1251
|
-
|
|
1252
|
-
- ✅ **模式类型**:
|
|
1253
|
-
- objectId - MongoDB ObjectId
|
|
1254
|
-
- hexColor - 十六进制颜色
|
|
1255
|
-
- macAddress - MAC 地址
|
|
1256
|
-
- cron - Cron 表达式
|
|
1257
|
-
- phone - 手机号(支持多国)
|
|
1258
|
-
- idCard - 身份证号
|
|
1259
|
-
- creditCard - 信用卡号
|
|
1260
|
-
- licensePlate - 车牌号
|
|
1261
|
-
- postalCode - 邮政编码
|
|
1262
|
-
- passport - 护照号
|
|
1263
|
-
|
|
1264
|
-
- ✅ **验证功能**:
|
|
1265
|
-
- `validate(schema, data)` - 同步验证
|
|
1266
|
-
- `validateAsync(schema, data)` - 异步验证
|
|
1267
|
-
|
|
1268
|
-
- ✅ **错误格式化**: 友好的错误消息
|
|
1269
|
-
|
|
1270
|
-
- ✅ **多语言支持**: zh-CN, en-US
|
|
1271
|
-
|
|
1272
|
-
- ✅ **链式 API**: String 扩展,支持 `.label()`, `.messages()`, `.pattern()` 等
|
|
1273
|
-
|
|
1274
|
-
- ✅ **SchemaUtils**: omit, pick, partial, extend 工具方法
|
|
1275
|
-
|
|
1276
|
-
#### 文档和测试
|
|
1277
|
-
- ✅ **完整文档**: README, API 文档, 示例代码
|
|
1278
|
-
- ✅ **测试覆盖**: 447+ 测试用例,覆盖率 >90%
|
|
1279
|
-
|
|
1280
|
-
---
|
|
118
|
+
**完整文档**: [docs/INDEX.md](./docs/INDEX.md)
|
|
119
|
+
**GitHub**: [https://github.com/vextjs/schema-dsl](https://github.com/vextjs/schema-dsl)
|
|
120
|
+
**npm**: [https://www.npmjs.com/package/schema-dsl](https://www.npmjs.com/package/schema-dsl)
|
|
1281
121
|
|
|
1282
|
-
**更多历史版本信息请参考 Git 提交记录**
|