schema-dsl 2.0.0 → 2.0.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.
Files changed (145) hide show
  1. package/CHANGELOG.md +130 -113
  2. package/LICENSE +21 -21
  3. package/README.md +628 -628
  4. package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
  5. package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
  6. package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
  7. package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
  8. package/dist/index.cjs +75 -29
  9. package/dist/index.d.cts +10 -4
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.js +75 -29
  12. package/dist/plugins/custom-format.cjs +33 -17
  13. package/dist/plugins/custom-format.d.cts +1 -1
  14. package/dist/plugins/custom-format.d.ts +1 -1
  15. package/dist/plugins/custom-format.js +33 -17
  16. package/dist/plugins/custom-type-example.cjs +33 -17
  17. package/dist/plugins/custom-type-example.d.cts +1 -1
  18. package/dist/plugins/custom-type-example.d.ts +1 -1
  19. package/dist/plugins/custom-type-example.js +33 -17
  20. package/dist/plugins/custom-validator.cjs +0 -2
  21. package/dist/plugins/custom-validator.d.cts +1 -1
  22. package/dist/plugins/custom-validator.d.ts +1 -1
  23. package/dist/plugins/custom-validator.js +0 -2
  24. package/docs/FEATURE-INDEX.md +553 -553
  25. package/docs/add-custom-locale.md +496 -496
  26. package/docs/add-keyword.md +24 -24
  27. package/docs/api-reference.md +1047 -1047
  28. package/docs/api.md +13 -13
  29. package/docs/best-practices-project-structure.md +417 -417
  30. package/docs/best-practices.md +712 -712
  31. package/docs/cache-manager.md +344 -344
  32. package/docs/compile.md +45 -45
  33. package/docs/conditional-api.md +1307 -1307
  34. package/docs/custom-extensions-guide.md +339 -339
  35. package/docs/design-philosophy.md +606 -606
  36. package/docs/doc-index.md +324 -324
  37. package/docs/dsl-syntax.md +714 -714
  38. package/docs/dynamic-locale.md +608 -608
  39. package/docs/enum.md +482 -482
  40. package/docs/error-handling.md +1975 -1975
  41. package/docs/export-guide.md +501 -501
  42. package/docs/export-limitations.md +567 -567
  43. package/docs/faq.md +596 -596
  44. package/docs/frontend-i18n-guide.md +307 -307
  45. package/docs/i18n-user-guide.md +487 -487
  46. package/docs/i18n.md +476 -476
  47. package/docs/index.md +48 -48
  48. package/docs/json-schema-basics.md +40 -40
  49. package/docs/label-vs-description.md +271 -271
  50. package/docs/markdown-exporter.md +406 -406
  51. package/docs/mongodb-exporter.md +302 -302
  52. package/docs/multi-language.md +26 -26
  53. package/docs/multi-type-support.md +322 -322
  54. package/docs/mysql-exporter.md +280 -280
  55. package/docs/number-operators.md +449 -449
  56. package/docs/optional-marker-guide.md +326 -326
  57. package/docs/performance-guide.md +49 -49
  58. package/docs/plugin-system.md +381 -381
  59. package/docs/plugin-type-registration.md +34 -34
  60. package/docs/postgresql-exporter.md +311 -311
  61. package/docs/public/favicon.svg +4 -4
  62. package/docs/quick-start.md +435 -435
  63. package/docs/runtime-locale-support.md +532 -532
  64. package/docs/schema-helper.md +345 -345
  65. package/docs/schema-utils-advanced-issues.md +23 -23
  66. package/docs/schema-utils-best-practices.md +20 -20
  67. package/docs/schema-utils-chaining.md +150 -150
  68. package/docs/schema-utils.md +524 -524
  69. package/docs/security-checklist.md +20 -20
  70. package/docs/string-extensions.md +488 -488
  71. package/docs/troubleshooting.md +486 -486
  72. package/docs/type-converter.md +310 -310
  73. package/docs/type-reference.md +242 -242
  74. package/docs/typescript-guide.md +584 -584
  75. package/docs/union-type-guide.md +157 -157
  76. package/docs/union-types.md +284 -284
  77. package/docs/validate-async.md +491 -491
  78. package/docs/validate-batch.md +49 -49
  79. package/docs/validate-dsl-object-support.md +578 -578
  80. package/docs/validate.md +506 -506
  81. package/docs/validation-guide.md +502 -502
  82. package/docs/validator.md +39 -39
  83. package/package.json +131 -131
  84. package/plugins/custom-format.cjs +8 -8
  85. package/plugins/custom-type-example.cjs +8 -8
  86. package/plugins/custom-validator.cjs +8 -8
  87. package/src/adapters/DslAdapter.ts +111 -111
  88. package/src/adapters/index.ts +1 -1
  89. package/src/config/constants.ts +83 -83
  90. package/src/config/index.ts +2 -2
  91. package/src/config/patterns.ts +77 -77
  92. package/src/core/CacheManager.ts +169 -159
  93. package/src/core/ConditionalBuilder.ts +382 -382
  94. package/src/core/ConditionalRuntime.ts +27 -27
  95. package/src/core/ConditionalValidator.ts +254 -254
  96. package/src/core/DslBuilder.ts +687 -677
  97. package/src/core/ErrorCodes.ts +38 -38
  98. package/src/core/ErrorFormatter.ts +271 -271
  99. package/src/core/JSONSchemaCore.ts +65 -65
  100. package/src/core/Locale.ts +187 -187
  101. package/src/core/MessageTemplate.ts +42 -42
  102. package/src/core/ObjectDslBuilder.ts +64 -64
  103. package/src/core/PluginManager.ts +326 -326
  104. package/src/core/StringExtensions.ts +140 -140
  105. package/src/core/TemplateEngine.ts +44 -44
  106. package/src/core/Validator.ts +448 -448
  107. package/src/errors/I18nError.ts +159 -159
  108. package/src/errors/ValidationError.ts +105 -105
  109. package/src/exporters/BaseExporter.ts +60 -60
  110. package/src/exporters/MarkdownExporter.ts +305 -305
  111. package/src/exporters/MongoDBExporter.ts +126 -126
  112. package/src/exporters/MySQLExporter.ts +156 -155
  113. package/src/exporters/PostgreSQLExporter.ts +222 -222
  114. package/src/exporters/index.ts +18 -18
  115. package/src/index.ts +651 -633
  116. package/src/locales/en-US.ts +160 -160
  117. package/src/locales/es-ES.ts +160 -160
  118. package/src/locales/fr-FR.ts +160 -160
  119. package/src/locales/index.ts +103 -103
  120. package/src/locales/ja-JP.ts +160 -160
  121. package/src/locales/types.ts +156 -156
  122. package/src/locales/zh-CN.ts +160 -160
  123. package/src/parser/ConstraintParser.ts +101 -101
  124. package/src/parser/DslParser.ts +470 -470
  125. package/src/parser/SchemaCompiler.ts +66 -66
  126. package/src/parser/TypeRegistry.ts +250 -250
  127. package/src/parser/index.ts +6 -6
  128. package/src/plugins/custom-format.ts +124 -126
  129. package/src/plugins/custom-type-example.ts +106 -108
  130. package/src/plugins/custom-validator.ts +138 -140
  131. package/src/types/conditional.ts +28 -28
  132. package/src/types/config.ts +59 -59
  133. package/src/types/dsl.ts +131 -131
  134. package/src/types/error.ts +60 -60
  135. package/src/types/index.ts +17 -17
  136. package/src/types/infer.ts +127 -127
  137. package/src/types/plugin.ts +58 -58
  138. package/src/types/safe-regex.d.ts +9 -9
  139. package/src/types/schema.ts +66 -66
  140. package/src/types/validate.ts +71 -71
  141. package/src/utils/SchemaHelper.ts +196 -196
  142. package/src/utils/SchemaUtils.ts +365 -346
  143. package/src/utils/TypeConverter.ts +215 -215
  144. package/src/utils/index.ts +10 -10
  145. package/src/validators/CustomKeywords.ts +477 -477
@@ -1,488 +1,488 @@
1
- # String 扩展文档
2
-
3
- > **更新时间**: 2026-05-22
4
-
5
- ---
6
-
7
- ## 📑 目录
8
-
9
- - [核心特性](#核心特性)
10
- - [可用方法](#可用方法)
11
- - [快速开始](#快速开始)
12
- - [详细示例](#详细示例)
13
- - [多语言支持](#多语言支持)
14
- - [安装与卸载](#安装与卸载)
15
- - [最佳实践](#最佳实践)
16
- - [常见问题](#常见问题)
17
-
18
- ---
19
-
20
- ## 核心特性
21
-
22
- **字符串可以直接调用链式方法**
23
-
24
- ```javascript
25
- // ✅ 字符串直接链式调用
26
- email: 'email!'.pattern(/custom/).label('邮箱')
27
-
28
- // ✅ 纯DSL仍然有效
29
- age: 'number:18-120'
30
- ```
31
-
32
- **优势**:
33
- - ✅ 更简洁自然
34
- - ✅ 减少代码量
35
- - ✅ 100%向后兼容
36
-
37
- ## 替代方案(非侵入式)
38
-
39
- 如果你介意修改 `String.prototype`,可以直接使用 `dsl()` 包裹字符串:
40
-
41
- ```javascript
42
- const { dsl } = require('schema-dsl');
43
-
44
- // 禁用 String 扩展
45
- require('schema-dsl').uninstallStringExtensions();
46
-
47
- const schema = dsl({
48
- // 使用 dsl() 包裹字符串
49
- email: dsl('email!').pattern(/custom/).label('邮箱'),
50
-
51
- // 纯DSL不受影响
52
- age: 'number:18-120'
53
- });
54
- ```
55
-
56
- ---
57
-
58
- ## 可用方法
59
-
60
- | 方法 | 说明 | 示例 |
61
- |------|------|------|
62
- | `.pattern(regex, msg?)` | 正则验证 | `'string!'.pattern(/^\w+$/)` |
63
- | `.label(text)` | 字段标签 | `'email!'.label('邮箱地址')` |
64
- | `.messages(obj)` | 自定义消息 | `'string!'.messages({...})` |
65
- | `.description(text)` | 描述 | `'url'.description('主页')` |
66
- | `.custom(fn)` | 自定义同步验证 | `'string!'.custom(value => value !== "admin")` |
67
- | `.default(value)` | 默认值 | `'string'.default('guest')` |
68
- | `.username(range?)` | 用户名验证 | `'string!'.username('5-20')` |
69
- | `.phone(country)` | 手机号验证 | `'string!'.phone('cn')` |
70
- | `.phoneNumber(country)` | 手机号验证(别名) | `'string!'.phoneNumber('cn')` |
71
- | `.idCard(country)` | 身份证验证 | `'string!'.idCard('cn')` |
72
- | `.slug()` | URL别名验证 | `'string!'.slug()` |
73
- | `.password(strength)` | 密码验证 | `'string!'.password('strong')` |
74
- | `.format(name)` | 设置格式 | `'string'.format('email')` |
75
- | `.toSchema()` | 转为Schema | `'string!'.toSchema()` |
76
- | `.creditCard(type)` | 信用卡验证 | `'string!'.creditCard('visa')` |
77
- | `.licensePlate(country)` | 车牌验证 | `'string!'.licensePlate('cn')` |
78
- | `.postalCode(country)` | 邮编验证 | `'string!'.postalCode('cn')` |
79
- | `.passport(country)` | 护照验证 | `'string!'.passport('cn')` |
80
-
81
- ---
82
-
83
- ## 快速开始
84
-
85
- ```javascript
86
- const { dsl } = require('schema-dsl');
87
-
88
- const schema = dsl({
89
- // 字符串直接链式调用
90
- email: 'email!'.label('邮箱地址'),
91
-
92
- username: 'string:3-32!'
93
- .pattern(/^[a-zA-Z0-9_]+$/)
94
- .label('用户名'),
95
-
96
- // 简单字段用纯DSL
97
- age: 'number:18-120',
98
- role: 'user|admin'
99
- });
100
- ```
101
-
102
- ---
103
-
104
- ## 详细示例
105
-
106
- ### 1. 正则验证
107
-
108
- ```javascript
109
- const schema = dsl({
110
- username: 'string:3-32!'
111
- .pattern(/^[a-zA-Z0-9_]+$/)
112
- .messages({
113
- 'pattern': '只能包含字母、数字和下划线'
114
- })
115
- .label('用户名'),
116
-
117
- phone: 'string:11!'
118
- .pattern(/^1[3-9]\d{9}$/)
119
- .messages({
120
- 'pattern': '请输入有效的手机号'
121
- })
122
- .label('手机号'),
123
-
124
- password: 'string:8-64!'
125
- .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/)
126
- .messages({
127
- 'pattern': '密码必须包含大小写字母和数字'
128
- })
129
- .label('密码')
130
- });
131
- ```
132
-
133
- **正确的错误码**:
134
- - `'required'` - 必填字段
135
- - `'min'` - 最小长度/值
136
- - `'max'` - 最大长度/值
137
- - `'pattern'` - 正则验证
138
- - `'format'` - 格式验证(email/url等)
139
- - `'enum'` - 枚举值
140
-
141
- ---
142
-
143
- ### 2. 自定义错误消息
144
-
145
- ```javascript
146
- const schema = dsl({
147
- email: 'email!'
148
- .label('邮箱地址')
149
- .messages({
150
- 'format': '请输入有效的邮箱地址',
151
- 'required': '邮箱地址不能为空'
152
- }),
153
-
154
- bio: 'string:500'
155
- .label('个人简介')
156
- .messages({
157
- 'max': '个人简介不能超过{{#limit}}个字符'
158
- }),
159
-
160
- age: 'number:18-120'
161
- .messages({
162
- 'min': '年龄不能小于{{#limit}}',
163
- 'max': '年龄不能大于{{#limit}}'
164
- })
165
- });
166
- ```
167
-
168
- **消息模板变量**:
169
- - `{{#label}}` - 字段标签
170
- - `{{#limit}}` - 约束值(min/max)
171
- - `{{#value}}` - 当前值
172
- - `{{#pattern}}` - 正则表达式
173
-
174
- ---
175
-
176
- ### 3. 自定义验证器
177
-
178
- ```javascript
179
- const schema = dsl({
180
- // 最优雅:只在失败时返回错误消息
181
- username: 'string:3-32!'
182
- .custom((value) => {
183
- if (value === 'admin') return '用户名已被占用';
184
- // 成功时无需返回
185
- })
186
- .label('用户名'),
187
-
188
- // 支持同步验证
189
- password: 'string:8-64!'
190
- .custom((value) => {
191
- if (!/[A-Z]/.test(value)) return '必须包含大写字母';
192
- if (!/[a-z]/.test(value)) return '必须包含小写字母';
193
- if (!/\d/.test(value)) return '必须包含数字';
194
- })
195
- .label('密码')
196
- });
197
- ```
198
-
199
- ⚠️ `.custom()` 当前仅支持同步函数;需要异步查库或远程调用时,请在 `validate()` / `validateAsync()` 通过后于业务层单独执行。
200
-
201
- **支持的返回值**:
202
- - 不返回/`undefined` → 验证通过 ✅
203
- - 返回字符串 → 验证失败(错误消息)
204
- - 返回 `{ error, message }` → 自定义错误码
205
- - 抛出异常 → 验证失败
206
- - 返回 `true` → 验证通过
207
- - 返回 `false` → 验证失败(默认消息)
208
-
209
- **注意**:
210
- - 当前版本**不支持**在 `.custom()` 中直接返回 `Promise`;即使调用 `validateAsync()`,异步 custom validator 仍会报 `同步验证不支持异步操作`。
211
- - 需要异步校验时,请改为:① 先用 `schema-dsl` 做同步结构校验;② 再在业务层执行异步检查。
212
-
213
-
214
- ---
215
-
216
- ### 5. 默认验证器
217
-
218
- ```javascript
219
- const schema = dsl({
220
- // 用户名验证(自动正则+长度)
221
- username: 'string!'.username('5-20'), // 5-20个字符
222
-
223
- // 手机号验证
224
- phone: 'string!'.phone('cn'), // 中国手机号
225
-
226
- // 密码强度
227
- password: 'string!'.password('strong'), // 强密码
228
-
229
- // 身份证验证
230
- idCard: 'string!'.idCard('cn'),
231
-
232
- // URL别名验证
233
- slug: 'string!'.slug()
234
- });
235
- ```
236
-
237
- **username 预设**:
238
- - `'short'` - 2-16
239
- - `'medium'` - 3-32(默认)
240
- - `'long'` - 5-64
241
- - `'3-32'` - 自定义范围
242
-
243
- **phone 支持的国家**:
244
- - `'cn'` - 中国(11位)
245
- - `'us'` - 美国
246
- - `'uk'` - 英国
247
-
248
- **password 强度**:
249
- - `'weak'` - 6-64
250
- - `'medium'` - 8-64(默认)
251
- - `'strong'` - 8-64(大小写+数字)
252
-
253
- ---
254
-
255
- ### 6. 完整表单示例
256
-
257
- ```javascript
258
- const { dsl, Validator } = require('schema-dsl');
259
-
260
- const formSchema = dsl({
261
- email: 'email!'
262
- .label('邮箱地址')
263
- .description('用于登录和接收通知')
264
- .messages({
265
- 'format': '请输入有效的邮箱地址'
266
- }),
267
-
268
- username: 'string:3-32!'
269
- .pattern(/^[a-zA-Z0-9_]+$/)
270
- .messages({
271
- 'pattern': '只能包含字母、数字和下划线',
272
- 'min': '用户名至少3个字符',
273
- 'max': '用户名最多32个字符'
274
- })
275
- .label('用户名'),
276
-
277
- password: 'string:8-64!'
278
- .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).+$/)
279
- .messages({
280
- 'pattern': '密码必须包含大小写字母、数字和特殊字符'
281
- })
282
- .label('密码'),
283
-
284
- // 简单字段
285
- age: 'number:18-120',
286
- gender: 'male|female|other'
287
- });
288
-
289
- // 验证
290
- const { validate } = require('schema-dsl');
291
- const result = validate(formSchema, {
292
- email: 'user@example.com',
293
- username: 'john_doe',
294
- password: 'Password123!',
295
- age: 25,
296
- gender: 'male'
297
- });
298
-
299
- console.log(result.valid); // true
300
- ```
301
-
302
- ---
303
-
304
- ## 多语言支持
305
-
306
- ### 方案1: 全局多语言配置(推荐)
307
-
308
- ```javascript
309
- const { Locale } = require('schema-dsl');
310
-
311
- // 设置语言
312
- Locale.setLocale('zh-CN');
313
-
314
- // 添加自定义语言包
315
- Locale.addLocale('zh-CN', {
316
- 'required': '{{#label}}不能为空',
317
- 'min': '{{#label}}至少{{#limit}}个字符',
318
- 'max': '{{#label}}最多{{#limit}}个字符',
319
- 'pattern': '{{#label}}格式不正确',
320
- 'format': '请输入有效的{{#label}}'
321
- });
322
-
323
- // Schema中使用label
324
- const schema = dsl({
325
- email: 'email!'
326
- .label('邮箱地址'), // 错误消息会自动使用"邮箱地址"
327
-
328
- username: 'string:3-32!'
329
- .label('用户名')
330
- });
331
-
332
- // 切换语言
333
- Locale.setLocale('en-US'); // 自动切换为英文消息
334
- ```
335
-
336
- ### 方案2: 字段级多语言
337
-
338
- ```javascript
339
- const schema = dsl({
340
- email: 'email!'
341
- .label('邮箱地址')
342
- .messages({
343
- 'format': '请输入有效的邮箱地址',
344
- 'required': '邮箱地址不能为空'
345
- })
346
- });
347
- ```
348
-
349
- ### 方案3: 运行时动态切换
350
-
351
- ```javascript
352
- const { Locale } = require('schema-dsl');
353
-
354
- // 根据用户语言偏好切换
355
- function getSchema(locale) {
356
- Locale.setLocale(locale);
357
-
358
- return dsl({
359
- email: 'email!'.label(
360
- locale === 'zh-CN' ? '邮箱地址' : 'Email Address'
361
- )
362
- });
363
- }
364
-
365
- const zhSchema = getSchema('zh-CN');
366
- const enSchema = getSchema('en-US');
367
- ```
368
-
369
- **推荐方案**: 方案1(全局配置) + 方案2(特殊字段覆盖)
370
-
371
- ---
372
-
373
- ## 安装与卸载
374
-
375
- ### 自动安装
376
-
377
- String扩展在导入时自动安装:
378
-
379
- ```javascript
380
- const { dsl } = require('schema-dsl');
381
- // String扩展已自动安装
382
- ```
383
-
384
- ### 手动禁用
385
-
386
- ```javascript
387
- const { uninstallStringExtensions } = require('schema-dsl');
388
-
389
- uninstallStringExtensions();
390
-
391
- // 之后只能用纯DSL
392
- 'email!'.pattern(/custom/) // ❌ 报错
393
- ```
394
-
395
- ### 重新启用
396
-
397
- ```javascript
398
- const { installStringExtensions } = require('schema-dsl');
399
-
400
- installStringExtensions();
401
-
402
- // String扩展恢复
403
- 'email!'.pattern(/custom/) // ✅ 正常
404
- ```
405
-
406
- ---
407
-
408
- ## 最佳实践
409
-
410
- ### 1. 简单字段用纯DSL
411
-
412
- ```javascript
413
- const schema = dsl({
414
- name: 'string:1-50!',
415
- age: 'number:18-120',
416
- role: 'user|admin'
417
- });
418
- ```
419
-
420
- ### 2. 复杂字段用链式调用
421
-
422
- ```javascript
423
- const schema = dsl({
424
- email: 'email!'
425
- .pattern(/custom/)
426
- .messages({...})
427
- .label('邮箱'),
428
-
429
- username: 'string:3-32!'
430
- .pattern(/^\w+$/)
431
- .custom(checkExists)
432
- });
433
- ```
434
-
435
- ### 3. 遵循 80/20 法则
436
-
437
- **80%字段用纯DSL,20%字段用String扩展**
438
-
439
- ---
440
-
441
- ## 常见问题
442
-
443
- ### Q1: String扩展会污染全局吗?
444
-
445
- **A**: 会扩展 `String.prototype`,但冲突概率极低。提供 `uninstallStringExtensions()` 可以卸载。
446
-
447
- ### Q2: 性能如何?
448
-
449
- **A**: 性能开销极小(<5%),测试显示反而更快(少了函数调用)。
450
-
451
- ### Q3: TypeScript 支持吗?
452
-
453
- **A**: 完全支持,通过类型定义文件。
454
-
455
- ### Q4: 正确的错误码是什么?
456
-
457
- **A**:
458
- - `'required'` - 必填
459
- - `'min'` / `'max'` - 长度/值范围
460
- - `'pattern'` - 正则
461
- - `'format'` - 格式(email/url)
462
- - `'enum'` - 枚举
463
-
464
- ### Q5: 如何支持多语言?
465
-
466
- **A**: 使用 `Locale` 全局配置(推荐)或字段级 `.messages()` 覆盖。
467
-
468
- ---
469
-
470
- ## 相关文档
471
-
472
- - [DSL 语法](./dsl-syntax.md)
473
- - [API 参考](./api-reference.md)
474
- - [多语言支持](./multi-language.md)
475
- - [示例代码](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/string-extensions.ts)
476
-
477
- ---
478
-
479
- ## 对应示例文件
480
-
481
- **示例入口**: [string-extensions.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/string-extensions.ts)
482
- **说明**: 覆盖 String.prototype 扩展的安装/卸载、链式 `.label()` / `.messages()` / `.pattern()` 调用,以及校验成功/失败路径。
483
-
484
- ---
485
-
486
- **最后更新**: 2026-05-08
487
-
488
-
1
+ # String 扩展文档
2
+
3
+ > **更新时间**: 2026-05-22
4
+
5
+ ---
6
+
7
+ ## 📑 目录
8
+
9
+ - [核心特性](#核心特性)
10
+ - [可用方法](#可用方法)
11
+ - [快速开始](#快速开始)
12
+ - [详细示例](#详细示例)
13
+ - [多语言支持](#多语言支持)
14
+ - [安装与卸载](#安装与卸载)
15
+ - [最佳实践](#最佳实践)
16
+ - [常见问题](#常见问题)
17
+
18
+ ---
19
+
20
+ ## 核心特性
21
+
22
+ **字符串可以直接调用链式方法**
23
+
24
+ ```javascript
25
+ // ✅ 字符串直接链式调用
26
+ email: 'email!'.pattern(/custom/).label('邮箱')
27
+
28
+ // ✅ 纯DSL仍然有效
29
+ age: 'number:18-120'
30
+ ```
31
+
32
+ **优势**:
33
+ - ✅ 更简洁自然
34
+ - ✅ 减少代码量
35
+ - ✅ 100%向后兼容
36
+
37
+ ## 替代方案(非侵入式)
38
+
39
+ 如果你介意修改 `String.prototype`,可以直接使用 `dsl()` 包裹字符串:
40
+
41
+ ```javascript
42
+ const { dsl } = require('schema-dsl');
43
+
44
+ // 禁用 String 扩展
45
+ require('schema-dsl').uninstallStringExtensions();
46
+
47
+ const schema = dsl({
48
+ // 使用 dsl() 包裹字符串
49
+ email: dsl('email!').pattern(/custom/).label('邮箱'),
50
+
51
+ // 纯DSL不受影响
52
+ age: 'number:18-120'
53
+ });
54
+ ```
55
+
56
+ ---
57
+
58
+ ## 可用方法
59
+
60
+ | 方法 | 说明 | 示例 |
61
+ |------|------|------|
62
+ | `.pattern(regex, msg?)` | 正则验证 | `'string!'.pattern(/^\w+$/)` |
63
+ | `.label(text)` | 字段标签 | `'email!'.label('邮箱地址')` |
64
+ | `.messages(obj)` | 自定义消息 | `'string!'.messages({...})` |
65
+ | `.description(text)` | 描述 | `'url'.description('主页')` |
66
+ | `.custom(fn)` | 自定义同步验证 | `'string!'.custom(value => value !== "admin")` |
67
+ | `.default(value)` | 默认值 | `'string'.default('guest')` |
68
+ | `.username(range?)` | 用户名验证 | `'string!'.username('5-20')` |
69
+ | `.phone(country)` | 手机号验证 | `'string!'.phone('cn')` |
70
+ | `.phoneNumber(country)` | 手机号验证(别名) | `'string!'.phoneNumber('cn')` |
71
+ | `.idCard(country)` | 身份证验证 | `'string!'.idCard('cn')` |
72
+ | `.slug()` | URL别名验证 | `'string!'.slug()` |
73
+ | `.password(strength)` | 密码验证 | `'string!'.password('strong')` |
74
+ | `.format(name)` | 设置格式 | `'string'.format('email')` |
75
+ | `.toSchema()` | 转为Schema | `'string!'.toSchema()` |
76
+ | `.creditCard(type)` | 信用卡验证 | `'string!'.creditCard('visa')` |
77
+ | `.licensePlate(country)` | 车牌验证 | `'string!'.licensePlate('cn')` |
78
+ | `.postalCode(country)` | 邮编验证 | `'string!'.postalCode('cn')` |
79
+ | `.passport(country)` | 护照验证 | `'string!'.passport('cn')` |
80
+
81
+ ---
82
+
83
+ ## 快速开始
84
+
85
+ ```javascript
86
+ const { dsl } = require('schema-dsl');
87
+
88
+ const schema = dsl({
89
+ // 字符串直接链式调用
90
+ email: 'email!'.label('邮箱地址'),
91
+
92
+ username: 'string:3-32!'
93
+ .pattern(/^[a-zA-Z0-9_]+$/)
94
+ .label('用户名'),
95
+
96
+ // 简单字段用纯DSL
97
+ age: 'number:18-120',
98
+ role: 'user|admin'
99
+ });
100
+ ```
101
+
102
+ ---
103
+
104
+ ## 详细示例
105
+
106
+ ### 1. 正则验证
107
+
108
+ ```javascript
109
+ const schema = dsl({
110
+ username: 'string:3-32!'
111
+ .pattern(/^[a-zA-Z0-9_]+$/)
112
+ .messages({
113
+ 'pattern': '只能包含字母、数字和下划线'
114
+ })
115
+ .label('用户名'),
116
+
117
+ phone: 'string:11!'
118
+ .pattern(/^1[3-9]\d{9}$/)
119
+ .messages({
120
+ 'pattern': '请输入有效的手机号'
121
+ })
122
+ .label('手机号'),
123
+
124
+ password: 'string:8-64!'
125
+ .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/)
126
+ .messages({
127
+ 'pattern': '密码必须包含大小写字母和数字'
128
+ })
129
+ .label('密码')
130
+ });
131
+ ```
132
+
133
+ **正确的错误码**:
134
+ - `'required'` - 必填字段
135
+ - `'min'` - 最小长度/值
136
+ - `'max'` - 最大长度/值
137
+ - `'pattern'` - 正则验证
138
+ - `'format'` - 格式验证(email/url等)
139
+ - `'enum'` - 枚举值
140
+
141
+ ---
142
+
143
+ ### 2. 自定义错误消息
144
+
145
+ ```javascript
146
+ const schema = dsl({
147
+ email: 'email!'
148
+ .label('邮箱地址')
149
+ .messages({
150
+ 'format': '请输入有效的邮箱地址',
151
+ 'required': '邮箱地址不能为空'
152
+ }),
153
+
154
+ bio: 'string:500'
155
+ .label('个人简介')
156
+ .messages({
157
+ 'max': '个人简介不能超过{{#limit}}个字符'
158
+ }),
159
+
160
+ age: 'number:18-120'
161
+ .messages({
162
+ 'min': '年龄不能小于{{#limit}}',
163
+ 'max': '年龄不能大于{{#limit}}'
164
+ })
165
+ });
166
+ ```
167
+
168
+ **消息模板变量**:
169
+ - `{{#label}}` - 字段标签
170
+ - `{{#limit}}` - 约束值(min/max)
171
+ - `{{#value}}` - 当前值
172
+ - `{{#pattern}}` - 正则表达式
173
+
174
+ ---
175
+
176
+ ### 3. 自定义验证器
177
+
178
+ ```javascript
179
+ const schema = dsl({
180
+ // 最优雅:只在失败时返回错误消息
181
+ username: 'string:3-32!'
182
+ .custom((value) => {
183
+ if (value === 'admin') return '用户名已被占用';
184
+ // 成功时无需返回
185
+ })
186
+ .label('用户名'),
187
+
188
+ // 支持同步验证
189
+ password: 'string:8-64!'
190
+ .custom((value) => {
191
+ if (!/[A-Z]/.test(value)) return '必须包含大写字母';
192
+ if (!/[a-z]/.test(value)) return '必须包含小写字母';
193
+ if (!/\d/.test(value)) return '必须包含数字';
194
+ })
195
+ .label('密码')
196
+ });
197
+ ```
198
+
199
+ ⚠️ `.custom()` 当前仅支持同步函数;需要异步查库或远程调用时,请在 `validate()` / `validateAsync()` 通过后于业务层单独执行。
200
+
201
+ **支持的返回值**:
202
+ - 不返回/`undefined` → 验证通过 ✅
203
+ - 返回字符串 → 验证失败(错误消息)
204
+ - 返回 `{ error, message }` → 自定义错误码
205
+ - 抛出异常 → 验证失败
206
+ - 返回 `true` → 验证通过
207
+ - 返回 `false` → 验证失败(默认消息)
208
+
209
+ **注意**:
210
+ - 当前版本**不支持**在 `.custom()` 中直接返回 `Promise`;即使调用 `validateAsync()`,异步 custom validator 仍会报 `同步验证不支持异步操作`。
211
+ - 需要异步校验时,请改为:① 先用 `schema-dsl` 做同步结构校验;② 再在业务层执行异步检查。
212
+
213
+
214
+ ---
215
+
216
+ ### 5. 默认验证器
217
+
218
+ ```javascript
219
+ const schema = dsl({
220
+ // 用户名验证(自动正则+长度)
221
+ username: 'string!'.username('5-20'), // 5-20个字符
222
+
223
+ // 手机号验证
224
+ phone: 'string!'.phone('cn'), // 中国手机号
225
+
226
+ // 密码强度
227
+ password: 'string!'.password('strong'), // 强密码
228
+
229
+ // 身份证验证
230
+ idCard: 'string!'.idCard('cn'),
231
+
232
+ // URL别名验证
233
+ slug: 'string!'.slug()
234
+ });
235
+ ```
236
+
237
+ **username 预设**:
238
+ - `'short'` - 2-16
239
+ - `'medium'` - 3-32(默认)
240
+ - `'long'` - 5-64
241
+ - `'3-32'` - 自定义范围
242
+
243
+ **phone 支持的国家**:
244
+ - `'cn'` - 中国(11位)
245
+ - `'us'` - 美国
246
+ - `'uk'` - 英国
247
+
248
+ **password 强度**:
249
+ - `'weak'` - 6-64
250
+ - `'medium'` - 8-64(默认)
251
+ - `'strong'` - 8-64(大小写+数字)
252
+
253
+ ---
254
+
255
+ ### 6. 完整表单示例
256
+
257
+ ```javascript
258
+ const { dsl, Validator } = require('schema-dsl');
259
+
260
+ const formSchema = dsl({
261
+ email: 'email!'
262
+ .label('邮箱地址')
263
+ .description('用于登录和接收通知')
264
+ .messages({
265
+ 'format': '请输入有效的邮箱地址'
266
+ }),
267
+
268
+ username: 'string:3-32!'
269
+ .pattern(/^[a-zA-Z0-9_]+$/)
270
+ .messages({
271
+ 'pattern': '只能包含字母、数字和下划线',
272
+ 'min': '用户名至少3个字符',
273
+ 'max': '用户名最多32个字符'
274
+ })
275
+ .label('用户名'),
276
+
277
+ password: 'string:8-64!'
278
+ .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).+$/)
279
+ .messages({
280
+ 'pattern': '密码必须包含大小写字母、数字和特殊字符'
281
+ })
282
+ .label('密码'),
283
+
284
+ // 简单字段
285
+ age: 'number:18-120',
286
+ gender: 'male|female|other'
287
+ });
288
+
289
+ // 验证
290
+ const { validate } = require('schema-dsl');
291
+ const result = validate(formSchema, {
292
+ email: 'user@example.com',
293
+ username: 'john_doe',
294
+ password: 'Password123!',
295
+ age: 25,
296
+ gender: 'male'
297
+ });
298
+
299
+ console.log(result.valid); // true
300
+ ```
301
+
302
+ ---
303
+
304
+ ## 多语言支持
305
+
306
+ ### 方案1: 全局多语言配置(推荐)
307
+
308
+ ```javascript
309
+ const { Locale } = require('schema-dsl');
310
+
311
+ // 设置语言
312
+ Locale.setLocale('zh-CN');
313
+
314
+ // 添加自定义语言包
315
+ Locale.addLocale('zh-CN', {
316
+ 'required': '{{#label}}不能为空',
317
+ 'min': '{{#label}}至少{{#limit}}个字符',
318
+ 'max': '{{#label}}最多{{#limit}}个字符',
319
+ 'pattern': '{{#label}}格式不正确',
320
+ 'format': '请输入有效的{{#label}}'
321
+ });
322
+
323
+ // Schema中使用label
324
+ const schema = dsl({
325
+ email: 'email!'
326
+ .label('邮箱地址'), // 错误消息会自动使用"邮箱地址"
327
+
328
+ username: 'string:3-32!'
329
+ .label('用户名')
330
+ });
331
+
332
+ // 切换语言
333
+ Locale.setLocale('en-US'); // 自动切换为英文消息
334
+ ```
335
+
336
+ ### 方案2: 字段级多语言
337
+
338
+ ```javascript
339
+ const schema = dsl({
340
+ email: 'email!'
341
+ .label('邮箱地址')
342
+ .messages({
343
+ 'format': '请输入有效的邮箱地址',
344
+ 'required': '邮箱地址不能为空'
345
+ })
346
+ });
347
+ ```
348
+
349
+ ### 方案3: 运行时动态切换
350
+
351
+ ```javascript
352
+ const { Locale } = require('schema-dsl');
353
+
354
+ // 根据用户语言偏好切换
355
+ function getSchema(locale) {
356
+ Locale.setLocale(locale);
357
+
358
+ return dsl({
359
+ email: 'email!'.label(
360
+ locale === 'zh-CN' ? '邮箱地址' : 'Email Address'
361
+ )
362
+ });
363
+ }
364
+
365
+ const zhSchema = getSchema('zh-CN');
366
+ const enSchema = getSchema('en-US');
367
+ ```
368
+
369
+ **推荐方案**: 方案1(全局配置) + 方案2(特殊字段覆盖)
370
+
371
+ ---
372
+
373
+ ## 安装与卸载
374
+
375
+ ### 自动安装
376
+
377
+ String扩展在导入时自动安装:
378
+
379
+ ```javascript
380
+ const { dsl } = require('schema-dsl');
381
+ // String扩展已自动安装
382
+ ```
383
+
384
+ ### 手动禁用
385
+
386
+ ```javascript
387
+ const { uninstallStringExtensions } = require('schema-dsl');
388
+
389
+ uninstallStringExtensions();
390
+
391
+ // 之后只能用纯DSL
392
+ 'email!'.pattern(/custom/) // ❌ 报错
393
+ ```
394
+
395
+ ### 重新启用
396
+
397
+ ```javascript
398
+ const { installStringExtensions } = require('schema-dsl');
399
+
400
+ installStringExtensions();
401
+
402
+ // String扩展恢复
403
+ 'email!'.pattern(/custom/) // ✅ 正常
404
+ ```
405
+
406
+ ---
407
+
408
+ ## 最佳实践
409
+
410
+ ### 1. 简单字段用纯DSL
411
+
412
+ ```javascript
413
+ const schema = dsl({
414
+ name: 'string:1-50!',
415
+ age: 'number:18-120',
416
+ role: 'user|admin'
417
+ });
418
+ ```
419
+
420
+ ### 2. 复杂字段用链式调用
421
+
422
+ ```javascript
423
+ const schema = dsl({
424
+ email: 'email!'
425
+ .pattern(/custom/)
426
+ .messages({...})
427
+ .label('邮箱'),
428
+
429
+ username: 'string:3-32!'
430
+ .pattern(/^\w+$/)
431
+ .custom(checkExists)
432
+ });
433
+ ```
434
+
435
+ ### 3. 遵循 80/20 法则
436
+
437
+ **80%字段用纯DSL,20%字段用String扩展**
438
+
439
+ ---
440
+
441
+ ## 常见问题
442
+
443
+ ### Q1: String扩展会污染全局吗?
444
+
445
+ **A**: 会扩展 `String.prototype`,但冲突概率极低。提供 `uninstallStringExtensions()` 可以卸载。
446
+
447
+ ### Q2: 性能如何?
448
+
449
+ **A**: 性能开销极小(<5%),测试显示反而更快(少了函数调用)。
450
+
451
+ ### Q3: TypeScript 支持吗?
452
+
453
+ **A**: 完全支持,通过类型定义文件。
454
+
455
+ ### Q4: 正确的错误码是什么?
456
+
457
+ **A**:
458
+ - `'required'` - 必填
459
+ - `'min'` / `'max'` - 长度/值范围
460
+ - `'pattern'` - 正则
461
+ - `'format'` - 格式(email/url)
462
+ - `'enum'` - 枚举
463
+
464
+ ### Q5: 如何支持多语言?
465
+
466
+ **A**: 使用 `Locale` 全局配置(推荐)或字段级 `.messages()` 覆盖。
467
+
468
+ ---
469
+
470
+ ## 相关文档
471
+
472
+ - [DSL 语法](./dsl-syntax.md)
473
+ - [API 参考](./api-reference.md)
474
+ - [多语言支持](./multi-language.md)
475
+ - [示例代码](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/string-extensions.ts)
476
+
477
+ ---
478
+
479
+ ## 对应示例文件
480
+
481
+ **示例入口**: [string-extensions.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/string-extensions.ts)
482
+ **说明**: 覆盖 String.prototype 扩展的安装/卸载、链式 `.label()` / `.messages()` / `.pattern()` 调用,以及校验成功/失败路径。
483
+
484
+ ---
485
+
486
+ **最后更新**: 2026-05-08
487
+
488
+