schema-dsl 1.2.5 → 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 (243) hide show
  1. package/CHANGELOG.md +130 -238
  2. package/LICENSE +21 -21
  3. package/README.md +628 -2486
  4. package/dist/DslBuilder-BIgQOAXp.d.ts +343 -0
  5. package/dist/DslBuilder-CjHTucNQ.d.cts +343 -0
  6. package/dist/Validator-CllRdrY0.d.ts +192 -0
  7. package/dist/Validator-D6okG9tr.d.cts +192 -0
  8. package/dist/index.cjs +6640 -0
  9. package/dist/index.d.cts +1151 -0
  10. package/dist/index.d.ts +1151 -0
  11. package/dist/index.js +6574 -0
  12. package/dist/plugin-CIKtTMtS.d.cts +246 -0
  13. package/dist/plugin-CIKtTMtS.d.ts +246 -0
  14. package/dist/plugins/custom-format.cjs +3818 -0
  15. package/dist/plugins/custom-format.d.cts +12 -0
  16. package/dist/plugins/custom-format.d.ts +12 -0
  17. package/dist/plugins/custom-format.js +3788 -0
  18. package/dist/plugins/custom-type-example.cjs +3811 -0
  19. package/dist/plugins/custom-type-example.d.cts +8 -0
  20. package/dist/plugins/custom-type-example.d.ts +8 -0
  21. package/dist/plugins/custom-type-example.js +3781 -0
  22. package/dist/plugins/custom-validator.cjs +144 -0
  23. package/dist/plugins/custom-validator.d.cts +10 -0
  24. package/dist/plugins/custom-validator.d.ts +10 -0
  25. package/dist/plugins/custom-validator.js +119 -0
  26. package/docs/FEATURE-INDEX.md +553 -519
  27. package/docs/add-custom-locale.md +496 -483
  28. package/docs/add-keyword.md +24 -0
  29. package/docs/api-reference.md +1047 -805
  30. package/docs/api.md +13 -0
  31. package/docs/best-practices-project-structure.md +417 -408
  32. package/docs/best-practices.md +712 -672
  33. package/docs/cache-manager.md +344 -336
  34. package/docs/compile.md +45 -0
  35. package/docs/conditional-api.md +1307 -1278
  36. package/docs/custom-extensions-guide.md +339 -411
  37. package/docs/design-philosophy.md +606 -601
  38. package/docs/doc-index.md +324 -0
  39. package/docs/dsl-syntax.md +714 -664
  40. package/docs/dynamic-locale.md +608 -598
  41. package/docs/enum.md +482 -475
  42. package/docs/error-handling.md +1975 -1966
  43. package/docs/export-guide.md +501 -462
  44. package/docs/export-limitations.md +567 -551
  45. package/docs/faq.md +596 -577
  46. package/docs/frontend-i18n-guide.md +307 -293
  47. package/docs/i18n-user-guide.md +487 -474
  48. package/docs/i18n.md +476 -457
  49. package/docs/index.md +48 -0
  50. package/docs/json-schema-basics.md +40 -0
  51. package/docs/label-vs-description.md +271 -262
  52. package/docs/markdown-exporter.md +406 -397
  53. package/docs/mongodb-exporter.md +302 -295
  54. package/docs/multi-language.md +26 -0
  55. package/docs/multi-type-support.md +322 -329
  56. package/docs/mysql-exporter.md +280 -273
  57. package/docs/number-operators.md +449 -442
  58. package/docs/optional-marker-guide.md +326 -321
  59. package/docs/performance-guide.md +49 -0
  60. package/docs/plugin-system.md +381 -542
  61. package/docs/plugin-type-registration.md +34 -0
  62. package/docs/postgresql-exporter.md +311 -304
  63. package/docs/public/favicon.svg +5 -0
  64. package/docs/quick-start.md +435 -761
  65. package/docs/runtime-locale-support.md +532 -521
  66. package/docs/schema-helper.md +345 -340
  67. package/docs/schema-utils-advanced-issues.md +23 -0
  68. package/docs/schema-utils-best-practices.md +20 -0
  69. package/docs/schema-utils-chaining.md +150 -143
  70. package/docs/schema-utils.md +524 -490
  71. package/docs/security-checklist.md +20 -0
  72. package/docs/string-extensions.md +488 -480
  73. package/docs/troubleshooting.md +486 -471
  74. package/docs/type-converter.md +310 -319
  75. package/docs/type-reference.md +242 -219
  76. package/docs/typescript-guide.md +584 -573
  77. package/docs/union-type-guide.md +157 -147
  78. package/docs/union-types.md +284 -277
  79. package/docs/validate-async.md +491 -480
  80. package/docs/validate-batch.md +49 -0
  81. package/docs/validate-dsl-object-support.md +578 -573
  82. package/docs/validate.md +506 -486
  83. package/docs/validation-guide.md +502 -484
  84. package/docs/validator.md +39 -0
  85. package/package.json +131 -73
  86. package/plugins/custom-format.cjs +8 -0
  87. package/plugins/custom-type-example.cjs +8 -0
  88. package/plugins/custom-validator.cjs +8 -0
  89. package/src/adapters/DslAdapter.ts +111 -0
  90. package/src/adapters/index.ts +1 -0
  91. package/src/config/constants.ts +83 -0
  92. package/src/config/index.ts +2 -0
  93. package/src/config/patterns.ts +77 -0
  94. package/src/core/CacheManager.ts +169 -0
  95. package/src/core/ConditionalBuilder.ts +382 -0
  96. package/src/core/ConditionalRuntime.ts +28 -0
  97. package/src/core/ConditionalValidator.ts +255 -0
  98. package/src/core/DslBuilder.ts +687 -0
  99. package/src/core/ErrorCodes.ts +38 -0
  100. package/src/core/ErrorFormatter.ts +271 -0
  101. package/src/core/JSONSchemaCore.ts +65 -0
  102. package/src/core/Locale.ts +187 -0
  103. package/src/core/MessageTemplate.ts +42 -0
  104. package/src/core/ObjectDslBuilder.ts +64 -0
  105. package/src/core/PluginManager.ts +326 -0
  106. package/src/core/StringExtensions.ts +140 -0
  107. package/src/core/TemplateEngine.ts +44 -0
  108. package/src/core/Validator.ts +448 -0
  109. package/src/errors/I18nError.ts +159 -0
  110. package/src/errors/ValidationError.ts +105 -0
  111. package/src/exporters/BaseExporter.ts +60 -0
  112. package/src/exporters/MarkdownExporter.ts +305 -0
  113. package/src/exporters/MongoDBExporter.ts +126 -0
  114. package/src/exporters/MySQLExporter.ts +156 -0
  115. package/src/exporters/PostgreSQLExporter.ts +222 -0
  116. package/src/exporters/index.ts +18 -0
  117. package/src/index.ts +651 -0
  118. package/{lib/locales/en-US.js → src/locales/en-US.ts} +160 -176
  119. package/{lib/locales/es-ES.js → src/locales/es-ES.ts} +160 -113
  120. package/{lib/locales/fr-FR.js → src/locales/fr-FR.ts} +160 -113
  121. package/src/locales/index.ts +103 -0
  122. package/{lib/locales/ja-JP.js → src/locales/ja-JP.ts} +160 -118
  123. package/src/locales/types.ts +156 -0
  124. package/{lib/locales/zh-CN.js → src/locales/zh-CN.ts} +160 -177
  125. package/src/parser/ConstraintParser.ts +101 -0
  126. package/src/parser/DslParser.ts +470 -0
  127. package/src/parser/SchemaCompiler.ts +66 -0
  128. package/src/parser/TypeRegistry.ts +250 -0
  129. package/src/parser/index.ts +6 -0
  130. package/src/plugins/custom-format.ts +124 -0
  131. package/src/plugins/custom-type-example.ts +106 -0
  132. package/src/plugins/custom-validator.ts +138 -0
  133. package/src/types/conditional.ts +28 -0
  134. package/src/types/config.ts +59 -0
  135. package/src/types/dsl.ts +131 -0
  136. package/src/types/error.ts +60 -0
  137. package/src/types/index.ts +17 -0
  138. package/src/types/infer.ts +128 -0
  139. package/src/types/plugin.ts +58 -0
  140. package/src/types/safe-regex.d.ts +9 -0
  141. package/src/types/schema.ts +66 -0
  142. package/src/types/validate.ts +71 -0
  143. package/src/utils/SchemaHelper.ts +196 -0
  144. package/src/utils/SchemaUtils.ts +365 -0
  145. package/src/utils/TypeConverter.ts +215 -0
  146. package/src/utils/index.ts +10 -0
  147. package/src/validators/CustomKeywords.ts +477 -0
  148. package/.eslintignore +0 -11
  149. package/.eslintrc.json +0 -27
  150. package/CONTRIBUTING.md +0 -368
  151. package/STATUS.md +0 -491
  152. package/changelogs/v1.0.0.md +0 -328
  153. package/changelogs/v1.0.9.md +0 -367
  154. package/changelogs/v1.1.0.md +0 -389
  155. package/changelogs/v1.1.1.md +0 -308
  156. package/changelogs/v1.1.2.md +0 -183
  157. package/changelogs/v1.1.3.md +0 -161
  158. package/changelogs/v1.1.4.md +0 -432
  159. package/changelogs/v1.1.5.md +0 -493
  160. package/changelogs/v1.1.6.md +0 -211
  161. package/changelogs/v1.1.8.md +0 -376
  162. package/changelogs/v1.2.3.md +0 -124
  163. package/docs/INDEX.md +0 -252
  164. package/docs/issues-resolved-summary.md +0 -196
  165. package/docs/performance-benchmark-report.md +0 -179
  166. package/docs/performance-quick-reference.md +0 -123
  167. package/docs/user-questions-answered.md +0 -353
  168. package/docs/validation-rules-v1.0.2.md +0 -1608
  169. package/examples/README.md +0 -81
  170. package/examples/array-dsl-example.js +0 -227
  171. package/examples/conditional-example.js +0 -288
  172. package/examples/conditional-non-object.js +0 -129
  173. package/examples/conditional-validate-example.js +0 -321
  174. package/examples/custom-extension.js +0 -85
  175. package/examples/dsl-match-example.js +0 -74
  176. package/examples/dsl-style.js +0 -118
  177. package/examples/dynamic-locale-configuration.js +0 -348
  178. package/examples/dynamic-locale-example.js +0 -287
  179. package/examples/enum.examples.js +0 -324
  180. package/examples/export-demo.js +0 -130
  181. package/examples/express-integration.js +0 -376
  182. package/examples/i18n-error-handling-complete.js +0 -381
  183. package/examples/i18n-error-handling-quickstart.md +0 -0
  184. package/examples/i18n-error.examples.js +0 -181
  185. package/examples/i18n-full-demo.js +0 -301
  186. package/examples/i18n-memory-safety.examples.js +0 -268
  187. package/examples/markdown-export.js +0 -71
  188. package/examples/middleware-usage.js +0 -93
  189. package/examples/new-features-comparison.js +0 -315
  190. package/examples/password-reset/README.md +0 -153
  191. package/examples/password-reset/schema.js +0 -26
  192. package/examples/password-reset/test.js +0 -101
  193. package/examples/plugin-system.examples.js +0 -205
  194. package/examples/schema-utils-chaining.examples.js +0 -250
  195. package/examples/simple-example.js +0 -122
  196. package/examples/slug.examples.js +0 -179
  197. package/examples/string-extensions.js +0 -297
  198. package/examples/union-type-example.js +0 -127
  199. package/examples/union-types-example.js +0 -77
  200. package/examples/user-registration/README.md +0 -156
  201. package/examples/user-registration/routes.js +0 -92
  202. package/examples/user-registration/schema.js +0 -150
  203. package/examples/user-registration/server.js +0 -74
  204. package/index.d.ts +0 -3658
  205. package/index.js +0 -475
  206. package/index.mjs +0 -60
  207. package/lib/adapters/DslAdapter.js +0 -995
  208. package/lib/adapters/index.js +0 -20
  209. package/lib/config/constants.js +0 -286
  210. package/lib/config/patterns/common.js +0 -47
  211. package/lib/config/patterns/creditCard.js +0 -9
  212. package/lib/config/patterns/idCard.js +0 -9
  213. package/lib/config/patterns/index.js +0 -9
  214. package/lib/config/patterns/licensePlate.js +0 -4
  215. package/lib/config/patterns/passport.js +0 -4
  216. package/lib/config/patterns/phone.js +0 -9
  217. package/lib/config/patterns/postalCode.js +0 -5
  218. package/lib/core/CacheManager.js +0 -376
  219. package/lib/core/ConditionalBuilder.js +0 -503
  220. package/lib/core/DslBuilder.js +0 -1589
  221. package/lib/core/ErrorCodes.js +0 -233
  222. package/lib/core/ErrorFormatter.js +0 -445
  223. package/lib/core/JSONSchemaCore.js +0 -347
  224. package/lib/core/Locale.js +0 -130
  225. package/lib/core/MessageTemplate.js +0 -98
  226. package/lib/core/PluginManager.js +0 -448
  227. package/lib/core/StringExtensions.js +0 -240
  228. package/lib/core/Validator.js +0 -654
  229. package/lib/errors/I18nError.js +0 -328
  230. package/lib/errors/ValidationError.js +0 -191
  231. package/lib/exporters/MarkdownExporter.js +0 -420
  232. package/lib/exporters/MongoDBExporter.js +0 -162
  233. package/lib/exporters/MySQLExporter.js +0 -212
  234. package/lib/exporters/PostgreSQLExporter.js +0 -289
  235. package/lib/exporters/index.js +0 -24
  236. package/lib/locales/index.js +0 -8
  237. package/lib/utils/LRUCache.js +0 -174
  238. package/lib/utils/SchemaHelper.js +0 -240
  239. package/lib/utils/SchemaUtils.js +0 -445
  240. package/lib/utils/TypeConverter.js +0 -245
  241. package/lib/utils/index.js +0 -13
  242. package/lib/validators/CustomKeywords.js +0 -616
  243. package/lib/validators/index.js +0 -11
@@ -1,93 +0,0 @@
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
-
@@ -1,315 +0,0 @@
1
- /**
2
- * SchemaIO v2.1.0 新旧 API 对比示例
3
- *
4
- * 展示新功能如何简化代码和提升开发体验
5
- */
6
-
7
- const { dsl, validate, validateAsync, ValidationError, SchemaUtils } = require('../index');
8
-
9
- console.log('========================================');
10
- console.log('SchemaIO v2.1.0 新旧 API 对比');
11
- console.log('========================================\n');
12
-
13
- // ==========================================
14
- // 场景 1:异步验证方法
15
- // ==========================================
16
-
17
- console.log('【场景 1】异步验证方法 - 自动抛出错误\n');
18
-
19
- const userSchema = dsl({
20
- name: 'string:1-50!',
21
- email: 'email!',
22
- age: 'integer:18-120'
23
- });
24
-
25
- // --- 旧方法 ---
26
- console.log('❌ 旧方法(手动检查 valid):');
27
- function validateUserOldWay(data) {
28
- const result = validate(userSchema, data);
29
-
30
- if (!result.valid) {
31
- const errors = result.errors.map(e => e.message).join(', ');
32
- throw new Error(`验证失败: ${errors}`);
33
- }
34
-
35
- return result.data;
36
- }
37
-
38
- try {
39
- validateUserOldWay({ name: '', email: 'invalid' });
40
- } catch (error) {
41
- console.log(' 错误:', error.message);
42
- }
43
-
44
- // --- 新方法 ---
45
- console.log('\n✅ 新方法(自动抛出错误):');
46
- async function validateUserNewWay(data) {
47
- return await validateAsync(userSchema, data);
48
- }
49
-
50
- (async () => {
51
- try {
52
- await validateUserNewWay({ name: '', email: 'invalid' });
53
- } catch (error) {
54
- if (error instanceof ValidationError) {
55
- console.log(' 错误:', error.message);
56
- console.log(' 详细:', error.toJSON());
57
- }
58
- }
59
- })();
60
-
61
- console.log('\n代码行数对比:');
62
- console.log(' 旧方法: 8 行代码');
63
- console.log(' 新方法: 2 行代码(减少 75%)\n');
64
-
65
- // ==========================================
66
- // 场景 2:Express 路由
67
- // ==========================================
68
-
69
- console.log('【场景 2】Express 路由 - 统一错误处理\n');
70
-
71
- // --- 旧方法 ---
72
- console.log('❌ 旧方法(每个路由都要检查):');
73
- console.log(`
74
- app.post('/users', (req, res) => {
75
- const result = validate(userSchema, req.body);
76
-
77
- if (!result.valid) {
78
- return res.status(400).json({
79
- error: 'Validation Failed',
80
- details: result.errors
81
- });
82
- }
83
-
84
- // 继续处理...
85
- });
86
- `);
87
-
88
- // --- 新方法 ---
89
- console.log('✅ 新方法(统一错误处理):');
90
- console.log(`
91
- // 全局错误处理中间件(只写一次)
92
- app.use((error, req, res, next) => {
93
- if (error instanceof ValidationError) {
94
- return res.status(400).json(error.toJSON());
95
- }
96
- next(error);
97
- });
98
-
99
- // 路由中使用(简洁)
100
- app.post('/users', async (req, res, next) => {
101
- try {
102
- const data = await validateAsync(userSchema, req.body);
103
- const user = await db.users.insert(data);
104
- res.json(user);
105
- } catch (error) {
106
- next(error);
107
- }
108
- });
109
- `);
110
-
111
- console.log('优势:');
112
- console.log(' ✓ 错误处理代码减少 60%');
113
- console.log(' ✓ 统一错误响应格式');
114
- console.log(' ✓ 代码更易维护\n');
115
-
116
- // ==========================================
117
- // 场景 3:Schema 复用 - 严格验证
118
- // ==========================================
119
-
120
- console.log('【场景 3】Schema 复用 - 处理额外字段\n');
121
-
122
- const baseUserSchema = dsl({
123
- name: 'string:1-50!',
124
- email: 'email!'
125
- });
126
-
127
- const testData = {
128
- name: 'John Doe',
129
- email: 'john@example.com',
130
- avatar: 'https://example.com/avatar.jpg' // 额外字段
131
- };
132
-
133
- // --- 旧方法 ---
134
- console.log('❌ 旧方法(无法灵活处理额外字段):');
135
- const result1 = validate(baseUserSchema, testData);
136
- console.log(' 验证结果:', result1.valid ? '通过' : '失败');
137
- console.log(' 问题: 需要手动筛选字段\n');
138
-
139
- // --- 新方法: omit ---
140
- console.log('✅ 新方法 - omit(排除敏感字段):');
141
- const publicSchema = SchemaUtils.omit(baseUserSchema, ['password']);
142
- const result2 = validate(publicSchema, testData);
143
- console.log(' 验证结果:', result2.valid ? '通过' : '失败');
144
- console.log(' 返回数据(自动移除password):', result2.data);
145
-
146
- // --- 新方法: extend ---
147
- console.log('\n✅ 新方法 - extend(扩展字段):');
148
- const extendedSchema = SchemaUtils.extend(
149
- baseUserSchema,
150
- { avatar: 'url' }
151
- );
152
- const result4 = validate(extendedSchema, testData);
153
- console.log(' 验证结果:', result4.valid ? '通过' : '失败');
154
- console.log(' avatar 被验证:', result4.data.avatar);
155
-
156
- console.log('\n提供的模式:');
157
- console.log(' 1. strict() - 严格验证,拒绝额外字段');
158
- console.log(' 2. loose() - 宽松模式,允许额外字段');
159
- console.log(' 3. clean() - 清理模式,移除额外字段');
160
- console.log(' 4. extend() - 扩展模式,验证指定字段');
161
- console.log(' 5. partial() - 部分验证,只验证指定字段\n');
162
-
163
- // ==========================================
164
- // 场景 4:CRUD 完整示例
165
- // ==========================================
166
-
167
- console.log('【场景 4】CRUD 完整示例\n');
168
-
169
- const fullUserSchema = dsl({
170
- id: 'objectId!',
171
- name: 'string:1-50!',
172
- email: 'email!',
173
- password: 'string:8-32!',
174
- age: 'integer:0-150',
175
- createdAt: 'date',
176
- updatedAt: 'date'
177
- });
178
-
179
- // --- 旧方法 ---
180
- console.log('❌ 旧方法(为每个场景重复定义 Schema):');
181
- console.log(`
182
- // POST /users
183
- const createUserSchemaOld = dsl({
184
- name: 'string:1-50!',
185
- email: 'email!',
186
- password: 'string:8-32!',
187
- age: 'integer:0-150'
188
- });
189
-
190
- // PATCH /users/:id
191
- const updateUserSchemaOld = dsl({
192
- name: 'string:1-50',
193
- age: 'integer:0-150'
194
- });
195
-
196
- // GET /users/:id
197
- const publicUserSchemaOld = dsl({
198
- id: 'objectId!',
199
- name: 'string:1-50!',
200
- email: 'email!',
201
- age: 'integer:0-150',
202
- createdAt: 'date',
203
- updatedAt: 'date'
204
- });
205
-
206
- 问题:重复代码多,修改不便
207
- `);
208
-
209
- // --- 新方法 ---
210
- console.log('✅ 新方法(复用 + 灵活处理):');
211
- console.log(`
212
- // 定义一次
213
- const fullUserSchema = dsl({ ... });
214
-
215
- // POST /users - 排除系统字段
216
- const createSchema = SchemaUtils.omit(fullUserSchema, ['id', 'createdAt', 'updatedAt']);
217
-
218
- // PATCH /users/:id - 部分更新
219
- const updateSchema = SchemaUtils.partial(fullUserSchema, ['name', 'age']);
220
-
221
- // GET /users/:id - 排除敏感字段
222
- const publicSchema = SchemaUtils.omit(fullUserSchema, ['password']);
223
-
224
- 优势:
225
- ✓ 单一数据源,修改方便
226
- ✓ 代码量减少 70%
227
- ✓ 灵活的字段处理
228
- `);
229
-
230
- // ==========================================
231
- // 场景 5:业务逻辑
232
- // ==========================================
233
-
234
- console.log('【场景 5】业务逻辑 - Service 层\n');
235
-
236
- // --- 旧方法 ---
237
- console.log('❌ 旧方法(手动检查):');
238
- console.log(`
239
- class UserService {
240
- create(data) {
241
- const result = validate(createUserSchema, data);
242
-
243
- if (!result.valid) {
244
- const errors = result.errors.map(e => e.message).join(', ');
245
- throw new Error(\`验证失败: \${errors}\`);
246
- }
247
-
248
- return db.users.insert(result.data);
249
- }
250
-
251
- update(id, data) {
252
- const result = validate(updateUserSchema, data);
253
-
254
- if (!result.valid) {
255
- const errors = result.errors.map(e => e.message).join(', ');
256
- throw new Error(\`验证失败: \${errors}\`);
257
- }
258
-
259
- return db.users.update(id, result.data);
260
- }
261
- }
262
- `);
263
-
264
- // --- 新方法 ---
265
- console.log('✅ 新方法(自动抛出):');
266
- console.log(`
267
- class UserService {
268
- async create(data) {
269
- const validData = await validateAsync(createUserSchema, data);
270
- return await db.users.insert(validData);
271
- }
272
-
273
- async update(id, data) {
274
- const validData = await validateAsync(updateUserSchema, data);
275
- return await db.users.update(id, validData);
276
- }
277
- }
278
-
279
- 优势:
280
- ✓ 代码行数减少 60%
281
- ✓ 逻辑更清晰
282
- ✓ 无需重复错误处理代码
283
- `);
284
-
285
- // ==========================================
286
- // 总结
287
- // ==========================================
288
-
289
- console.log('\n========================================');
290
- console.log('总结');
291
- console.log('========================================\n');
292
-
293
- console.log('异步验证方法(validateAsync):');
294
- console.log(' ✓ 减少 75% 的验证代码');
295
- console.log(' ✓ 统一错误处理');
296
- console.log(' ✓ 更符合异步编程习惯\n');
297
-
298
- console.log('灵活的 Schema 复用:');
299
- console.log(' ✓ 5 种模式覆盖所有场景');
300
- console.log(' ✓ 代码量减少 70%');
301
- console.log(' ✓ 单一数据源,易于维护\n');
302
-
303
- console.log('迁移成本:');
304
- console.log(' ✓ 100% 向后兼容');
305
- console.log(' ✓ 无需修改现有代码');
306
- console.log(' ✓ 可选择性使用新 API\n');
307
-
308
- console.log('========================================\n');
309
-
310
- module.exports = {
311
- userSchema,
312
- baseUserSchema,
313
- fullUserSchema
314
- };
315
-
@@ -1,153 +0,0 @@
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
-
@@ -1,26 +0,0 @@
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
-
@@ -1,101 +0,0 @@
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
-