schema-dsl 1.1.0 → 1.1.2

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 CHANGED
@@ -11,6 +11,8 @@
11
11
 
12
12
  | 版本 | 日期 | 变更摘要 | 详细 |
13
13
  |------------------|------|---------|------|
14
+ | [v1.1.2](#v112) | 2026-01-06 | 🎉 新功能:数字比较运算符 + Bug修复 | [查看详情](#v112) |
15
+ | [v1.1.1](#v111) | 2026-01-06 | 🎉 新功能:ConditionalBuilder 独立消息支持 | [查看详情](#v111) |
14
16
  | [v1.1.0](#v110) | 2026-01-05 | 🎉 重大功能:跨类型联合验证 + 插件系统增强 | [查看详情](#v110) |
15
17
  | [v1.0.9](#v109) | 2026-01-04 | 🎉 重大改进:多语言支持完善 + TypeScript 类型完整 | [查看详情](#v109) |
16
18
  | [v1.0.8](#v108) | 2026-01-04 | 优化:错误消息过滤增强 | [查看详情](#v108) |
@@ -25,31 +27,322 @@
25
27
 
26
28
  ---
27
29
 
28
- ## [v1.1.0] - 2026-01-05
30
+ ## [v1.1.2] - 2026-01-06
29
31
 
30
32
  ### 🎉 新功能
31
33
 
32
- #### 1. 跨类型联合验证 - `types:` 语法
34
+ #### 数字比较运算符 - 更直观的数值验证
33
35
 
34
- **一个字段支持多种类型**
36
+ **新增 5 种比较运算符,让数值验证更语义化**
35
37
 
36
- 现在可以使用 `types:` 前缀定义跨类型联合验证,支持字段匹配多种不同的数据类型。
38
+ 现在 `number` 和 `integer` 类型支持 5 种比较运算符:`>`, `>=`, `<`, `<=`, `=`
37
39
 
38
40
  **基础用法**:
39
41
 
40
42
  ```javascript
41
43
  const { dsl, validate } = require('schema-dsl');
42
44
 
43
- // 字段可以是字符串或数字
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:温度监控
44
114
  const schema = dsl({
45
- value: 'types:string|number'
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 修复
147
+
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
+ - 完全向后兼容
171
+
172
+ ---
173
+
174
+ ### 📚 文档更新
175
+
176
+ - 新增 [数字比较运算符文档](./docs/number-operators.md)
177
+ - 更新 README.md 添加比较运算符示例
178
+ - 更新 index.d.ts 添加比较运算符类型注释
179
+ - 新增 28 个比较运算符单元测试
180
+
181
+ ---
182
+
183
+ ### 🧪 测试
184
+
185
+ - **新增测试**:28 个比较运算符测试用例
186
+ - **测试总数**:949 个(921 + 28)
187
+ - **测试覆盖**:100% 通过
188
+ - **测试文件**:`test/unit/number-operators.test.js`
189
+
190
+ ---
191
+
192
+ ### 📊 变更统计
193
+
194
+ - **新增功能**:1 个(数字比较运算符)
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)
199
+
200
+ ---
201
+
202
+ ## [v1.1.1] - 2026-01-06
203
+
204
+ ### 🎉 新功能
205
+
206
+ #### ConditionalBuilder 独立消息支持 - `.and()/.or()` 后可调用 `.message()`
207
+
208
+ **每个条件都可以有自己的错误消息**
209
+
210
+ 现在支持在 `.and()` 和 `.or()` 后调用 `.message()` 设置独立的错误消息,让错误提示更精确。
211
+
212
+ **基础用法**:
213
+
214
+ ```javascript
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-验证)
253
+
254
+ ---
255
+
256
+ #### I18nError 多语言错误抛出机制
257
+
258
+ **统一的多语言错误抛出**
259
+
260
+ 新增 `I18nError` 类和 `dsl.error` 快捷方法,提供统一的多语言错误抛出机制。
261
+
262
+ **基础用法**:
263
+
264
+ ```javascript
265
+ const { I18nError, dsl } = require('schema-dsl');
266
+
267
+ // 方式1:直接抛出
268
+ I18nError.throw('account.notFound');
269
+ // 中文: "账户不存在"
270
+ // 英文: "Account not found"
271
+
272
+ // 方式2:带参数插值
273
+ I18nError.throw('account.insufficientBalance', {
274
+ balance: 50,
275
+ required: 100
46
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 集成**:
47
306
 
48
- validate(schema, { value: 'hello' }); // ✅ 通过
49
- validate(schema, { value: 123 }); // 通过
50
- validate(schema, { value: true }); // ❌ 失败
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
+ });
51
314
  ```
52
315
 
316
+ **文档链接**:
317
+ - [使用示例](./examples/i18n-error.examples.js)
318
+ - [README FAQ Q8](./README.md#q8-如何统一抛出多语言错误v111)
319
+
320
+ ---
321
+
322
+ ### 📝 测试
323
+
324
+ - **新增**: 52 个测试用例(24个独立消息 + 28个 I18nError)
325
+ - **总计**: 921 个测试全部通过 (100%)
326
+
327
+ ### 📖 文档
328
+
329
+ - **新增**: docs/conditional-api.md 新增 600+ 行功能说明
330
+ - **新增**: examples/i18n-error.examples.js I18nError 使用示例
331
+ - **更新**: README.md FAQ Q7/Q8 添加新功能说明
332
+ - **更新**: index.d.ts TypeScript 类型注释和示例(I18nError + dsl.error)
333
+
334
+ ---
335
+
336
+ ## [v1.1.0] - 2026-01-05
337
+
338
+ ### 🎉 新功能
339
+
340
+ #### 1. 跨类型联合验证 - `types:` 语法
341
+
342
+ **一个字段支持多种类型**
343
+
344
+ ...existing content...
345
+
53
346
  **带约束的联合类型**:
54
347
 
55
348
  ```javascript