schema-dsl 2.3.0 → 2.3.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.
@@ -45,42 +45,13 @@ class SchemaUtils {
45
45
  return fragments;
46
46
  }
47
47
 
48
- // ========== Schema合并 ==========
49
-
50
- /**
51
- * 合并多个Schema
52
- * @param {...Object} schemas - 要合并的Schema对象
53
- * @returns {Object} 合并后的Schema
54
- *
55
- * @example
56
- * const baseUser = dsl({ name: 'string!', email: 'email!' });
57
- * const withAge = dsl({ age: 'number:18-120' });
58
- * const merged = SchemaUtils.merge(baseUser, withAge);
59
- */
60
- static merge(...schemas) {
61
- const result = {
62
- type: 'object',
63
- properties: {},
64
- required: []
65
- };
66
-
67
- schemas.forEach(schema => {
68
- if (schema.properties) {
69
- Object.assign(result.properties, schema.properties);
70
- }
71
- if (schema.required) {
72
- result.required = [...new Set([...result.required, ...schema.required])];
73
- }
74
- });
75
-
76
- return result;
77
- }
48
+ // ========== Schema复用和扩展 ==========
78
49
 
79
50
  /**
80
51
  * 扩展Schema(类似继承)
81
52
  * @param {Object} baseSchema - 基础Schema
82
53
  * @param {Object} extensions - 扩展定义
83
- * @returns {Object} 扩展后的Schema
54
+ * @returns {Object} 扩展后的Schema(支持链式调用)
84
55
  *
85
56
  * @example
86
57
  * const baseUser = dsl({ name: 'string!', email: 'email!' });
@@ -90,12 +61,35 @@ class SchemaUtils {
90
61
  * });
91
62
  */
92
63
  static extend(baseSchema, extensions) {
93
- const { dsl } = require('../adapters/DslAdapter');
64
+ const dsl = require('../adapters/DslAdapter');
94
65
  const extensionSchema = typeof extensions === 'function'
95
66
  ? extensions
96
67
  : dsl(extensions);
97
68
 
98
- return this.merge(baseSchema, extensionSchema);
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);
99
93
  }
100
94
 
101
95
  /**
@@ -124,19 +118,86 @@ class SchemaUtils {
124
118
  }
125
119
  });
126
120
 
127
- return result;
121
+ return this._makeChainable(result);
128
122
  }
129
123
 
130
124
  /**
131
125
  * 排除Schema的部分字段
132
126
  * @param {Object} schema - 原始Schema
133
127
  * @param {string[]} fields - 要排除的字段
134
- * @returns {Object} 新Schema
128
+ * @returns {Object} 新Schema(支持链式调用)
135
129
  */
136
130
  static omit(schema, fields) {
137
- const allFields = Object.keys(schema.properties || {});
138
- const keepFields = allFields.filter(f => !fields.includes(f));
139
- return this.pick(schema, keepFields);
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);
140
201
  }
141
202
 
142
203
  // ========== 性能监控 ==========
@@ -307,6 +368,77 @@ class SchemaUtils {
307
368
  static clone(schema) {
308
369
  return JSON.parse(JSON.stringify(schema));
309
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
+ }
310
442
  }
311
443
 
312
444
  module.exports = SchemaUtils;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schema-dsl",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "简洁强大的JSON Schema验证库 - DSL语法 + String扩展 + 便捷validate",
5
5
  "main": "index.js",
6
6
  "exports": {