schema-dsl 2.3.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.
Files changed (109) hide show
  1. package/.eslintignore +10 -0
  2. package/.eslintrc.json +27 -0
  3. package/.github/CODE_OF_CONDUCT.md +45 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +57 -0
  5. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +45 -0
  7. package/.github/ISSUE_TEMPLATE/question.md +31 -0
  8. package/.github/PULL_REQUEST_TEMPLATE.md +70 -0
  9. package/.github/SECURITY.md +184 -0
  10. package/.github/workflows/ci.yml +35 -0
  11. package/CHANGELOG.md +633 -0
  12. package/CONTRIBUTING.md +368 -0
  13. package/LICENSE +21 -0
  14. package/README.md +1122 -0
  15. package/STATUS.md +273 -0
  16. package/docs/FEATURE-INDEX.md +521 -0
  17. package/docs/INDEX.md +224 -0
  18. package/docs/api-reference.md +1098 -0
  19. package/docs/best-practices.md +672 -0
  20. package/docs/cache-manager.md +336 -0
  21. package/docs/design-philosophy.md +602 -0
  22. package/docs/dsl-syntax.md +654 -0
  23. package/docs/dynamic-locale.md +552 -0
  24. package/docs/error-handling.md +703 -0
  25. package/docs/export-guide.md +459 -0
  26. package/docs/faq.md +576 -0
  27. package/docs/frontend-i18n-guide.md +290 -0
  28. package/docs/i18n-user-guide.md +488 -0
  29. package/docs/label-vs-description.md +262 -0
  30. package/docs/markdown-exporter.md +398 -0
  31. package/docs/mongodb-exporter.md +279 -0
  32. package/docs/multi-type-support.md +319 -0
  33. package/docs/mysql-exporter.md +257 -0
  34. package/docs/plugin-system.md +542 -0
  35. package/docs/postgresql-exporter.md +290 -0
  36. package/docs/quick-start.md +761 -0
  37. package/docs/schema-helper.md +340 -0
  38. package/docs/schema-utils.md +492 -0
  39. package/docs/string-extensions.md +480 -0
  40. package/docs/troubleshooting.md +471 -0
  41. package/docs/type-converter.md +319 -0
  42. package/docs/type-reference.md +219 -0
  43. package/docs/validate.md +486 -0
  44. package/docs/validation-guide.md +484 -0
  45. package/examples/array-dsl-example.js +227 -0
  46. package/examples/custom-extension.js +85 -0
  47. package/examples/dsl-match-example.js +74 -0
  48. package/examples/dsl-style.js +118 -0
  49. package/examples/dynamic-locale-configuration.js +348 -0
  50. package/examples/dynamic-locale-example.js +287 -0
  51. package/examples/export-demo.js +130 -0
  52. package/examples/i18n-full-demo.js +310 -0
  53. package/examples/i18n-memory-safety.examples.js +268 -0
  54. package/examples/markdown-export.js +71 -0
  55. package/examples/middleware-usage.js +93 -0
  56. package/examples/password-reset/README.md +153 -0
  57. package/examples/password-reset/schema.js +26 -0
  58. package/examples/password-reset/test.js +101 -0
  59. package/examples/plugin-system.examples.js +205 -0
  60. package/examples/simple-example.js +122 -0
  61. package/examples/string-extensions.js +297 -0
  62. package/examples/user-registration/README.md +156 -0
  63. package/examples/user-registration/routes.js +92 -0
  64. package/examples/user-registration/schema.js +150 -0
  65. package/examples/user-registration/server.js +74 -0
  66. package/index.d.ts +1999 -0
  67. package/index.js +270 -0
  68. package/index.mjs +30 -0
  69. package/lib/adapters/DslAdapter.js +653 -0
  70. package/lib/adapters/index.js +20 -0
  71. package/lib/config/constants.js +286 -0
  72. package/lib/config/patterns/creditCard.js +9 -0
  73. package/lib/config/patterns/idCard.js +9 -0
  74. package/lib/config/patterns/index.js +8 -0
  75. package/lib/config/patterns/licensePlate.js +4 -0
  76. package/lib/config/patterns/passport.js +4 -0
  77. package/lib/config/patterns/phone.js +9 -0
  78. package/lib/config/patterns/postalCode.js +5 -0
  79. package/lib/core/CacheManager.js +376 -0
  80. package/lib/core/DslBuilder.js +740 -0
  81. package/lib/core/ErrorCodes.js +233 -0
  82. package/lib/core/ErrorFormatter.js +342 -0
  83. package/lib/core/JSONSchemaCore.js +347 -0
  84. package/lib/core/Locale.js +119 -0
  85. package/lib/core/MessageTemplate.js +89 -0
  86. package/lib/core/PluginManager.js +448 -0
  87. package/lib/core/StringExtensions.js +209 -0
  88. package/lib/core/Validator.js +316 -0
  89. package/lib/exporters/MarkdownExporter.js +420 -0
  90. package/lib/exporters/MongoDBExporter.js +162 -0
  91. package/lib/exporters/MySQLExporter.js +212 -0
  92. package/lib/exporters/PostgreSQLExporter.js +289 -0
  93. package/lib/exporters/index.js +24 -0
  94. package/lib/locales/en-US.js +65 -0
  95. package/lib/locales/es-ES.js +66 -0
  96. package/lib/locales/fr-FR.js +66 -0
  97. package/lib/locales/index.js +8 -0
  98. package/lib/locales/ja-JP.js +66 -0
  99. package/lib/locales/zh-CN.js +93 -0
  100. package/lib/utils/LRUCache.js +174 -0
  101. package/lib/utils/SchemaHelper.js +240 -0
  102. package/lib/utils/SchemaUtils.js +313 -0
  103. package/lib/utils/TypeConverter.js +245 -0
  104. package/lib/utils/index.js +13 -0
  105. package/lib/validators/CustomKeywords.js +203 -0
  106. package/lib/validators/index.js +11 -0
  107. package/package.json +70 -0
  108. package/plugins/custom-format.js +101 -0
  109. package/plugins/custom-validator.js +200 -0
package/README.md ADDED
@@ -0,0 +1,1122 @@
1
+ <div align="center">
2
+
3
+ # 🎯 schema-dsl
4
+
5
+ **最简洁的 JSON Schema 验证库 - 一行代码搞定复杂验证**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/schema-dsl.svg?style=flat-square)](https://www.npmjs.com/package/schema-dsl)
8
+ [![npm downloads](https://img.shields.io/npm/dm/schema-dsl.svg?style=flat-square)](https://www.npmjs.com/package/schema-dsl)
9
+ [![Build Status](https://github.com/vextjs/schema-dsl/workflows/CI/badge.svg)](https://github.com/vextjs/schema-dsl/actions)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
11
+ [![Node Version](https://img.shields.io/badge/node-%3E%3D12.0.0-brightgreen.svg?style=flat-square)](https://nodejs.org)
12
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](CONTRIBUTING.md)
13
+
14
+ [快速开始](#-快速开始) · [文档](./docs/INDEX.md) · [示例](./examples) · [更新日志](./CHANGELOG.md)
15
+
16
+ ---
17
+
18
+ ### 代码量减少 65% | 性能优秀 | 独家数据库导出
19
+
20
+ </div>
21
+
22
+ ---
23
+
24
+ ## 📑 目录
25
+
26
+ - [安装](#-安装)
27
+ - [快速开始](#-快速开始)
28
+ - [核心特性](#-核心特性)
29
+ - [DSL 语法](#-dsl-语法)
30
+ - [String 扩展](#-string-扩展)
31
+ - [默认验证器](#-默认验证器)
32
+ - [验证功能](#-验证功能)
33
+ - [数据库导出](#-数据库导出)
34
+ - [多语言支持](#-多语言支持)
35
+ - [插件系统](#-插件系统)
36
+ - [错误处理](#-错误处理)
37
+ - [工具函数](#-工具函数)
38
+ - [完整文档](#-完整文档)
39
+
40
+ ---
41
+
42
+ ## 📦 安装
43
+
44
+ ```bash
45
+ npm install schema-dsl
46
+ ```
47
+
48
+ ---
49
+
50
+ ## 🚀 快速开始
51
+
52
+ ```javascript
53
+ const { dsl, validate } = require('schema-dsl');
54
+
55
+ // 定义Schema
56
+ const schema = dsl({
57
+ username: 'string:3-32!',
58
+ email: 'email!',
59
+ age: 'number:18-120'
60
+ });
61
+
62
+ // 验证数据
63
+ const result = validate(schema, {
64
+ username: 'john_doe',
65
+ email: 'john@example.com',
66
+ age: 25
67
+ });
68
+
69
+ console.log(result.valid); // true
70
+ ```
71
+
72
+ ### 📊 与其他库对比
73
+
74
+ <table>
75
+ <tr>
76
+ <td width="50%">
77
+
78
+ **schema-dsl - 简洁优雅** ✨
79
+ ```javascript
80
+ const schema = dsl({
81
+ username: 'string:3-32!',
82
+ email: 'email!',
83
+ age: 'number:18-120'
84
+ });
85
+ ```
86
+
87
+ </td>
88
+ <td width="50%">
89
+
90
+ **其他库 - 冗长繁琐**
91
+ ```javascript
92
+ const schema = Joi.object({
93
+ username: Joi.string()
94
+ .min(3).max(32).required(),
95
+ email: Joi.string()
96
+ .email().required(),
97
+ age: Joi.number()
98
+ .min(18).max(120)
99
+ });
100
+ ```
101
+
102
+ </td>
103
+ </tr>
104
+ </table>
105
+
106
+ **代码量减少 65%,开发效率提升 3 倍!** 🚀
107
+
108
+ **📖 详细教程**: [快速开始](docs/quick-start.md)
109
+
110
+ ---
111
+
112
+ ## ✨ 核心特性
113
+
114
+ - **简洁语法**: 一行代码定义验证规则
115
+ - **String扩展**: 字符串直接链式调用方法
116
+ - **默认验证器**: 内置用户名、手机号、密码验证
117
+ - **插件系统**: 强大的插件机制,支持自定义验证器和格式 🆕
118
+ - **数据库导出**: 导出MongoDB/MySQL/PostgreSQL Schema
119
+ - **多语言支持**: 内置中英文,可自定义语言包
120
+ - **高性能**: 基于ajv,支持编译缓存
121
+ - **轻量级**: 无冗余依赖
122
+
123
+ ---
124
+
125
+ ## 🎯 为什么选择 schema-dsl?
126
+
127
+ ### 三大核心优势
128
+
129
+ #### 1. 代码最简洁 ⭐⭐⭐⭐⭐
130
+
131
+ **一行代码搞定复杂验证**:
132
+ ```javascript
133
+ // schema-dsl - 简洁明了
134
+ username: 'string:3-32!'
135
+
136
+ // 其他库 - 冗长繁琐
137
+ username: Joi.string().min(3).max(32).required()
138
+ ```
139
+
140
+ 代码量减少 **65%**,开发效率提升 **3倍**!
141
+
142
+ ---
143
+
144
+ #### 2. 性能优秀 ⭐⭐⭐⭐
145
+
146
+ **真实测试结果**(10,000次验证):
147
+
148
+ | 库名 | 速度 | 排名 |
149
+ |------|------|------|
150
+ | Ajv | 2,000,000 次/秒 | 🥇 第1 |
151
+ | Zod | 526,316 次/秒 | 🥈 第2 |
152
+ | **schema-dsl** | **277,778 次/秒** | 🥉 **第3** |
153
+ | Joi | 97,087 次/秒 | 第4 |
154
+ | Yup | 60,241 次/秒 | 第5 |
155
+
156
+ - ✅ 比 Joi 快 **2.86倍**
157
+ - ✅ 比 Yup 快 **4.61倍**
158
+ - ✅ 对大多数应用足够(27万+次/秒)
159
+
160
+ ---
161
+
162
+ #### 3. 独家功能 ⭐⭐⭐⭐⭐
163
+
164
+ **唯一支持的功能**:
165
+
166
+ ✅ **数据库 Schema 导出**
167
+ ```javascript
168
+ // 自动生成 MongoDB/MySQL/PostgreSQL Schema
169
+ const mongoSchema = exporters.MongoDBExporter.export(schema);
170
+ const mysqlDDL = new exporters.MySQLExporter().export('users', schema);
171
+ ```
172
+
173
+ ✅ **完整的多语言系统**
174
+ ```javascript
175
+ // 用户可自定义语言包
176
+ dsl.config({
177
+ i18n: {
178
+ locales: { 'zh-CN': { 'username': '用户名' } }
179
+ }
180
+ });
181
+ ```
182
+
183
+ ✅ **Markdown 文档自动生成**
184
+ ```javascript
185
+ // 一键生成 API 文档
186
+ const markdown = exporters.MarkdownExporter.export(schema);
187
+ ```
188
+
189
+ ---
190
+
191
+ ### 适合你吗?
192
+
193
+ **✅ 选择 schema-dsl,如果你**:
194
+ - 想快速开发,减少代码量
195
+ - 需要多语言支持(国际化项目)
196
+ - 需要自动生成数据库 Schema
197
+ - 需要配置驱动的验证规则(多租户系统)
198
+ - 想 5 分钟上手
199
+
200
+ **⚠️ 考虑其他库,如果**:
201
+ - TypeScript 项目需要强类型推断 → 推荐 **Zod**
202
+ - 需要极致性能(>50万次/秒)→ 推荐 **Ajv** 或 **Zod**
203
+
204
+ ---
205
+
206
+ ## 🤔 适合你的项目吗?
207
+
208
+ ### ✅ 选择 schema-dsl,如果你:
209
+
210
+ - 🚀 **追求开发效率** - 想用最少的代码完成验证
211
+ - 🌍 **需要多语言支持** - 国际化项目必备(内置5种语言)
212
+ - 🗄️ **需要数据库 Schema** - 自动生成 MongoDB/MySQL/PostgreSQL DDL
213
+ - 🔧 **配置驱动** - 验证规则需要从配置/数据库动态读取
214
+ - 👨‍💻 **快速上手** - 5 分钟学会,文档完善
215
+
216
+ ### 🔀 考虑其他库,如果:
217
+
218
+ - 📘 **TypeScript 类型推断** - 项目需要极强的类型安全 → 推荐 **[Zod](https://github.com/colinhacks/zod)**
219
+ - ⚡ **极致性能要求** - 需要 50 万+ ops/s → 推荐 **[Ajv](https://github.com/ajv-validator/ajv)**
220
+ - 🏢 **企业级成熟方案** - 需要经过大规模验证 → 推荐 **[Joi](https://github.com/sideway/joi)**
221
+
222
+ ### 💡 独家优势
223
+
224
+ schema-dsl 是**唯一**同时提供以下功能的库:
225
+
226
+ | 功能 | schema-dsl | Joi | Yup | Zod | Ajv |
227
+ |------|----------|-----|-----|-----|-----|
228
+ | **DSL 语法** | ✅ | ❌ | ❌ | ❌ | ❌ |
229
+ | **数据库导出** | ✅ | ❌ | ❌ | ❌ | ❌ |
230
+ | **完整多语言** | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ |
231
+ | **Markdown 导出** | ✅ | ❌ | ❌ | ❌ | ❌ |
232
+ | **配置驱动** | ✅ | ❌ | ❌ | ❌ | ✅ |
233
+
234
+ ---
235
+
236
+ ## 💡 性能说明
237
+
238
+ ### 为什么比 Zod 慢但依然值得选择?
239
+
240
+ schema-dsl 使用**运行时解析**,而 Zod 使用**编译时构建**。
241
+
242
+ **这个选择带来的好处**:
243
+
244
+ 1. ✅ **完全动态** - 验证规则可以从配置文件、数据库动态读取
245
+ ```javascript
246
+ // 从数据库读取规则
247
+ const rules = await db.findOne({ entity: 'user' });
248
+ const schema = dsl({ username: `string:${rules.min}-${rules.max}!` });
249
+ ```
250
+
251
+ 2. ✅ **多租户支持** - 每个租户可以有不同的验证规则
252
+ ```javascript
253
+ // 租户A: 用户名3-32字符,租户B: 5-50字符
254
+ const schema = dsl(tenantConfig[tenantId]);
255
+ ```
256
+
257
+ 3. ✅ **前后端共享规则** - 一套规则,两端使用
258
+ ```javascript
259
+ // 后端定义规则,通过 API 传给前端
260
+ res.json({ rules: { username: 'string:3-32!' } });
261
+ ```
262
+
263
+ **权衡结果**:
264
+ ```
265
+ 损失:比 Zod 慢 1.9倍
266
+ 换来:代码量减少 65% + 完全动态性 + 独家功能
267
+ 结论:对大多数应用(<10万次/秒验证)完全够用
268
+ ```
269
+
270
+ ---
271
+
272
+ ## 📚 DSL 语法
273
+
274
+ ### 基本类型
275
+
276
+ ```javascript
277
+ const schema = dsl({
278
+ name: 'string', // 字符串
279
+ age: 'number', // 数字
280
+ count: 'integer', // 整数
281
+ active: 'boolean', // 布尔值
282
+ email: 'email', // 邮箱
283
+ website: 'url', // URL
284
+ id: 'uuid', // UUID
285
+ created: 'date' // 日期
286
+ });
287
+ ```
288
+
289
+ ### 约束条件
290
+
291
+ ```javascript
292
+ const schema = dsl({
293
+ // 范围约束
294
+ username: 'string:3-32', // 长度3-32(最小3,最大32)
295
+ age: 'number:18-120', // 范围18-120
296
+
297
+ // 单边约束
298
+ bio: 'string:500', // 最大长度500(简写)
299
+ bio: 'string:-500', // 最大长度500(明确写法,与上面等价)
300
+ content: 'string:10-', // 最小长度10(无最大限制)
301
+
302
+ // 数组约束
303
+ tags: 'array:1-10', // 数组长度1-10
304
+ items: 'array:1-', // 数组最少1个
305
+ options: 'array:-20' // 数组最多20个
306
+ });
307
+ ```
308
+
309
+ **语法规则**:
310
+ - `type:max` → 最大值(简写,常用)
311
+ - `type:min-max` → 范围(最小-最大)
312
+ - `type:min-` → 只限制最小值
313
+ - `type:-max` → 只限制最大值(与简写等价)
314
+
315
+ ### 必填标记
316
+
317
+ ```javascript
318
+ const schema = dsl({
319
+ username: 'string:3-32!', // 必填
320
+ email: 'email!', // 必填
321
+ age: 'number:18-120' // 可选
322
+ });
323
+ ```
324
+
325
+ ### 枚举值
326
+
327
+ ```javascript
328
+ const schema = dsl({
329
+ status: 'active|inactive|pending', // 三选一
330
+ role: 'admin|user|guest' // 三选一
331
+ });
332
+ ```
333
+
334
+ ### 数组类型
335
+
336
+ ```javascript
337
+ const schema = dsl({
338
+ // 基础数组
339
+ tags: 'array<string>',
340
+ scores: 'array<number>',
341
+
342
+ // 带长度约束
343
+ images: 'array:1-5<url>', // 1-5个URL
344
+ items: 'array:1-<string>', // 至少1个
345
+
346
+ // 元素带约束
347
+ tags: 'array<string:1-20>', // 每项1-20字符
348
+ scores: 'array:1-5<number:0-100>' // 1-5个,每个0-100
349
+ });
350
+ ```
351
+
352
+ **📖 完整语法**: [DSL 语法指南](docs/dsl-syntax.md)
353
+
354
+ ### 嵌套对象
355
+
356
+ ```javascript
357
+ const schema = dsl({
358
+ user: {
359
+ name: 'string:1-100!',
360
+ email: 'email!',
361
+ profile: {
362
+ bio: 'string:500',
363
+ website: 'url',
364
+ social: {
365
+ twitter: 'url',
366
+ github: 'url'
367
+ }
368
+ }
369
+ }
370
+ });
371
+ ```
372
+
373
+ ---
374
+
375
+ ## 🆕 String 扩展
376
+
377
+ 字符串可以直接调用方法,无需 `dsl()` 包裹:
378
+
379
+ ```javascript
380
+ const schema = dsl({
381
+ // 正则验证
382
+ username: 'string:3-32!'
383
+ .pattern(/^[a-zA-Z0-9_]+$/)
384
+ .messages({ 'pattern': '只能包含字母、数字和下划线' })
385
+ .label('用户名'),
386
+
387
+ // 自定义验证(优雅方式:只在失败时返回)
388
+ email: 'email!'
389
+ .custom(async (value) => {
390
+ const exists = await checkEmailExists(value);
391
+ if (exists) return '邮箱已被占用'; // 失败时返回错误消息
392
+ // 成功时无需返回
393
+ })
394
+ .label('邮箱'),
395
+
396
+ // 条件验证 - 使用 dsl.match()
397
+ contactType: 'email|phone',
398
+ contact: dsl.match('contactType', {
399
+ email: 'email!',
400
+ phone: 'string'.pattern(/^\d{11}$/)
401
+ })
402
+ });
403
+ ```
404
+
405
+ **可用方法**:
406
+ - `.pattern(regex, msg)` - 正则验证
407
+ - `.label(text)` - 字段标签
408
+ - `.messages(obj)` - 自定义消息
409
+ - `.description(text)` - 字段描述
410
+ - `.custom(fn)` - 自定义验证(支持多种返回方式)
411
+ - `.default(value)` - 默认值
412
+
413
+ **📖 详细文档**: [String 扩展](docs/string-extensions.md)
414
+
415
+ ---
416
+
417
+ ## 🎯 默认验证器
418
+
419
+ ### 用户名验证
420
+
421
+ ```javascript
422
+ const schema = dsl({
423
+ // ✨ 简洁写法
424
+ username: 'string!'.username(), // 自动3-32(默认 medium)
425
+
426
+ // 自定义长度(多种方式)
427
+ username: 'string!'.username('5-20'), // 字符串范围
428
+ username: 'string!'.username('short'), // 短用户名(3-16)
429
+ username: 'string!'.username('medium'), // 中等(3-32)
430
+ username: 'string!'.username('long'), // 长用户名(3-64)
431
+ });
432
+ ```
433
+
434
+ **预设选项**:
435
+ - 默认(不传参) - 3-32位
436
+ - `'short'` - 3-16位(短用户名)
437
+ - `'medium'` - 3-32位(中等,默认值)
438
+ - `'long'` - 3-64位(长用户名)
439
+ - `'5-20'` - 自定义范围(字符串格式)
440
+
441
+ ### 手机号验证
442
+
443
+ ```javascript
444
+ const schema = dsl({
445
+ // ✨ 简洁优雅
446
+ phone: 'string!'.phone('cn'), // 推荐 ✅
447
+
448
+ // 自动纠正:即使写成 number 也能自动纠正为 string
449
+ phone: 'number!'.phone('cn'), // 自动纠正 ✅
450
+ });
451
+ ```
452
+
453
+ **💡 为什么用 string 不用 number?**
454
+ - 手机号可能有前导0
455
+ - 国际手机号有 + 号前缀
456
+ - 不用于数学计算
457
+ - phone() 会自动纠正类型
458
+
459
+ **支持国家**: `cn`, `us`, `uk`, `hk`, `tw`, `international`
460
+
461
+ ### 密码强度验证
462
+
463
+ ```javascript
464
+ const schema = dsl({
465
+ password: 'string!'.password('strong') // 自动8-64长度
466
+ });
467
+ ```
468
+
469
+ **强度级别**:
470
+ - `weak` - 最少6位
471
+ - `medium` - 8位,字母+数字
472
+ - `strong` - 8位,大小写+数字
473
+ - `veryStrong` - 10位,大小写+数字+特殊字符
474
+
475
+ ### 完整示例
476
+
477
+ ```javascript
478
+ // ✨ 极简写法
479
+ const registrationSchema = dsl({
480
+ username: 'string!'.username('5-20'), // 5-20位
481
+ phone: 'string!'.phone('cn').label('手机号'), // 简洁 ✅
482
+ password: 'string!'.password('strong').label('密码'), // 自动8-64
483
+ email: 'email!'.label('邮箱')
484
+ });
485
+ ```
486
+
487
+ ---
488
+
489
+ ## ✅ 验证功能
490
+
491
+ ### 基础验证
492
+
493
+ ```javascript
494
+ const { validate } = require('schema-dsl');
495
+
496
+ const result = validate(schema, data);
497
+
498
+ console.log(result.valid); // true/false
499
+ console.log(result.errors); // 错误列表
500
+ console.log(result.data); // 验证后的数据
501
+ ```
502
+
503
+ ### 使用 Validator 类(高级用法)
504
+
505
+ 当需要自定义配置(如关闭默认值、启用类型转换)时,使用 `Validator` 类:
506
+
507
+ ```javascript
508
+ const { Validator } = require('schema-dsl');
509
+
510
+ // 1. 创建实例(支持自定义配置)
511
+ const validator = new Validator({
512
+ allErrors: true, // 返回所有错误
513
+ useDefaults: true, // 应用默认值
514
+ coerceTypes: true // ✨ 启用类型转换(如字符串转数字)
515
+ });
516
+
517
+ const result = validator.validate(schema, data);
518
+ ```
519
+
520
+ **💡 提示**: 对于大多数场景,直接使用 `validate(schema, data)` 即可(它使用默认配置的单例)。
521
+
522
+ **📖 详细文档**: [validate 方法](docs/validate.md)
523
+
524
+ ### 批量验证
525
+
526
+ ```javascript
527
+ const dataArray = [
528
+ { username: 'user1', email: 'user1@example.com' },
529
+ { username: 'user2', email: 'user2@example.com' }
530
+ ];
531
+
532
+ const results = validator.validateBatch(schema, dataArray);
533
+
534
+ console.log(results.performance); // 性能统计
535
+ ```
536
+
537
+ ### 编译缓存
538
+
539
+ ```javascript
540
+ // 编译一次,重复使用
541
+ const validate = validator.compile(schema, 'user-schema');
542
+
543
+ // 使用缓存
544
+ const result = validator.validate(validate, data);
545
+ ```
546
+
547
+ ---
548
+
549
+ ## � 插件系统
550
+
551
+ **v2.2.0 新增**:强大的插件机制,轻松扩展 SchemaIO 功能。
552
+
553
+ ### 快速开始
554
+
555
+ ```javascript
556
+ const { PluginManager } = require('schema-dsl');
557
+
558
+ // 1. 创建插件管理器
559
+ const pluginManager = new PluginManager();
560
+
561
+ // 2. 注册插件
562
+ const customPlugin = require('./plugins/custom-validator');
563
+ pluginManager.register(customPlugin);
564
+
565
+ // 3. 安装插件
566
+ const schema-dsl = require('schema-dsl');
567
+ pluginManager.install(schema-dsl);
568
+ ```
569
+
570
+ ### 内置示例插件
571
+
572
+ #### 1. custom-validator - 自定义验证器
573
+
574
+ ```javascript
575
+ const customValidator = require('schema-dsl/plugins/custom-validator');
576
+ pluginManager.register(customValidator);
577
+ pluginManager.install(schema-dsl);
578
+
579
+ // 现在可以使用自定义关键字
580
+ const schema = dsl({
581
+ email: { type: 'string', unique: { table: 'users', field: 'email' } },
582
+ password: { type: 'string', passwordStrength: 'strong' },
583
+ idCard: { type: 'string', idCard: true }
584
+ });
585
+ ```
586
+
587
+ **提供的验证器**:
588
+ - `unique` - 异步唯一性验证(数据库检查)
589
+ - `passwordStrength` - 密码强度验证(weak/medium/strong)
590
+ - `idCard` - 中国身份证号验证(含校验和)
591
+
592
+ #### 2. custom-format - 自定义格式
593
+
594
+ ```javascript
595
+ const customFormat = require('schema-dsl/plugins/custom-format');
596
+ pluginManager.register(customFormat);
597
+ pluginManager.install(schema-dsl);
598
+
599
+ // 使用新增的格式
600
+ const schema = dsl({
601
+ phone: { type: 'string', format: 'phone-cn' },
602
+ bankCard: { type: 'string', format: 'bank-card' },
603
+ licensePlate: { type: 'string', format: 'license-plate' }
604
+ });
605
+ ```
606
+
607
+ **提供的格式**:
608
+ - `phone-cn` - 中国手机号
609
+ - `postal-code-cn` - 中国邮编
610
+ - `wechat` - 微信号
611
+ - `qq` - QQ号
612
+ - `bank-card` - 银行卡号(Luhn算法)
613
+ - `license-plate` - 车牌号
614
+ - `credit-code` - 统一社会信用代码
615
+ - `passport-cn` - 中国护照
616
+ - `hk-macao-pass` - 港澳通行证
617
+ - `ipv4` - IPv4地址
618
+
619
+ ### 创建自定义插件
620
+
621
+ ```javascript
622
+ const myPlugin = {
623
+ name: 'my-plugin',
624
+ version: '1.0.0',
625
+ description: '我的自定义插件',
626
+
627
+ // 安装函数
628
+ install(schema-dsl, options, context) {
629
+ // 添加自定义功能
630
+ schemaDsl.myMethod = () => { /* ... */ };
631
+ },
632
+
633
+ // 卸载函数(可选)
634
+ uninstall(schema-dsl, context) {
635
+ delete schemaDsl.myMethod;
636
+ },
637
+
638
+ // 生命周期钩子(可选)
639
+ hooks: {
640
+ onBeforeValidate(schema, data) {
641
+ // 验证前处理
642
+ },
643
+ onAfterValidate(result) {
644
+ // 验证后处理
645
+ }
646
+ }
647
+ };
648
+
649
+ pluginManager.register(myPlugin);
650
+ pluginManager.install(schema-dsl, 'my-plugin', { /* 选项 */ });
651
+ ```
652
+
653
+ ### 生命周期钩子
654
+
655
+ 插件系统提供9个生命周期钩子:
656
+ - `onBeforeRegister` - 插件注册前
657
+ - `onAfterRegister` - 插件注册后
658
+ - `onBeforeValidate` - 验证前
659
+ - `onAfterValidate` - 验证后
660
+ - `onBeforeExport` - 导出前
661
+ - `onAfterExport` - 导出后
662
+ - `onBeforeCompile` - 编译前
663
+ - `onAfterCompile` - 编译后
664
+ - `onError` - 错误处理
665
+
666
+ ### 插件管理
667
+
668
+ ```javascript
669
+ // 查看所有插件
670
+ pluginManager.list();
671
+
672
+ // 检查插件是否存在
673
+ pluginManager.has('custom-validator');
674
+
675
+ // 获取插件信息
676
+ pluginManager.get('custom-validator');
677
+
678
+ // 卸载插件
679
+ pluginManager.uninstall('custom-validator', schema-dsl);
680
+
681
+ // 清空所有插件
682
+ pluginManager.clear(schema-dsl);
683
+ ```
684
+
685
+ **📖 完整文档**: [插件系统指南](docs/plugin-system.md)
686
+
687
+ ---
688
+
689
+ ## �🗄️ 数据库导出
690
+
691
+ ### MongoDB Schema
692
+
693
+ ```javascript
694
+ const { exporters } = require('schema-dsl');
695
+
696
+ const mongoExporter = new exporters.MongoDBExporter();
697
+ const mongoSchema = mongoExporter.export(jsonSchema);
698
+
699
+ // 生成命令
700
+ const command = mongoExporter.generateCommand('users', jsonSchema);
701
+ ```
702
+
703
+ ### MySQL DDL
704
+
705
+ ```javascript
706
+ const mysqlExporter = new exporters.MySQLExporter();
707
+ const ddl = mysqlExporter.export('users', jsonSchema);
708
+
709
+ // 输出:
710
+ // CREATE TABLE `users` (
711
+ // `username` VARCHAR(32) NOT NULL,
712
+ // `email` VARCHAR(255) NOT NULL,
713
+ // ...
714
+ // ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
715
+ ```
716
+
717
+ ### PostgreSQL DDL
718
+
719
+ ```javascript
720
+ const pgExporter = new exporters.PostgreSQLExporter();
721
+ const ddl = pgExporter.export('users', jsonSchema);
722
+
723
+ // 输出:
724
+ // CREATE TABLE public.users (
725
+ // username VARCHAR(32) NOT NULL,
726
+ // email VARCHAR(255) NOT NULL,
727
+ // ...
728
+ // );
729
+ ```
730
+
731
+ ---
732
+
733
+ ## 🌍 多语言支持
734
+
735
+ ### 内置语言包
736
+
737
+ 默认支持5种语言:
738
+ - `zh-CN` - 简体中文
739
+ - `en-US` - 英语
740
+ - `ja-JP` - 日语
741
+ - `es-ES` - 西班牙语
742
+ - `fr-FR` - 法语
743
+
744
+ ### 全局配置
745
+
746
+ ```javascript
747
+ const { dsl } = require('schema-dsl');
748
+
749
+ // 配置多语言目录
750
+ dsl.config({
751
+ locales: './locales' // 目录路径,包含 zh-CN.js, en-US.js 等
752
+ });
753
+
754
+ // 或者直接传入对象
755
+ dsl.config({
756
+ locales: {
757
+ 'fr-FR': {
758
+ 'required': '{{#label}} est requis',
759
+ 'pattern.phone.cn': 'Numéro de téléphone invalide'
760
+ }
761
+ }
762
+ });
763
+ ```
764
+
765
+ ### 切换语言
766
+
767
+ ```javascript
768
+ const { Locale } = require('schema-dsl');
769
+
770
+ Locale.setLocale('zh-CN'); // 中文
771
+ Locale.setLocale('en-US'); // 英文
772
+ ```
773
+
774
+ **内置语言**: `en-US` (英语), `zh-CN` (中文)
775
+
776
+ ### 添加语言包
777
+
778
+ ```javascript
779
+ Locale.addLocale('ja-JP', {
780
+ 'minLength': '{{#label}}は{{#limit}}文字以上である必要があります',
781
+ 'required': '{{#label}}は必須です'
782
+ });
783
+ ```
784
+
785
+ ### 全局自定义消息
786
+
787
+ ```javascript
788
+ Locale.setMessages({
789
+ 'format': '格式不正确',
790
+ 'required': '这是必填项',
791
+ 'minLength': '长度不能少于{{#limit}}个字符'
792
+ });
793
+ ```
794
+
795
+ ### 动态切换与 Label 翻译 (v2.1.0)
796
+
797
+ 支持在验证时动态指定语言,并自动翻译字段标签。
798
+
799
+ ```javascript
800
+ // 1. 定义 Schema (使用 Label Key)
801
+ const schema = dsl({
802
+ username: 'string!'.label('label.username')
803
+ });
804
+
805
+ // 2. 配置语言包 (包含 Label 翻译)
806
+ Locale.addLocale('zh-CN', {
807
+ 'label.username': '用户名',
808
+ 'required': '{{#label}}不能为空'
809
+ });
810
+
811
+ // 3. 验证时指定语言
812
+ validator.validate(schema, data, { locale: 'zh-CN' });
813
+ // 错误消息: "用户名不能为空"
814
+ ```
815
+
816
+ **📖 详细文档**: [动态多语言配置](docs/dynamic-locale.md)
817
+
818
+ **📖 详细文档**: [错误处理指南](docs/error-handling.md)
819
+
820
+ ---
821
+
822
+ ## 🔧 错误处理
823
+
824
+ ### label、message、description
825
+
826
+ ```javascript
827
+ const schema = dsl({
828
+ email: 'email!'
829
+ .label('邮箱地址') // 错误消息中显示
830
+ .description('用于登录和接收通知') // 表单提示/文档
831
+ .messages({ // 自定义错误消息
832
+ 'required': '{{#label}}不能为空',
833
+ 'format': '请输入有效的{{#label}}'
834
+ // 💡 'format' 是 JSON Schema 标准对 email/url/uuid 等格式验证失败的错误关键字
835
+ }),
836
+
837
+ username: 'string:3-32!'
838
+ .label('用户名')
839
+ .messages({
840
+ 'min': '{{#label}}至少{{#limit}}个字符',
841
+ 'max': '{{#label}}最多{{#limit}}个字符',
842
+ 'pattern': '{{#label}}格式不正确', // pattern 是正则验证失败的错误关键字
843
+ 'required': '{{#label}}不能为空'
844
+ })
845
+ });
846
+ ```
847
+
848
+ | 属性 | 用途 | 场景 |
849
+ |------|------|------|
850
+ | **label** | 字段名称 | 错误消息 |
851
+ | **messages** | 自定义错误 | 验证失败 |
852
+ | **description** | 详细说明 | 表单提示/文档 |
853
+
854
+ **常见错误关键字**(来自 JSON Schema / ajv):
855
+ - `required` - 必填字段为空
856
+ - `min` / `max` - 字符串长度不符
857
+ - `minimum` / `maximum` - 数字范围不符
858
+ - `format` - 格式验证失败(email、url、uuid、date 等都用这个)
859
+ - `pattern` - 正则表达式不匹配
860
+ - `enum` - 不在枚举值中
861
+ - `type` - 类型不匹配
862
+
863
+ **💡 简化的错误关键字**:
864
+ SchemaIO 对常见的错误关键字做了简化:
865
+ - `min` / `max` 代替 `minLength` / `maxLength` - 更简洁
866
+ - 同时也支持完整关键字 `minLength` / `maxLength` - 向后兼容
867
+
868
+ **💡 为什么 email 用 `format` 而不是 `email`?**
869
+ 因为在 JSON Schema 标准中,email、url、uuid 等都是 `format` 属性的不同值,验证失败时统一使用 `format` 作为错误关键字。
870
+
871
+ **📖 详细说明**: [label vs description](docs/label-vs-description.md)
872
+
873
+ ### 自定义验证器
874
+
875
+ `.custom()` 方法支持多种优雅的返回方式:
876
+
877
+ ```javascript
878
+ const schema = dsl({
879
+ // 方式1: 返回错误消息字符串(推荐,最简洁)
880
+ email: 'email!'
881
+ .custom(async (value) => {
882
+ const exists = await checkEmailExists(value);
883
+ if (exists) return '邮箱已被占用';
884
+ // 验证通过时无需返回
885
+ }),
886
+
887
+ // 方式2: 返回错误对象(需要自定义错误码)
888
+ username: 'string:3-32!'
889
+ .custom(async (value) => {
890
+ const exists = await checkUsernameExists(value);
891
+ if (exists) {
892
+ return { error: 'username.exists', message: '用户名已被占用' };
893
+ }
894
+ }),
895
+
896
+ // 方式3: 抛出异常
897
+ userId: 'string!'
898
+ .custom(async (value) => {
899
+ const user = await findUser(value);
900
+ if (!user) throw new Error('用户不存在');
901
+ })
902
+ });
903
+ ```
904
+
905
+ **支持的返回方式**:
906
+ - 不返回/返回 `undefined` → 验证通过 ✅
907
+ - 返回字符串 → 验证失败,字符串作为错误消息
908
+ - 返回 `{ error, message }` → 验证失败,自定义错误码和消息
909
+ - 抛出异常 → 验证失败,异常消息作为错误
910
+ - 返回 `true` → 验证通过(兼容旧写法)
911
+ - 返回 `false` → 验证失败(使用默认消息)
912
+
913
+ ---
914
+
915
+ ## 🧰 工具函数
916
+
917
+ ### Schema 复用
918
+
919
+ ```javascript
920
+ const { SchemaUtils } = require('schema-dsl');
921
+
922
+ // 创建可复用片段
923
+ const emailField = SchemaUtils.reusable(() => dsl('email!'));
924
+
925
+ const schema1 = dsl({ email: emailField() });
926
+ const schema2 = dsl({ contactEmail: emailField() });
927
+ ```
928
+
929
+ ### Schema 合并
930
+
931
+ ```javascript
932
+ const baseUser = dsl({ name: 'string!', email: 'email!' });
933
+ const withAge = dsl({ age: 'number:18-120' });
934
+
935
+ const merged = SchemaUtils.merge(baseUser, withAge);
936
+ ```
937
+
938
+ ### Schema 筛选
939
+
940
+ ```javascript
941
+ // 选择字段
942
+ const picked = SchemaUtils.pick(schema, ['name', 'email']);
943
+
944
+ // 排除字段
945
+ const omitted = SchemaUtils.omit(schema, ['password', 'secret']);
946
+ ```
947
+
948
+ ### Schema 导出
949
+
950
+ ```javascript
951
+ // 导出为 Markdown
952
+ const markdown = SchemaUtils.toMarkdown(schema);
953
+
954
+ // 导出为 HTML
955
+ const html = SchemaUtils.toHTML(schema);
956
+ ```
957
+
958
+ **📖 完整API**: [功能索引](docs/FEATURE-INDEX.md)
959
+
960
+ ---
961
+
962
+ ## 📖 完整文档
963
+
964
+ ### 核心文档
965
+
966
+ - [快速开始](docs/quick-start.md) - 5分钟入门
967
+ - [DSL 语法指南](docs/dsl-syntax.md) - 完整语法(2815行)
968
+ - [API 参考](docs/api-reference.md) - 所有API
969
+ - [功能索引](docs/FEATURE-INDEX.md) - 功能查找
970
+
971
+ ### 专题文档
972
+
973
+ - [String 扩展](docs/string-extensions.md) - 链式调用
974
+ - [validate 方法](docs/validate.md) - 验证详解
975
+ - [错误处理指南](docs/error-handling.md) - 多语言/自定义消息
976
+ - [label vs description](docs/label-vs-description.md) - 属性区别
977
+
978
+ ### 示例代码
979
+
980
+ - [examples/string-extensions.js](examples/string-extensions.js) - String扩展
981
+ - [examples/dsl-style.js](examples/dsl-style.js) - DSL基础
982
+ - [examples/user-registration/](examples/user-registration/) - 注册场景
983
+ - [examples/export-demo.js](examples/export-demo.js) - 数据库导出
984
+
985
+ ---
986
+
987
+ ## 🌟 谁在使用?
988
+
989
+ schema-dsl 适用于以下场景:
990
+
991
+ - 📱 **RESTful API 验证** - Express、Koa、Fastify 等框架
992
+ - 🎨 **前端表单验证** - React、Vue、Angular 项目
993
+ - 🏢 **微服务系统** - 多服务间的数据验证
994
+ - 🌐 **多租户 SaaS** - 每个租户不同的验证规则
995
+ - 🔄 **数据库 Schema 管理** - 自动生成和同步数据库结构
996
+ - 🌍 **国际化项目** - 多语言错误消息支持
997
+
998
+ ### 真实案例
999
+
1000
+ ```javascript
1001
+ // Express API 验证
1002
+ app.post('/api/users', (req, res) => {
1003
+ const result = validate(userSchema, req.body);
1004
+ if (!result.valid) {
1005
+ return res.status(400).json({ errors: result.errors });
1006
+ }
1007
+ // 处理业务逻辑
1008
+ });
1009
+
1010
+ // 多租户动态规则
1011
+ const tenantRules = await db.collection('rules').findOne({
1012
+ tenantId
1013
+ });
1014
+ const schema = dsl({
1015
+ username: `string:${tenantRules.usernameMin}-${tenantRules.usernameMax}!`
1016
+ });
1017
+
1018
+ // 自动生成数据库表
1019
+ const ddl = new MySQLExporter().export('users', schema);
1020
+ await db.query(ddl);
1021
+ ```
1022
+
1023
+ ---
1024
+
1025
+ ## 🧪 测试
1026
+
1027
+ ```bash
1028
+ npm test # 运行测试
1029
+ npm run coverage # 测试覆盖率
1030
+ ```
1031
+
1032
+ **测试状态**: ✅ 150+ 测试用例全部通过 | 覆盖率 > 90%
1033
+
1034
+ ---
1035
+
1036
+ ## 🗺️ 路线图
1037
+
1038
+ ### v2.4.0(计划中)
1039
+
1040
+ - [ ] GraphQL Schema 导出
1041
+ - [ ] 在线 Playground
1042
+ - [ ] 性能优化(目标:40 万+ ops/s)
1043
+ - [ ] 更多数据库支持(SQLite、Oracle)
1044
+
1045
+ ### 长期规划
1046
+
1047
+ - [ ] VSCode 插件(智能提示)
1048
+ - [ ] 可视化 Schema 编辑器
1049
+ - [ ] JSON Schema 2020-12 完整支持
1050
+
1051
+ **建议和想法?** [提交 Feature Request](https://github.com/vextjs/schema-dsl/issues/new?template=feature_request.md)
1052
+
1053
+ ---
1054
+
1055
+ ## 🤝 贡献
1056
+
1057
+ 我们欢迎所有形式的贡献!
1058
+
1059
+ ### 如何贡献
1060
+
1061
+ - 🐛 [报告 Bug](https://github.com/vextjs/schema-dsl/issues/new?template=bug_report.md)
1062
+ - ✨ [建议新功能](https://github.com/vextjs/schema-dsl/issues/new?template=feature_request.md)
1063
+ - 📖 改进文档
1064
+ - 💻 提交代码
1065
+
1066
+ 查看 [贡献指南](CONTRIBUTING.md) 了解详情。
1067
+
1068
+ ### 贡献者
1069
+
1070
+ 感谢所有为 SchemaIO 做出贡献的开发者!
1071
+
1072
+ <a href="https://github.com/vextjs/schema-dsl/graphs/contributors">
1073
+ <img src="https://contrib.rocks/image?repo=vextjs/schema-dsl" />
1074
+ </a>
1075
+
1076
+ ---
1077
+
1078
+ ## 📄 许可证
1079
+
1080
+ [MIT](LICENSE) © 2025 vextjs
1081
+
1082
+ ---
1083
+
1084
+ ## ⭐ Star History
1085
+
1086
+ 如果 schema-dsl 对你有帮助,请给我们一个 Star ⭐
1087
+
1088
+ [![Star History Chart](https://api.star-history.com/svg?repos=vextjs/schema-dsl&type=Date)](https://star-history.com/#vextjs/schema-dsl&Date)
1089
+
1090
+ ---
1091
+
1092
+ ## 💬 社区
1093
+
1094
+ - 💬 [GitHub Discussions](https://github.com/vextjs/schema-dsl/discussions) - 提问、讨论、分享
1095
+ - 🐛 [Issue Tracker](https://github.com/vextjs/schema-dsl/issues) - Bug 报告和功能请求
1096
+ - 📧 [Email](mailto:rockyshi1993@gmail.com) - 联系维护者
1097
+
1098
+ ---
1099
+
1100
+ ## 🔗 相关链接
1101
+
1102
+ - [npm 包](https://www.npmjs.com/package/schema-dsl)
1103
+ - [GitHub 仓库](https://github.com/vextjs/schema-dsl)
1104
+ - [更新日志](./CHANGELOG.md)
1105
+ - [贡献指南](./CONTRIBUTING.md)
1106
+ - [安全政策](./.github/SECURITY.md)
1107
+ - [行为准则](./.github/CODE_OF_CONDUCT.md)
1108
+
1109
+ ---
1110
+
1111
+ <div align="center">
1112
+
1113
+ **如果 schema-dsl 帮助到你,请给我们一个 ⭐ Star!**
1114
+
1115
+ **你的支持是我们最大的动力!**
1116
+
1117
+ Made with ❤️ by [vextjs](https://github.com/vextjs)
1118
+
1119
+ </div>
1120
+
1121
+
1122
+