schema-dsl 1.0.8 → 1.1.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 (49) hide show
  1. package/CHANGELOG.md +338 -3
  2. package/README.md +296 -17
  3. package/STATUS.md +74 -3
  4. package/docs/FEATURE-INDEX.md +1 -1
  5. package/docs/add-custom-locale.md +395 -0
  6. package/docs/best-practices.md +3 -3
  7. package/docs/cache-manager.md +1 -1
  8. package/docs/conditional-api.md +1032 -0
  9. package/docs/dsl-syntax.md +1 -1
  10. package/docs/dynamic-locale.md +76 -30
  11. package/docs/error-handling.md +2 -2
  12. package/docs/export-guide.md +2 -2
  13. package/docs/export-limitations.md +3 -3
  14. package/docs/faq.md +6 -6
  15. package/docs/frontend-i18n-guide.md +19 -16
  16. package/docs/i18n-user-guide.md +7 -9
  17. package/docs/i18n.md +65 -2
  18. package/docs/mongodb-exporter.md +3 -3
  19. package/docs/multi-type-support.md +12 -2
  20. package/docs/mysql-exporter.md +1 -1
  21. package/docs/plugin-system.md +4 -4
  22. package/docs/postgresql-exporter.md +1 -1
  23. package/docs/quick-start.md +4 -4
  24. package/docs/troubleshooting.md +2 -2
  25. package/docs/type-reference.md +5 -5
  26. package/docs/typescript-guide.md +5 -6
  27. package/docs/union-type-guide.md +147 -0
  28. package/docs/union-types.md +277 -0
  29. package/docs/validate-async.md +1 -1
  30. package/examples/array-dsl-example.js +1 -1
  31. package/examples/conditional-example.js +288 -0
  32. package/examples/conditional-non-object.js +129 -0
  33. package/examples/conditional-validate-example.js +321 -0
  34. package/examples/union-type-example.js +127 -0
  35. package/examples/union-types-example.js +77 -0
  36. package/index.d.ts +395 -12
  37. package/index.js +31 -4
  38. package/lib/adapters/DslAdapter.js +14 -5
  39. package/lib/core/ConditionalBuilder.js +401 -0
  40. package/lib/core/DslBuilder.js +113 -0
  41. package/lib/core/ErrorFormatter.js +81 -33
  42. package/lib/core/Locale.js +13 -8
  43. package/lib/core/Validator.js +252 -16
  44. package/lib/locales/en-US.js +14 -0
  45. package/lib/locales/es-ES.js +4 -0
  46. package/lib/locales/fr-FR.js +4 -0
  47. package/lib/locales/ja-JP.js +9 -0
  48. package/lib/locales/zh-CN.js +14 -0
  49. package/package.json +5 -2
@@ -479,12 +479,11 @@ const schema = dsl({
479
479
  const schema = dsl({
480
480
  userType: dsl('string!').label('用户类型'),
481
481
 
482
- companyName: dsl('string')
483
- .when('userType', {
484
- is: 'company',
485
- then: dsl('string!').label('公司名称'), // 企业用户必填
486
- otherwise: dsl('string').label('公司名称') // 个人用户可选
487
- })
482
+ // 使用 dsl.match() 根据 userType 字段动态验证
483
+ companyName: dsl.match('userType', {
484
+ 'company': 'string!', // 企业用户必填
485
+ '_default': 'string' // 个人用户可选
486
+ })
488
487
  });
489
488
  ```
490
489
 
@@ -0,0 +1,147 @@
1
+ # 一个字段支持多种类型
2
+
3
+ > 使用 `.pattern()` 方法匹配多种格式
4
+
5
+ ---
6
+
7
+ ## 基本用法
8
+
9
+ ```javascript
10
+ const { dsl, validate } = require('schema-dsl');
11
+
12
+ // 邮箱 或 手机号
13
+ const schema = dsl({
14
+ contact: dsl('string!')
15
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/)
16
+ .messages({ pattern: '必须是邮箱或手机号' })
17
+ });
18
+
19
+ validate(schema, { contact: 'test@example.com' }); // ✅
20
+ validate(schema, { contact: '13800138000' }); // ✅
21
+ validate(schema, { contact: 'invalid' }); // ❌
22
+ ```
23
+
24
+ **说明**:
25
+ - 正则中使用 `|` 表示"或",括号 `()` 分组
26
+ - 使用 `.messages()` 设置错误消息,支持多语言
27
+
28
+ ---
29
+
30
+ ## 常用示例
31
+
32
+ ### 用户登录(用户名或邮箱)
33
+
34
+ ```javascript
35
+ const loginSchema = dsl({
36
+ username: dsl('string:3-32!')
37
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|[a-zA-Z0-9_]+)$/)
38
+ .messages({ pattern: '必须是邮箱或用户名' }),
39
+ password: 'string:8-32!'
40
+ });
41
+ ```
42
+
43
+ ### 联系方式(邮箱或手机号)
44
+
45
+ ```javascript
46
+ const schema = dsl({
47
+ contact: dsl('string!')
48
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/)
49
+ .messages({ pattern: '必须是邮箱或手机号' })
50
+ });
51
+ ```
52
+
53
+ ### URL(http 或 https)
54
+
55
+ ```javascript
56
+ const schema = dsl({
57
+ website: dsl('string!')
58
+ .pattern(/^https?:\/\/.+$/)
59
+ .messages({ pattern: '必须是 http 或 https 开头的 URL' })
60
+ });
61
+ ```
62
+
63
+ ---
64
+
65
+ ## 支持多语言
66
+
67
+ ```javascript
68
+ // 使用多语言 key
69
+ const schema = dsl({
70
+ contact: dsl('string!')
71
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/)
72
+ .messages({ pattern: 'pattern.emailOrPhone' }) // 多语言 key
73
+ });
74
+
75
+ // 验证时指定语言
76
+ validate(schema, { contact: 'invalid' }, { locale: 'zh-CN' }); // 中文:必须是邮箱或手机号
77
+ validate(schema, { contact: 'invalid' }, { locale: 'en-US' }); // 英文:Must be an email or phone number
78
+ ```
79
+
80
+ **内置多语言 key**:
81
+ - `pattern.emailOrPhone` - 邮箱或手机号
82
+ - `pattern.usernameOrEmail` - 用户名或邮箱
83
+ - `pattern.httpOrHttps` - http 或 https URL
84
+
85
+ ---
86
+
87
+ ## 正则表达式速查
88
+
89
+ ```javascript
90
+ // 邮箱 或 手机号
91
+ /^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/
92
+
93
+ // http 或 https
94
+ /^https?:\/\/.+$/
95
+
96
+ // 用户名 或 邮箱
97
+ /^([^\s@]+@[^\s@]+\.[^\s@]+|[a-zA-Z0-9_]{3,32})$/
98
+
99
+ // 数字ID 或 UUID
100
+ /^(\d+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i
101
+
102
+ // 多个邮箱域名
103
+ /^[^\s@]+@(gmail\.com|qq\.com|163\.com)$/
104
+
105
+ // 中国或美国手机号
106
+ /^(1[3-9]\d{9}|\+1\d{10})$/
107
+ ```
108
+
109
+ ---
110
+
111
+
112
+ ## 完整示例
113
+
114
+ ```javascript
115
+ const { dsl, validate } = require('schema-dsl');
116
+
117
+ const registerSchema = dsl({
118
+ name: 'string:1-50!',
119
+ contact: dsl('string!')
120
+ .pattern(/^([^\s@]+@[^\s@]+\.[^\s@]+|1[3-9]\d{9})$/)
121
+ .messages({ pattern: '必须是邮箱或手机号' })
122
+ });
123
+
124
+ const testData = [
125
+ { name: '张三', contact: 'zhangsan@example.com' },
126
+ { name: '李四', contact: '13800138000' },
127
+ { name: '王五', contact: 'invalid' }
128
+ ];
129
+
130
+ testData.forEach((data, index) => {
131
+ const result = validate(registerSchema, data);
132
+ console.log(`测试${index + 1}:`, result.valid ? '✅' : '❌');
133
+ if (!result.valid) {
134
+ console.log(' 错误:', result.errors[0].message);
135
+ }
136
+ });
137
+ ```
138
+
139
+ **输出**:
140
+ ```
141
+ 测试1: ✅
142
+ 测试2: ✅
143
+ 测试3: ❌
144
+ 错误: 必须是邮箱或手机号
145
+ ```
146
+
147
+
@@ -0,0 +1,277 @@
1
+ # 跨类型联合验证 - types: 语法
2
+
3
+ > **版本**: v1.1.0+
4
+ > **状态**: ✅ 稳定
5
+
6
+ ---
7
+
8
+ ## 概述
9
+
10
+ `types:` 语法允许您定义跨类型联合验证,支持字段匹配多种不同的数据类型。
11
+
12
+ ### 特性
13
+
14
+ ✅ **简洁语法** - `'types:string|number'` 一行搞定
15
+ ✅ **带约束** - `'types:string:3-10|number:0-100'`
16
+ ✅ **插件扩展** - 支持自定义类型注册
17
+ ✅ **多语言** - 完整的i18n支持
18
+
19
+ ---
20
+
21
+ ## 快速开始
22
+
23
+ ### 基础用法
24
+
25
+ ```javascript
26
+ const { dsl, validate } = require('schema-dsl');
27
+
28
+ // 定义联合类型
29
+ const schema = dsl({
30
+ value: 'types:string|number'
31
+ });
32
+
33
+ // 验证
34
+ validate(schema, { value: 'hello' }); // ✅ 通过
35
+ validate(schema, { value: 123 }); // ✅ 通过
36
+ validate(schema, { value: true }); // ❌ 失败
37
+ ```
38
+
39
+ ### 带约束
40
+
41
+ ```javascript
42
+ const schema = dsl({
43
+ value: 'types:string:3-10|number:0-100!'
44
+ });
45
+
46
+ validate(schema, { value: 'abc' }); // ✅ 通过
47
+ validate(schema, { value: 50 }); // ✅ 通过
48
+ validate(schema, { value: 'ab' }); // ❌ 太短
49
+ validate(schema, { value: 101 }); // ❌ 超范围
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 语法说明
55
+
56
+ ### 基本格式
57
+
58
+ ```
59
+ types:type1|type2|type3[!]
60
+ ```
61
+
62
+ - `types:` - 固定前缀
63
+ - `type1|type2` - 类型列表,用 `|` 分隔
64
+ - `!` - 可选的必填标记
65
+
66
+ ### 带约束格式
67
+
68
+ ```
69
+ types:type1:constraint1|type2:constraint2
70
+ ```
71
+
72
+ ---
73
+
74
+ ## 支持的类型
75
+
76
+ ### 内置类型
77
+
78
+ 所有内置类型都可以在 `types:` 中使用:
79
+
80
+ - **基本类型**: `string`, `number`, `integer`, `boolean`, `null`, `any`
81
+ - **格式类型**: `email`, `url`, `uuid`, `date`, `datetime`, `time`
82
+ - **特殊类型**: `phone`, `idCard`, `objectId`, `hexColor` 等
83
+
84
+ ### 插件自定义类型
85
+
86
+ 通过插件注册的自定义类型也可以使用:
87
+
88
+ ```javascript
89
+ const { DslBuilder, PluginManager } = require('schema-dsl');
90
+
91
+ // 注册自定义类型
92
+ DslBuilder.registerType('order-id', {
93
+ type: 'string',
94
+ pattern: /^ORD[0-9]{12}$/.source,
95
+ minLength: 15,
96
+ maxLength: 15
97
+ });
98
+
99
+ // 在types:中使用
100
+ const schema = dsl({
101
+ identifier: 'types:uuid|order-id'
102
+ });
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 实际应用场景
108
+
109
+ ### 场景1:用户注册(邮箱或手机号)
110
+
111
+ ```javascript
112
+ const registerSchema = dsl({
113
+ username: 'string:3-20!',
114
+ password: 'string:8-20!',
115
+ contact: 'types:email|phone!' // 邮箱或手机号
116
+ });
117
+ ```
118
+
119
+ ### 场景2:灵活的价格输入
120
+
121
+ ```javascript
122
+ const productSchema = dsl({
123
+ price: 'types:number:0-|string:1-20' // 数字价格或"面议"
124
+ });
125
+
126
+ validate(productSchema, { price: 99.99 }); // ✅ 数字
127
+ validate(productSchema, { price: '面议' }); // ✅ 字符串
128
+ ```
129
+
130
+ ### 场景3:订单查询(订单号或SKU)
131
+
132
+ ```javascript
133
+ // 先注册自定义类型
134
+ DslBuilder.registerType('order-id', { ... });
135
+ DslBuilder.registerType('sku', { ... });
136
+
137
+ const querySchema = dsl({
138
+ identifier: 'types:order-id|sku!'
139
+ });
140
+ ```
141
+
142
+ ---
143
+
144
+ ## 插件开发指南
145
+
146
+ ### 注册自定义类型
147
+
148
+ ```javascript
149
+ // 在插件的install方法中
150
+ install(schemaDsl, options, context) {
151
+ const { DslBuilder } = schemaDsl;
152
+
153
+ // 注册DSL类型
154
+ DslBuilder.registerType('custom-type', {
155
+ type: 'string',
156
+ pattern: /^CUSTOM-\d+$/.source,
157
+ minLength: 8,
158
+ maxLength: 20
159
+ });
160
+
161
+ // 同时注册ajv format(可选)
162
+ const validator = schemaDsl.getDefaultValidator();
163
+ const ajv = validator.getAjv();
164
+ ajv.addFormat('custom-type', {
165
+ validate: /^CUSTOM-\d+$/
166
+ });
167
+ }
168
+ ```
169
+
170
+ ### DslBuilder API
171
+
172
+ #### `DslBuilder.registerType(name, schema)`
173
+
174
+ 注册自定义类型。
175
+
176
+ **参数**:
177
+ - `name` (string) - 类型名称
178
+ - `schema` (Object|Function) - JSON Schema对象或生成函数
179
+
180
+ #### `DslBuilder.hasType(type)`
181
+
182
+ 检查类型是否已注册。
183
+
184
+ #### `DslBuilder.getCustomTypes()`
185
+
186
+ 获取所有已注册的自定义类型。
187
+
188
+ #### `DslBuilder.clearCustomTypes()`
189
+
190
+ 清除所有自定义类型(主要用于测试)。
191
+
192
+ ---
193
+
194
+ ## 多语言支持
195
+
196
+ ### 中文
197
+
198
+ ```javascript
199
+ validate(schema, { value: true }, { locale: 'zh-CN' });
200
+ // 错误: "必须匹配以下类型之一"
201
+ ```
202
+
203
+ ### 英文
204
+
205
+ ```javascript
206
+ validate(schema, { value: true }, { locale: 'en-US' });
207
+ // Error: "Must match one of the following types"
208
+ ```
209
+
210
+ 支持的语言:`zh-CN`, `en-US`, `es-ES`, `fr-FR`, `ja-JP`
211
+
212
+ ---
213
+
214
+ ## 最佳实践
215
+
216
+ ### 1. 优先使用内置类型
217
+
218
+ ```javascript
219
+ // ✅ 推荐
220
+ 'types:email|phone'
221
+
222
+ // ⚠️ 不推荐(性能较差)
223
+ 'types:string:custom-email-pattern|string:custom-phone-pattern'
224
+ ```
225
+
226
+ ### 2. 合理使用约束
227
+
228
+ ```javascript
229
+ // ✅ 明确约束
230
+ 'types:string:3-32|number:0-100'
231
+
232
+ // ❌ 过于宽松
233
+ 'types:string|number' // 没有约束
234
+ ```
235
+
236
+ ### 3. 插件类型命名规范
237
+
238
+ ```javascript
239
+ // ✅ 使用kebab-case
240
+ DslBuilder.registerType('order-id', { ... });
241
+ DslBuilder.registerType('phone-cn', { ... });
242
+
243
+ // ❌ 不推荐
244
+ DslBuilder.registerType('OrderID', { ... });
245
+ DslBuilder.registerType('phone_cn', { ... });
246
+ ```
247
+
248
+ ---
249
+
250
+ ## 注意事项
251
+
252
+ ### oneOf语义
253
+
254
+ `types:` 语法内部使用JSON Schema的 `oneOf`,表示"恰好匹配其中一种类型"。
255
+
256
+ ### 性能考虑
257
+
258
+ 联合类型会依次验证每个类型,直到匹配成功。类型越多,性能开销越大。
259
+
260
+ **建议**:
261
+ - 联合类型数量控制在5个以内
262
+ - 将最常用的类型放在前面
263
+
264
+ ---
265
+
266
+ ## 相关文档
267
+
268
+ - [插件系统](./plugin-system.md)
269
+ - [自定义类型注册](./plugin-type-registration.md)
270
+ - [代码规范](../specs/rules/代码规范.md)
271
+
272
+ ---
273
+
274
+ ## 版本历史
275
+
276
+ - **v1.1.0** - 首次发布跨类型联合验证功能
277
+
@@ -476,5 +476,5 @@ new ValidationError(errors, data)
476
476
 
477
477
  **版本**: v1.0.4
478
478
  **更新日期**: 2025-12-29
479
- **作者**: SchemaIO Team
479
+ **作者**: SchemaI-DSL Team
480
480
 
@@ -104,7 +104,7 @@ const withAge = dsl({
104
104
  // ✨ 新特性:merge方法
105
105
  const extendedSchema = SchemaUtils.extend(baseUser, withAge);
106
106
 
107
- console.log('合并后字段数:', Object.keys(mergedSchema.properties).length);
107
+ console.log('合并后字段数:', Object.keys(extendedSchema.properties).length);
108
108
  console.log('');
109
109
 
110
110
  // ========== 7. Schema pick/omit ==========