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/docs/faq.md ADDED
@@ -0,0 +1,576 @@
1
+ # 常见问题解答 (FAQ)
2
+
3
+ > **更新时间**: 2025-12-25
4
+
5
+
6
+ ---
7
+
8
+ ## 📑 目录
9
+
10
+ - [基础问题](#基础问题)
11
+ - [DSL 语法问题](#dsl-语法问题)
12
+ - [验证问题](#验证问题)
13
+ - [性能问题](#性能问题)
14
+ - [设计理念](#设计理念)
15
+ - [错误处理](#错误处理)
16
+ - [数据库导出](#数据库导出)
17
+ - [TypeScript 支持](#typescript-支持)
18
+
19
+ ---
20
+
21
+ ## 基础问题
22
+
23
+ ### Q: SchemaIO 和 Joi、Yup 有什么区别?
24
+
25
+ **A**: SchemaIO 采用 DSL 语法,更简洁:
26
+
27
+ ```javascript
28
+ // SchemaIO - 简洁
29
+ const schema = dsl({
30
+ username: 'string:3-32!',
31
+ email: 'email!'
32
+ });
33
+
34
+ // Joi - 繁琐
35
+ const schema = Joi.object({
36
+ username: Joi.string().min(3).max(32).required(),
37
+ email: Joi.string().email().required()
38
+ });
39
+ ```
40
+
41
+ **主要区别**:
42
+ - 更简洁的 DSL 语法
43
+ - 支持数据库 Schema 导出
44
+ - 内置常见验证器(username、password、phone)
45
+ - 基于 JSON Schema 标准
46
+
47
+ ---
48
+
49
+ ### Q: 如何安装 SchemaIO?
50
+
51
+ ```bash
52
+ npm install schema-dsl
53
+ ```
54
+
55
+ **Node.js 版本要求**:>= 12.0.0
56
+
57
+ ---
58
+
59
+ ### Q: 支持 ES Modules 吗?
60
+
61
+ **A**: 支持。
62
+
63
+ ```javascript
64
+ // CommonJS
65
+ const { dsl, validate } = require('schema-dsl');
66
+
67
+ // ES Modules
68
+ import { dsl, validate } from 'schema-dsl';
69
+ ```
70
+
71
+ ---
72
+
73
+ ## DSL 语法问题
74
+
75
+ ### Q: `'string:3-32!'` 是什么意思?
76
+
77
+ **A**: 这是 DSL 语法:
78
+ - `string` - 类型
79
+ - `3-32` - 长度范围(最小3,最大32)
80
+ - `!` - 必填
81
+
82
+ 更多示例:
83
+ ```javascript
84
+ 'string:10' // 最大长度10
85
+ 'string:3-' // 最小长度3
86
+ 'number:0-100' // 数值范围0-100
87
+ 'email!' // 必填邮箱
88
+ 'a|b|c' // 枚举值
89
+ ```
90
+
91
+ ---
92
+
93
+ ### Q: 如何定义数组?
94
+
95
+ **A**: 使用 `array` 类型:
96
+
97
+ ```javascript
98
+ // 简单数组
99
+ tags: 'array'
100
+
101
+ // 带长度约束
102
+ tags: 'array:1-10' // 1-10个元素
103
+ tags: 'array!1-10' // 必填,1-10个元素
104
+
105
+ // 带元素类型
106
+ tags: 'array<string>' // 字符串数组
107
+ tags: 'array<number>' // 数字数组
108
+ tags: 'array<string:1-20>' // 带约束的字符串数组
109
+ ```
110
+
111
+ ---
112
+
113
+ ### Q: 如何定义嵌套对象?
114
+
115
+ **A**: 直接嵌套即可:
116
+
117
+ ```javascript
118
+ const schema = dsl({
119
+ user: {
120
+ name: 'string!',
121
+ address: {
122
+ city: 'string!',
123
+ zip: 'string:5-10!'
124
+ }
125
+ }
126
+ });
127
+ ```
128
+
129
+ ---
130
+
131
+ ### Q: 如何使用 String 扩展?
132
+
133
+ **A**: 字符串可以直接链式调用方法:
134
+
135
+ ```javascript
136
+ const schema = dsl({
137
+ email: 'email!'
138
+ .label('邮箱地址')
139
+ .messages({
140
+ 'required': '{{#label}}不能为空',
141
+ 'format': '请输入有效的{{#label}}'
142
+ }),
143
+
144
+ username: 'string:3-32!'
145
+ .pattern(/^[a-z0-9_]+$/)
146
+ .label('用户名')
147
+ .username('medium')
148
+ });
149
+ ```
150
+
151
+ ---
152
+
153
+ ## 验证问题
154
+
155
+ ### Q: 如何验证数据?
156
+
157
+ **A**: 使用 `validate()` 函数或 `Validator` 类:
158
+
159
+ ```javascript
160
+ // 方式1:便捷函数
161
+ const { dsl, validate } = require('schema-dsl');
162
+ const result = validate(schema, data);
163
+
164
+ // 方式2:Validator 实例
165
+ const { Validator } = require('schema-dsl');
166
+ const validator = new Validator();
167
+ const result = validator.validate(schema, data);
168
+ ```
169
+
170
+ ---
171
+
172
+ ### Q: 验证结果的格式是什么?
173
+
174
+ **A**: 返回对象包含:
175
+
176
+ ```javascript
177
+ {
178
+ valid: true/false, // 是否通过
179
+ errors: [], // 错误数组(如果有)
180
+ data: {}, // 验证后的数据(可能包含默认值)
181
+ performance: { // 性能信息
182
+ duration: 1.5 // 验证耗时(毫秒)
183
+ }
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ### Q: 如何获取所有错误而不是只有第一个?
190
+
191
+ **A**: 默认就是返回所有错误。如果只需要第一个:
192
+
193
+ ```javascript
194
+ const validator = new Validator({ allErrors: false });
195
+ ```
196
+
197
+ ---
198
+
199
+ ### Q: 如何使用默认值?
200
+
201
+ **A**: 使用 `.default()` 方法:
202
+
203
+ ```javascript
204
+ const schema = dsl({
205
+ status: 'string'.default('active'),
206
+ count: 'integer'.default(0)
207
+ });
208
+
209
+ const result = validate(schema, {});
210
+ console.log(result.data);
211
+ // { status: 'active', count: 0 }
212
+ ```
213
+
214
+ ---
215
+
216
+ ## 性能问题
217
+
218
+ ### Q: Schema-DSL 的性能怎么样?
219
+
220
+ **A**: 性能优秀,排名第3:
221
+
222
+ | 库名 | 每秒操作数 | 排名 |
223
+ |------|-----------|------|
224
+ | Ajv | 2,000,000 ops/s | 🥇 第1 |
225
+ | Zod | 526,316 ops/s | 🥈 第2 |
226
+ | **Schema-DSL** | **277,778 ops/s** | 🥉 **第3** |
227
+ | Joi | 97,087 ops/s | 第4 |
228
+ | Yup | 60,241 ops/s | 第5 |
229
+
230
+ **结论**:
231
+ - ✅ 比 Joi 快 **2.86倍**
232
+ - ✅ 比 Yup 快 **4.61倍**
233
+ - ✅ 对大多数应用足够(27万+ ops/s)
234
+
235
+ ---
236
+
237
+ ### Q: 为什么比 Zod 慢?
238
+
239
+ **A**: 因为 Schema-DSL 使用**运行时解析 DSL**,而 Zod 是**编译时构建**。
240
+
241
+ **权衡**:
242
+ ```
243
+ 损失: 比 Zod 慢 1.9倍
244
+ 换来:
245
+ ✅ 代码量减少 65%
246
+ ✅ 完全动态的验证规则
247
+ ✅ 多租户/配置驱动支持
248
+ ✅ 前后端共享规则
249
+ ```
250
+
251
+ ---
252
+
253
+ ### Q: 什么时候性能会成为瓶颈?
254
+
255
+ **A**: 以下场景才可能成为瓶颈:
256
+
257
+ 1. **API 网关**(每秒 >50万次验证)
258
+ 2. **高并发服务**(每秒 >50万次请求)
259
+ 3. **实时数据处理**(毫秒级延迟要求)
260
+
261
+ **大多数应用**(每秒 <10万次验证)不会遇到性能瓶颈。
262
+
263
+ ---
264
+
265
+ ### Q: 验证速度慢怎么办?
266
+
267
+ **A**: 使用预编译和缓存:
268
+
269
+ ```javascript
270
+ // 1. 使用预编译
271
+ const validator = new Validator();
272
+ const validateUser = validator.compile(userSchema);
273
+
274
+ // 2. 启用缓存(v2.3.0)
275
+ const validator = new Validator({
276
+ cache: {
277
+ maxSize: 5000, // 缓存5000个Schema
278
+ ttl: 3600000 // 1小时过期
279
+ }
280
+ });
281
+
282
+ // 3. 复用 Validator 实例
283
+ // ❌ 错误:每次都创建新实例
284
+ app.post('/api/users', (req, res) => {
285
+ const validator = new Validator(); // 慢
286
+ // ...
287
+ });
288
+
289
+ // ✅ 正确:复用实例
290
+ const validator = new Validator();
291
+ app.post('/api/users', (req, res) => {
292
+ const result = validator.validate(schema, req.body); // 快
293
+ // ...
294
+ });
295
+ ```
296
+
297
+ ---
298
+
299
+ ### Q: 缓存如何工作?
300
+
301
+ **A**: SchemaIO 内置 LRU 缓存(v2.3.0):
302
+
303
+ ```javascript
304
+ const validator = new Validator({
305
+ cache: {
306
+ maxSize: 5000, // 最大缓存5000条
307
+ ttl: 3600000 // 1小时过期
308
+ }
309
+ });
310
+
311
+ // 缓存统计
312
+ const stats = validator.cache.getStats();
313
+ console.log(stats);
314
+ // {
315
+ // size: 150, // 当前缓存数
316
+ // hits: 8500, // 缓存命中次数
317
+ // misses: 150, // 缓存未命中次数
318
+ // evictions: 0 // 驱逐次数
319
+ // }
320
+ ```
321
+
322
+ ---
323
+
324
+ ### Q: 如何批量验证?
325
+
326
+ **A**: 使用 `validateBatch()`:
327
+
328
+ ```javascript
329
+ const results = validator.validateBatch(schema, [data1, data2, data3]);
330
+ // 返回结果数组
331
+ ```
332
+
333
+ ---
334
+
335
+ ## 设计理念
336
+
337
+ ### Q: 为什么选择运行时解析而不是编译时构建?
338
+
339
+ **A**: 这是有意的设计选择,优先考虑**灵活性**而非**极致性能**。
340
+
341
+ **运行时解析的优势**:
342
+ 1. ✅ **完全动态** - 可从配置/数据库动态生成规则
343
+ 2. ✅ **多租户支持** - 每个租户不同规则,零代码修改
344
+ 3. ✅ **可序列化** - 可存储、传输、共享
345
+ 4. ✅ **前后端共享** - 一套规则,两端使用
346
+ 5. ✅ **低代码基础** - 可视化配置表单验证
347
+
348
+ **编译时构建的限制**:
349
+ - ❌ Schema 固定,无法动态调整
350
+ - ❌ 无法序列化和传输
351
+ - ❌ 多租户困难
352
+ - ❌ 无法从数据库读取规则
353
+
354
+ **详细说明**: [设计理念文档](design-philosophy.md)
355
+
356
+ ---
357
+
358
+ ### Q: Schema-DSL 适合什么场景?
359
+
360
+ **A**: ✅ **最适合的场景**:
361
+
362
+ 1. **多租户 SaaS 系统** - 每个租户不同验证规则
363
+ 2. **后台管理系统** - 管理员配置表单验证
364
+ 3. **配置驱动开发** - 验证规则存储在配置/数据库
365
+ 4. **低代码/无代码平台** - 可视化表单构建器
366
+ 5. **快速原型开发** - 5分钟上手,代码量最少
367
+ 6. **前后端共享验证** - 一套规则,两端使用
368
+
369
+ ⚠️ **不适合的场景**:
370
+ 1. 极致性能要求(>50万 ops/s)→ 推荐 **Zod** 或 **Ajv**
371
+ 2. TypeScript 强类型推断 → 推荐 **Zod**
372
+ 3. 静态验证规则 → 推荐 **Zod**
373
+
374
+ ---
375
+
376
+ ### Q: 为什么不做成像 Zod 那样的编译时库?
377
+
378
+ **A**: 因为会失去核心价值:
379
+
380
+ **失去的能力**:
381
+ ```javascript
382
+ // ❌ 无法从数据库读取规则
383
+ const rules = await db.findOne({ entity: 'user' });
384
+ const schema = dsl(rules);
385
+
386
+ // ❌ 无法多租户动态规则
387
+ function getTenantSchema(tenantId) {
388
+ return dsl(tenantConfig[tenantId]);
389
+ }
390
+
391
+ // ❌ 无法通过 API 传输
392
+ res.json({ validationRules: rules });
393
+
394
+ // ❌ 无法后台配置表单验证
395
+ ```
396
+
397
+ **保留的能力**:
398
+ ```javascript
399
+ // ✅ 完全动态
400
+ const schema = dsl({
401
+ username: `string:${config.min}-${config.max}!`
402
+ });
403
+
404
+ // ✅ 可序列化
405
+ JSON.stringify({ username: 'string:3-32!' });
406
+
407
+ // ✅ 前后端共享
408
+ // 后端定义 → API传输 → 前端使用
409
+ ```
410
+
411
+ ---
412
+
413
+ ### Q: 性能和灵活性如何平衡?
414
+
415
+ **A**: Schema-DSL 的设计优先级:
416
+
417
+ ```
418
+ 灵活性 > 易用性 > 性能
419
+ ```
420
+
421
+ **权衡结果**:
422
+ - 损失:比 Zod 慢 1.9倍
423
+ - 换来:完全的动态性 + 代码量减少 65%
424
+ - 结论:对大多数应用(<10万 ops/s)足够
425
+
426
+ **如果需要极致性能**: 推荐使用 Zod(526k ops/s)或 Ajv(2M ops/s)
427
+
428
+ ---
429
+
430
+ ## 错误处理
431
+
432
+ ### Q: 如何自定义错误消息?
433
+
434
+ **A**: 使用 `.messages()` 方法:
435
+
436
+ ```javascript
437
+ username: 'string:3-32!'
438
+ .label('用户名')
439
+ .messages({
440
+ 'min': '{{#label}}太短了',
441
+ 'max': '{{#label}}太长了',
442
+ 'required': '请输入{{#label}}'
443
+ })
444
+ ```
445
+
446
+ ---
447
+
448
+ ### Q: 如何支持多语言?
449
+
450
+ **A**: 使用 `Locale` 类:
451
+
452
+ ```javascript
453
+ const { Locale } = require('schema-dsl');
454
+
455
+ // 添加语言包
456
+ Locale.addLocale('zh-CN', {
457
+ 'required': '{{#label}}不能为空',
458
+ 'min': '{{#label}}长度不能少于{{#limit}}'
459
+ });
460
+
461
+ // 验证时指定语言
462
+ validator.validate(schema, data, { locale: 'zh-CN' });
463
+ ```
464
+
465
+ ---
466
+
467
+ ### Q: 错误路径格式是什么?
468
+
469
+ **A**: JSON Pointer 格式:
470
+
471
+ ```javascript
472
+ '/username' // 顶层字段
473
+ '/user/name' // 嵌套字段
474
+ '/items/0/name' // 数组元素
475
+ ```
476
+
477
+ ---
478
+
479
+ ## 数据库导出
480
+
481
+ ### Q: 如何导出为 MongoDB Schema?
482
+
483
+ ```javascript
484
+ const { exporters } = require('schema-dsl');
485
+
486
+ const exporter = new exporters.MongoDBExporter();
487
+ const mongoSchema = exporter.export(schema);
488
+ ```
489
+
490
+ ---
491
+
492
+ ### Q: 如何导出为 MySQL DDL?
493
+
494
+ ```javascript
495
+ const exporter = new exporters.MySQLExporter();
496
+ const ddl = exporter.export('table_name', schema);
497
+ ```
498
+
499
+ ---
500
+
501
+ ### Q: 如何导出为 PostgreSQL DDL?
502
+
503
+ ```javascript
504
+ const exporter = new exporters.PostgreSQLExporter({ schema: 'public' });
505
+ const ddl = exporter.export('table_name', schema);
506
+ ```
507
+
508
+ ---
509
+
510
+ ### Q: 导出时如何添加注释?
511
+
512
+ **A**: 使用 `.description()`:
513
+
514
+ ```javascript
515
+ username: 'string:3-32!'
516
+ .description('用户登录名,只能包含字母数字')
517
+ ```
518
+
519
+ MySQL 会生成 `COMMENT`,PostgreSQL 会生成 `COMMENT ON COLUMN`。
520
+
521
+ ---
522
+
523
+ ## TypeScript 支持
524
+
525
+ ### Q: SchemaIO 支持 TypeScript 吗?
526
+
527
+ **A**: 支持,类型定义在 `index.d.ts`:
528
+
529
+ ```typescript
530
+ import { dsl, validate, DslBuilder, Validator } from 'schema-dsl';
531
+
532
+ const schema = dsl({
533
+ username: 'string:3-32!',
534
+ email: 'email!'
535
+ });
536
+
537
+ const result = validate(schema, data);
538
+ if (result.valid) {
539
+ console.log(result.data);
540
+ }
541
+ ```
542
+
543
+ ---
544
+
545
+ ### Q: 如何获得 String 扩展的类型提示?
546
+
547
+ **A**: 类型定义包含全局 String 扩展:
548
+
549
+ ```typescript
550
+ // TypeScript 会识别这些方法
551
+ const schema = dsl({
552
+ email: 'email!'.label('邮箱').messages({ ... })
553
+ });
554
+ ```
555
+
556
+ ---
557
+
558
+ ## 更多问题
559
+
560
+ 如果您有其他问题:
561
+
562
+ 1. 查看 [完整文档](INDEX.md)
563
+ 2. 查看 [DSL 语法指南](dsl-syntax.md)
564
+ 3. 查看 [API 参考](api-reference.md)
565
+ 4. 提交 [GitHub Issue](https://github.com/schema-dsl/schema-dsl/issues)
566
+
567
+ ---
568
+
569
+ ## 相关文档
570
+
571
+ - [快速开始](quick-start.md)
572
+ - [DSL 语法](dsl-syntax.md)
573
+ - [验证指南](validation-guide.md)
574
+ - [导出指南](export-guide.md)
575
+ - [错误处理](error-handling.md)
576
+