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.
package/README.md CHANGED
@@ -181,12 +181,13 @@ if (result.valid) {
181
181
  |------|-----------|------|
182
182
  | **基本验证** | ✅ | string、number、boolean、date、email、url... |
183
183
  | **高级验证** | ✅ | 正则、自定义、条件、嵌套、数组... |
184
- | **🆕 跨类型联合** | ✅ | `types:string|number` 一个字段支持多种类型 (v1.1.0+) |
184
+ | **🆕 跨类型联合** | ✅ | `types:string|number` 一个字段支持多种类型 (v1.1.1) |
185
185
  | **错误格式化** | ✅ | 自动多语言翻译 |
186
+ | **🆕 多语言错误** | ✅ | `I18nError` 统一的多语言错误抛出 (v1.1.1) |
186
187
  | **数据库导出** | ✅ | MongoDB、MySQL、PostgreSQL |
187
188
  | **TypeScript** | ✅ | 完整类型定义 |
188
189
  | **性能优化** | ✅ | WeakMap 缓存、智能编译 |
189
- | **插件系统** | ✅ | 支持自定义类型注册 (v1.1.0+) |
190
+ | **插件系统** | ✅ | 支持自定义类型注册 (v1.1.1) |
190
191
  | **文档生成** | ✅ | Markdown、HTML |
191
192
 
192
193
  ### 🆕 v1.1.0 新特性:跨类型联合验证
@@ -776,86 +777,6 @@ const vipSchema = dsl({
776
777
  validate(vipSchema, { isVip: true, discount: 30 });
777
778
 
778
779
  // ❌ 非 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
- ---
859
780
  validate(vipSchema, { isVip: false, discount: 15 });
860
781
 
861
782
 
@@ -1179,241 +1100,293 @@ try {
1179
1100
 
1180
1101
  ---
1181
1102
 
1182
- ## 🎯 适用场景
1183
-
1184
- ### ✅ 特别适合
1103
+ ## 常见问题 FAQ
1185
1104
 
1186
- - 🚀 **快速开发** - API 开发、表单验证,追求开发效率
1187
- - 🌍 **国际化项目** - 需要完整的多语言错误消息支持
1188
- - 🗄️ **全栈开发** - 需要从 Schema 自动生成数据库表结构
1189
- - 📋 **配置驱动** - 验证规则需要从配置文件或数据库动态读取
1190
- - 🏢 **中小型项目** - Node.js + Express/Koa/Egg.js 后端项目
1105
+ ### Q1: 如何判断数据不能为空?(类似 `if(!data)`)
1191
1106
 
1192
- ### 💡 使用场景示例
1107
+ **方案1:使用必填标记**(推荐)
1108
+ ```javascript
1109
+ const schema = dsl({
1110
+ username: 'string!', // 必填,不能为空
1111
+ email: 'email!'
1112
+ });
1113
+ ```
1193
1114
 
1194
- **RESTful API 开发**
1115
+ **方案2:使用条件验证 + 抛错**
1195
1116
  ```javascript
1196
- // 统一的验证中间件
1197
- const validateMiddleware = (schema) => {
1198
- return async (req, res, next) => {
1199
- try {
1200
- req.body = await validateAsync(schema, req.body);
1201
- next();
1202
- } catch (error) {
1203
- next(error);
1204
- }
1205
- };
1206
- };
1117
+ // 验证失败自动抛错
1118
+ dsl.if(d => !d)
1119
+ .message('数据不能为空')
1120
+ .assert(data);
1121
+ ```
1207
1122
 
1208
- app.post('/api/users',
1209
- validateMiddleware(createUserSchema),
1210
- userController.create
1211
- );
1123
+ **方案3:异步验证**
1124
+ ```javascript
1125
+ // Express/Koa 推荐
1126
+ await dsl.if(d => !d)
1127
+ .message('数据不能为空')
1128
+ .validateAsync(data);
1212
1129
  ```
1213
1130
 
1214
- **表单验证**
1131
+ ---
1132
+
1133
+ ### Q2: 如何判断数据是否是对象?(类似 `typeof data === 'object'`)
1134
+
1135
+ **方案1:使用内置 object 类型**(推荐)
1215
1136
  ```javascript
1216
- // 前端也可以使用(支持浏览器)
1217
- const formSchema = dsl({
1218
- username: 'string:3-32!',
1219
- email: 'email!',
1220
- password: 'string:8-32!',
1221
- confirmPassword: 'string!'
1137
+ const schema = dsl({
1138
+ data: 'object!' // 必须是对象(排除 null 和 array)
1222
1139
  });
1223
1140
 
1224
- const result = validate(formSchema, formData);
1225
- if (!result.valid) {
1226
- // 显示错误消息
1227
- showErrors(result.errors);
1228
- }
1141
+ validate(schema, { data: { name: 'John' } }); // ✅ 通过
1142
+ validate(schema, { data: 'string' }); // ❌ 失败
1143
+ validate(schema, { data: [] }); // ❌ 失败
1229
1144
  ```
1230
1145
 
1231
- **动态配置验证**
1146
+ **方案2:条件验证 + 抛错**
1232
1147
  ```javascript
1233
- // 从数据库读取验证规则
1234
- const rules = await db.validationRules.find({ formId: 'user-register' });
1235
-
1236
- // 动态构建 Schema
1237
- const dynamicSchema = dsl(
1238
- rules.reduce((schema, rule) => {
1239
- schema[rule.field] = rule.dsl;
1240
- return schema;
1241
- }, {})
1242
- );
1148
+ dsl.if(d => typeof d !== 'object' || d === null || Array.isArray(d))
1149
+ .message('data 必须是一个对象')
1150
+ .assert(data);
1151
+ ```
1152
+
1153
+ **方案3:带结构验证**
1154
+ ```javascript
1155
+ const schema = dsl({
1156
+ data: {
1157
+ name: 'string!',
1158
+ age: 'integer!',
1159
+ email: 'email'
1160
+ }
1161
+ });
1162
+
1163
+ await validateAsync(schema, input); // 验证对象结构
1243
1164
  ```
1244
1165
 
1245
1166
  ---
1246
1167
 
1247
- ## 性能对比
1168
+ ### Q3: 如何验证嵌套对象?
1248
1169
 
1249
- **测试环境**: Node.js 18, 10,000 次验证
1170
+ ```javascript
1171
+ const schema = dsl({
1172
+ user: {
1173
+ profile: 'object!', // profile 必须是对象
1174
+ settings: {
1175
+ theme: 'string',
1176
+ notifications: 'object!' // 嵌套对象验证
1177
+ }
1178
+ }
1179
+ });
1180
+ ```
1250
1181
 
1251
- | 库名 | 速度 (ops/sec) | 相对速度 |
1252
- |------|---------------|---------|
1253
- | Ajv | 2,000,000 | 🥇 最快 |
1254
- | Zod | 526,316 | 🥈 很快 |
1255
- | **schema-dsl** | **277,778** | 🥉 **快** |
1256
- | Joi | 97,087 | 中等 |
1257
- | Yup | 60,241 | 较慢 |
1182
+ ---
1258
1183
 
1259
- **结论**:
1260
- - ✅ 比 Joi 快 **2.86倍**
1261
- - ✅ 比 Yup 快 **4.61倍**
1262
- - 99% 的应用场景足够快(27万+次/秒)
1263
- - ⚠️ 如果需要极致性能(100万+次/秒),推荐使用 Ajv
1184
+ ### Q4: 如何在 Express/Koa 中使用?
1185
+
1186
+ ```javascript
1187
+ app.post('/api/user', async (req, res) => {
1188
+ try {
1189
+ // 1. 验证请求体是对象
1190
+ await dsl.if(d => typeof d !== 'object' || d === null)
1191
+ .message('请求体必须是对象')
1192
+ .validateAsync(req.body);
1193
+
1194
+ // 2. 验证字段
1195
+ const schema = dsl({
1196
+ username: 'string:3-32!',
1197
+ email: 'email!',
1198
+ password: 'string:8-!'
1199
+ });
1200
+
1201
+ const validData = await validateAsync(schema, req.body);
1202
+
1203
+ // 继续处理...
1204
+ res.json({ success: true, data: validData });
1205
+ } catch (error) {
1206
+ res.status(400).json({ error: error.message });
1207
+ }
1208
+ });
1209
+ ```
1264
1210
 
1265
1211
  ---
1266
1212
 
1267
- ## 🆚 与其他库对比
1213
+ ### Q5: 如何自定义错误消息?
1268
1214
 
1269
- ### 选择建议
1215
+ ```javascript
1216
+ const schema = dsl({
1217
+ username: dsl('string:3-32!')
1218
+ .label('用户名')
1219
+ .messages({
1220
+ minLength: '用户名至少需要 {{#limit}} 个字符',
1221
+ required: '用户名不能为空'
1222
+ }),
1223
+
1224
+ email: dsl('email!')
1225
+ .label('邮箱地址')
1226
+ .messages({
1227
+ format: '请输入有效的邮箱地址',
1228
+ required: '邮箱不能为空'
1229
+ })
1230
+ });
1231
+ ```
1270
1232
 
1271
- | 项目需求 | 推荐方案 | 原因 |
1272
- |---------|---------|------|
1273
- | 快速开发,减少代码量 | **schema-dsl** | 代码量最少,学习成本最低 |
1274
- | TypeScript 强类型推断 | Zod | 最佳的 TypeScript 支持 |
1275
- | 极致性能要求 | Ajv | 性能最强 |
1276
- | 企业级成熟方案 | Joi | 社区最大,经过大规模验证 |
1277
- | 多语言 + 数据库导出 | **schema-dsl** | 独家功能 |
1233
+ ---
1278
1234
 
1279
- ### 详细对比
1235
+ ### Q6: 类型对照表
1280
1236
 
1281
- <table>
1282
- <tr>
1283
- <th>特性</th>
1284
- <th>schema-dsl</th>
1285
- <th>Joi</th>
1286
- <th>Yup</th>
1287
- <th>Zod</th>
1288
- <th>Ajv</th>
1289
- </tr>
1290
- <tr>
1291
- <td><strong>语法简洁度</strong></td>
1292
- <td>⭐⭐⭐⭐⭐<br>一行代码</td>
1293
- <td>⭐⭐<br>链式调用冗长</td>
1294
- <td>⭐⭐<br>链式调用冗长</td>
1295
- <td>⭐⭐⭐<br>相对简洁</td>
1296
- <td>⭐⭐<br>JSON 配置繁琐</td>
1297
- </tr>
1298
- <tr>
1299
- <td><strong>学习成本</strong></td>
1300
- <td>⭐⭐⭐⭐⭐<br>5分钟</td>
1301
- <td>⭐⭐⭐<br>30分钟</td>
1302
- <td>⭐⭐⭐<br>30分钟</td>
1303
- <td>⭐⭐⭐⭐<br>15分钟</td>
1304
- <td>⭐⭐⭐<br>20分钟</td>
1305
- </tr>
1306
- <tr>
1307
- <td><strong>性能(简单验证)</strong></td>
1308
- <td>⭐⭐⭐⭐<br>55.6万/秒</td>
1309
- <td>⭐⭐⭐<br>23.3万/秒</td>
1310
- <td>⭐⭐<br>18.9万/秒</td>
1311
- <td>⭐⭐⭐⭐⭐<br>100万/秒</td>
1312
- <td>⭐⭐⭐⭐⭐<br>250万/秒</td>
1313
- </tr>
1314
- <tr>
1315
- <td><strong>性能(复杂验证)</strong></td>
1316
- <td>⭐⭐⭐⭐⭐<br>62.5万/秒</td>
1317
- <td>⭐⭐⭐<br>12.5万/秒</td>
1318
- <td>⭐⭐<br>5.5万/秒</td>
1319
- <td>⭐⭐⭐⭐<br>38.5万/秒</td>
1320
- <td>⭐⭐⭐⭐⭐<br>250万/秒</td>
1321
- </tr>
1322
- <tr>
1323
- <td><strong>TypeScript 支持</strong></td>
1324
- <td>⭐⭐⭐<br>.d.ts 类型定义</td>
1325
- <td>⭐⭐⭐<br>.d.ts 类型定义</td>
1326
- <td>⭐⭐⭐<br>.d.ts 类型定义</td>
1327
- <td>⭐⭐⭐⭐⭐<br>完美类型推断</td>
1328
- <td>⭐⭐<br>基础支持</td>
1329
- </tr>
1330
- <tr>
1331
- <td><strong>数据库导出</strong></td>
1332
- <td>✅ MongoDB<br>✅ MySQL<br>✅ PostgreSQL</td>
1333
- <td>❌</td>
1334
- <td>❌</td>
1335
- <td>❌</td>
1336
- <td>❌</td>
1337
- </tr>
1338
- <tr>
1339
- <td><strong>多语言支持</strong></td>
1340
- <td>✅ 完整支持<br>可自定义语言包</td>
1341
- <td>⚠️ 基础支持</td>
1342
- <td>⚠️ 基础支持</td>
1343
- <td>⚠️ 基础支持</td>
1344
- <td>⚠️ 基础支持</td>
1345
- </tr>
1346
- <tr>
1347
- <td><strong>文档生成</strong></td>
1348
- <td>✅ Markdown<br>✅ HTML</td>
1349
- <td>❌</td>
1350
- <td>❌</td>
1351
- <td>❌</td>
1352
- <td>❌</td>
1353
- </tr>
1354
- <tr>
1355
- <td><strong>社区规模</strong></td>
1356
- <td>⭐⭐⭐<br>成长中</td>
1357
- <td>⭐⭐⭐⭐⭐<br>最大</td>
1358
- <td>⭐⭐⭐⭐<br>很大</td>
1359
- <td>⭐⭐⭐⭐<br>快速增长</td>
1360
- <td>⭐⭐⭐⭐<br>成熟</td>
1361
- </tr>
1362
- </table>
1237
+ | JavaScript 条件 | schema-dsl 写法 |
1238
+ |----------------|----------------|
1239
+ | `if (!data)` | `'string!'` 或 `.assert(data)` |
1240
+ | `if (typeof data === 'object')` | `'object!'` |
1241
+ | `if (typeof data === 'string')` | `'string!'` |
1242
+ | `if (typeof data === 'number')` | `'number!'` |
1243
+ | `if (Array.isArray(data))` | `'array!'` |
1244
+ | `if (data === null)` | `'null!'` |
1245
+ | `if (data > 0)` | `'number:0-!'` |
1246
+ | `if (data.length >= 3)` | `'string:3-!'` |
1363
1247
 
1364
1248
  ---
1365
1249
 
1366
- ## 📚 完整文档
1250
+ ### Q7: 如何合并多个 dsl.if() 验证?
1367
1251
 
1368
- ### 核心文档
1369
- - [快速开始](./docs/quick-start.md) - 5分钟上手指南
1370
- - [DSL 语法完整参考](./docs/dsl-syntax.md) - 所有语法详解
1371
- - [API 文档](./docs/api-reference.md) - 完整 API 说明
1372
- - [**TypeScript 使用指南**](./docs/typescript-guide.md) - TypeScript 最佳实践 ⭐
1252
+ **原代码(多个独立验证)**:
1253
+ ```javascript
1254
+ dsl.if(d => !d)
1255
+ .message('ACCOUNT_NOT_FOUND')
1256
+ .assert(account);
1373
1257
 
1374
- ### 功能指南
1375
- - [String 扩展方法](./docs/string-extensions.md) - 链式调用详解
1376
- - [Schema 复用](./docs/schema-utils.md) - omit/pick/extend/partial
1377
- - [异步验证](./docs/validate-async.md) - validateAsync 使用指南
1378
- - [错误处理](./docs/error-handling.md) - ValidationError 详解
1379
- - [多语言支持](./docs/i18n.md) - 国际化配置指南
1380
- - [插件开发](./docs/plugin-system.md) - 自定义插件教程
1258
+ dsl.if(d => d.tradable_credits < amount)
1259
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
1260
+ .assert(account.tradable_credits);
1261
+ ```
1381
1262
 
1382
- ### 导出功能
1383
- - [MongoDB 导出](./docs/mongodb-exporter.md) - MongoDB Schema 生成
1384
- - [MySQL 导出](./docs/mysql-exporter.md) - MySQL DDL 生成
1385
- - [PostgreSQL 导出](./docs/postgresql-exporter.md) - PostgreSQL DDL 生成
1386
- - [Markdown 导出](./docs/markdown-exporter.md) - API 文档生成
1263
+ **✅ 方案1:使用 .and() 链式合并(v1.1.1 推荐)**
1264
+ ```javascript
1265
+ // 每个条件都有独立的错误消息
1266
+ dsl.if(d => !d)
1267
+ .message('ACCOUNT_NOT_FOUND')
1268
+ .and(d => d.tradable_credits < amount)
1269
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
1270
+ .assert(account);
1271
+
1272
+ // 工作原理:
1273
+ // - 第一个条件失败 → 返回 'ACCOUNT_NOT_FOUND'
1274
+ // - 第二个条件失败 → 返回 'INSUFFICIENT_TRADABLE_CREDITS'
1275
+ // - 所有条件通过 → 验证成功
1276
+ ```
1387
1277
 
1388
- ### 集成示例
1389
- - [Express 集成](./examples/express-integration.js)
1278
+ **✅ 方案2:使用 .elseIf() 分支验证**
1279
+ ```javascript
1280
+ // ✅ 按优先级检查,找到第一个失败的
1281
+ dsl.if(d => !d)
1282
+ .message('ACCOUNT_NOT_FOUND')
1283
+ .elseIf(d => d.tradable_credits < amount)
1284
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
1285
+ .assert(account);
1286
+ ```
1287
+
1288
+ **✅ 方案3:保持独立验证**(最清晰)
1289
+ ```javascript
1290
+ // ✅ 两个独立的验证器
1291
+ dsl.if(d => !d).message('ACCOUNT_NOT_FOUND').assert(account);
1292
+ dsl.if(d => d.tradable_credits < amount)
1293
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
1294
+ .assert(account.tradable_credits);
1295
+ ```
1296
+
1297
+ **⚠️ 注意事项**:
1298
+ - `.and()` 用于组合多个条件,每个条件可以有**独立的** `.message()` (v1.1.1)
1299
+ - 如果 `.and()` 后不调用 `.message()`,则使用前一个条件的消息
1300
+ - `.elseIf()` 按顺序检查,找到第一个失败的就停止(if-else-if 逻辑)
1301
+
1302
+ **何时使用**:
1303
+ - ✅ 使用 `.and()` - 多个条件,每个有不同错误消息(v1.1.1)
1304
+ - ✅ 使用 `.elseIf()` - 不同分支有不同验证规则
1305
+ - ✅ 独立验证 - 最清晰,最可靠
1306
+
1307
+ **实际应用示例**:
1308
+ ```javascript
1309
+ // 账户验证:检查存在性 + 余额 + 状态
1310
+ dsl.if(d => !d)
1311
+ .message('ACCOUNT_NOT_FOUND')
1312
+ .and(d => d.status !== 'active')
1313
+ .message('ACCOUNT_INACTIVE')
1314
+ .and(d => d.tradable_credits < amount)
1315
+ .message('INSUFFICIENT_TRADABLE_CREDITS')
1316
+ .assert(account);
1317
+
1318
+ // 每个失败条件都有清晰的错误消息!
1319
+ ```
1320
+
1321
+ 📖 更多示例请查看 [完整文档](./docs/INDEX.md)
1390
1322
 
1391
1323
  ---
1392
1324
 
1393
- ## 💻 示例代码
1325
+ ### Q8: 如何统一抛出多语言错误?(v1.1.1+)
1394
1326
 
1395
- 项目包含 30+ 完整示例,涵盖所有功能:
1327
+ **问题**: 业务代码中抛出的错误无法多语言,与 `.message()` 和 `.label()` 不一致
1396
1328
 
1397
- ```bash
1398
- # 安装依赖(首次运行)
1399
- npm install
1329
+ **✅ 解决方案:使用 `I18nError` 或 `dsl.error`**
1330
+
1331
+ ```javascript
1332
+ const { I18nError, dsl } = require('schema-dsl');
1400
1333
 
1401
- # 查看所有示例
1402
- ls examples/
1334
+ // 方式1:直接抛出
1335
+ I18nError.throw('account.notFound');
1336
+ // 中文: "账户不存在"
1337
+ // 英文: "Account not found"
1403
1338
 
1404
- # 运行基础示例
1405
- node examples/simple-example.js
1339
+ // 方式2:带参数插值
1340
+ I18nError.throw('account.insufficientBalance', {
1341
+ balance: 50,
1342
+ required: 100
1343
+ });
1344
+ // 输出: "余额不足,当前余额50,需要100"
1345
+
1346
+ // 方式3:断言风格(推荐)
1347
+ I18nError.assert(account, 'account.notFound');
1348
+ I18nError.assert(
1349
+ account.balance >= 100,
1350
+ 'account.insufficientBalance',
1351
+ { balance: account.balance, required: 100 }
1352
+ );
1406
1353
 
1407
- # 运行数据库导出示例
1408
- node examples/export-demo.js
1354
+ // 方式4:快捷方法
1355
+ dsl.error.throw('user.noPermission');
1356
+ dsl.error.assert(user.role === 'admin', 'user.noPermission');
1357
+ ```
1409
1358
 
1410
- # 运行 Express 集成示例
1411
- node examples/express-integration.js
1359
+ **Express/Koa 集成**:
1360
+ ```javascript
1361
+ // 错误处理中间件
1362
+ app.use((error, req, res, next) => {
1363
+ if (error instanceof I18nError) {
1364
+ return res.status(error.statusCode).json(error.toJSON());
1365
+ }
1366
+ next(error);
1367
+ });
1412
1368
 
1413
- # 🆕 v1.0.3 新增:运行 slug 类型示例
1414
- node examples/slug.examples.js
1369
+ // 业务代码中使用
1370
+ app.post('/withdraw', (req, res) => {
1371
+ const account = getAccount(req.user.id);
1372
+ I18nError.assert(account, 'account.notFound');
1373
+ I18nError.assert(
1374
+ account.balance >= req.body.amount,
1375
+ 'account.insufficientBalance',
1376
+ { balance: account.balance, required: req.body.amount }
1377
+ );
1378
+ // ...
1379
+ });
1415
1380
  ```
1416
1381
 
1382
+ **内置错误代码**:
1383
+ - 通用: `error.notFound`, `error.forbidden`, `error.unauthorized`
1384
+ - 账户: `account.notFound`, `account.insufficientBalance`
1385
+ - 用户: `user.notFound`, `user.noPermission`
1386
+ - 订单: `order.notPaid`, `order.paymentMissing`
1387
+
1388
+ 📖 完整文档请查看 [examples/i18n-error.examples.js](./examples/i18n-error.examples.js)
1389
+
1417
1390
  ---
1418
1391
 
1419
1392
  ## 🤝 贡献指南
package/STATUS.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # schema-dsl 项目状态
2
2
 
3
- > **最后更新**: 2026-01-04
4
- > **当前版本**: v1.0.9
5
- > **项目状态**: ✅ 全部完成,测试100%通过(725个测试)
3
+ > **最后更新**: 2026-01-06
4
+ > **当前版本**: v1.1.1
5
+ > **项目状态**: ✅ 全部完成,测试100%通过(921个测试)
6
6
 
7
7
  ---
8
8
 
9
9
  ## 📋 目录
10
10
 
11
+ - [v1.1.1](#v111) - 2026-01-06 ✅ 已完成
12
+ - [v1.1.0](#v110) - 2026-01-05 ✅ 已完成
11
13
  - [v1.0.9](#v109) - 2026-01-04 ✅ 已完成
12
14
  - [v1.0.8](#v108) - 2026-01-04 ✅ 已完成
13
15
  - [v1.0.7](#v107) - 2026-01-04 ✅ 已完成
@@ -23,6 +25,66 @@
23
25
 
24
26
  ## 版本发布计划
25
27
 
28
+ ### v1.1.1
29
+
30
+ **发布日期**: 2026-01-06
31
+ **状态**: ✅ 已完成
32
+ **类型**: 🎉 新功能 - ConditionalBuilder 独立消息 + I18nError 多语言错误
33
+ **进度**: 100%完成 | 测试: 921个通过 | 新增: 52个测试
34
+
35
+ | 需求标题 | 状态 | 优先级 | 详细 |
36
+ |---------|------|--------|------|
37
+ | ConditionalBuilder 独立消息 | ✅ 完成 | P1 | `.and()/.or()` 后可调用 `.message()` |
38
+ | I18nError 多语言错误 | ✅ 完成 | P1 | 统一的多语言错误抛出机制 |
39
+ | dsl.error 快捷方法 | ✅ 完成 | P1 | create/throw/assert 三个方法 |
40
+ | TypeScript 类型定义 | ✅ 完成 | P0 | I18nError + dsl.error 类型 |
41
+ | 测试用例 | ✅ 完成 | P0 | 52个新测试(24+28) |
42
+ | 文档更新 | ✅ 完成 | P0 | README + CHANGELOG + examples |
43
+ | 语言包扩充 | ✅ 完成 | P1 | 中英文 20+ 错误消息 |
44
+
45
+ **实现状态统计**:
46
+ - ✅ 完成: 7个
47
+ - 🔄 进行中: 0个
48
+ - ⏳ 待完成: 0个
49
+
50
+ **核心变更**:
51
+ - ✅ **新增**: ConditionalBuilder 支持 `.and()/.or()` 独立消息
52
+ - ✅ **新增**: I18nError 类 (lib/errors/I18nError.js)
53
+ - ✅ **新增**: dsl.error 快捷方法 (create/throw/assert)
54
+ - ✅ **新增**: 20+ 内置错误代码(通用/账户/用户/订单)
55
+ - ✅ **新增**: examples/i18n-error.examples.js 示例文件
56
+ - ✅ **更新**: README FAQ Q7/Q8
57
+ - ✅ **更新**: CHANGELOG v1.1.1 完整记录
58
+ - ✅ **更新**: index.d.ts TypeScript 类型定义
59
+
60
+ ---
61
+
62
+ ### v1.1.0
63
+
64
+ **发布日期**: 2026-01-05
65
+ **状态**: ✅ 已完成
66
+ **类型**: 🎉 重大功能 - 跨类型联合验证 + 插件系统增强
67
+ **进度**: 100%完成
68
+
69
+ | 需求标题 | 状态 | 优先级 | 详细 |
70
+ |---------|------|--------|------|
71
+ | 跨类型联合验证 | ✅ 完成 | P1 | `types:string|number` 语法 |
72
+ | 插件 DSL 类型注册 | ✅ 完成 | P1 | 插件可注册自定义 DSL 类型 |
73
+ | DslBuilder.registerType | ✅ 完成 | P0 | 静态方法供插件使用 |
74
+ | 文档更新 | ✅ 完成 | P0 | README + CHANGELOG |
75
+
76
+ **实现状态统计**:
77
+ - ✅ 完成: 4个
78
+ - 🔄 进行中: 0个
79
+ - ⏳ 待完成: 0个
80
+
81
+ **核心变更**:
82
+ - ✅ **新增**: `types:` 语法支持跨类型联合验证
83
+ - ✅ **增强**: 插件系统支持 DSL 类型注册
84
+ - ✅ **新增**: DslBuilder.registerType() 静态方法
85
+
86
+ ---
87
+
26
88
  ### v1.0.9
27
89
 
28
90
  **发布日期**: 2026-01-04