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,240 +0,0 @@
1
- /**
2
- * SchemaHelper - Schema辅助函数
3
- *
4
- * 提供Schema操作的常用辅助方法
5
- *
6
- * @module lib/utils/SchemaHelper
7
- * @version 1.0.0
8
- */
9
-
10
- /**
11
- * Schema辅助工具类
12
- * @class SchemaHelper
13
- */
14
- class SchemaHelper {
15
- /**
16
- * 检查是否为有效的JSON Schema
17
- * @static
18
- * @param {Object} schema - 待检查的Schema对象
19
- * @returns {boolean} 是否有效
20
- */
21
- static isValidSchema(schema) {
22
- if (!schema || typeof schema !== 'object') {
23
- return false;
24
- }
25
-
26
- // 至少要有type或properties或items之一
27
- return !!(schema.type || schema.properties || schema.items || schema.$ref);
28
- }
29
-
30
- /**
31
- * 生成Schema的唯一ID
32
- * @static
33
- * @param {Object} schema - JSON Schema对象
34
- * @returns {string} 唯一ID
35
- */
36
- static generateSchemaId(schema) {
37
- // 使用简单hash生成ID
38
- const str = JSON.stringify(schema);
39
- let hash = 0;
40
- for (let i = 0; i < str.length; i++) {
41
- const char = str.charCodeAt(i);
42
- hash = ((hash << 5) - hash) + char;
43
- hash = hash & hash; // Convert to 32bit integer
44
- }
45
- return `schema_${Math.abs(hash).toString(36)}`;
46
- }
47
-
48
- /**
49
- * 深度克隆Schema对象
50
- * @static
51
- * @param {Object} schema - 原Schema对象
52
- * @returns {Object} 克隆的Schema对象
53
- */
54
- static cloneSchema(schema) {
55
- return JSON.parse(JSON.stringify(schema));
56
- }
57
-
58
- /**
59
- * 扁平化嵌套Schema
60
- * @static
61
- * @param {Object} schema - 嵌套的Schema对象
62
- * @param {string} prefix - 属性前缀
63
- * @returns {Object} 扁平化的Schema对象
64
- */
65
- static flattenSchema(schema, prefix = '') {
66
- const result = {};
67
-
68
- if (schema.properties) {
69
- for (const [key, value] of Object.entries(schema.properties)) {
70
- const fullKey = prefix ? `${prefix}.${key}` : key;
71
-
72
- if (value.type === 'object' && value.properties) {
73
- // 递归扁平化嵌套对象
74
- Object.assign(result, this.flattenSchema(value, fullKey));
75
- } else {
76
- result[fullKey] = value;
77
- }
78
- }
79
- }
80
-
81
- return result;
82
- }
83
-
84
- /**
85
- * 获取Schema中所有字段路径
86
- * @static
87
- * @param {Object} schema - JSON Schema对象
88
- * @returns {Array} 字段路径数组
89
- */
90
- static getFieldPaths(schema) {
91
- const paths = [];
92
-
93
- function traverse(obj, currentPath = '') {
94
- if (obj.properties) {
95
- for (const [key, value] of Object.entries(obj.properties)) {
96
- const path = currentPath ? `${currentPath}.${key}` : key;
97
- paths.push(path);
98
-
99
- if (value.type === 'object') {
100
- traverse(value, path);
101
- } else if (value.type === 'array' && value.items) {
102
- traverse(value.items, `${path}[]`);
103
- }
104
- }
105
- }
106
- }
107
-
108
- traverse(schema);
109
- return paths;
110
- }
111
-
112
- /**
113
- * 提取Schema中的所有required字段
114
- * @static
115
- * @param {Object} schema - JSON Schema对象
116
- * @returns {Array} required字段数组
117
- */
118
- static extractRequiredFields(schema) {
119
- const required = [];
120
-
121
- function traverse(obj, prefix = '') {
122
- if (obj.required && Array.isArray(obj.required)) {
123
- obj.required.forEach(field => {
124
- const fullPath = prefix ? `${prefix}.${field}` : field;
125
- required.push(fullPath);
126
- });
127
- }
128
-
129
- if (obj.properties) {
130
- for (const [key, value] of Object.entries(obj.properties)) {
131
- if (value.type === 'object') {
132
- const newPrefix = prefix ? `${prefix}.${key}` : key;
133
- traverse(value, newPrefix);
134
- }
135
- }
136
- }
137
- }
138
-
139
- traverse(schema);
140
- return required;
141
- }
142
-
143
- /**
144
- * 比较两个Schema是否相同
145
- * @static
146
- * @param {Object} schema1 - Schema对象1
147
- * @param {Object} schema2 - Schema对象2
148
- * @returns {boolean} 是否相同
149
- */
150
- static compareSchemas(schema1, schema2) {
151
- return JSON.stringify(schema1) === JSON.stringify(schema2);
152
- }
153
-
154
- /**
155
- * 简化Schema(移除不必要的字段)
156
- * @static
157
- * @param {Object} schema - JSON Schema对象
158
- * @returns {Object} 简化后的Schema
159
- */
160
- static simplifySchema(schema) {
161
- const simplified = this.cloneSchema(schema);
162
-
163
- // 移除$schema
164
- delete simplified.$schema;
165
-
166
- // 移除空的properties
167
- if (simplified.properties && Object.keys(simplified.properties).length === 0) {
168
- delete simplified.properties;
169
- }
170
-
171
- // 移除空的required
172
- if (simplified.required && simplified.required.length === 0) {
173
- delete simplified.required;
174
- }
175
-
176
- return simplified;
177
- }
178
-
179
- /**
180
- * 验证属性名是否合法
181
- * @static
182
- * @param {string} name - 属性名
183
- * @returns {boolean} 是否合法
184
- */
185
- static isValidPropertyName(name) {
186
- // 属性名只能包含字母、数字、下划线、连字符
187
- return /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name);
188
- }
189
-
190
- /**
191
- * 获取Schema的复杂度(嵌套层级)
192
- * @static
193
- * @param {Object} schema - JSON Schema对象
194
- * @returns {number} 复杂度(最大嵌套层级)
195
- */
196
- static getSchemaComplexity(schema) {
197
- let maxDepth = 0;
198
-
199
- function traverse(obj, depth = 0) {
200
- maxDepth = Math.max(maxDepth, depth);
201
-
202
- if (obj.properties) {
203
- for (const value of Object.values(obj.properties)) {
204
- if (value.type === 'object') {
205
- traverse(value, depth + 1);
206
- } else if (value.type === 'array' && value.items) {
207
- traverse(value.items, depth + 1);
208
- }
209
- }
210
- }
211
- }
212
-
213
- traverse(schema);
214
- return maxDepth;
215
- }
216
-
217
- /**
218
- * 生成Schema摘要信息
219
- * @static
220
- * @param {Object} schema - JSON Schema对象
221
- * @returns {Object} 摘要信息
222
- */
223
- static summarizeSchema(schema) {
224
- const paths = this.getFieldPaths(schema);
225
- const required = this.extractRequiredFields(schema);
226
- const complexity = this.getSchemaComplexity(schema);
227
-
228
- return {
229
- type: schema.type || 'unknown',
230
- fieldCount: paths.length,
231
- requiredCount: required.length,
232
- complexity,
233
- hasNested: complexity > 0,
234
- fields: paths
235
- };
236
- }
237
- }
238
-
239
- module.exports = SchemaHelper;
240
-
@@ -1,445 +0,0 @@
1
- /**
2
- * Schema工具类 v2.0.1
3
- *
4
- * 提供Schema复用、合并、性能监控等功能
5
- */
6
-
7
- class SchemaUtils {
8
- // ========== Schema复用 ==========
9
-
10
- /**
11
- * 创建可复用的Schema片段
12
- * @param {Function} factory - Schema工厂函数
13
- * @returns {Function} 返回工厂函数
14
- *
15
- * @example
16
- * const emailField = SchemaUtils.reusable(() =>
17
- * 'email!'.pattern(/custom/).label('邮箱')
18
- * );
19
- *
20
- * const schema1 = dsl({ email: emailField() });
21
- * const schema2 = dsl({ contactEmail: emailField() });
22
- */
23
- static reusable(factory) {
24
- return factory;
25
- }
26
-
27
- /**
28
- * 创建Schema片段库
29
- * @param {Object} fragments - Schema片段对象
30
- * @returns {Object} 片段库
31
- *
32
- * @example
33
- * const fields = SchemaUtils.createLibrary({
34
- * email: () => 'email!'.label('邮箱'),
35
- * phone: () => 'string:11!'.phoneNumber('cn').label('手机号'),
36
- * username: () => 'string:3-32!'.username().label('用户名')
37
- * });
38
- *
39
- * const schema = dsl({
40
- * email: fields.email(),
41
- * phone: fields.phone()
42
- * });
43
- */
44
- static createLibrary(fragments) {
45
- return fragments;
46
- }
47
-
48
- // ========== Schema复用和扩展 ==========
49
-
50
- /**
51
- * 扩展Schema(类似继承)
52
- * @param {Object} baseSchema - 基础Schema
53
- * @param {Object} extensions - 扩展定义
54
- * @returns {Object} 扩展后的Schema(支持链式调用)
55
- *
56
- * @example
57
- * const baseUser = dsl({ name: 'string!', email: 'email!' });
58
- * const admin = SchemaUtils.extend(baseUser, {
59
- * role: 'admin|superadmin',
60
- * permissions: 'array<string>'
61
- * });
62
- */
63
- static extend(baseSchema, extensions) {
64
- const dsl = require('../adapters/DslAdapter');
65
- const extensionSchema = typeof extensions === 'function'
66
- ? extensions
67
- : dsl(extensions);
68
-
69
- // 合并 properties
70
- const result = {
71
- type: 'object',
72
- properties: {},
73
- required: []
74
- };
75
-
76
- // 复制基础 schema
77
- if (baseSchema.properties) {
78
- Object.assign(result.properties, baseSchema.properties);
79
- }
80
- if (baseSchema.required) {
81
- result.required = [...baseSchema.required];
82
- }
83
-
84
- // 添加扩展
85
- if (extensionSchema.properties) {
86
- Object.assign(result.properties, extensionSchema.properties);
87
- }
88
- if (extensionSchema.required) {
89
- result.required = [...new Set([...result.required, ...extensionSchema.required])];
90
- }
91
-
92
- return this._makeChainable(result);
93
- }
94
-
95
- /**
96
- * 挑选Schema的部分字段
97
- * @param {Object} schema - 原始Schema
98
- * @param {string[]} fields - 要挑选的字段
99
- * @returns {Object} 新Schema
100
- *
101
- * @example
102
- * const fullUser = dsl({ name: 'string!', email: 'email!', age: 'number' });
103
- * const publicUser = SchemaUtils.pick(fullUser, ['name', 'email']);
104
- */
105
- static pick(schema, fields) {
106
- const result = {
107
- type: 'object',
108
- properties: {},
109
- required: []
110
- };
111
-
112
- fields.forEach(field => {
113
- if (schema.properties && schema.properties[field]) {
114
- result.properties[field] = schema.properties[field];
115
- if (schema.required && schema.required.includes(field)) {
116
- result.required.push(field);
117
- }
118
- }
119
- });
120
-
121
- return this._makeChainable(result);
122
- }
123
-
124
- /**
125
- * 排除Schema的部分字段
126
- * @param {Object} schema - 原始Schema
127
- * @param {string[]} fields - 要排除的字段
128
- * @returns {Object} 新Schema(支持链式调用)
129
- */
130
- static omit(schema, fields) {
131
- const result = this._clone(schema);
132
-
133
- fields.forEach(field => {
134
- if (result.properties) {
135
- delete result.properties[field];
136
- }
137
- if (result.required) {
138
- result.required = result.required.filter(f => f !== field);
139
- }
140
- });
141
-
142
- // 清理空数组
143
- if (result.required && result.required.length === 0) {
144
- delete result.required;
145
- }
146
-
147
- return this._makeChainable(result);
148
- }
149
-
150
- // ========== v2.1.0 新增:Schema转换方法(支持链式调用) ==========
151
-
152
- /**
153
- * 部分验证:移除必填限制
154
- *
155
- * @param {Object} schema - 原始Schema
156
- * @param {string[]} fields - 要验证的字段(可选,默认全部)
157
- * @returns {Object} 新Schema(支持链式调用)
158
- *
159
- * @example
160
- * // 所有字段变为可选
161
- * const partialSchema = SchemaUtils.partial(userSchema);
162
- *
163
- * @example
164
- * // 只验证指定字段
165
- * const updateSchema = SchemaUtils.partial(userSchema, ['name', 'age']);
166
- *
167
- * @example
168
- * // 链式调用
169
- * const patchSchema = SchemaUtils
170
- * .pick(userSchema, ['name', 'age'])
171
- * .partial();
172
- */
173
- static partial(schema, fields = null) {
174
- let result;
175
-
176
- if (fields) {
177
- // 只保留指定字段 (pick 已经返回 chainable 对象)
178
- result = this.pick(schema, fields);
179
- // 提取原始 schema
180
- if (result._isChainable) {
181
- result = this._extractSchema(result);
182
- }
183
- } else {
184
- result = this._clone(schema);
185
- }
186
-
187
- // 移除所有 required
188
- delete result.required;
189
-
190
- // 递归处理嵌套对象
191
- if (result.properties) {
192
- Object.keys(result.properties).forEach(key => {
193
- const prop = result.properties[key];
194
- if (prop && prop.type === 'object' && prop.required) {
195
- delete prop.required;
196
- }
197
- });
198
- }
199
-
200
- return this._makeChainable(result);
201
- }
202
-
203
- // ========== 性能监控 ==========
204
-
205
- /**
206
- * 创建带性能监控的Validator
207
- * @param {Validator} validator - Validator实例
208
- * @returns {Validator} 增强的Validator
209
- *
210
- * @example
211
- * const validator = SchemaUtils.withPerformance(new Validator());
212
- * const result = validator.validate(schema, data);
213
- * console.log(result.performance);
214
- */
215
- static withPerformance(validator) {
216
- const originalValidate = validator.validate.bind(validator);
217
-
218
- validator.validate = function(schema, data) {
219
- const startTime = Date.now();
220
- const result = originalValidate(schema, data);
221
- const endTime = Date.now();
222
-
223
- result.performance = {
224
- duration: endTime - startTime,
225
- timestamp: new Date().toISOString()
226
- };
227
-
228
- return result;
229
- };
230
-
231
- return validator;
232
- }
233
-
234
- /**
235
- * 批量验证优化
236
- * @param {Object} schema - Schema对象
237
- * @param {Array} dataArray - 数据数组
238
- * @param {Validator} validator - Validator实例
239
- * @returns {Array} 验证结果数组
240
- *
241
- * @example
242
- * const results = SchemaUtils.validateBatch(schema, users, validator);
243
- */
244
- static validateBatch(schema, dataArray, validator) {
245
- const startTime = Date.now();
246
-
247
- // 复用编译后的Schema
248
- const compiledValidate = validator.getAjv().compile(schema);
249
-
250
- const results = dataArray.map((data, index) => {
251
- const valid = compiledValidate(data);
252
- return {
253
- index,
254
- valid,
255
- errors: valid ? null : compiledValidate.errors,
256
- data: valid ? data : null
257
- };
258
- });
259
-
260
- const endTime = Date.now();
261
-
262
- return {
263
- results,
264
- summary: {
265
- total: dataArray.length,
266
- valid: results.filter(r => r.valid).length,
267
- invalid: results.filter(r => !r.valid).length,
268
- duration: endTime - startTime,
269
- averageTime: (endTime - startTime) / dataArray.length
270
- }
271
- };
272
- }
273
-
274
- // ========== Schema导出 ==========
275
-
276
- /**
277
- * 导出Schema为Markdown文档
278
- * @param {Object} schema - Schema对象
279
- * @param {Object} options - 选项
280
- * @returns {string} Markdown文档
281
- *
282
- * @example
283
- * const markdown = SchemaUtils.toMarkdown(schema, { title: 'User Schema' });
284
- */
285
- static toMarkdown(schema, options = {}) {
286
- const { title = 'Schema文档', locale = 'zh-CN' } = options;
287
-
288
- let md = `# ${title}\n\n`;
289
-
290
- if (schema.properties) {
291
- md += '## 字段列表\n\n';
292
- md += '| 字段 | 类型 | 必填 | 说明 |\n';
293
- md += '|------|------|------|------|\n';
294
-
295
- Object.keys(schema.properties).forEach(key => {
296
- const prop = schema.properties[key];
297
- const required = schema.required?.includes(key) ? '✅' : '❌';
298
- const type = prop.type || 'any';
299
- const label = prop._label || key;
300
- const desc = prop._description || '-';
301
-
302
- md += `| ${key} | ${type} | ${required} | ${label} |\n`;
303
-
304
- if (desc !== '-') {
305
- md += `| | | | *${desc}* |\n`;
306
- }
307
-
308
- // 约束信息
309
- const constraints = [];
310
- if (prop.minLength) constraints.push(`最小长度: ${prop.minLength}`);
311
- if (prop.maxLength) constraints.push(`最大长度: ${prop.maxLength}`);
312
- if (prop.minimum) constraints.push(`最小值: ${prop.minimum}`);
313
- if (prop.maximum) constraints.push(`最大值: ${prop.maximum}`);
314
- if (prop.pattern) constraints.push(`格式: \`${prop.pattern}\``);
315
- if (prop.enum) constraints.push(`可选值: ${prop.enum.join(', ')}`);
316
-
317
- if (constraints.length > 0) {
318
- md += `| | | | ${constraints.join('; ')} |\n`;
319
- }
320
- });
321
- }
322
-
323
- return md;
324
- }
325
-
326
- /**
327
- * 导出Schema为HTML文档
328
- * @param {Object} schema - Schema对象
329
- * @param {Object} options - 选项
330
- * @returns {string} HTML文档
331
- */
332
- static toHTML(schema, options = {}) {
333
- const { title = 'Schema文档' } = options;
334
- const markdown = this.toMarkdown(schema, options);
335
-
336
- // 简单的Markdown到HTML转换
337
- let html = `<!DOCTYPE html>
338
- <html>
339
- <head>
340
- <meta charset="UTF-8">
341
- <title>${title}</title>
342
- <style>
343
- body { font-family: Arial, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
344
- table { width: 100%; border-collapse: collapse; margin: 20px 0; }
345
- th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
346
- th { background-color: #f5f5f5; font-weight: bold; }
347
- code { background-color: #f5f5f5; padding: 2px 6px; border-radius: 3px; }
348
- </style>
349
- </head>
350
- <body>
351
- `;
352
-
353
- html += markdown
354
- .replace(/^# (.+)$/gm, '<h1>$1</h1>')
355
- .replace(/^## (.+)$/gm, '<h2>$1</h2>')
356
- .replace(/\`([^`]+)\`/g, '<code>$1</code>');
357
-
358
- html += '\n</body>\n</html>';
359
-
360
- return html;
361
- }
362
-
363
- /**
364
- * 克隆Schema
365
- * @param {Object} schema - 原始Schema
366
- * @returns {Object} 克隆的Schema
367
- */
368
- static clone(schema) {
369
- return JSON.parse(JSON.stringify(schema));
370
- }
371
-
372
- /**
373
- * 深拷贝Schema
374
- * @private
375
- * @param {Object} schema - 原始Schema
376
- * @returns {Object} 拷贝后的Schema
377
- */
378
- static _clone(schema) {
379
- // 如果是 chainable 对象,先提取原始 schema
380
- if (schema && schema._isChainable) {
381
- schema = this._extractSchema(schema);
382
- }
383
- return JSON.parse(JSON.stringify(schema));
384
- }
385
-
386
- /**
387
- * 使 schema 支持链式调用
388
- * @private
389
- * @param {Object} schema - Schema对象
390
- * @returns {Object} 支持链式调用的 Schema
391
- */
392
- static _makeChainable(schema) {
393
- // 如果已经是 chainable,直接返回
394
- if (schema && schema._isChainable) {
395
- return schema;
396
- }
397
-
398
- // 复制 schema 的所有属性
399
- const chainable = Object.assign({}, schema);
400
-
401
- // 标记为 chainable
402
- Object.defineProperty(chainable, '_isChainable', {
403
- value: true,
404
- enumerable: false,
405
- configurable: false
406
- });
407
-
408
- // 添加链式方法(只保留核心4个方法)
409
- const methods = ['partial', 'omit', 'pick', 'extend'];
410
- const self = this; // 保存 this 引用
411
- methods.forEach(method => {
412
- Object.defineProperty(chainable, method, {
413
- value: (...args) => {
414
- // 提取原始 schema(去掉链式方法)
415
- const rawSchema = self._extractSchema(chainable);
416
- // 调用 SchemaUtils 的静态方法
417
- return SchemaUtils[method](rawSchema, ...args);
418
- },
419
- enumerable: false,
420
- configurable: false
421
- });
422
- });
423
-
424
- return chainable;
425
- }
426
-
427
- /**
428
- * 从 chainable 对象中提取原始 schema
429
- * @private
430
- * @param {Object} chainable - Chainable对象
431
- * @returns {Object} 原始 Schema
432
- */
433
- static _extractSchema(chainable) {
434
- const schema = {};
435
- for (const key in chainable) {
436
- if (chainable.hasOwnProperty(key) && key !== '_isChainable') {
437
- schema[key] = chainable[key];
438
- }
439
- }
440
- return schema;
441
- }
442
- }
443
-
444
- module.exports = SchemaUtils;
445
-