@x0k/json-schema-merge 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/json-schema/merge/merge.d.ts.map +1 -1
- package/dist/lib/json-schema/merge/merge.js +41 -1
- package/dist/lib/json-schema/merge/merge.js.map +1 -1
- package/package.json +7 -2
- package/src/index.ts +1 -0
- package/src/lib/array.ts +172 -0
- package/src/lib/function.ts +3 -0
- package/src/lib/json-schema/compare/compare.ts +343 -0
- package/src/lib/json-schema/compare/index.ts +1 -0
- package/src/lib/json-schema/index.ts +5 -0
- package/src/lib/json-schema/json-schema.ts +113 -0
- package/src/lib/json-schema/merge/all-of-merge.ts +44 -0
- package/src/lib/json-schema/merge/index.ts +3 -0
- package/src/lib/json-schema/merge/merge.ts +922 -0
- package/src/lib/json-schema/merge/patterns.ts +7 -0
- package/src/lib/json-schema/transform.ts +99 -0
- package/src/lib/json-schema/traverse.ts +61 -0
- package/src/lib/math.ts +3 -0
- package/src/lib/memoize.ts +24 -0
- package/src/lib/object.ts +16 -0
- package/src/lib/ord.ts +16 -0
- package/src/lib/traverser.ts +4 -0
|
@@ -0,0 +1,922 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
JSONSchema7,
|
|
3
|
+
JSONSchema7Definition,
|
|
4
|
+
JSONSchema7Type,
|
|
5
|
+
JSONSchema7TypeName,
|
|
6
|
+
} from "json-schema";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
intersection,
|
|
10
|
+
union,
|
|
11
|
+
type Deduplicator,
|
|
12
|
+
type Intersector,
|
|
13
|
+
} from "../../array.ts";
|
|
14
|
+
import { identity } from "../../function.ts";
|
|
15
|
+
import { lcm } from "../../math.ts";
|
|
16
|
+
import { isAllowAnySchema } from "../json-schema.ts";
|
|
17
|
+
|
|
18
|
+
import { simplePatternsMerger } from "./patterns.ts";
|
|
19
|
+
|
|
20
|
+
type SchemaKey = keyof JSONSchema7;
|
|
21
|
+
|
|
22
|
+
function createPairCombinations<T, R>(
|
|
23
|
+
l: T[],
|
|
24
|
+
r: T[],
|
|
25
|
+
action: (a: T, b: T) => R
|
|
26
|
+
) {
|
|
27
|
+
const ll = l.length;
|
|
28
|
+
const rl = r.length;
|
|
29
|
+
if (ll > 0 && rl > 0) {
|
|
30
|
+
for (let i = 0; i < ll; i++) {
|
|
31
|
+
const lv = l[i]!;
|
|
32
|
+
for (let j = 0; j < rl; j++) {
|
|
33
|
+
action(lv, r[j]!);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function mergeBooleans(l: boolean, r: boolean) {
|
|
40
|
+
return l || r;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createRecordsMerge<T>(merge: (l: T, r: T) => T) {
|
|
44
|
+
return (left: Record<string, T>, right: Record<string, T>) => {
|
|
45
|
+
const target = { ...left };
|
|
46
|
+
const keys = Object.keys(right);
|
|
47
|
+
const l = keys.length;
|
|
48
|
+
for (let i = 0; i < l; i++) {
|
|
49
|
+
const key = keys[i]!;
|
|
50
|
+
target[key] =
|
|
51
|
+
left[key] === undefined ? right[key]! : merge(left[key], right[key]!);
|
|
52
|
+
}
|
|
53
|
+
return target;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* An assigner function operates at the schema-object level.
|
|
59
|
+
* It receives the partially merged `target` and the original
|
|
60
|
+
* `left` and `right` schemas.
|
|
61
|
+
*
|
|
62
|
+
* In most cases, it modifies and returns the `target` object,
|
|
63
|
+
* but it may also return a completely new schema object if needed.
|
|
64
|
+
*
|
|
65
|
+
* Assigners are used for keywords that cannot be merged by simple
|
|
66
|
+
* value-level functions, often because they interact with other
|
|
67
|
+
* keywords or require holistic decisions.
|
|
68
|
+
*/
|
|
69
|
+
export type Assigner<R extends {}> = (target: R, l: R, r: R) => R;
|
|
70
|
+
|
|
71
|
+
function createMap<R>(items: Iterable<[SchemaKey[], R]>) {
|
|
72
|
+
const map = new Map<SchemaKey, R>();
|
|
73
|
+
for (const pair of items) {
|
|
74
|
+
for (const key of pair[0]) {
|
|
75
|
+
map.set(key, pair[1]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return map;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function assignSchemaDefinitionOrRecordOfSchemaDefinitions<
|
|
82
|
+
K extends {
|
|
83
|
+
[T in SchemaKey]: JSONSchema7[T] extends
|
|
84
|
+
JSONSchema7Definition | Record<string, JSONSchema7Definition> | undefined
|
|
85
|
+
? T
|
|
86
|
+
: never;
|
|
87
|
+
}[SchemaKey],
|
|
88
|
+
>(target: JSONSchema7, key: K, value: JSONSchema7[K]) {
|
|
89
|
+
if (value === undefined || isAllowAnySchema(value)) {
|
|
90
|
+
delete target[key];
|
|
91
|
+
} else {
|
|
92
|
+
target[key] = value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const PROPERTIES_ASSIGNER_KEYS = [
|
|
97
|
+
"properties",
|
|
98
|
+
"patternProperties",
|
|
99
|
+
"additionalProperties",
|
|
100
|
+
] as const satisfies SchemaKey[];
|
|
101
|
+
|
|
102
|
+
interface CompiledPattern {
|
|
103
|
+
regExp: RegExp;
|
|
104
|
+
schema: JSONSchema7Definition;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function compilePatterns(patterns: Record<string, JSONSchema7Definition>) {
|
|
108
|
+
const keys = Object.keys(patterns);
|
|
109
|
+
const l = keys.length;
|
|
110
|
+
const result: CompiledPattern[] = [];
|
|
111
|
+
for (let i = 0; i < l; i++) {
|
|
112
|
+
const source = keys[i]!;
|
|
113
|
+
result.push({
|
|
114
|
+
regExp: new RegExp(source),
|
|
115
|
+
schema: patterns[source]!,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return [result, keys] as const;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const EMPTY_PATTERNS_AND_KEYS: [CompiledPattern[], string[]] = [[], []];
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @returns `true` when `false` schema occurred
|
|
125
|
+
*/
|
|
126
|
+
function appendKeyConstraints(
|
|
127
|
+
target: (JSONSchema7 | true)[],
|
|
128
|
+
key: string,
|
|
129
|
+
patterns: CompiledPattern[]
|
|
130
|
+
): boolean {
|
|
131
|
+
const l = patterns.length;
|
|
132
|
+
for (let i = 0; i < l; i++) {
|
|
133
|
+
const p = patterns[i]!;
|
|
134
|
+
if (!p.regExp.test(key)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const s = p.schema;
|
|
138
|
+
if (s === false) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
target.push(s);
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ITEMS_ASSIGNER_KEYS = [
|
|
147
|
+
"items",
|
|
148
|
+
"additionalItems",
|
|
149
|
+
] as const satisfies SchemaKey[];
|
|
150
|
+
|
|
151
|
+
const CONDITION_ASSIGNER_KEYS = [
|
|
152
|
+
"if",
|
|
153
|
+
"then",
|
|
154
|
+
"else",
|
|
155
|
+
] as const satisfies SchemaKey[];
|
|
156
|
+
|
|
157
|
+
const CONTAINS_ASSIGNER_KEYS = ["contains"] as const satisfies SchemaKey[];
|
|
158
|
+
|
|
159
|
+
function assignCondition(target: JSONSchema7, source: JSONSchema7) {
|
|
160
|
+
if (source.if !== undefined) {
|
|
161
|
+
target.if = source.if;
|
|
162
|
+
}
|
|
163
|
+
if (source.then !== undefined) {
|
|
164
|
+
target.then = source.then;
|
|
165
|
+
}
|
|
166
|
+
if (source.else !== undefined) {
|
|
167
|
+
target.else = source.else;
|
|
168
|
+
}
|
|
169
|
+
return target;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
type AssignerKey =
|
|
173
|
+
| (typeof PROPERTIES_ASSIGNER_KEYS)[number]
|
|
174
|
+
| (typeof ITEMS_ASSIGNER_KEYS)[number]
|
|
175
|
+
| (typeof CONDITION_ASSIGNER_KEYS)[number]
|
|
176
|
+
| (typeof CONTAINS_ASSIGNER_KEYS)[number];
|
|
177
|
+
|
|
178
|
+
function intersectSchemaTypes(
|
|
179
|
+
a: JSONSchema7TypeName,
|
|
180
|
+
b: JSONSchema7TypeName
|
|
181
|
+
): JSONSchema7TypeName | undefined {
|
|
182
|
+
if (a === b) {
|
|
183
|
+
return a;
|
|
184
|
+
}
|
|
185
|
+
switch (a) {
|
|
186
|
+
case "number": {
|
|
187
|
+
if (b === "integer") {
|
|
188
|
+
return "integer";
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// eslint-disable-next-line no-fallthrough
|
|
192
|
+
case "integer": {
|
|
193
|
+
if (b === "number") {
|
|
194
|
+
return "integer";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// eslint-disable-next-line no-fallthrough
|
|
198
|
+
default:
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* A merger function combines two values for a specific JSON Schema keyword.
|
|
205
|
+
*/
|
|
206
|
+
export type Merger<T> = (a: T, b: T) => T;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* A validation function that ensures consistency between two schema keywords.
|
|
210
|
+
*/
|
|
211
|
+
export type Check<K extends SchemaKey> = (
|
|
212
|
+
target: Required<Pick<JSONSchema7, K>>
|
|
213
|
+
) => boolean;
|
|
214
|
+
|
|
215
|
+
export type CheckEntry<A extends SchemaKey, B extends SchemaKey> = readonly [
|
|
216
|
+
A,
|
|
217
|
+
B,
|
|
218
|
+
Check<A | B>,
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
export function check<A extends SchemaKey, B extends SchemaKey>(
|
|
222
|
+
a: A,
|
|
223
|
+
b: B,
|
|
224
|
+
check: Check<A | B>
|
|
225
|
+
): CheckEntry<A, B> {
|
|
226
|
+
return [a, b, check];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function createChecksMap(checks: Iterable<CheckEntry<SchemaKey, SchemaKey>>) {
|
|
230
|
+
const map = new Map<
|
|
231
|
+
SchemaKey,
|
|
232
|
+
{ oppositeKey: SchemaKey; check: (target: JSONSchema7) => void }[]
|
|
233
|
+
>();
|
|
234
|
+
for (const [a, b, check] of checks) {
|
|
235
|
+
const fn = (target: JSONSchema7) => {
|
|
236
|
+
if (!check(target as Required<JSONSchema7>)) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Schema keys '${a}' and '${b}' are conflicting (${a}: ${JSON.stringify(target[a])}, ${b}: ${JSON.stringify(target[b])})`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
for (const k of [
|
|
243
|
+
[a, b],
|
|
244
|
+
[b, a],
|
|
245
|
+
]) {
|
|
246
|
+
let arr = map.get(k[0]);
|
|
247
|
+
if (arr === undefined) {
|
|
248
|
+
arr = [];
|
|
249
|
+
map.set(k[0], arr);
|
|
250
|
+
}
|
|
251
|
+
arr.push({ oppositeKey: k[1], check: fn });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return map;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface MergeOptions {
|
|
258
|
+
/**
|
|
259
|
+
* Custom function to test whether a regular expression `subExpr`
|
|
260
|
+
* is considered a subset of another `superExpr`.
|
|
261
|
+
* @default Object.is
|
|
262
|
+
*/
|
|
263
|
+
isSubRegExp?: (subExpr: string, superExpr: string) => boolean;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Merger function for combining regular expression patterns
|
|
267
|
+
* @default simplePatternsMerger
|
|
268
|
+
*/
|
|
269
|
+
mergePatterns?: Merger<string>;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Intersector function for merging JSON values (enum keyword)
|
|
273
|
+
* @default intersection
|
|
274
|
+
*/
|
|
275
|
+
intersectJson?: Intersector<JSONSchema7Type>;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Deduplication strategy for JSON Schema definitions.
|
|
279
|
+
* @default identity
|
|
280
|
+
*/
|
|
281
|
+
deduplicateJsonSchemaDef?: Deduplicator<JSONSchema7Definition>;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Fallback merger applied when no keyword-specific merger is defined.
|
|
285
|
+
* @default identity
|
|
286
|
+
*/
|
|
287
|
+
defaultMerger?: Merger<any>;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* A mapping of schema keywords to merger functions.
|
|
291
|
+
*
|
|
292
|
+
* - A merger operates on **values of a single keyword** (`a`, `b` → merged value).
|
|
293
|
+
* - When provided, a custom merger **overrides the default merger** for that keyword.
|
|
294
|
+
*/
|
|
295
|
+
mergers?: Partial<{
|
|
296
|
+
[K in SchemaKey]: Merger<Exclude<JSONSchema7[K], undefined>>;
|
|
297
|
+
}>;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* A collection of keyword groups with associated assigner functions.
|
|
301
|
+
*
|
|
302
|
+
* - An assigner operates at the **schema-object level** (`target`, `left`, `right`).
|
|
303
|
+
* - Custom assigners are **appended** to the default assigners,
|
|
304
|
+
* but can also **replace behavior** for specific keywords if they overlap.
|
|
305
|
+
*/
|
|
306
|
+
assigners?: Iterable<[SchemaKey[], Assigner<JSONSchema7>]>;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Consistency checks to validate relationships between
|
|
310
|
+
* pairs of schema keywords (e.g. `minimum` ≤ `maximum`).
|
|
311
|
+
*
|
|
312
|
+
* - A check ensures that two related keywords do not conflict.
|
|
313
|
+
* - Providing this option **replaces the default checks** completely.
|
|
314
|
+
*
|
|
315
|
+
* @default DEFAULT_CHECKS
|
|
316
|
+
*/
|
|
317
|
+
checks?: Iterable<CheckEntry<SchemaKey, SchemaKey>>;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export const DEFAULT_CHECKS = [
|
|
321
|
+
check("minimum", "maximum", (t) => t.maximum >= t.minimum),
|
|
322
|
+
check("exclusiveMinimum", "maximum", (t) => t.maximum > t.exclusiveMinimum),
|
|
323
|
+
check("minimum", "exclusiveMaximum", (t) => t.exclusiveMaximum > t.minimum),
|
|
324
|
+
check(
|
|
325
|
+
"exclusiveMinimum",
|
|
326
|
+
"exclusiveMaximum",
|
|
327
|
+
(t) => t.exclusiveMaximum > t.exclusiveMinimum
|
|
328
|
+
),
|
|
329
|
+
check("minLength", "maxLength", (t) => t.maxLength >= t.minLength),
|
|
330
|
+
check("minItems", "maxItems", (t) => t.maxItems >= t.minItems),
|
|
331
|
+
check(
|
|
332
|
+
"minProperties",
|
|
333
|
+
"maxProperties",
|
|
334
|
+
(t) => t.maxProperties >= t.minProperties
|
|
335
|
+
),
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
export function createMerger({
|
|
339
|
+
mergePatterns = simplePatternsMerger,
|
|
340
|
+
isSubRegExp = Object.is,
|
|
341
|
+
intersectJson = intersection,
|
|
342
|
+
deduplicateJsonSchemaDef = identity,
|
|
343
|
+
defaultMerger = identity,
|
|
344
|
+
assigners = [],
|
|
345
|
+
checks = DEFAULT_CHECKS,
|
|
346
|
+
mergers,
|
|
347
|
+
}: MergeOptions = {}) {
|
|
348
|
+
function mergeArrayOfSchemaDefinitions(
|
|
349
|
+
schemas: JSONSchema7Definition[]
|
|
350
|
+
): JSONSchema7Definition {
|
|
351
|
+
const l = schemas.length;
|
|
352
|
+
let result = schemas[0]!;
|
|
353
|
+
for (let i = 1; i < l; i++) {
|
|
354
|
+
const r = mergeSchemaDefinitions(result, schemas[i]!);
|
|
355
|
+
if (r === false) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
if (isAllowAnySchema(r)) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
result = r;
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function createProperty(
|
|
367
|
+
constraints: (JSONSchema7 | true)[],
|
|
368
|
+
key: string,
|
|
369
|
+
value: JSONSchema7Definition,
|
|
370
|
+
patterns: CompiledPattern[],
|
|
371
|
+
oppositeValue: JSONSchema7Definition | undefined,
|
|
372
|
+
oppositePatterns: CompiledPattern[],
|
|
373
|
+
oppositeAdditional: JSONSchema7 | false | undefined
|
|
374
|
+
): JSONSchema7Definition | undefined {
|
|
375
|
+
constraints.length = 0;
|
|
376
|
+
if (value === false) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
constraints.push(value);
|
|
380
|
+
const isOppositeValueDefined = oppositeValue !== undefined;
|
|
381
|
+
if (isOppositeValueDefined) {
|
|
382
|
+
if (oppositeValue === false) {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
constraints.push(oppositeValue);
|
|
386
|
+
}
|
|
387
|
+
if (appendKeyConstraints(constraints, key, oppositePatterns)) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
const isNotYetAllowed = constraints.length < 2;
|
|
391
|
+
if (oppositeAdditional === false) {
|
|
392
|
+
// There are no allowing constraints from opposite side -> drop property
|
|
393
|
+
if (isNotYetAllowed) {
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
// Applying patterns of current schema cause they may disappear
|
|
397
|
+
if (appendKeyConstraints(constraints, key, patterns)) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
} else if (isNotYetAllowed && oppositeAdditional !== undefined) {
|
|
401
|
+
constraints.push(oppositeAdditional);
|
|
402
|
+
}
|
|
403
|
+
const l = constraints.length;
|
|
404
|
+
if (l === 1) {
|
|
405
|
+
return constraints[0];
|
|
406
|
+
}
|
|
407
|
+
return mergeArrayOfSchemaDefinitions(constraints);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function assignPatternPropertiesAndAdditionalPropertiesMerge(
|
|
411
|
+
target: Record<string, JSONSchema7Definition>,
|
|
412
|
+
patterns: Record<string, JSONSchema7Definition> | undefined,
|
|
413
|
+
patternKeys: string[],
|
|
414
|
+
matchedPatterns: Set<string>,
|
|
415
|
+
oppositeAdditional: JSONSchema7Definition,
|
|
416
|
+
isOppositeTruthy: boolean
|
|
417
|
+
) {
|
|
418
|
+
const l = patternKeys.length;
|
|
419
|
+
if (l > 0 && oppositeAdditional !== false) {
|
|
420
|
+
if (isOppositeTruthy) {
|
|
421
|
+
// TODO: in some cases we can just assign new value instead of copying
|
|
422
|
+
Object.assign(target, patterns);
|
|
423
|
+
} else {
|
|
424
|
+
for (let i = 0; i < l; i++) {
|
|
425
|
+
const pattern = patternKeys[i]!;
|
|
426
|
+
if (matchedPatterns.has(pattern)) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
target[pattern] = mergeSchemaDefinitions(
|
|
430
|
+
patterns![pattern]!,
|
|
431
|
+
oppositeAdditional
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return target;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const propertiesAssigner: Assigner<JSONSchema7> = (
|
|
440
|
+
target,
|
|
441
|
+
{
|
|
442
|
+
properties: lProps = {},
|
|
443
|
+
patternProperties: lPatterns,
|
|
444
|
+
additionalProperties: lAdditional = true,
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
properties: rProps = {},
|
|
448
|
+
patternProperties: rPatterns,
|
|
449
|
+
additionalProperties: rAdditional = true,
|
|
450
|
+
}
|
|
451
|
+
) => {
|
|
452
|
+
// Special case
|
|
453
|
+
const isLAddTruthy = isAllowAnySchema(lAdditional);
|
|
454
|
+
const isRAddTruthy = isAllowAnySchema(rAdditional);
|
|
455
|
+
if (isLAddTruthy && isRAddTruthy) {
|
|
456
|
+
assignSchemaDefinitionOrRecordOfSchemaDefinitions(
|
|
457
|
+
target,
|
|
458
|
+
"properties",
|
|
459
|
+
mergeRecordsOfSchemaDefinitions(lProps, rProps)
|
|
460
|
+
);
|
|
461
|
+
assignSchemaDefinitionOrRecordOfSchemaDefinitions(
|
|
462
|
+
target,
|
|
463
|
+
"patternProperties",
|
|
464
|
+
lPatterns && rPatterns
|
|
465
|
+
? mergeRecordsOfSchemaDefinitions(lPatterns, rPatterns)
|
|
466
|
+
: (lPatterns ?? rPatterns)
|
|
467
|
+
);
|
|
468
|
+
delete target.additionalProperties;
|
|
469
|
+
return target;
|
|
470
|
+
}
|
|
471
|
+
// Additional Properties
|
|
472
|
+
const additionalProperties = mergeSchemaDefinitions(
|
|
473
|
+
lAdditional,
|
|
474
|
+
rAdditional
|
|
475
|
+
);
|
|
476
|
+
assignSchemaDefinitionOrRecordOfSchemaDefinitions(
|
|
477
|
+
target,
|
|
478
|
+
"additionalProperties",
|
|
479
|
+
additionalProperties
|
|
480
|
+
);
|
|
481
|
+
// Properties
|
|
482
|
+
const properties: Record<string, JSONSchema7Definition> = {};
|
|
483
|
+
const lKeys = Object.keys(lProps);
|
|
484
|
+
const lKeysLen = lKeys.length;
|
|
485
|
+
const [lCompiledPatterns, lPatternKeys] = lPatterns
|
|
486
|
+
? compilePatterns(lPatterns)
|
|
487
|
+
: EMPTY_PATTERNS_AND_KEYS;
|
|
488
|
+
const [rCompiledPatterns, rPatternKeys] = rPatterns
|
|
489
|
+
? compilePatterns(rPatterns)
|
|
490
|
+
: EMPTY_PATTERNS_AND_KEYS;
|
|
491
|
+
const constraints: (JSONSchema7 | true)[] = [];
|
|
492
|
+
const lKeysSet = new Set<string>();
|
|
493
|
+
const mappedRAdditional = isRAddTruthy ? undefined : rAdditional;
|
|
494
|
+
for (let i = 0; i < lKeysLen; i++) {
|
|
495
|
+
const key = lKeys[i]!;
|
|
496
|
+
lKeysSet.add(key);
|
|
497
|
+
const prop = createProperty(
|
|
498
|
+
constraints,
|
|
499
|
+
key,
|
|
500
|
+
lProps[key]!,
|
|
501
|
+
lCompiledPatterns,
|
|
502
|
+
rProps[key],
|
|
503
|
+
rCompiledPatterns,
|
|
504
|
+
mappedRAdditional
|
|
505
|
+
);
|
|
506
|
+
if (prop !== undefined) {
|
|
507
|
+
properties[key] = prop;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
const rKeys = Object.keys(rProps);
|
|
511
|
+
const rKeysLen = rKeys.length;
|
|
512
|
+
const mappedLAdditional = isLAddTruthy ? undefined : lAdditional;
|
|
513
|
+
for (let i = 0; i < rKeysLen; i++) {
|
|
514
|
+
const key = rKeys[i]!;
|
|
515
|
+
if (lKeysSet.has(key)) {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const prop = createProperty(
|
|
519
|
+
constraints,
|
|
520
|
+
key,
|
|
521
|
+
rProps[key]!,
|
|
522
|
+
rCompiledPatterns,
|
|
523
|
+
undefined,
|
|
524
|
+
lCompiledPatterns,
|
|
525
|
+
mappedLAdditional
|
|
526
|
+
);
|
|
527
|
+
if (prop !== undefined) {
|
|
528
|
+
properties[key] = prop;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
assignSchemaDefinitionOrRecordOfSchemaDefinitions(
|
|
532
|
+
target,
|
|
533
|
+
"properties",
|
|
534
|
+
properties
|
|
535
|
+
);
|
|
536
|
+
// Pattern Properties
|
|
537
|
+
// (lPatterns and rPatterns) or (lPatterns and rAdditional) or (rPatterns and lAdditional)
|
|
538
|
+
let patterns: Record<string, JSONSchema7Definition> = {};
|
|
539
|
+
const matchedPatterns = new Set<string>();
|
|
540
|
+
if (lPatternKeys.length > 0 && rPatternKeys.length > 0) {
|
|
541
|
+
createPairCombinations(lPatternKeys, rPatternKeys, (lKey, rKey) => {
|
|
542
|
+
if (isSubRegExp(lKey, rKey)) {
|
|
543
|
+
matchedPatterns.add(lKey);
|
|
544
|
+
}
|
|
545
|
+
if (isSubRegExp(rKey, lKey)) {
|
|
546
|
+
matchedPatterns.add(rKey);
|
|
547
|
+
}
|
|
548
|
+
patterns[mergePatterns(lKey, rKey)] = mergeSchemaDefinitions(
|
|
549
|
+
lPatterns![lKey]!,
|
|
550
|
+
rPatterns![rKey]!
|
|
551
|
+
);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
patterns = assignPatternPropertiesAndAdditionalPropertiesMerge(
|
|
555
|
+
patterns,
|
|
556
|
+
lPatterns,
|
|
557
|
+
lPatternKeys,
|
|
558
|
+
matchedPatterns,
|
|
559
|
+
rAdditional,
|
|
560
|
+
isRAddTruthy
|
|
561
|
+
);
|
|
562
|
+
patterns = assignPatternPropertiesAndAdditionalPropertiesMerge(
|
|
563
|
+
patterns,
|
|
564
|
+
rPatterns,
|
|
565
|
+
rPatternKeys,
|
|
566
|
+
matchedPatterns,
|
|
567
|
+
lAdditional,
|
|
568
|
+
isLAddTruthy
|
|
569
|
+
);
|
|
570
|
+
assignSchemaDefinitionOrRecordOfSchemaDefinitions(
|
|
571
|
+
target,
|
|
572
|
+
"patternProperties",
|
|
573
|
+
patterns
|
|
574
|
+
);
|
|
575
|
+
return target;
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
const itemsAssigner: Assigner<JSONSchema7> = (
|
|
579
|
+
target,
|
|
580
|
+
// NOTE: Schema that has `additionalItems` without an `items` keyword is invalid
|
|
581
|
+
// so the assigner should be triggered only be colliding `items` properties
|
|
582
|
+
// so default values are used only for type narrowing
|
|
583
|
+
{ items: lItems = [], additionalItems: lAdditional },
|
|
584
|
+
{ items: rItems = [], additionalItems: rAdditional }
|
|
585
|
+
) => {
|
|
586
|
+
const isLArr = Array.isArray(lItems);
|
|
587
|
+
const isRArr = Array.isArray(rItems);
|
|
588
|
+
const itemsArray: JSONSchema7Definition[] = [];
|
|
589
|
+
target.items = itemsArray;
|
|
590
|
+
if (isLArr && isRArr) {
|
|
591
|
+
const [l, additional, tail] =
|
|
592
|
+
lItems.length < rItems.length
|
|
593
|
+
? [lItems.length, lAdditional, rItems]
|
|
594
|
+
: [rItems.length, rAdditional, lItems];
|
|
595
|
+
let i = 0;
|
|
596
|
+
for (; i < l; i++) {
|
|
597
|
+
itemsArray.push(mergeSchemaDefinitions(lItems[i]!, rItems[i]!));
|
|
598
|
+
}
|
|
599
|
+
if (additional === false) {
|
|
600
|
+
target.additionalItems = false;
|
|
601
|
+
} else {
|
|
602
|
+
const isAdditionalTruthy =
|
|
603
|
+
additional === undefined || isAllowAnySchema(additional);
|
|
604
|
+
for (; i < tail.length; i++) {
|
|
605
|
+
itemsArray.push(
|
|
606
|
+
isAdditionalTruthy
|
|
607
|
+
? tail[i]!
|
|
608
|
+
: mergeSchemaDefinitions(tail[i]!, additional)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
assignSchemaDefinitionOrRecordOfSchemaDefinitions(
|
|
612
|
+
target,
|
|
613
|
+
"additionalItems",
|
|
614
|
+
lAdditional !== undefined && rAdditional !== undefined
|
|
615
|
+
? mergeSchemaDefinitions(lAdditional, rAdditional)
|
|
616
|
+
: (lAdditional ?? rAdditional)
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
} else if (isLArr || isRArr) {
|
|
620
|
+
const [arr, item, additional] = (
|
|
621
|
+
isLArr ? [lItems, rItems, lAdditional] : [rItems, lItems, rAdditional]
|
|
622
|
+
) as [
|
|
623
|
+
JSONSchema7Definition[],
|
|
624
|
+
JSONSchema7Definition,
|
|
625
|
+
JSONSchema7Definition | undefined,
|
|
626
|
+
];
|
|
627
|
+
assignSchemaDefinitionOrRecordOfSchemaDefinitions(
|
|
628
|
+
target,
|
|
629
|
+
"additionalItems",
|
|
630
|
+
additional && mergeSchemaDefinitions(additional, item)
|
|
631
|
+
);
|
|
632
|
+
for (let i = 0; i < arr.length; i++) {
|
|
633
|
+
itemsArray.push(mergeSchemaDefinitions(arr[i]!, item));
|
|
634
|
+
}
|
|
635
|
+
} else {
|
|
636
|
+
delete target.additionalItems;
|
|
637
|
+
target.items = mergeSchemaDefinitions(lItems, rItems);
|
|
638
|
+
}
|
|
639
|
+
return target;
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
const conditionAssigner: Assigner<JSONSchema7> = (target, l, r) => {
|
|
643
|
+
assignCondition(target, l);
|
|
644
|
+
const cond = assignCondition({}, r);
|
|
645
|
+
if (target.allOf === undefined) {
|
|
646
|
+
target.allOf = [cond];
|
|
647
|
+
} else {
|
|
648
|
+
target.allOf = target.allOf.concat(cond);
|
|
649
|
+
}
|
|
650
|
+
return target;
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
const containsAssigner: Assigner<JSONSchema7> = (target, l, r) => {
|
|
654
|
+
const lContains = l.contains!;
|
|
655
|
+
const rContains = r.contains!;
|
|
656
|
+
// Cheapest hot path: identical reference (target already holds it via spread).
|
|
657
|
+
if (lContains === rContains) return target;
|
|
658
|
+
// `contains: false` rejects every array, so it dominates the conjunction
|
|
659
|
+
// (non-array instances ignore `contains` on both sides).
|
|
660
|
+
if (lContains === false || rContains === false) {
|
|
661
|
+
target.contains = false;
|
|
662
|
+
return target;
|
|
663
|
+
}
|
|
664
|
+
// `contains: true` (or `{}`) only requires a non-empty array, which is
|
|
665
|
+
// already implied by any other `contains`, so the other side wins.
|
|
666
|
+
// (If both sides allow any, either one is equivalent.)
|
|
667
|
+
if (isAllowAnySchema(lContains)) {
|
|
668
|
+
target.contains = rContains;
|
|
669
|
+
return target;
|
|
670
|
+
}
|
|
671
|
+
if (isAllowAnySchema(rContains)) {
|
|
672
|
+
target.contains = lContains;
|
|
673
|
+
return target;
|
|
674
|
+
}
|
|
675
|
+
// Idempotence: `contains: C` ∧ `contains: C` ≡ `contains: C`
|
|
676
|
+
// (target already holds `lContains` via spread).
|
|
677
|
+
if (deduplicateJsonSchemaDef([lContains, rContains]).length === 1) {
|
|
678
|
+
return target;
|
|
679
|
+
}
|
|
680
|
+
// Existential conjunction cannot be expressed as a single `contains`
|
|
681
|
+
// (`∃i C1(i) ∧ ∃j C2(j)` allows `i ≠ j`, while `∃k C1(k) ∧ C2(k)`
|
|
682
|
+
// requires a single witness), so preserve both branches exactly.
|
|
683
|
+
// Like `conditionAssigner`, keep left at root and move only right to `allOf`.
|
|
684
|
+
const branch: JSONSchema7Definition = { contains: rContains };
|
|
685
|
+
target.allOf =
|
|
686
|
+
target.allOf === undefined
|
|
687
|
+
? [branch]
|
|
688
|
+
: deduplicateJsonSchemaDef(target.allOf.concat(branch));
|
|
689
|
+
return target;
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
function mergeArraysOfSchemaDefinition(
|
|
693
|
+
l: JSONSchema7Definition[],
|
|
694
|
+
r: JSONSchema7Definition[]
|
|
695
|
+
) {
|
|
696
|
+
const definitions: JSONSchema7Definition[] = [];
|
|
697
|
+
createPairCombinations(l, r, (a, b) => {
|
|
698
|
+
try {
|
|
699
|
+
definitions.push(mergeSchemaDefinitions(a, b));
|
|
700
|
+
} catch {}
|
|
701
|
+
});
|
|
702
|
+
if (definitions.length === 0) {
|
|
703
|
+
throw new Error(
|
|
704
|
+
`No valid schema combinations could be produced for "${JSON.stringify(l)}" and "${JSON.stringify(r)}"; the merged result is empty`
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
return deduplicateJsonSchemaDef(definitions);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const ASSIGNERS_MAP = createMap([
|
|
711
|
+
[PROPERTIES_ASSIGNER_KEYS, propertiesAssigner],
|
|
712
|
+
[ITEMS_ASSIGNER_KEYS, itemsAssigner],
|
|
713
|
+
[CONDITION_ASSIGNER_KEYS, conditionAssigner],
|
|
714
|
+
[CONTAINS_ASSIGNER_KEYS, containsAssigner],
|
|
715
|
+
...assigners,
|
|
716
|
+
]);
|
|
717
|
+
|
|
718
|
+
const CHECKS_MAP = createChecksMap(checks);
|
|
719
|
+
|
|
720
|
+
function mergeSchemaDefinitions(
|
|
721
|
+
left: JSONSchema7Definition,
|
|
722
|
+
right: JSONSchema7Definition
|
|
723
|
+
) {
|
|
724
|
+
if (left === false || right === false) {
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
if (isAllowAnySchema(left)) {
|
|
728
|
+
if (isAllowAnySchema(right)) {
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
return right;
|
|
732
|
+
}
|
|
733
|
+
if (isAllowAnySchema(right)) {
|
|
734
|
+
return left;
|
|
735
|
+
}
|
|
736
|
+
let target = { ...left };
|
|
737
|
+
const assigners = new Set<Assigner<JSONSchema7>>();
|
|
738
|
+
const checks = new Set<(target: JSONSchema7) => void>();
|
|
739
|
+
const rKeys = Reflect.ownKeys(right) as SchemaKey[];
|
|
740
|
+
const l = rKeys.length;
|
|
741
|
+
for (let i = 0; i < l; i++) {
|
|
742
|
+
const rKey = rKeys[i]!;
|
|
743
|
+
const rv = right[rKey];
|
|
744
|
+
if (rv === undefined) {
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
const checkData = CHECKS_MAP.get(rKey);
|
|
748
|
+
if (checkData !== undefined) {
|
|
749
|
+
const l = checkData.length;
|
|
750
|
+
for (let j = 0; j < l; j++) {
|
|
751
|
+
const item = checkData[j];
|
|
752
|
+
if (left[item.oppositeKey] !== undefined) {
|
|
753
|
+
checks.add(item.check);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
const lv = left[rKey];
|
|
758
|
+
if (lv === undefined) {
|
|
759
|
+
// @ts-expect-error too complex
|
|
760
|
+
target[rKey] = rv;
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const assign = ASSIGNERS_MAP.get(rKey);
|
|
764
|
+
if (assign) {
|
|
765
|
+
assigners.add(assign);
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
const merge = MERGERS[rKey] ?? defaultMerger;
|
|
769
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
770
|
+
target[rKey] = merge(lv as never, rv as never);
|
|
771
|
+
}
|
|
772
|
+
for (const assign of assigners) {
|
|
773
|
+
target = assign(target, left, right);
|
|
774
|
+
}
|
|
775
|
+
for (const check of checks) {
|
|
776
|
+
check(target);
|
|
777
|
+
}
|
|
778
|
+
return target;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const mergeRecordsOfSchemaDefinitions = createRecordsMerge(
|
|
782
|
+
mergeSchemaDefinitions
|
|
783
|
+
);
|
|
784
|
+
|
|
785
|
+
const MERGERS: {
|
|
786
|
+
[K in SchemaKey]?: Merger<Exclude<JSONSchema7[K], undefined>>;
|
|
787
|
+
} = {
|
|
788
|
+
$id: defaultMerger,
|
|
789
|
+
$ref: defaultMerger,
|
|
790
|
+
$schema: defaultMerger,
|
|
791
|
+
$comment: defaultMerger,
|
|
792
|
+
$defs: mergeRecordsOfSchemaDefinitions,
|
|
793
|
+
definitions: mergeRecordsOfSchemaDefinitions,
|
|
794
|
+
type: (a, b) => {
|
|
795
|
+
if (a === b) {
|
|
796
|
+
return a;
|
|
797
|
+
}
|
|
798
|
+
const isAArr = Array.isArray(a);
|
|
799
|
+
const isBArr = Array.isArray(b);
|
|
800
|
+
if (!isAArr && !isBArr) {
|
|
801
|
+
const intersection = intersectSchemaTypes(a, b);
|
|
802
|
+
if (intersection !== undefined) {
|
|
803
|
+
return intersection;
|
|
804
|
+
}
|
|
805
|
+
} else if (isAArr || isBArr) {
|
|
806
|
+
const r = new Set<JSONSchema7TypeName>();
|
|
807
|
+
if (isAArr && isBArr) {
|
|
808
|
+
createPairCombinations(a, b, (x, y) => {
|
|
809
|
+
const type = intersectSchemaTypes(x, y);
|
|
810
|
+
if (type !== undefined) {
|
|
811
|
+
r.add(type);
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
} else {
|
|
815
|
+
const arr = (isAArr ? a : b) as JSONSchema7TypeName[];
|
|
816
|
+
const el = (isAArr ? b : a) as JSONSchema7TypeName;
|
|
817
|
+
const l = arr.length;
|
|
818
|
+
for (let i = 0; i < l; i++) {
|
|
819
|
+
const intersection = intersectSchemaTypes(el, arr[i]!);
|
|
820
|
+
if (intersection !== undefined) {
|
|
821
|
+
r.add(intersection);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const s = r.size;
|
|
826
|
+
if (s === 1) {
|
|
827
|
+
return r.values().next().value!;
|
|
828
|
+
}
|
|
829
|
+
if (s > 1) {
|
|
830
|
+
return Array.from(r);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
throw new Error(
|
|
834
|
+
`It is not possible to create an intersection of the following incompatible types: ${a.toString()}, ${b.toString()}`
|
|
835
|
+
);
|
|
836
|
+
},
|
|
837
|
+
default: defaultMerger,
|
|
838
|
+
description: defaultMerger,
|
|
839
|
+
title: defaultMerger,
|
|
840
|
+
const: defaultMerger,
|
|
841
|
+
format: defaultMerger,
|
|
842
|
+
contentEncoding: defaultMerger,
|
|
843
|
+
contentMediaType: defaultMerger,
|
|
844
|
+
not: (a, b) => {
|
|
845
|
+
const items = deduplicateJsonSchemaDef([a, b]);
|
|
846
|
+
return items.length === 1 ? items[0]! : { anyOf: items };
|
|
847
|
+
},
|
|
848
|
+
pattern: mergePatterns,
|
|
849
|
+
readOnly: mergeBooleans,
|
|
850
|
+
writeOnly: mergeBooleans,
|
|
851
|
+
enum: (a, b) => {
|
|
852
|
+
const data = intersectJson(a, b);
|
|
853
|
+
if (data.length === 0) {
|
|
854
|
+
throw new Error(
|
|
855
|
+
`Intersection of the following enums is empty: "${JSON.stringify(
|
|
856
|
+
a
|
|
857
|
+
)}", "${JSON.stringify(b)}"`
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
return data;
|
|
861
|
+
},
|
|
862
|
+
anyOf: mergeArraysOfSchemaDefinition,
|
|
863
|
+
oneOf: mergeArraysOfSchemaDefinition,
|
|
864
|
+
allOf: (l, r) => deduplicateJsonSchemaDef(l.concat(r)),
|
|
865
|
+
propertyNames: mergeSchemaDefinitions,
|
|
866
|
+
dependencies: createRecordsMerge((a, b) => {
|
|
867
|
+
if (Array.isArray(a)) {
|
|
868
|
+
if (Array.isArray(b)) {
|
|
869
|
+
return union(a, b);
|
|
870
|
+
}
|
|
871
|
+
return mergeSchemaDefinitions(b, { required: a });
|
|
872
|
+
}
|
|
873
|
+
if (Array.isArray(b)) {
|
|
874
|
+
return mergeSchemaDefinitions(a, { required: b });
|
|
875
|
+
}
|
|
876
|
+
return mergeSchemaDefinitions(a, b);
|
|
877
|
+
}),
|
|
878
|
+
examples: (l, r) => {
|
|
879
|
+
// https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-10.4
|
|
880
|
+
if (!Array.isArray(l) || !Array.isArray(r)) {
|
|
881
|
+
throw new Error(
|
|
882
|
+
`Value of the 'examples' field should be an array, but got "${JSON.stringify(
|
|
883
|
+
l
|
|
884
|
+
)}" and "${JSON.stringify(r)}"`
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
// TODO: Proper deduplication
|
|
888
|
+
return union(l, r);
|
|
889
|
+
},
|
|
890
|
+
multipleOf: (a, b) => {
|
|
891
|
+
let factor = 1;
|
|
892
|
+
while (!Number.isInteger(a) || !Number.isInteger(b)) {
|
|
893
|
+
factor *= 10;
|
|
894
|
+
a *= 10;
|
|
895
|
+
b *= 10;
|
|
896
|
+
}
|
|
897
|
+
return lcm(a, b) / factor;
|
|
898
|
+
},
|
|
899
|
+
exclusiveMaximum: Math.min,
|
|
900
|
+
maximum: Math.min,
|
|
901
|
+
maxItems: Math.min,
|
|
902
|
+
maxLength: Math.min,
|
|
903
|
+
maxProperties: Math.min,
|
|
904
|
+
exclusiveMinimum: Math.max,
|
|
905
|
+
minimum: Math.max,
|
|
906
|
+
minItems: Math.max,
|
|
907
|
+
minLength: Math.max,
|
|
908
|
+
minProperties: Math.max,
|
|
909
|
+
uniqueItems: mergeBooleans,
|
|
910
|
+
required: union,
|
|
911
|
+
...mergers,
|
|
912
|
+
} satisfies {
|
|
913
|
+
[K in Exclude<SchemaKey, AssignerKey>]-?: Merger<
|
|
914
|
+
Exclude<JSONSchema7[K], undefined>
|
|
915
|
+
>;
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
return {
|
|
919
|
+
mergeSchemaDefinitions,
|
|
920
|
+
mergeArrayOfSchemaDefinitions,
|
|
921
|
+
};
|
|
922
|
+
}
|