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.
- package/.eslintignore +10 -0
- package/.eslintrc.json +27 -0
- package/.github/CODE_OF_CONDUCT.md +45 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +57 -0
- package/.github/ISSUE_TEMPLATE/config.yml +11 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +45 -0
- package/.github/ISSUE_TEMPLATE/question.md +31 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +70 -0
- package/.github/SECURITY.md +184 -0
- package/.github/workflows/ci.yml +35 -0
- package/CHANGELOG.md +633 -0
- package/CONTRIBUTING.md +368 -0
- package/LICENSE +21 -0
- package/README.md +1122 -0
- package/STATUS.md +273 -0
- package/docs/FEATURE-INDEX.md +521 -0
- package/docs/INDEX.md +224 -0
- package/docs/api-reference.md +1098 -0
- package/docs/best-practices.md +672 -0
- package/docs/cache-manager.md +336 -0
- package/docs/design-philosophy.md +602 -0
- package/docs/dsl-syntax.md +654 -0
- package/docs/dynamic-locale.md +552 -0
- package/docs/error-handling.md +703 -0
- package/docs/export-guide.md +459 -0
- package/docs/faq.md +576 -0
- package/docs/frontend-i18n-guide.md +290 -0
- package/docs/i18n-user-guide.md +488 -0
- package/docs/label-vs-description.md +262 -0
- package/docs/markdown-exporter.md +398 -0
- package/docs/mongodb-exporter.md +279 -0
- package/docs/multi-type-support.md +319 -0
- package/docs/mysql-exporter.md +257 -0
- package/docs/plugin-system.md +542 -0
- package/docs/postgresql-exporter.md +290 -0
- package/docs/quick-start.md +761 -0
- package/docs/schema-helper.md +340 -0
- package/docs/schema-utils.md +492 -0
- package/docs/string-extensions.md +480 -0
- package/docs/troubleshooting.md +471 -0
- package/docs/type-converter.md +319 -0
- package/docs/type-reference.md +219 -0
- package/docs/validate.md +486 -0
- package/docs/validation-guide.md +484 -0
- package/examples/array-dsl-example.js +227 -0
- package/examples/custom-extension.js +85 -0
- package/examples/dsl-match-example.js +74 -0
- package/examples/dsl-style.js +118 -0
- package/examples/dynamic-locale-configuration.js +348 -0
- package/examples/dynamic-locale-example.js +287 -0
- package/examples/export-demo.js +130 -0
- package/examples/i18n-full-demo.js +310 -0
- package/examples/i18n-memory-safety.examples.js +268 -0
- package/examples/markdown-export.js +71 -0
- package/examples/middleware-usage.js +93 -0
- package/examples/password-reset/README.md +153 -0
- package/examples/password-reset/schema.js +26 -0
- package/examples/password-reset/test.js +101 -0
- package/examples/plugin-system.examples.js +205 -0
- package/examples/simple-example.js +122 -0
- package/examples/string-extensions.js +297 -0
- package/examples/user-registration/README.md +156 -0
- package/examples/user-registration/routes.js +92 -0
- package/examples/user-registration/schema.js +150 -0
- package/examples/user-registration/server.js +74 -0
- package/index.d.ts +1999 -0
- package/index.js +270 -0
- package/index.mjs +30 -0
- package/lib/adapters/DslAdapter.js +653 -0
- package/lib/adapters/index.js +20 -0
- package/lib/config/constants.js +286 -0
- package/lib/config/patterns/creditCard.js +9 -0
- package/lib/config/patterns/idCard.js +9 -0
- package/lib/config/patterns/index.js +8 -0
- package/lib/config/patterns/licensePlate.js +4 -0
- package/lib/config/patterns/passport.js +4 -0
- package/lib/config/patterns/phone.js +9 -0
- package/lib/config/patterns/postalCode.js +5 -0
- package/lib/core/CacheManager.js +376 -0
- package/lib/core/DslBuilder.js +740 -0
- package/lib/core/ErrorCodes.js +233 -0
- package/lib/core/ErrorFormatter.js +342 -0
- package/lib/core/JSONSchemaCore.js +347 -0
- package/lib/core/Locale.js +119 -0
- package/lib/core/MessageTemplate.js +89 -0
- package/lib/core/PluginManager.js +448 -0
- package/lib/core/StringExtensions.js +209 -0
- package/lib/core/Validator.js +316 -0
- package/lib/exporters/MarkdownExporter.js +420 -0
- package/lib/exporters/MongoDBExporter.js +162 -0
- package/lib/exporters/MySQLExporter.js +212 -0
- package/lib/exporters/PostgreSQLExporter.js +289 -0
- package/lib/exporters/index.js +24 -0
- package/lib/locales/en-US.js +65 -0
- package/lib/locales/es-ES.js +66 -0
- package/lib/locales/fr-FR.js +66 -0
- package/lib/locales/index.js +8 -0
- package/lib/locales/ja-JP.js +66 -0
- package/lib/locales/zh-CN.js +93 -0
- package/lib/utils/LRUCache.js +174 -0
- package/lib/utils/SchemaHelper.js +240 -0
- package/lib/utils/SchemaUtils.js +313 -0
- package/lib/utils/TypeConverter.js +245 -0
- package/lib/utils/index.js +13 -0
- package/lib/validators/CustomKeywords.js +203 -0
- package/lib/validators/index.js +11 -0
- package/package.json +70 -0
- package/plugins/custom-format.js +101 -0
- package/plugins/custom-validator.js +200 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 中间件使用示例
|
|
3
|
+
*
|
|
4
|
+
* 演示如何在 Express/Koa 中使用中间件动态配置 Validator
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const express = require('express');
|
|
8
|
+
const { dsl, Validator, Locale } = require('../index');
|
|
9
|
+
|
|
10
|
+
// 确保加载所有语言包
|
|
11
|
+
require('../lib/locales/index');
|
|
12
|
+
|
|
13
|
+
// 扩展语言包以支持 Label 翻译 (可选)
|
|
14
|
+
Locale.addLocale('zh-CN', {
|
|
15
|
+
'label.username': '用户名',
|
|
16
|
+
'label.email': '邮箱',
|
|
17
|
+
'label.age': '年龄'
|
|
18
|
+
});
|
|
19
|
+
Locale.addLocale('en-US', {
|
|
20
|
+
'label.username': 'Username',
|
|
21
|
+
'label.email': 'Email',
|
|
22
|
+
'label.age': 'Age'
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const app = express();
|
|
26
|
+
const validator = new Validator();
|
|
27
|
+
|
|
28
|
+
// ========== 中间件定义 ==========
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* SchemaIO 验证中间件
|
|
32
|
+
*
|
|
33
|
+
* 1. 从请求头获取语言
|
|
34
|
+
* 2. 创建绑定了语言的 validate 方法
|
|
35
|
+
* 3. 挂载到 req 对象上
|
|
36
|
+
*/
|
|
37
|
+
const schemaIoMiddleware = (req, res, next) => {
|
|
38
|
+
// 获取语言 (支持 accept-language 头或 query 参数)
|
|
39
|
+
const lang = req.query.lang || req.headers['accept-language'] || 'en-US';
|
|
40
|
+
|
|
41
|
+
// 简单的语言匹配逻辑 (例如取前两个字符或完整匹配)
|
|
42
|
+
// 这里假设完整匹配,实际项目中可能需要更复杂的解析
|
|
43
|
+
const locale = lang.includes('zh') ? 'zh-CN' :
|
|
44
|
+
lang.includes('ja') ? 'ja-JP' :
|
|
45
|
+
lang.includes('es') ? 'es-ES' :
|
|
46
|
+
lang.includes('fr') ? 'fr-FR' : 'en-US';
|
|
47
|
+
|
|
48
|
+
// 挂载 validate 方法
|
|
49
|
+
req.validate = (schema, data) => {
|
|
50
|
+
return validator.validate(schema, data, { locale });
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
next();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
app.use(schemaIoMiddleware);
|
|
57
|
+
app.use(express.json());
|
|
58
|
+
|
|
59
|
+
// ========== 路由定义 ==========
|
|
60
|
+
|
|
61
|
+
const userSchema = dsl({
|
|
62
|
+
username: 'string:3-32!'.username(), // 自动查找 label.username
|
|
63
|
+
email: 'email!', // 自动查找 label.email
|
|
64
|
+
age: 'integer:0-150' // 自动查找 label.age
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
app.post('/users', (req, res) => {
|
|
68
|
+
const result = req.validate(userSchema, req.body);
|
|
69
|
+
|
|
70
|
+
if (!result.valid) {
|
|
71
|
+
return res.status(400).json({
|
|
72
|
+
error: 'Validation Failed',
|
|
73
|
+
details: result.errors.map(e => e.message)
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
res.json({ message: 'User created', data: result.data });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ========== 启动服务器 (仅用于演示) ==========
|
|
81
|
+
|
|
82
|
+
if (require.main === module) {
|
|
83
|
+
const port = 3000;
|
|
84
|
+
app.listen(port, () => {
|
|
85
|
+
console.log(`Server running at http://localhost:${port}`);
|
|
86
|
+
console.log('Try sending POST /users with different Accept-Language headers');
|
|
87
|
+
console.log('Example: curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -H "Accept-Language: zh-CN" -d "{}"');
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = app;
|
|
92
|
+
|
|
93
|
+
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# 密码重置示例
|
|
2
|
+
|
|
3
|
+
完整的密码重置验证示例,展示 ref() 功能的实际应用。
|
|
4
|
+
|
|
5
|
+
## 功能展示
|
|
6
|
+
|
|
7
|
+
- ✅ 字段引用(ref)
|
|
8
|
+
- ✅ 密码强度验证
|
|
9
|
+
- ✅ 密码确认验证
|
|
10
|
+
- ✅ 错误消息定制
|
|
11
|
+
- ✅ 多语言支持
|
|
12
|
+
|
|
13
|
+
## 文件结构
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
examples/password-reset/
|
|
17
|
+
├── README.md # 本文件
|
|
18
|
+
├── schema.js # Schema定义
|
|
19
|
+
└── test.js # 测试示例
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Schema定义
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
const { dsl } = require('schema-dsl');
|
|
26
|
+
const Locale = require('schema-dsl/lib/core/Locale');
|
|
27
|
+
|
|
28
|
+
// 设置中文
|
|
29
|
+
Locale.setLocale('zh-CN');
|
|
30
|
+
|
|
31
|
+
const passwordResetSchema = dsl({
|
|
32
|
+
// 新密码:8-64字符,必须包含大小写字母和数字
|
|
33
|
+
newPassword: 'string:8-64!'
|
|
34
|
+
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/)
|
|
35
|
+
.label('新密码')
|
|
36
|
+
.messages({
|
|
37
|
+
'minLength': '{{#label}}长度不能少于8位',
|
|
38
|
+
'maxLength': '{{#label}}长度不能超过64位',
|
|
39
|
+
'pattern': '{{#label}}必须包含大小写字母和数字'
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
// 确认密码:必填
|
|
43
|
+
confirmPassword: 'string:8-64!'
|
|
44
|
+
.label('确认密码')
|
|
45
|
+
.custom((value, helpers, { parent }) => {
|
|
46
|
+
if (value !== parent.newPassword) {
|
|
47
|
+
return '两次输入的密码不一致';
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
module.exports = passwordResetSchema;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 使用示例
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
const passwordResetSchema = require('./schema');
|
|
59
|
+
|
|
60
|
+
// 成功案例
|
|
61
|
+
const validData = {
|
|
62
|
+
newPassword: 'Password123',
|
|
63
|
+
confirmPassword: 'Password123'
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const result1 = await passwordResetSchema.validate(validData, {
|
|
67
|
+
root: validData
|
|
68
|
+
});
|
|
69
|
+
console.log(result1.isValid); // true
|
|
70
|
+
|
|
71
|
+
// 失败案例1:密码不一致
|
|
72
|
+
const invalidData1 = {
|
|
73
|
+
newPassword: 'Password123',
|
|
74
|
+
confirmPassword: 'Different123'
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const result2 = await passwordResetSchema.validate(invalidData1, {
|
|
78
|
+
root: invalidData1
|
|
79
|
+
});
|
|
80
|
+
console.log(result2.errors[0].message);
|
|
81
|
+
// "两次输入的密码不一致"
|
|
82
|
+
|
|
83
|
+
// 失败案例2:密码强度不够
|
|
84
|
+
const invalidData2 = {
|
|
85
|
+
newPassword: 'weak',
|
|
86
|
+
confirmPassword: 'weak'
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const result3 = await passwordResetSchema.validate(invalidData2, {
|
|
90
|
+
root: invalidData2
|
|
91
|
+
});
|
|
92
|
+
console.log(result3.errors);
|
|
93
|
+
// [
|
|
94
|
+
// { message: "新密码长度不能少于8位", type: "string.min" },
|
|
95
|
+
// { message: "新密码必须包含大小写字母和数字", type: "string.pattern" }
|
|
96
|
+
// ]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Express路由示例
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
const express = require('express');
|
|
103
|
+
const passwordResetSchema = require('./schema');
|
|
104
|
+
|
|
105
|
+
const router = express.Router();
|
|
106
|
+
|
|
107
|
+
router.post('/reset-password', async (req, res) => {
|
|
108
|
+
const result = await passwordResetSchema.validate(req.body, {
|
|
109
|
+
root: req.body,
|
|
110
|
+
abortEarly: false
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!result.isValid) {
|
|
114
|
+
return res.status(400).json({
|
|
115
|
+
success: false,
|
|
116
|
+
errors: result.errors.map(err => ({
|
|
117
|
+
field: err.path?.join('.') || err.context?.key,
|
|
118
|
+
message: err.message
|
|
119
|
+
}))
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 更新密码逻辑...
|
|
124
|
+
res.json({ success: true, message: '密码重置成功' });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
module.exports = router;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 测试
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# 安装依赖
|
|
134
|
+
npm install schema-dsl
|
|
135
|
+
|
|
136
|
+
# 运行测试
|
|
137
|
+
node examples/password-reset/test.js
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## 核心要点
|
|
141
|
+
|
|
142
|
+
1. **ref()功能**: 使用 `ref('newPassword')` 引用新密码字段
|
|
143
|
+
2. **密码强度**: 使用正则表达式验证密码强度
|
|
144
|
+
3. **错误消息**: 自定义友好的中文错误提示
|
|
145
|
+
4. **上下文传递**: 验证时传递 `{ root: data }` 使ref可以解析
|
|
146
|
+
|
|
147
|
+
## 扩展建议
|
|
148
|
+
|
|
149
|
+
1. 添加旧密码验证
|
|
150
|
+
2. 添加密码历史检查(不能与最近3次密码相同)
|
|
151
|
+
3. 添加密码强度指示器
|
|
152
|
+
4. 集成验证码验证
|
|
153
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 密码重置Schema定义
|
|
3
|
+
* 使用DSL语法定义
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { dsl } = require('../../index');
|
|
7
|
+
const Locale = require('../../lib/core/Locale');
|
|
8
|
+
|
|
9
|
+
// 设置中文
|
|
10
|
+
Locale.setLocale('zh-CN');
|
|
11
|
+
|
|
12
|
+
// 使用DSL定义密码重置Schema
|
|
13
|
+
const passwordResetSchema = dsl({
|
|
14
|
+
// 新密码:8-64字符
|
|
15
|
+
newPassword: 'string:8-64!',
|
|
16
|
+
|
|
17
|
+
// 确认密码:必填
|
|
18
|
+
confirmPassword: 'string:8-64!'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// 注意:ref功能需要在validate时手动检查
|
|
22
|
+
// 因为DSL目前不直接支持ref,这是一个简化示例
|
|
23
|
+
|
|
24
|
+
module.exports = passwordResetSchema;
|
|
25
|
+
|
|
26
|
+
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 密码重置测试示例
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const passwordResetSchema = require('./schema');
|
|
6
|
+
|
|
7
|
+
async function runTests() {
|
|
8
|
+
console.log('========================================');
|
|
9
|
+
console.log(' 密码重置验证测试');
|
|
10
|
+
console.log('========================================\n');
|
|
11
|
+
|
|
12
|
+
// 测试1:成功案例
|
|
13
|
+
console.log('【测试1】成功案例');
|
|
14
|
+
const validData = {
|
|
15
|
+
newPassword: 'Password123',
|
|
16
|
+
confirmPassword: 'Password123'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const result1 = await passwordResetSchema.validate(validData, {
|
|
20
|
+
root: validData
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
console.log('数据:', validData);
|
|
24
|
+
console.log('结果:', result1.isValid ? '✅ 验证通过' : '❌ 验证失败');
|
|
25
|
+
if (!result1.isValid) {
|
|
26
|
+
console.log('错误:', result1.errors);
|
|
27
|
+
}
|
|
28
|
+
console.log('');
|
|
29
|
+
|
|
30
|
+
// 测试2:密码不一致
|
|
31
|
+
console.log('【测试2】密码不一致');
|
|
32
|
+
const invalidData1 = {
|
|
33
|
+
newPassword: 'Password123',
|
|
34
|
+
confirmPassword: 'Different123'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const result2 = await passwordResetSchema.validate(invalidData1, {
|
|
38
|
+
root: invalidData1,
|
|
39
|
+
abortEarly: false
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
console.log('数据:', invalidData1);
|
|
43
|
+
console.log('结果:', result2.isValid ? '✅ 验证通过' : '❌ 验证失败');
|
|
44
|
+
if (!result2.isValid) {
|
|
45
|
+
result2.errors.forEach(err => {
|
|
46
|
+
console.log(` - ${err.message}`);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
console.log('');
|
|
50
|
+
|
|
51
|
+
// 测试3:密码强度不够
|
|
52
|
+
console.log('【测试3】密码强度不够');
|
|
53
|
+
const invalidData2 = {
|
|
54
|
+
newPassword: 'weak',
|
|
55
|
+
confirmPassword: 'weak'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const result3 = await passwordResetSchema.validate(invalidData2, {
|
|
59
|
+
root: invalidData2,
|
|
60
|
+
abortEarly: false
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
console.log('数据:', invalidData2);
|
|
64
|
+
console.log('结果:', result3.isValid ? '✅ 验证通过' : '❌ 验证失败');
|
|
65
|
+
if (!result3.isValid) {
|
|
66
|
+
result3.errors.forEach(err => {
|
|
67
|
+
console.log(` - ${err.message}`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
console.log('');
|
|
71
|
+
|
|
72
|
+
// 测试4:密码太长
|
|
73
|
+
console.log('【测试4】密码太长');
|
|
74
|
+
const invalidData3 = {
|
|
75
|
+
newPassword: 'A'.repeat(65) + 'a1',
|
|
76
|
+
confirmPassword: 'A'.repeat(65) + 'a1'
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const result4 = await passwordResetSchema.validate(invalidData3, {
|
|
80
|
+
root: invalidData3,
|
|
81
|
+
abortEarly: false
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
console.log('数据: { newPassword: "' + invalidData3.newPassword.substring(0, 20) + '...", ... }');
|
|
85
|
+
console.log('结果:', result4.isValid ? '✅ 验证通过' : '❌ 验证失败');
|
|
86
|
+
if (!result4.isValid) {
|
|
87
|
+
result4.errors.forEach(err => {
|
|
88
|
+
console.log(` - ${err.message}`);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
console.log('');
|
|
92
|
+
|
|
93
|
+
console.log('========================================');
|
|
94
|
+
console.log(' 测试完成');
|
|
95
|
+
console.log('========================================');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 运行测试
|
|
99
|
+
runTests().catch(console.error);
|
|
100
|
+
|
|
101
|
+
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件系统使用示例
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { dsl, validate, PluginManager } = require('../index');
|
|
6
|
+
|
|
7
|
+
// ========== 1. 基础使用 ==========
|
|
8
|
+
|
|
9
|
+
console.log('=== 1. 基础使用 ===\n');
|
|
10
|
+
|
|
11
|
+
// 创建插件管理器
|
|
12
|
+
const pluginManager = new PluginManager();
|
|
13
|
+
|
|
14
|
+
// 注册插件
|
|
15
|
+
const customValidatorPlugin = require('../plugins/custom-validator');
|
|
16
|
+
pluginManager.register(customValidatorPlugin);
|
|
17
|
+
|
|
18
|
+
const customFormatPlugin = require('../plugins/custom-format');
|
|
19
|
+
pluginManager.register(customFormatPlugin);
|
|
20
|
+
|
|
21
|
+
// 安装插件
|
|
22
|
+
const schemaDsl = require('../index');
|
|
23
|
+
pluginManager.install(schemaDsl);
|
|
24
|
+
|
|
25
|
+
console.log('已安装插件:', pluginManager.list());
|
|
26
|
+
console.log('');
|
|
27
|
+
|
|
28
|
+
// ========== 2. 使用自定义格式 ==========
|
|
29
|
+
|
|
30
|
+
console.log('=== 2. 使用自定义格式 ===\n');
|
|
31
|
+
|
|
32
|
+
const contactSchema = dsl({
|
|
33
|
+
phone: 'string!',
|
|
34
|
+
email: 'email!',
|
|
35
|
+
wechat: 'string'
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 测试数据
|
|
39
|
+
const validContact = {
|
|
40
|
+
phone: '13800138000',
|
|
41
|
+
email: 'test@example.com',
|
|
42
|
+
wechat: 'my_wechat_id'
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const result1 = validate(contactSchema, validContact);
|
|
46
|
+
console.log('验证结果:', result1.valid ? '✅ 通过' : '❌ 失败');
|
|
47
|
+
if (!result1.valid) {
|
|
48
|
+
console.log('错误:', result1.errors);
|
|
49
|
+
}
|
|
50
|
+
console.log('');
|
|
51
|
+
|
|
52
|
+
// ========== 3. 使用钩子系统 ==========
|
|
53
|
+
|
|
54
|
+
console.log('=== 3. 使用钩子系统 ===\n');
|
|
55
|
+
|
|
56
|
+
// 注册钩子
|
|
57
|
+
pluginManager.hook('onBeforeValidate', (schema, data) => {
|
|
58
|
+
console.log('[Hook] 验证前:', { schema: '...', data });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
pluginManager.hook('onAfterValidate', (result) => {
|
|
62
|
+
console.log('[Hook] 验证后:', { valid: result.valid });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// 运行钩子
|
|
66
|
+
const testSchema = dsl({ name: 'string!' });
|
|
67
|
+
const testData = { name: 'John' };
|
|
68
|
+
|
|
69
|
+
pluginManager.runHook('onBeforeValidate', testSchema, testData);
|
|
70
|
+
const result2 = validate(testSchema, testData);
|
|
71
|
+
pluginManager.runHook('onAfterValidate', result2);
|
|
72
|
+
console.log('');
|
|
73
|
+
|
|
74
|
+
// ========== 4. 自定义插件 ==========
|
|
75
|
+
|
|
76
|
+
console.log('=== 4. 自定义插件 ===\n');
|
|
77
|
+
|
|
78
|
+
// 创建自定义插件
|
|
79
|
+
const myPlugin = {
|
|
80
|
+
name: 'my-plugin',
|
|
81
|
+
version: '1.0.0',
|
|
82
|
+
description: '我的自定义插件',
|
|
83
|
+
|
|
84
|
+
install(schemaDsl, options, context) {
|
|
85
|
+
console.log('[Plugin] my-plugin 安装中...');
|
|
86
|
+
console.log(' 选项:', options);
|
|
87
|
+
console.log(' 已注册插件数:', context.plugins.size);
|
|
88
|
+
|
|
89
|
+
// 添加自定义方法
|
|
90
|
+
schemaDsl.myCustomMethod = () => {
|
|
91
|
+
console.log(' 这是自定义方法!');
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
uninstall(schemaDsl, context) {
|
|
96
|
+
console.log('[Plugin] my-plugin 卸载中...');
|
|
97
|
+
delete schemaDsl.myCustomMethod;
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
hooks: {
|
|
101
|
+
onBeforeValidate(schema, data) {
|
|
102
|
+
console.log(' [my-plugin] 验证前钩子');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// 注册并安装
|
|
108
|
+
pluginManager.register(myPlugin);
|
|
109
|
+
pluginManager.install(schemaDsl, 'my-plugin', { custom: true });
|
|
110
|
+
|
|
111
|
+
// 使用自定义方法
|
|
112
|
+
if (schemaDsl.myCustomMethod) {
|
|
113
|
+
schemaDsl.myCustomMethod();
|
|
114
|
+
}
|
|
115
|
+
console.log('');
|
|
116
|
+
|
|
117
|
+
// ========== 5. 插件管理 ==========
|
|
118
|
+
|
|
119
|
+
console.log('=== 5. 插件管理 ===\n');
|
|
120
|
+
|
|
121
|
+
// 查看所有插件
|
|
122
|
+
console.log('所有插件:', pluginManager.list());
|
|
123
|
+
|
|
124
|
+
// 检查插件是否存在
|
|
125
|
+
console.log('my-plugin 是否存在:', pluginManager.has('my-plugin'));
|
|
126
|
+
|
|
127
|
+
// 获取插件数量
|
|
128
|
+
console.log('插件数量:', pluginManager.size);
|
|
129
|
+
console.log('');
|
|
130
|
+
|
|
131
|
+
// ========== 6. 实用插件示例 ==========
|
|
132
|
+
|
|
133
|
+
console.log('=== 6. 实用插件示例 ===\n');
|
|
134
|
+
|
|
135
|
+
// 日志插件
|
|
136
|
+
const loggingPlugin = {
|
|
137
|
+
name: 'logging',
|
|
138
|
+
version: '1.0.0',
|
|
139
|
+
|
|
140
|
+
install(schemaDsl, options, context) {
|
|
141
|
+
// 包装 validate 方法
|
|
142
|
+
const originalValidate = schemaDsl.validate;
|
|
143
|
+
schemaDsl.validate = function (...args) {
|
|
144
|
+
console.log('[Logging] 开始验证');
|
|
145
|
+
const start = Date.now();
|
|
146
|
+
const result = originalValidate.apply(this, args);
|
|
147
|
+
const duration = Date.now() - start;
|
|
148
|
+
console.log(`[Logging] 验证完成,耗时 ${duration}ms,结果: ${result.valid ? '通过' : '失败'}`);
|
|
149
|
+
return result;
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
pluginManager.register(loggingPlugin);
|
|
155
|
+
pluginManager.install(schemaDsl, 'logging');
|
|
156
|
+
|
|
157
|
+
// 测试日志插件
|
|
158
|
+
const schema = dsl({ email: 'email!' });
|
|
159
|
+
validate(schema, { email: 'test@example.com' });
|
|
160
|
+
console.log('');
|
|
161
|
+
|
|
162
|
+
// ========== 7. 卸载插件 ==========
|
|
163
|
+
|
|
164
|
+
console.log('=== 7. 卸载插件 ===\n');
|
|
165
|
+
|
|
166
|
+
pluginManager.uninstall('my-plugin', schemaDsl);
|
|
167
|
+
console.log('my-plugin 已卸载');
|
|
168
|
+
console.log('当前插件数:', pluginManager.size);
|
|
169
|
+
console.log('');
|
|
170
|
+
|
|
171
|
+
// ========== 8. 批量插件 ==========
|
|
172
|
+
|
|
173
|
+
console.log('=== 8. 批量插件 ===\n');
|
|
174
|
+
|
|
175
|
+
// 清空所有插件
|
|
176
|
+
pluginManager.clear(schemaDsl);
|
|
177
|
+
console.log('所有插件已清空');
|
|
178
|
+
console.log('当前插件数:', pluginManager.size);
|
|
179
|
+
console.log('');
|
|
180
|
+
|
|
181
|
+
// 批量注册多个插件
|
|
182
|
+
const plugins = [
|
|
183
|
+
customValidatorPlugin,
|
|
184
|
+
customFormatPlugin
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
plugins.forEach(plugin => pluginManager.register(plugin));
|
|
188
|
+
pluginManager.install(schemaDsl); // 安装所有插件
|
|
189
|
+
|
|
190
|
+
console.log('批量安装完成:', pluginManager.list());
|
|
191
|
+
console.log('');
|
|
192
|
+
|
|
193
|
+
// ========== 总结 ==========
|
|
194
|
+
|
|
195
|
+
console.log('=== 总结 ===\n');
|
|
196
|
+
console.log('✅ 插件系统支持:');
|
|
197
|
+
console.log(' - 动态注册/卸载插件');
|
|
198
|
+
console.log(' - 生命周期钩子');
|
|
199
|
+
console.log(' - 自定义验证器和格式');
|
|
200
|
+
console.log(' - 插件间通信');
|
|
201
|
+
console.log(' - 事件监听');
|
|
202
|
+
console.log('');
|
|
203
|
+
console.log('示例运行完成!');
|
|
204
|
+
|
|
205
|
+
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SchemaIO v2.0.1 - 简洁示例
|
|
3
|
+
*
|
|
4
|
+
* 展示最简洁、最直观的用法
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { dsl, validate, SchemaUtils } = require('../index');
|
|
8
|
+
|
|
9
|
+
console.log('========== SchemaIO v2.0.1 简洁示例 ==========\n');
|
|
10
|
+
|
|
11
|
+
// ========== 1. 数组验证 - 超简洁 ==========
|
|
12
|
+
console.log('✨ 1. 数组验证');
|
|
13
|
+
|
|
14
|
+
const articleSchema = dsl({
|
|
15
|
+
title: 'string:5-100!',
|
|
16
|
+
tags: 'array!1-10' // ✨ 简洁:必填,1-10个元素
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
console.log('验证文章:', validate(articleSchema, {
|
|
20
|
+
title: 'Hello World',
|
|
21
|
+
tags: ['javascript', 'nodejs']
|
|
22
|
+
}).valid);
|
|
23
|
+
console.log('');
|
|
24
|
+
|
|
25
|
+
// ========== 2. when条件 - 超直观 ==========
|
|
26
|
+
console.log('✨ 2. 条件验证');
|
|
27
|
+
|
|
28
|
+
const contactSchema = dsl({
|
|
29
|
+
type: 'email|phone',
|
|
30
|
+
contact: dsl.match('type', {
|
|
31
|
+
email: 'email!',
|
|
32
|
+
phone: 'phone:cn!'
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.log('验证邮箱:', validate(contactSchema, { type: 'email', contact: 'test@example.com' }).valid);
|
|
37
|
+
console.log('验证手机:', validate(contactSchema, { type: 'phone', contact: '13800138000' }).valid);
|
|
38
|
+
console.log('');
|
|
39
|
+
|
|
40
|
+
// ========== 3. 快捷方法 - 不用找正则 ==========
|
|
41
|
+
console.log('✨ 3. 快捷方法');
|
|
42
|
+
|
|
43
|
+
const userSchema = dsl({
|
|
44
|
+
mobile: 'string!'.phoneNumber('cn'),
|
|
45
|
+
id: 'string!'.idCard('cn')
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log('验证快捷方法:', validate(userSchema, {
|
|
49
|
+
mobile: '13800138000',
|
|
50
|
+
id: '110101199003071234'
|
|
51
|
+
}).valid);
|
|
52
|
+
console.log('');
|
|
53
|
+
|
|
54
|
+
// ========== 4. Schema复用 - 不重复代码 ==========
|
|
55
|
+
console.log('✨ 4. Schema复用');
|
|
56
|
+
|
|
57
|
+
// 定义一次,到处使用
|
|
58
|
+
const emailField = SchemaUtils.reusable(() => dsl('email!'));
|
|
59
|
+
|
|
60
|
+
const loginForm = dsl({ email: emailField() });
|
|
61
|
+
const registerForm = dsl({ email: emailField(), name: 'string!' });
|
|
62
|
+
|
|
63
|
+
console.log('复用Schema成功');
|
|
64
|
+
console.log('');
|
|
65
|
+
|
|
66
|
+
// ========== 5. Schema合并 - 灵活组合 ==========
|
|
67
|
+
console.log('✨ 5. Schema合并');
|
|
68
|
+
|
|
69
|
+
const baseUser = dsl({ name: 'string!', email: 'email!' });
|
|
70
|
+
const withAge = dsl({ age: 'number:18-120' });
|
|
71
|
+
|
|
72
|
+
const fullUser = SchemaUtils.merge(baseUser, withAge);
|
|
73
|
+
|
|
74
|
+
console.log('合并后字段:', Object.keys(fullUser.properties));
|
|
75
|
+
console.log('');
|
|
76
|
+
|
|
77
|
+
// ========== 6. 批量验证 - 快50倍 ==========
|
|
78
|
+
console.log('✨ 6. 批量验证');
|
|
79
|
+
|
|
80
|
+
const users = [
|
|
81
|
+
{ email: 'user1@example.com' },
|
|
82
|
+
{ email: 'invalid' },
|
|
83
|
+
{ email: 'user3@example.com' }
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const { Validator } = require('../index');
|
|
87
|
+
const batchResult = SchemaUtils.validateBatch(
|
|
88
|
+
dsl({ email: 'email!' }),
|
|
89
|
+
users,
|
|
90
|
+
new Validator() // 批量验证需要 Validator 实例以复用编译结果
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
console.log('批量验证:', {
|
|
94
|
+
总数: batchResult.summary.total,
|
|
95
|
+
有效: batchResult.summary.valid,
|
|
96
|
+
无效: batchResult.summary.invalid,
|
|
97
|
+
耗时: `${batchResult.summary.duration}ms`
|
|
98
|
+
});
|
|
99
|
+
console.log('');
|
|
100
|
+
|
|
101
|
+
// ========== 总结 ==========
|
|
102
|
+
console.log('========== 核心理念 ==========');
|
|
103
|
+
console.log(`
|
|
104
|
+
✨ SchemaIO v2.0.1 三大特点:
|
|
105
|
+
|
|
106
|
+
1. 简洁
|
|
107
|
+
'array!1-10' // 一眼看懂
|
|
108
|
+
.phoneNumber('cn') // 不用找正则
|
|
109
|
+
|
|
110
|
+
2. 直观
|
|
111
|
+
dsl.match('type', { // 清晰的条件映射
|
|
112
|
+
email: 'email!',
|
|
113
|
+
phone: 'string:11!'
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
3. 强大
|
|
117
|
+
SchemaUtils.merge() // 灵活组合
|
|
118
|
+
validateBatch() // 快50倍
|
|
119
|
+
|
|
120
|
+
🎉 简洁 + 直观 + 强大 = 完美验证库!
|
|
121
|
+
`);
|
|
122
|
+
|