schema-dsl 1.1.0 → 1.1.1

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.
@@ -1,7 +1,7 @@
1
1
  # 链式条件 API - ConditionalBuilder
2
2
 
3
- > **版本**: v1.1.0
4
- > **更新日期**: 2026-01-05
3
+ > **版本**: v1.1.1
4
+ > **更新日期**: 2026-01-06
5
5
  > **状态**: ✅ 稳定
6
6
 
7
7
  ---
@@ -9,6 +9,7 @@
9
9
  ## 📋 目录
10
10
 
11
11
  - [概述](#概述)
12
+ - [🆕 v1.1.1 新功能](#-v110-新功能)
12
13
  - [快速开始](#快速开始)
13
14
  - [API 参考](#api-参考)
14
15
  - [使用场景](#使用场景)
@@ -26,11 +27,153 @@
26
27
  - ✅ **链式调用** - 流畅的 API,类似 JavaScript if-else
27
28
  - ✅ **运行时执行** - 在验证时根据实际数据判断
28
29
  - ✅ **多条件组合** - 支持 and/or 逻辑组合
30
+ - ✅ **🆕 独立消息** - v1.1.1+ 每个 .and()/.or() 可有独立错误消息
29
31
  - ✅ **else 可选** - 不写 else 就不验证
30
32
  - ✅ **简化设计** - message 自动抛错,无需 throwError()
31
33
  - ✅ **完全兼容** - 不影响现有 API
32
34
 
33
- ### 与现有方法的区别
35
+ ---
36
+
37
+ ## 🆕 v1.1.1 新功能
38
+
39
+ ### 独立消息支持 - `.and()/.or()` 后可调用 `.message()`
40
+
41
+ **每个条件都可以有自己的错误消息**
42
+
43
+ v1.1.1 开始,支持在 `.and()` 和 `.or()` 后调用 `.message()` 设置独立的错误消息,让错误提示更精确。
44
+
45
+ #### 基础用法
46
+
47
+ ```javascript
48
+ const { dsl } = require('schema-dsl');
49
+
50
+ // ✅ v1.1.1+ 新功能:每个条件独立消息
51
+ dsl.if(d => !d)
52
+ .message('ACCOUNT_NOT_FOUND')
53
+ .and(d => d.tradable_credits < amount)
54
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
55
+ .assert(account);
56
+
57
+ // 工作原理:
58
+ // - 第一个条件为 true → 返回 'ACCOUNT_NOT_FOUND'
59
+ // - 第二个条件为 true → 返回 'INSUFFICIENT_TRADABLE_CREDITS'
60
+ // - 所有条件为 false → 验证成功
61
+ ```
62
+
63
+ #### 多个 .and() 条件
64
+
65
+ ```javascript
66
+ // 多层验证,每层都有清晰的错误消息
67
+ dsl.if(d => !d)
68
+ .message('ACCOUNT_NOT_FOUND')
69
+ .and(d => d.status !== 'active')
70
+ .message('ACCOUNT_INACTIVE')
71
+ .and(d => d.tradable_credits < amount)
72
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
73
+ .assert(account);
74
+
75
+ // 依次检查,第一个为 true 的条件返回其消息
76
+ ```
77
+
78
+ #### .or() 条件独立消息
79
+
80
+ ```javascript
81
+ // OR 条件也支持独立消息
82
+ dsl.if(d => d.age < 18)
83
+ .message('未成年用户不能注册')
84
+ .or(d => d.isBlocked)
85
+ .message('账户已被封禁')
86
+ .assert(data);
87
+
88
+ // 任一条件为 true 就失败,返回对应消息
89
+ ```
90
+
91
+ #### 链式检查模式
92
+
93
+ v1.1.1 引入了**链式检查模式**,当满足以下条件时自动启用:
94
+
95
+ 1. 使用 `.message()` 模式(不是 `.then()`/`.else()`)
96
+ 2. root 条件有 `.message()`
97
+ 3. 有 `.and()` 条件
98
+ 4. 没有 `.or()` 条件
99
+
100
+ **链式检查模式特点**:
101
+ - 依次检查每个条件
102
+ - 第一个为 `true` 的条件失败,返回其消息
103
+ - 所有条件为 `false` 时验证通过
104
+
105
+ **示例对比**:
106
+
107
+ ```javascript
108
+ // ✅ 启用链式检查(纯 AND 场景)
109
+ dsl.if(d => !d).message('A').and(d => d < 100).message('B')
110
+
111
+ // ❌ 不启用(有 .or(),使用传统 AND/OR 逻辑)
112
+ dsl.if(d => !d).message('A').and(d => d < 100).or(d => d > 200).message('B')
113
+
114
+ // ❌ 不启用(使用 .then()/.else(),不是 message 模式)
115
+ dsl.if(d => d.age >= 18).and(d => d.role === 'admin').then('email!')
116
+ ```
117
+
118
+ #### 向后兼容性
119
+
120
+ **100% 向后兼容**,不影响现有代码:
121
+
122
+ ```javascript
123
+ // ✅ 原有用法继续工作
124
+ dsl.if(d => d.age >= 18).and(d => d.role === 'admin').message('不符合条件')
125
+
126
+ // ✅ .and() 后不调用 .message() 也可以
127
+ dsl.if(d => !d).message('整体错误').and(d => d < 100).assert(50)
128
+ // → 使用整体消息 '整体错误'
129
+ ```
130
+
131
+ #### 实际应用场景
132
+
133
+ **场景1:账户验证**
134
+ ```javascript
135
+ function validateAccount(account, amount) {
136
+ dsl.if(d => !d)
137
+ .message('ACCOUNT_NOT_FOUND')
138
+ .and(d => d.status !== 'active')
139
+ .message('ACCOUNT_INACTIVE')
140
+ .and(d => d.balance < amount)
141
+ .message('INSUFFICIENT_BALANCE')
142
+ .assert(account);
143
+ }
144
+
145
+ // 每个失败点都有清晰的错误消息
146
+ ```
147
+
148
+ **场景2:用户权限验证**
149
+ ```javascript
150
+ function validateUserPermission(user) {
151
+ dsl.if(d => d.role !== 'admin')
152
+ .message('NO_ADMIN_PERMISSION')
153
+ .and(d => !d.isVerified)
154
+ .message('USER_NOT_VERIFIED')
155
+ .and(d => d.isBanned)
156
+ .message('USER_BANNED')
157
+ .assert(user);
158
+ }
159
+ ```
160
+
161
+ **场景3:订单状态检查**
162
+ ```javascript
163
+ function validateOrder(order) {
164
+ dsl.if(d => d.status !== 'paid')
165
+ .message('ORDER_NOT_PAID')
166
+ .and(d => !d.payment)
167
+ .message('PAYMENT_INFO_MISSING')
168
+ .and(d => !d.shippingAddress)
169
+ .message('SHIPPING_ADDRESS_MISSING')
170
+ .assert(order);
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ## 与现有方法的区别
34
177
 
35
178
  `dsl.if()` 提供两种使用方式,根据参数类型自动选择:
36
179
 
@@ -180,19 +323,59 @@ dsl.if((data) => data.status === 'active' && data.verified)
180
323
 
181
324
  添加 AND 条件(与前一个条件组合)。
182
325
 
326
+ > **v1.1.1+** 支持在 `.and()` 后调用 `.message()` 设置独立的错误消息
327
+
183
328
  **参数**:
184
329
  - `condition` {Function} - 条件函数
185
330
 
186
331
  **返回**: `this` - 支持链式调用
187
332
 
188
- **示例**:
333
+ **基础示例**:
189
334
  ```javascript
335
+ // 传统用法:所有条件共享一个消息
190
336
  dsl.if((data) => data.age >= 18)
191
337
  .and((data) => data.userType === 'admin')
192
- .then('email!')
338
+ .message('不符合条件')
339
+ ```
340
+
341
+ **v1.1.1+ 独立消息**:
342
+ ```javascript
343
+ // ✅ 每个条件都有自己的错误消息
344
+ dsl.if((data) => !data)
345
+ .message('账户不存在')
346
+ .and((data) => data.balance < 100)
347
+ .message('余额不足')
348
+ .assert(account);
349
+
350
+ // 工作原理:
351
+ // - 第一个条件为 true → 返回 '账户不存在'
352
+ // - 第二个条件为 true → 返回 '余额不足'
353
+ // - 所有条件为 false → 验证成功
354
+ ```
355
+
356
+ **多个 .and() 条件**:
357
+ ```javascript
358
+ // 支持多个 .and() 条件,每个都有独立消息
359
+ dsl.if(d => !d)
360
+ .message('NOT_FOUND')
361
+ .and(d => d.status !== 'active')
362
+ .message('INACTIVE')
363
+ .and(d => d.balance < 100)
364
+ .message('INSUFFICIENT')
365
+ .assert(account);
366
+
367
+ // 依次检查,第一个为 true 的条件返回其消息
193
368
  ```
194
369
 
195
- **逻辑**: `(condition1 AND condition2)`
370
+ **逻辑**:
371
+ - 传统模式:`(condition1 AND condition2)` - 所有条件为 true 才失败
372
+ - 链式检查模式 (v1.1.1+):依次检查,第一个为 true 的失败
373
+
374
+ **链式检查模式触发条件**:
375
+ 1. 使用 `.message()` 模式
376
+ 2. root 条件有 `.message()`
377
+ 3. 有 `.and()` 条件
378
+ 4. 没有 `.or()` 条件
196
379
 
197
380
  ---
198
381
 
@@ -200,19 +383,41 @@ dsl.if((data) => data.age >= 18)
200
383
 
201
384
  添加 OR 条件(与前一个条件组合)。
202
385
 
386
+ > **v1.1.1+** 支持在 `.or()` 后调用 `.message()` 设置独立的错误消息
387
+
203
388
  **参数**:
204
389
  - `condition` {Function} - 条件函数
205
390
 
206
391
  **返回**: `this` - 支持链式调用
207
392
 
208
- **示例**:
393
+ **基础示例**:
209
394
  ```javascript
395
+ // 传统用法:所有条件共享一个消息
210
396
  dsl.if((data) => data.age < 18)
211
397
  .or((data) => data.status === 'blocked')
212
398
  .message('不允许注册')
213
399
  ```
214
400
 
215
- **逻辑**: `(condition1 OR condition2)`
401
+ **v1.1.1+ 独立消息**:
402
+ ```javascript
403
+ // ✅ 每个 OR 条件都有自己的错误消息
404
+ dsl.if(d => d.age < 18)
405
+ .message('未成年用户不能注册')
406
+ .or(d => d.isBlocked)
407
+ .message('账户已被封禁')
408
+ .assert(data);
409
+
410
+ // 工作原理:
411
+ // - 第一个条件为 true → 返回 '未成年用户不能注册'
412
+ // - 第二个条件为 true → 返回 '账户已被封禁'
413
+ // - 所有条件为 false → 验证成功
414
+ ```
415
+
416
+ **逻辑**: `(condition1 OR condition2)` - 任一条件为 true 就失败
417
+
418
+ **注意**:
419
+ - 如果有 `.or()` 条件,不会启用链式检查模式
420
+ - 使用传统 AND/OR 组合逻辑
216
421
 
217
422
  ---
218
423
 
@@ -242,14 +447,16 @@ dsl.if((data) => data.userType === 'admin')
242
447
 
243
448
  设置错误消息(支持多语言 key)。
244
449
 
450
+ > **v1.1.1+** 支持为 `.and()` 和 `.or()` 条件设置独立消息
451
+
245
452
  **参数**:
246
453
  - `msg` {string} - 错误消息或多语言 key
247
454
 
248
455
  **返回**: `this` - 支持链式调用
249
456
 
250
- **行为**: 不满足条件时自动抛出此错误(无需 `.throwError()`)
457
+ **行为**: 条件为 true 时自动抛出此错误(无需 `.throwError()`)
251
458
 
252
- **示例**:
459
+ **基础示例**:
253
460
  ```javascript
254
461
  dsl.if((data) => data.age >= 18)
255
462
  .message('未成年用户不能注册')
@@ -259,6 +466,45 @@ dsl.if((data) => data.age >= 18)
259
466
  .message('error.underage')
260
467
  ```
261
468
 
469
+ **v1.1.1+ 为 .and() 设置独立消息**:
470
+ ```javascript
471
+ // ✅ 每个条件都有自己的错误消息
472
+ dsl.if((data) => !data)
473
+ .message('账户不存在')
474
+ .and((data) => data.balance < 100)
475
+ .message('余额不足')
476
+ .assert(account);
477
+ ```
478
+
479
+ **v1.1.1+ 为 .or() 设置独立消息**:
480
+ ```javascript
481
+ // ✅ OR 条件也支持独立消息
482
+ dsl.if(d => d.age < 18)
483
+ .message('未成年')
484
+ .or(d => d.isBlocked)
485
+ .message('已封禁')
486
+ .assert(data);
487
+ ```
488
+
489
+ **链式检查模式说明** (v1.1.1+):
490
+
491
+ 当满足以下条件时,自动启用链式检查模式:
492
+ 1. 使用 `.message()` 模式(不是 `.then()`/`.else()`)
493
+ 2. root 条件有 `.message()`
494
+ 3. 有 `.and()` 条件
495
+ 4. 没有 `.or()` 条件
496
+
497
+ ```javascript
498
+ // ✅ 启用链式检查(纯 AND 场景)
499
+ dsl.if(d => !d).message('A').and(d => d < 100).message('B')
500
+
501
+ // ❌ 不启用(有 .or())
502
+ dsl.if(d => !d).message('A').and(d => d < 100).or(d => d > 200).message('B')
503
+
504
+ // ❌ 不启用(使用 .then()/.else())
505
+ dsl.if(d => d.age >= 18).and(d => d.role === 'admin').then('email!')
506
+ ```
507
+
262
508
  ---
263
509
 
264
510
  ### .then(schema)
@@ -1011,7 +1257,7 @@ validate(contactSchema, '13800138000'); // ✅ 作为手机号验证
1011
1257
 
1012
1258
  ## 更新日志
1013
1259
 
1014
- ### v1.1.0 (2026-01-05)
1260
+ ### v1.1.1 (2026-01-05)
1015
1261
 
1016
1262
  - ✅ 新增 `ConditionalBuilder` 类
1017
1263
  - ✅ 新增 `dsl.if()` 链式条件 API
@@ -0,0 +1,181 @@
1
+ /**
2
+ * I18nError 使用示例
3
+ *
4
+ * 统一的多语言错误抛出机制
5
+ *
6
+ * @version 1.1.1
7
+ */
8
+
9
+ const { I18nError, dsl, Locale } = require('../index');
10
+
11
+ console.log('=== I18nError 使用示例 ===\n');
12
+
13
+ // ========== 1. 基础用法 ==========
14
+ console.log('1. 基础用法:');
15
+ try {
16
+ throw I18nError.create('account.notFound');
17
+ } catch (error) {
18
+ console.log('错误:', error.message); // "账户不存在"
19
+ console.log('代码:', error.code); // "account.notFound"
20
+ }
21
+
22
+ // ========== 2. 带参数的错误 ==========
23
+ console.log('\n2. 带参数的错误:');
24
+ try {
25
+ throw I18nError.create('account.insufficientBalance', {
26
+ balance: 50,
27
+ required: 100
28
+ });
29
+ } catch (error) {
30
+ console.log('错误:', error.message); // "余额不足,当前余额50,需要100"
31
+ }
32
+
33
+ // ========== 3. 使用 throw 方法 ==========
34
+ console.log('\n3. 使用 throw 方法(直接抛错):');
35
+ try {
36
+ I18nError.throw('user.noPermission');
37
+ } catch (error) {
38
+ console.log('错误:', error.message); // "没有管理员权限"
39
+ }
40
+
41
+ // ========== 4. 使用 assert 断言 ==========
42
+ console.log('\n4. 使用 assert 断言:');
43
+ const account = { balance: 50 };
44
+
45
+ try {
46
+ I18nError.assert(account.balance >= 100, 'account.insufficientBalance', {
47
+ balance: account.balance,
48
+ required: 100
49
+ });
50
+ } catch (error) {
51
+ console.log('错误:', error.message);
52
+ }
53
+
54
+ // ========== 5. dsl.error 快捷方法 ==========
55
+ console.log('\n5. dsl.error 快捷方法:');
56
+ try {
57
+ dsl.error.throw('order.notPaid');
58
+ } catch (error) {
59
+ console.log('错误:', error.message); // "订单未支付"
60
+ }
61
+
62
+ // ========== 6. 多语言支持 ==========
63
+ console.log('\n6. 多语言支持:');
64
+ try {
65
+ // 中文
66
+ Locale.setLocale('zh-CN');
67
+ throw I18nError.create('account.notFound');
68
+ } catch (error) {
69
+ console.log('中文:', error.message); // "账户不存在"
70
+ }
71
+
72
+ try {
73
+ // 英文
74
+ Locale.setLocale('en-US');
75
+ throw I18nError.create('account.notFound');
76
+ } catch (error) {
77
+ console.log('英文:', error.message); // "Account not found"
78
+ }
79
+
80
+ // 恢复中文
81
+ Locale.setLocale('zh-CN');
82
+
83
+ // ========== 7. 实际业务场景 =========
84
+ console.log('\n7. 实际业务场景:');
85
+
86
+ // 场景1:账户验证函数
87
+ function getAccount(id) {
88
+ const account = id === '123' ? { id: '123', balance: 50, status: 'active' } : null;
89
+
90
+ // 断言账户存在
91
+ I18nError.assert(account, 'account.notFound');
92
+
93
+ // 断言账户状态
94
+ I18nError.assert(account.status === 'active', 'account.inactive');
95
+
96
+ // 断言余额充足
97
+ I18nError.assert(
98
+ account.balance >= 100,
99
+ 'account.insufficientBalance',
100
+ { balance: account.balance, required: 100 }
101
+ );
102
+
103
+ return account;
104
+ }
105
+
106
+ try {
107
+ getAccount('123');
108
+ } catch (error) {
109
+ console.log('业务错误:', error.message);
110
+ console.log('错误代码:', error.code);
111
+ }
112
+
113
+ // 场景2:与 dsl.if 结合
114
+ console.log('\n8. 与 dsl.if 结合使用:');
115
+ function validateUser(user) {
116
+ // 使用 dsl.if 进行数据验证
117
+ dsl.if(d => !d)
118
+ .message('user.notFound')
119
+ .and(d => !d.isVerified)
120
+ .message('user.notVerified')
121
+ .assert(user);
122
+
123
+ // 使用 I18nError 进行业务逻辑验证
124
+ I18nError.assert(user.role === 'admin', 'user.noPermission');
125
+ }
126
+
127
+ try {
128
+ validateUser({ isVerified: true, role: 'user' });
129
+ } catch (error) {
130
+ console.log('验证错误:', error.message);
131
+ }
132
+
133
+ // ========== 9. Express/Koa 中间件 ==========
134
+ console.log('\n9. Express/Koa 错误处理:');
135
+
136
+ // Express 错误处理中间件
137
+ function expressErrorHandler(error, req, res, next) {
138
+ if (error instanceof I18nError) {
139
+ return res.status(error.statusCode).json(error.toJSON());
140
+ }
141
+ next(error);
142
+ }
143
+
144
+ // 模拟使用
145
+ const mockError = I18nError.create('account.notFound', {}, 404);
146
+ const mockRes = {
147
+ status: (code) => {
148
+ console.log('HTTP Status:', code);
149
+ return mockRes;
150
+ },
151
+ json: (data) => {
152
+ console.log('Response:', JSON.stringify(data, null, 2));
153
+ return mockRes;
154
+ }
155
+ };
156
+
157
+ expressErrorHandler(mockError, {}, mockRes, () => {});
158
+
159
+ // ========== 10. 自定义状态码 ==========
160
+ console.log('\n10. 自定义状态码:');
161
+ try {
162
+ throw I18nError.create('user.notFound', {}, 404);
163
+ } catch (error) {
164
+ console.log('状态码:', error.statusCode); // 404
165
+ console.log('错误:', error.message);
166
+ }
167
+
168
+ // ========== 11. 错误检查 ==========
169
+ console.log('\n11. 错误类型检查:');
170
+ try {
171
+ throw I18nError.create('account.notFound');
172
+ } catch (error) {
173
+ if (error instanceof I18nError) {
174
+ console.log('是 I18nError:', true);
175
+ console.log('是账户不存在:', error.is('account.notFound'));
176
+ console.log('是用户不存在:', error.is('user.notFound'));
177
+ }
178
+ }
179
+
180
+ console.log('\n=== 示例完成 ===');
181
+