schema-dsl 1.2.4 → 2.0.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 (242) hide show
  1. package/CHANGELOG.md +87 -210
  2. package/README.md +391 -2249
  3. package/dist/DslBuilder-DQDN0ZxZ.d.cts +341 -0
  4. package/dist/DslBuilder-DkLaOo9Q.d.ts +341 -0
  5. package/dist/Validator-C7GsVQOH.d.cts +192 -0
  6. package/dist/Validator-hFWKGxir.d.ts +192 -0
  7. package/dist/index.cjs +6594 -0
  8. package/dist/index.d.cts +1145 -0
  9. package/dist/index.d.ts +1145 -0
  10. package/dist/index.js +6528 -0
  11. package/dist/plugin-CIKtTMtS.d.cts +246 -0
  12. package/dist/plugin-CIKtTMtS.d.ts +246 -0
  13. package/dist/plugins/custom-format.cjs +3802 -0
  14. package/dist/plugins/custom-format.d.cts +12 -0
  15. package/dist/plugins/custom-format.d.ts +12 -0
  16. package/dist/plugins/custom-format.js +3772 -0
  17. package/dist/plugins/custom-type-example.cjs +3795 -0
  18. package/dist/plugins/custom-type-example.d.cts +8 -0
  19. package/dist/plugins/custom-type-example.d.ts +8 -0
  20. package/dist/plugins/custom-type-example.js +3765 -0
  21. package/dist/plugins/custom-validator.cjs +146 -0
  22. package/dist/plugins/custom-validator.d.cts +10 -0
  23. package/dist/plugins/custom-validator.d.ts +10 -0
  24. package/dist/plugins/custom-validator.js +121 -0
  25. package/docs/FEATURE-INDEX.md +102 -68
  26. package/docs/add-custom-locale.md +48 -35
  27. package/docs/add-keyword.md +24 -0
  28. package/docs/api-reference.md +396 -154
  29. package/docs/api.md +13 -0
  30. package/docs/best-practices-project-structure.md +19 -10
  31. package/docs/best-practices.md +93 -53
  32. package/docs/cache-manager.md +23 -15
  33. package/docs/compile.md +45 -0
  34. package/docs/conditional-api.md +40 -11
  35. package/docs/custom-extensions-guide.md +80 -152
  36. package/docs/design-philosophy.md +76 -71
  37. package/docs/doc-index.md +324 -0
  38. package/docs/dsl-syntax.md +69 -19
  39. package/docs/dynamic-locale.md +24 -14
  40. package/docs/enum.md +12 -5
  41. package/docs/error-handling.md +53 -44
  42. package/docs/export-guide.md +47 -8
  43. package/docs/export-limitations.md +27 -11
  44. package/docs/faq.md +86 -67
  45. package/docs/frontend-i18n-guide.md +26 -12
  46. package/docs/i18n-user-guide.md +60 -47
  47. package/docs/i18n.md +51 -32
  48. package/docs/index.md +48 -0
  49. package/docs/json-schema-basics.md +40 -0
  50. package/docs/label-vs-description.md +12 -3
  51. package/docs/markdown-exporter.md +15 -6
  52. package/docs/mongodb-exporter.md +11 -4
  53. package/docs/multi-language.md +26 -0
  54. package/docs/multi-type-support.md +26 -33
  55. package/docs/mysql-exporter.md +9 -2
  56. package/docs/number-operators.md +12 -5
  57. package/docs/optional-marker-guide.md +28 -23
  58. package/docs/performance-guide.md +49 -0
  59. package/docs/plugin-system.md +205 -366
  60. package/docs/plugin-type-registration.md +34 -0
  61. package/docs/postgresql-exporter.md +9 -2
  62. package/docs/public/favicon.svg +5 -0
  63. package/docs/quick-start.md +37 -363
  64. package/docs/runtime-locale-support.md +20 -9
  65. package/docs/schema-helper.md +10 -5
  66. package/docs/schema-utils-advanced-issues.md +23 -0
  67. package/docs/schema-utils-best-practices.md +20 -0
  68. package/docs/schema-utils-chaining.md +7 -0
  69. package/docs/schema-utils.md +76 -42
  70. package/docs/security-checklist.md +20 -0
  71. package/docs/string-extensions.md +17 -9
  72. package/docs/troubleshooting.md +36 -21
  73. package/docs/type-converter.md +41 -50
  74. package/docs/type-reference.md +38 -15
  75. package/docs/typescript-guide.md +53 -42
  76. package/docs/union-type-guide.md +11 -1
  77. package/docs/union-types.md +10 -3
  78. package/docs/validate-async.md +36 -25
  79. package/docs/validate-batch.md +49 -0
  80. package/docs/validate-dsl-object-support.md +33 -28
  81. package/docs/validate.md +36 -16
  82. package/docs/validation-guide.md +25 -7
  83. package/docs/validator.md +39 -0
  84. package/package.json +85 -27
  85. package/plugins/custom-format.cjs +8 -0
  86. package/plugins/custom-type-example.cjs +8 -0
  87. package/plugins/custom-validator.cjs +8 -0
  88. package/src/adapters/DslAdapter.ts +111 -0
  89. package/src/adapters/index.ts +1 -0
  90. package/src/config/constants.ts +83 -0
  91. package/src/config/index.ts +2 -0
  92. package/src/config/patterns.ts +77 -0
  93. package/src/core/CacheManager.ts +159 -0
  94. package/src/core/ConditionalBuilder.ts +382 -0
  95. package/src/core/ConditionalRuntime.ts +28 -0
  96. package/src/core/ConditionalValidator.ts +255 -0
  97. package/src/core/DslBuilder.ts +677 -0
  98. package/src/core/ErrorCodes.ts +38 -0
  99. package/src/core/ErrorFormatter.ts +271 -0
  100. package/src/core/JSONSchemaCore.ts +65 -0
  101. package/src/core/Locale.ts +187 -0
  102. package/src/core/MessageTemplate.ts +42 -0
  103. package/src/core/ObjectDslBuilder.ts +64 -0
  104. package/src/core/PluginManager.ts +326 -0
  105. package/src/core/StringExtensions.ts +140 -0
  106. package/src/core/TemplateEngine.ts +44 -0
  107. package/src/core/Validator.ts +448 -0
  108. package/src/errors/I18nError.ts +159 -0
  109. package/src/errors/ValidationError.ts +105 -0
  110. package/src/exporters/BaseExporter.ts +60 -0
  111. package/src/exporters/MarkdownExporter.ts +305 -0
  112. package/src/exporters/MongoDBExporter.ts +126 -0
  113. package/src/exporters/MySQLExporter.ts +155 -0
  114. package/src/exporters/PostgreSQLExporter.ts +222 -0
  115. package/src/exporters/index.ts +18 -0
  116. package/src/index.ts +633 -0
  117. package/{lib/locales/en-US.js → src/locales/en-US.ts} +21 -37
  118. package/{lib/locales/es-ES.js → src/locales/es-ES.ts} +63 -16
  119. package/{lib/locales/fr-FR.js → src/locales/fr-FR.ts} +74 -27
  120. package/src/locales/index.ts +103 -0
  121. package/{lib/locales/ja-JP.js → src/locales/ja-JP.ts} +59 -17
  122. package/src/locales/types.ts +156 -0
  123. package/{lib/locales/zh-CN.js → src/locales/zh-CN.ts} +21 -38
  124. package/src/parser/ConstraintParser.ts +101 -0
  125. package/src/parser/DslParser.ts +470 -0
  126. package/src/parser/SchemaCompiler.ts +66 -0
  127. package/src/parser/TypeRegistry.ts +250 -0
  128. package/src/parser/index.ts +6 -0
  129. package/src/plugins/custom-format.ts +126 -0
  130. package/src/plugins/custom-type-example.ts +108 -0
  131. package/src/plugins/custom-validator.ts +140 -0
  132. package/src/types/conditional.ts +28 -0
  133. package/src/types/config.ts +59 -0
  134. package/src/types/dsl.ts +131 -0
  135. package/src/types/error.ts +60 -0
  136. package/src/types/index.ts +17 -0
  137. package/src/types/infer.ts +128 -0
  138. package/src/types/plugin.ts +58 -0
  139. package/src/types/safe-regex.d.ts +9 -0
  140. package/src/types/schema.ts +66 -0
  141. package/src/types/validate.ts +71 -0
  142. package/src/utils/SchemaHelper.ts +196 -0
  143. package/src/utils/SchemaUtils.ts +346 -0
  144. package/src/utils/TypeConverter.ts +215 -0
  145. package/src/utils/index.ts +10 -0
  146. package/src/validators/CustomKeywords.ts +477 -0
  147. package/.eslintignore +0 -11
  148. package/.eslintrc.json +0 -27
  149. package/CONTRIBUTING.md +0 -368
  150. package/STATUS.md +0 -491
  151. package/changelogs/v1.0.0.md +0 -328
  152. package/changelogs/v1.0.9.md +0 -367
  153. package/changelogs/v1.1.0.md +0 -389
  154. package/changelogs/v1.1.1.md +0 -308
  155. package/changelogs/v1.1.2.md +0 -183
  156. package/changelogs/v1.1.3.md +0 -161
  157. package/changelogs/v1.1.4.md +0 -432
  158. package/changelogs/v1.1.5.md +0 -493
  159. package/changelogs/v1.1.6.md +0 -211
  160. package/changelogs/v1.1.8.md +0 -376
  161. package/changelogs/v1.2.3.md +0 -124
  162. package/docs/INDEX.md +0 -252
  163. package/docs/issues-resolved-summary.md +0 -196
  164. package/docs/performance-benchmark-report.md +0 -179
  165. package/docs/performance-quick-reference.md +0 -123
  166. package/docs/user-questions-answered.md +0 -353
  167. package/docs/validation-rules-v1.0.2.md +0 -1608
  168. package/examples/README.md +0 -81
  169. package/examples/array-dsl-example.js +0 -227
  170. package/examples/conditional-example.js +0 -288
  171. package/examples/conditional-non-object.js +0 -129
  172. package/examples/conditional-validate-example.js +0 -321
  173. package/examples/custom-extension.js +0 -85
  174. package/examples/dsl-match-example.js +0 -74
  175. package/examples/dsl-style.js +0 -118
  176. package/examples/dynamic-locale-configuration.js +0 -348
  177. package/examples/dynamic-locale-example.js +0 -287
  178. package/examples/enum.examples.js +0 -324
  179. package/examples/export-demo.js +0 -130
  180. package/examples/express-integration.js +0 -376
  181. package/examples/i18n-error-handling-complete.js +0 -381
  182. package/examples/i18n-error-handling-quickstart.md +0 -0
  183. package/examples/i18n-error.examples.js +0 -181
  184. package/examples/i18n-full-demo.js +0 -301
  185. package/examples/i18n-memory-safety.examples.js +0 -268
  186. package/examples/markdown-export.js +0 -71
  187. package/examples/middleware-usage.js +0 -93
  188. package/examples/new-features-comparison.js +0 -315
  189. package/examples/password-reset/README.md +0 -153
  190. package/examples/password-reset/schema.js +0 -26
  191. package/examples/password-reset/test.js +0 -101
  192. package/examples/plugin-system.examples.js +0 -205
  193. package/examples/schema-utils-chaining.examples.js +0 -250
  194. package/examples/simple-example.js +0 -122
  195. package/examples/slug.examples.js +0 -179
  196. package/examples/string-extensions.js +0 -297
  197. package/examples/union-type-example.js +0 -127
  198. package/examples/union-types-example.js +0 -77
  199. package/examples/user-registration/README.md +0 -156
  200. package/examples/user-registration/routes.js +0 -92
  201. package/examples/user-registration/schema.js +0 -150
  202. package/examples/user-registration/server.js +0 -74
  203. package/index.d.ts +0 -3540
  204. package/index.js +0 -457
  205. package/index.mjs +0 -60
  206. package/lib/adapters/DslAdapter.js +0 -871
  207. package/lib/adapters/index.js +0 -20
  208. package/lib/config/constants.js +0 -286
  209. package/lib/config/patterns/common.js +0 -47
  210. package/lib/config/patterns/creditCard.js +0 -9
  211. package/lib/config/patterns/idCard.js +0 -9
  212. package/lib/config/patterns/index.js +0 -9
  213. package/lib/config/patterns/licensePlate.js +0 -4
  214. package/lib/config/patterns/passport.js +0 -4
  215. package/lib/config/patterns/phone.js +0 -9
  216. package/lib/config/patterns/postalCode.js +0 -5
  217. package/lib/core/CacheManager.js +0 -376
  218. package/lib/core/ConditionalBuilder.js +0 -503
  219. package/lib/core/DslBuilder.js +0 -1400
  220. package/lib/core/ErrorCodes.js +0 -233
  221. package/lib/core/ErrorFormatter.js +0 -445
  222. package/lib/core/JSONSchemaCore.js +0 -347
  223. package/lib/core/Locale.js +0 -130
  224. package/lib/core/MessageTemplate.js +0 -98
  225. package/lib/core/PluginManager.js +0 -448
  226. package/lib/core/StringExtensions.js +0 -240
  227. package/lib/core/Validator.js +0 -654
  228. package/lib/errors/I18nError.js +0 -328
  229. package/lib/errors/ValidationError.js +0 -191
  230. package/lib/exporters/MarkdownExporter.js +0 -420
  231. package/lib/exporters/MongoDBExporter.js +0 -162
  232. package/lib/exporters/MySQLExporter.js +0 -212
  233. package/lib/exporters/PostgreSQLExporter.js +0 -289
  234. package/lib/exporters/index.js +0 -24
  235. package/lib/locales/index.js +0 -8
  236. package/lib/utils/LRUCache.js +0 -174
  237. package/lib/utils/SchemaHelper.js +0 -240
  238. package/lib/utils/SchemaUtils.js +0 -445
  239. package/lib/utils/TypeConverter.js +0 -245
  240. package/lib/utils/index.js +0 -13
  241. package/lib/validators/CustomKeywords.js +0 -616
  242. package/lib/validators/index.js +0 -11
@@ -1,130 +0,0 @@
1
- /**
2
- * 数据库导出示例
3
- *
4
- * 演示如何将JSON Schema导出为MongoDB、MySQL、PostgreSQL Schema
5
- * 包含v2.0.1 String扩展特性
6
- */
7
-
8
- const { dsl, exporters } = require('../index');
9
-
10
- // ========== 1. 定义用户Schema(使用String扩展)==========
11
-
12
- const userSchema = dsl({
13
- id: 'string!',
14
- // ✨ String扩展:username带正则验证
15
- username: 'string:3-32!'
16
- .pattern(/^[a-zA-Z0-9_]+$/)
17
- .label('用户名'),
18
- // ✨ String扩展:email带标签
19
- email: 'email!'
20
- .label('邮箱地址'),
21
- password: 'string:8-64!',
22
- age: 'number:18-120',
23
- gender: 'male|female|other',
24
- status: 'active|inactive|pending',
25
- role: 'user|admin|moderator',
26
- createdAt: 'date!',
27
- updatedAt: 'date!',
28
- profile: {
29
- bio: 'string:500',
30
- // ✨ String扩展:website带描述
31
- website: 'url'.description('个人主页'),
32
- avatar: 'url'.label('头像URL'),
33
- location: 'string:100'
34
- },
35
- preferences: {
36
- language: 'en|zh|ja|ko',
37
- theme: 'light|dark|auto',
38
- emailNotifications: 'boolean',
39
- smsNotifications: 'boolean'
40
- }
41
- });
42
-
43
- console.log('========== 用户Schema(JSON Schema格式) ==========');
44
- console.log(JSON.stringify(userSchema, null, 2));
45
-
46
- // ========== 2. 导出为MongoDB Schema ==========
47
-
48
- console.log('\n========== MongoDB验证Schema ==========');
49
- const mongoExporter = new exporters.MongoDBExporter({ strict: true });
50
- const mongoSchema = mongoExporter.export(userSchema);
51
- console.log(JSON.stringify(mongoSchema, null, 2));
52
-
53
- // 生成MongoDB命令
54
- console.log('\n========== MongoDB创建集合命令 ==========');
55
- const mongoCommand = mongoExporter.generateCommand('users', userSchema);
56
- console.log(mongoCommand);
57
-
58
- // ========== 3. 导出为MySQL DDL ==========
59
-
60
- console.log('\n========== MySQL CREATE TABLE ==========');
61
- const mysqlExporter = new exporters.MySQLExporter({
62
- engine: 'InnoDB',
63
- charset: 'utf8mb4',
64
- collate: 'utf8mb4_unicode_ci'
65
- });
66
- const mysqlDDL = mysqlExporter.export('users', userSchema);
67
- console.log(mysqlDDL);
68
-
69
- // 生成索引
70
- console.log('\n========== MySQL索引 ==========');
71
- console.log(mysqlExporter.generateIndex('users', 'username', { unique: true }));
72
- console.log(mysqlExporter.generateIndex('users', 'email', { unique: true }));
73
- console.log(mysqlExporter.generateIndex('users', 'status'));
74
-
75
- // ========== 4. 导出为PostgreSQL DDL ==========
76
-
77
- console.log('\n========== PostgreSQL CREATE TABLE ==========');
78
- const pgExporter = new exporters.PostgreSQLExporter({ schema: 'public' });
79
- const pgDDL = pgExporter.export('users', userSchema);
80
- console.log(pgDDL);
81
-
82
- // 生成索引
83
- console.log('\n========== PostgreSQL索引 ==========');
84
- console.log(pgExporter.generateIndex('users', 'username', { unique: true, method: 'btree' }));
85
- console.log(pgExporter.generateIndex('users', 'email', { unique: true, method: 'btree' }));
86
- console.log(pgExporter.generateIndex('users', 'status', { method: 'hash' }));
87
-
88
- // ========== 5. 多表导出示例 ==========
89
-
90
- console.log('\n========== 多表导出示例 ==========');
91
-
92
- // 文章表
93
- const articleSchema = dsl({
94
- id: 'string!',
95
- title: 'string:1-200!',
96
- content: 'string!',
97
- authorId: 'string!',
98
- categoryId: 'string!',
99
- status: 'draft|published|archived',
100
- tags: 'array<string:1-50>',
101
- viewCount: 'number',
102
- likeCount: 'number',
103
- createdAt: 'date!',
104
- updatedAt: 'date!'
105
- });
106
-
107
- // 评论表
108
- const commentSchema = dsl({
109
- id: 'string!',
110
- articleId: 'string!',
111
- userId: 'string!',
112
- content: 'string:1-500!',
113
- parentId: 'string',
114
- status: 'pending|approved|rejected',
115
- createdAt: 'date!'
116
- });
117
-
118
- console.log('\n--- MySQL多表DDL ---');
119
- console.log(mysqlExporter.export('articles', articleSchema));
120
- console.log('\n');
121
- console.log(mysqlExporter.export('comments', commentSchema));
122
-
123
- console.log('\n--- PostgreSQL多表DDL ---');
124
- console.log(pgExporter.export('articles', articleSchema));
125
- console.log('\n');
126
- console.log(pgExporter.export('comments', commentSchema));
127
-
128
- console.log('\n✅ 数据库导出示例完成!');
129
-
130
-
@@ -1,376 +0,0 @@
1
- /**
2
- * Express 集成示例 - 异步验证与 Schema 链式调用
3
- *
4
- * 展示如何在 Express 中使用:
5
- * 1. validateAsync 异步验证
6
- * 2. ValidationError 错误处理
7
- * 3. SchemaUtils 链式调用
8
- * 4. 完整 CRUD 场景
9
- *
10
- * @version 1.0.3
11
- * @date 2025-12-29
12
- */
13
-
14
- const express = require('express');
15
- const { dsl, validateAsync, ValidationError, SchemaUtils } = require('../index');
16
-
17
- const app = express();
18
- app.use(express.json());
19
-
20
- // ===== 模拟数据库 =====
21
- const db = {
22
- users: [],
23
- nextId: 1
24
- };
25
-
26
- // ===== 定义完整 User Schema =====
27
- const fullUserSchema = dsl({
28
- id: 'objectId!',
29
- name: 'string:1-50!',
30
- email: 'email!',
31
- password: 'string:8-32!',
32
- age: 'integer:18-120',
33
- role: 'admin|user|guest',
34
- createdAt: 'date',
35
- updatedAt: 'date'
36
- });
37
-
38
- // ===== 派生各种 Schema =====
39
-
40
- // POST /users - 创建用户 Schema(排除系统字段)
41
- const createUserSchema = SchemaUtils.omit(fullUserSchema, ['id', 'createdAt', 'updatedAt']);
42
-
43
- // GET /users/:id - 公开用户 Schema(移除敏感字段)
44
- const publicUserSchema = SchemaUtils.omit(fullUserSchema, ['password']);
45
-
46
- // PATCH /users/:id - 更新用户 Schema(部分验证)
47
- const updateUserSchema = SchemaUtils
48
- .pick(fullUserSchema, ['name', 'age'])
49
- .partial();
50
-
51
- // PUT /users/:id - 替换用户 Schema(排除系统字段)
52
- const replaceUserSchema = SchemaUtils.omit(fullUserSchema, ['id', 'createdAt', 'updatedAt']);
53
-
54
- // ===== 路由实现 =====
55
-
56
- /**
57
- * POST /users - 创建用户
58
- *
59
- * 使用 createUserSchema:
60
- * - 排除系统字段(id, createdAt, updatedAt)
61
- */
62
- app.post('/users', async (req, res, next) => {
63
- try {
64
- console.log('\n[POST /users] 创建用户');
65
- console.log('请求体:', req.body);
66
-
67
- // 使用 validateAsync 验证
68
- const data = await validateAsync(createUserSchema, req.body);
69
-
70
- console.log('验证通过,数据:', data);
71
-
72
- // 保存到数据库
73
- const user = {
74
- id: String(db.nextId++),
75
- ...data,
76
- createdAt: new Date().toISOString(),
77
- updatedAt: new Date().toISOString()
78
- };
79
-
80
- db.users.push(user);
81
-
82
- // 返回公开信息
83
- const { validate } = require('../index');
84
- const result = validate(publicUserSchema, user);
85
-
86
- console.log('返回数据:', result.data);
87
-
88
- res.status(201).json({
89
- success: true,
90
- user: result.data
91
- });
92
-
93
- } catch (error) {
94
- next(error);
95
- }
96
- });
97
-
98
- /**
99
- * GET /users - 获取所有用户
100
- *
101
- * 使用 publicUserSchema 自动移除敏感字段
102
- */
103
- app.get('/users', (req, res) => {
104
- console.log('\n[GET /users] 获取所有用户');
105
-
106
- const { validate } = require('../index');
107
-
108
- // 对每个用户应用 publicUserSchema
109
- const publicUsers = db.users.map(user => {
110
- const result = validate(publicUserSchema, user);
111
- return result.data;
112
- });
113
-
114
- console.log(`返回 ${publicUsers.length} 个用户`);
115
-
116
- res.json({
117
- success: true,
118
- count: publicUsers.length,
119
- users: publicUsers
120
- });
121
- });
122
-
123
- /**
124
- * GET /users/:id - 获取单个用户
125
- *
126
- * 使用 publicUserSchema 移除敏感字段
127
- */
128
- app.get('/users/:id', (req, res) => {
129
- console.log(`\n[GET /users/${req.params.id}] 获取用户`);
130
-
131
- const user = db.users.find(u => u.id === req.params.id);
132
-
133
- if (!user) {
134
- console.log('用户不存在');
135
- return res.status(404).json({
136
- success: false,
137
- error: '用户不存在'
138
- });
139
- }
140
-
141
- // 使用 clean 模式自动移除敏感字段
142
- const { validate } = require('../index');
143
- const result = validate(publicUserSchema, user);
144
-
145
- console.log('返回数据:', result.data);
146
-
147
- res.json({
148
- success: true,
149
- user: result.data
150
- });
151
- });
152
-
153
- /**
154
- * PATCH /users/:id - 部分更新用户
155
- *
156
- * 使用 updateUserSchema:
157
- * - 只验证 name 和 age
158
- * - 部分验证(可选)
159
- * - 宽松模式(允许额外字段)
160
- */
161
- app.patch('/users/:id', async (req, res, next) => {
162
- try {
163
- console.log(`\n[PATCH /users/${req.params.id}] 部分更新用户`);
164
- console.log('请求体:', req.body);
165
-
166
- const user = db.users.find(u => u.id === req.params.id);
167
-
168
- if (!user) {
169
- console.log('用户不存在');
170
- return res.status(404).json({
171
- success: false,
172
- error: '用户不存在'
173
- });
174
- }
175
-
176
- // 验证部分数据
177
- const data = await validateAsync(updateUserSchema, req.body);
178
-
179
- console.log('验证通过,更新字段:', data);
180
-
181
- // 更新用户
182
- Object.assign(user, data, {
183
- updatedAt: new Date().toISOString()
184
- });
185
-
186
- // 返回公开信息
187
- const { validate } = require('../index');
188
- const result = validate(publicUserSchema, user);
189
-
190
- console.log('返回数据:', result.data);
191
-
192
- res.json({
193
- success: true,
194
- user: result.data
195
- });
196
-
197
- } catch (error) {
198
- next(error);
199
- }
200
- });
201
-
202
- /**
203
- * PUT /users/:id - 替换用户
204
- *
205
- * 使用 replaceUserSchema:
206
- * - 排除系统字段
207
- * - 严格模式(必填字段必须全部提供)
208
- */
209
- app.put('/users/:id', async (req, res, next) => {
210
- try {
211
- console.log(`\n[PUT /users/${req.params.id}] 替换用户`);
212
- console.log('请求体:', req.body);
213
-
214
- const userIndex = db.users.findIndex(u => u.id === req.params.id);
215
-
216
- if (userIndex === -1) {
217
- console.log('用户不存在');
218
- return res.status(404).json({
219
- success: false,
220
- error: '用户不存在'
221
- });
222
- }
223
-
224
- // 验证完整数据
225
- const data = await validateAsync(replaceUserSchema, req.body);
226
-
227
- console.log('验证通过,替换用户:', data);
228
-
229
- // 替换用户(保留 id 和 createdAt)
230
- const oldUser = db.users[userIndex];
231
- db.users[userIndex] = {
232
- id: oldUser.id,
233
- ...data,
234
- createdAt: oldUser.createdAt,
235
- updatedAt: new Date().toISOString()
236
- };
237
-
238
- // 返回公开信息
239
- const { validate } = require('../index');
240
- const result = validate(publicUserSchema, db.users[userIndex]);
241
-
242
- console.log('返回数据:', result.data);
243
-
244
- res.json({
245
- success: true,
246
- user: result.data
247
- });
248
-
249
- } catch (error) {
250
- next(error);
251
- }
252
- });
253
-
254
- /**
255
- * DELETE /users/:id - 删除用户
256
- */
257
- app.delete('/users/:id', (req, res) => {
258
- console.log(`\n[DELETE /users/${req.params.id}] 删除用户`);
259
-
260
- const userIndex = db.users.findIndex(u => u.id === req.params.id);
261
-
262
- if (userIndex === -1) {
263
- console.log('用户不存在');
264
- return res.status(404).json({
265
- success: false,
266
- error: '用户不存在'
267
- });
268
- }
269
-
270
- db.users.splice(userIndex, 1);
271
-
272
- console.log('删除成功');
273
-
274
- res.json({
275
- success: true,
276
- message: '用户已删除'
277
- });
278
- });
279
-
280
- // ===== 全局错误处理中间件 =====
281
-
282
- /**
283
- * ValidationError 错误处理
284
- *
285
- * 自动捕获 validateAsync 抛出的 ValidationError
286
- * 返回友好的错误信息
287
- */
288
- app.use((error, req, res, next) => {
289
- if (error instanceof ValidationError) {
290
- console.log('\n[错误] ValidationError 被捕获');
291
- console.log('错误数量:', error.getErrorCount());
292
- console.log('字段错误:', error.getFieldErrors());
293
-
294
- return res.status(error.statusCode).json(error.toJSON());
295
- }
296
-
297
- // 其他错误
298
- console.error('\n[错误] 服务器错误:', error);
299
- res.status(500).json({
300
- error: 'Internal Server Error',
301
- message: error.message
302
- });
303
- });
304
-
305
- // ===== 启动服务器 =====
306
-
307
- const PORT = 3000;
308
-
309
- app.listen(PORT, () => {
310
- console.log(`\n========================================`);
311
- console.log(` Express 集成示例服务器已启动`);
312
- console.log(` 监听端口: ${PORT}`);
313
- console.log(`========================================\n`);
314
- console.log(`可用的 API 端点:`);
315
- console.log(` POST http://localhost:${PORT}/users - 创建用户`);
316
- console.log(` GET http://localhost:${PORT}/users - 获取所有用户`);
317
- console.log(` GET http://localhost:${PORT}/users/:id - 获取单个用户`);
318
- console.log(` PATCH http://localhost:${PORT}/users/:id - 部分更新用户`);
319
- console.log(` PUT http://localhost:${PORT}/users/:id - 替换用户`);
320
- console.log(` DELETE http://localhost:${PORT}/users/:id - 删除用户`);
321
- console.log(`\n========================================\n`);
322
-
323
- // 打印测试命令
324
- printTestCommands();
325
- });
326
-
327
- // ===== 测试命令 =====
328
-
329
- function printTestCommands() {
330
- console.log(`测试命令(使用 curl):\n`);
331
-
332
- console.log(`# 1. 创建用户(成功)`);
333
- console.log(`curl -X POST http://localhost:${PORT}/users \\`);
334
- console.log(` -H "Content-Type: application/json" \\`);
335
- console.log(` -d '{"name":"John Doe","email":"john@example.com","password":"password123","age":30,"role":"user"}'\n`);
336
-
337
- console.log(`# 2. 创建用户(失败 - 缺少必填字段)`);
338
- console.log(`curl -X POST http://localhost:${PORT}/users \\`);
339
- console.log(` -H "Content-Type: application/json" \\`);
340
- console.log(` -d '{"name":"Jane"}'\n`);
341
-
342
- console.log(`# 3. 创建用户(失败 - 额外字段被拒绝)`);
343
- console.log(`curl -X POST http://localhost:${PORT}/users \\`);
344
- console.log(` -H "Content-Type: application/json" \\`);
345
- console.log(` -d '{"name":"Bob","email":"bob@example.com","password":"password123","extraField":"not allowed"}'\n`);
346
-
347
- console.log(`# 4. 获取所有用户`);
348
- console.log(`curl http://localhost:${PORT}/users\n`);
349
-
350
- console.log(`# 5. 获取单个用户(替换 ID)`);
351
- console.log(`curl http://localhost:${PORT}/users/1\n`);
352
-
353
- console.log(`# 6. 部分更新用户(成功 - 只更新 name)`);
354
- console.log(`curl -X PATCH http://localhost:${PORT}/users/1 \\`);
355
- console.log(` -H "Content-Type: application/json" \\`);
356
- console.log(` -d '{"name":"John Updated"}'\n`);
357
-
358
- console.log(`# 7. 部分更新用户(成功 - 允许额外字段)`);
359
- console.log(`curl -X PATCH http://localhost:${PORT}/users/1 \\`);
360
- console.log(` -H "Content-Type: application/json" \\`);
361
- console.log(` -d '{"age":31,"extraField":"allowed"}'\n`);
362
-
363
- console.log(`# 8. 替换用户(成功 - 必须提供所有必填字段)`);
364
- console.log(`curl -X PUT http://localhost:${PORT}/users/1 \\`);
365
- console.log(` -H "Content-Type: application/json" \\`);
366
- console.log(` -d '{"name":"John Replaced","email":"john.new@example.com","password":"newpassword123","age":32}'\n`);
367
-
368
- console.log(`# 9. 删除用户`);
369
- console.log(`curl -X DELETE http://localhost:${PORT}/users/1\n`);
370
-
371
- console.log(`========================================\n`);
372
- }
373
-
374
- // 导出 app 用于测试
375
- module.exports = app;
376
-