@x0k/json-schema-merge 1.0.3 → 1.0.5
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/README.md +1 -3
- package/dist/lib/json-schema/json-schema.d.ts.map +1 -1
- package/dist/lib/json-schema/json-schema.js.map +1 -1
- package/dist/lib/json-schema/merge/merge.d.ts.map +1 -1
- package/dist/lib/json-schema/merge/merge.js +1 -1
- package/dist/lib/json-schema/merge/merge.js.map +1 -1
- package/dist/lib/json-schema/merge/patterns.js +1 -1
- package/dist/lib/json-schema/merge/patterns.js.map +1 -1
- package/package.json +14 -9
- 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 +880 -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,343 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
JSONSchema7Type as SchemaValue,
|
|
3
|
+
JSONSchema7TypeName as SchemaType,
|
|
4
|
+
JSONSchema7Definition,
|
|
5
|
+
JSONSchema7,
|
|
6
|
+
JSONSchema7Object,
|
|
7
|
+
} from "json-schema";
|
|
8
|
+
|
|
9
|
+
import { type Comparator, ascComparator } from "../../ord.ts";
|
|
10
|
+
import {
|
|
11
|
+
createArrayComparator,
|
|
12
|
+
createDeduplicator,
|
|
13
|
+
isArrayEmpty,
|
|
14
|
+
} from "../../array.ts";
|
|
15
|
+
import { isRecordEmpty } from "../../object.ts";
|
|
16
|
+
import { weakMemoize } from "../../memoize.ts";
|
|
17
|
+
|
|
18
|
+
import { isAllowAnySchema, isSchemaObject } from "../json-schema.ts";
|
|
19
|
+
|
|
20
|
+
const zero = () => 0;
|
|
21
|
+
const isUndefined = <T>(v: T | undefined): v is undefined => v === undefined;
|
|
22
|
+
|
|
23
|
+
type SchemaPrimitiveTypeExceptNullType = Extract<
|
|
24
|
+
SchemaType,
|
|
25
|
+
"string" | "number" | "boolean"
|
|
26
|
+
>;
|
|
27
|
+
type SchemaPrimitiveTypeExceptNull = string | number | boolean;
|
|
28
|
+
|
|
29
|
+
const isSchemaPrimitiveExceptNull = (
|
|
30
|
+
value: Exclude<SchemaValue, null>
|
|
31
|
+
): value is SchemaPrimitiveTypeExceptNull => typeof value !== "object";
|
|
32
|
+
|
|
33
|
+
const PRIMITIVE_TYPE_ORDER: Record<
|
|
34
|
+
SchemaPrimitiveTypeExceptNullType,
|
|
35
|
+
0 | 1 | 2
|
|
36
|
+
> = {
|
|
37
|
+
boolean: 0,
|
|
38
|
+
number: 1,
|
|
39
|
+
string: 2,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function compareSchemaPrimitive(
|
|
43
|
+
a: SchemaPrimitiveTypeExceptNull,
|
|
44
|
+
b: SchemaPrimitiveTypeExceptNull
|
|
45
|
+
) {
|
|
46
|
+
const ta = typeof a as SchemaPrimitiveTypeExceptNullType;
|
|
47
|
+
const tb = typeof b as SchemaPrimitiveTypeExceptNullType;
|
|
48
|
+
return ta === tb
|
|
49
|
+
? ascComparator(a, b)
|
|
50
|
+
: PRIMITIVE_TYPE_ORDER[ta] - PRIMITIVE_TYPE_ORDER[tb];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function insertUniqueValues<K>(mutableTarget: K[], mutableSource: K[]): K[] {
|
|
54
|
+
const tl = mutableTarget.length;
|
|
55
|
+
if (tl === 0) return mutableSource;
|
|
56
|
+
const sl = mutableSource.length;
|
|
57
|
+
if (sl === 0) return mutableTarget;
|
|
58
|
+
if (sl > tl) {
|
|
59
|
+
const t = mutableTarget;
|
|
60
|
+
mutableTarget = mutableSource;
|
|
61
|
+
mutableSource = t;
|
|
62
|
+
}
|
|
63
|
+
const seen = new Set(mutableTarget);
|
|
64
|
+
const l = mutableSource.length;
|
|
65
|
+
for (let i = 0; i < l; i++) {
|
|
66
|
+
const key = mutableSource[i]!;
|
|
67
|
+
if (!seen.has(key)) {
|
|
68
|
+
mutableTarget.push(key);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return mutableTarget;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createCmpMatcher<T, E extends T>(
|
|
75
|
+
isEmpty: (v: T) => v is E,
|
|
76
|
+
compare: Comparator<Exclude<T, E>>,
|
|
77
|
+
compareEmpty: Comparator<E> = zero
|
|
78
|
+
) {
|
|
79
|
+
return (a: T, b: T) => {
|
|
80
|
+
if (isEmpty(a)) {
|
|
81
|
+
if (isEmpty(b)) {
|
|
82
|
+
return compareEmpty(a, b);
|
|
83
|
+
}
|
|
84
|
+
return -1;
|
|
85
|
+
}
|
|
86
|
+
if (isEmpty(b)) {
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
return compare(a as Exclude<T, E>, b as Exclude<T, E>);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function createOptionalComparator<T>(compare: Comparator<T>) {
|
|
94
|
+
return createCmpMatcher<T | undefined, undefined>(isUndefined, compare);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function createNarrowingOptionalComparator<T, E extends T>(
|
|
98
|
+
isEmpty: (v: T) => v is E,
|
|
99
|
+
compare: Comparator<Exclude<T, E | undefined>>
|
|
100
|
+
) {
|
|
101
|
+
return createCmpMatcher<T | undefined, E | undefined>(
|
|
102
|
+
(v: T | undefined): v is undefined | E => v === undefined || isEmpty(v),
|
|
103
|
+
compare
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function createArrayOrItemComparator<T, T1>(
|
|
108
|
+
compare: Comparator<T>,
|
|
109
|
+
compareArray: Comparator<T1[]>
|
|
110
|
+
) {
|
|
111
|
+
return createCmpMatcher<T | T1[], T1[]>(Array.isArray, compare, compareArray);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const compareOptionalSameTypeSchemaPrimitives =
|
|
115
|
+
createOptionalComparator(ascComparator);
|
|
116
|
+
|
|
117
|
+
const compareNumbersWithZeroDefault = createNarrowingOptionalComparator(
|
|
118
|
+
(v: number): v is 0 => v === 0,
|
|
119
|
+
(a, b) => a - b
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
export interface ComparatorOptions {
|
|
123
|
+
deduplicationCache?: WeakMap<any[], any[]>;
|
|
124
|
+
sortedKeysCache?: WeakMap<Record<string, any>, string[]>;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function createComparator({
|
|
128
|
+
deduplicationCache = new WeakMap(),
|
|
129
|
+
sortedKeysCache = new WeakMap(),
|
|
130
|
+
}: ComparatorOptions = {}) {
|
|
131
|
+
const getSortedKeys = weakMemoize(sortedKeysCache, (obj) =>
|
|
132
|
+
Object.keys(obj).sort()
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
function createRecordsComparator<R extends Record<string, any>>(
|
|
136
|
+
compare: <K extends keyof R>(a: R[K], b: R[K]) => number
|
|
137
|
+
) {
|
|
138
|
+
return (a: R, b: R) => {
|
|
139
|
+
const aKeys = getSortedKeys(a);
|
|
140
|
+
const bKeys = getSortedKeys(b);
|
|
141
|
+
const l = Math.min(aKeys.length, bKeys.length);
|
|
142
|
+
for (let i = 0; i < l; i++) {
|
|
143
|
+
const cmp = ascComparator(aKeys[i]!, bKeys[i]!);
|
|
144
|
+
if (cmp !== 0) {
|
|
145
|
+
return cmp;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (aKeys.length !== bKeys.length) {
|
|
149
|
+
return aKeys.length - bKeys.length;
|
|
150
|
+
}
|
|
151
|
+
for (let i = 0; i < l; i++) {
|
|
152
|
+
const key = aKeys[i]!;
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
154
|
+
const cmp = compare(a[key], b[key]);
|
|
155
|
+
if (cmp !== 0) {
|
|
156
|
+
return cmp;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return 0;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function createArrayComparatorWithDeduplication<T>(compare: Comparator<T>) {
|
|
164
|
+
const cmp = createArrayComparator(compare);
|
|
165
|
+
const deduplicate = weakMemoize(
|
|
166
|
+
deduplicationCache as WeakMap<T[], T[]>,
|
|
167
|
+
// NOTE: Always sort output
|
|
168
|
+
createDeduplicator(compare, { threshold: 0 })
|
|
169
|
+
);
|
|
170
|
+
return (a: T[], b: T[]) => cmp(deduplicate(a), deduplicate(b));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const compareArrayOfSameTypePrimitivesWithDeduplication =
|
|
174
|
+
createArrayComparatorWithDeduplication(ascComparator);
|
|
175
|
+
|
|
176
|
+
function compareSchemaDefinitions(
|
|
177
|
+
a: JSONSchema7Definition,
|
|
178
|
+
b: JSONSchema7Definition
|
|
179
|
+
) {
|
|
180
|
+
if (isSchemaObject(a)) {
|
|
181
|
+
if (isSchemaObject(b)) {
|
|
182
|
+
const aKeys = Object.keys(a) as (keyof JSONSchema7)[];
|
|
183
|
+
const bKeys = Object.keys(b) as (keyof JSONSchema7)[];
|
|
184
|
+
const allKeys = insertUniqueValues(aKeys, bKeys);
|
|
185
|
+
const l = allKeys.length;
|
|
186
|
+
for (let i = 0; i < l; i++) {
|
|
187
|
+
const key = allKeys[i]!;
|
|
188
|
+
if (a[key] === b[key]) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const cmp = COMPARATORS[key] ?? compareOptionalSchemaValues;
|
|
192
|
+
const d = cmp(a[key] as undefined, b[key] as undefined);
|
|
193
|
+
if (d !== 0) {
|
|
194
|
+
return d;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
return b === true && isRecordEmpty(a) ? 0 : 1;
|
|
200
|
+
}
|
|
201
|
+
if (isSchemaObject(b)) {
|
|
202
|
+
return a === true && isRecordEmpty(b) ? 0 : -1;
|
|
203
|
+
}
|
|
204
|
+
return ascComparator(a, b);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const compareOptionalSchemaValues =
|
|
208
|
+
createOptionalComparator(compareSchemaValues);
|
|
209
|
+
|
|
210
|
+
const compareNonNullSchemaValue = createCmpMatcher(
|
|
211
|
+
isSchemaPrimitiveExceptNull,
|
|
212
|
+
createArrayOrItemComparator(
|
|
213
|
+
createRecordsComparator<JSONSchema7Object>(compareOptionalSchemaValues),
|
|
214
|
+
createArrayComparator(compareSchemaValues)
|
|
215
|
+
),
|
|
216
|
+
compareSchemaPrimitive
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
function compareSchemaValues(a: SchemaValue, b: SchemaValue): number {
|
|
220
|
+
if (a === null) {
|
|
221
|
+
return -1;
|
|
222
|
+
}
|
|
223
|
+
if (b === null) {
|
|
224
|
+
return 1;
|
|
225
|
+
}
|
|
226
|
+
return compareNonNullSchemaValue(a, b);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const compareOptionalSchemaDefinitions = createOptionalComparator(
|
|
230
|
+
compareSchemaDefinitions
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const compareRecordOfOptionalSchemasWithEmptyRecordDefault =
|
|
234
|
+
createNarrowingOptionalComparator(
|
|
235
|
+
isRecordEmpty,
|
|
236
|
+
createRecordsComparator<Record<string, JSONSchema7Definition>>(
|
|
237
|
+
compareOptionalSchemaDefinitions
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const compareOptionalArrayOfSchemasWithDeduplication =
|
|
242
|
+
createOptionalComparator(
|
|
243
|
+
createArrayComparatorWithDeduplication(compareSchemaDefinitions)
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const compareSchemaDefinitionsWithEmptyDefinitionDefault =
|
|
247
|
+
createNarrowingOptionalComparator(
|
|
248
|
+
isAllowAnySchema,
|
|
249
|
+
compareSchemaDefinitions
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const COMPARATORS: {
|
|
253
|
+
[K in keyof JSONSchema7]-?: Comparator<JSONSchema7[K]>;
|
|
254
|
+
} = {
|
|
255
|
+
$id: compareOptionalSameTypeSchemaPrimitives,
|
|
256
|
+
$comment: compareOptionalSameTypeSchemaPrimitives,
|
|
257
|
+
$defs: compareRecordOfOptionalSchemasWithEmptyRecordDefault,
|
|
258
|
+
$ref: compareOptionalSameTypeSchemaPrimitives,
|
|
259
|
+
$schema: compareOptionalSameTypeSchemaPrimitives,
|
|
260
|
+
const: compareOptionalSchemaValues,
|
|
261
|
+
contains: compareOptionalSchemaDefinitions,
|
|
262
|
+
contentEncoding: compareOptionalSameTypeSchemaPrimitives,
|
|
263
|
+
contentMediaType: compareOptionalSameTypeSchemaPrimitives,
|
|
264
|
+
default: compareOptionalSchemaValues,
|
|
265
|
+
definitions: compareRecordOfOptionalSchemasWithEmptyRecordDefault,
|
|
266
|
+
description: compareOptionalSameTypeSchemaPrimitives,
|
|
267
|
+
else: compareOptionalSchemaDefinitions,
|
|
268
|
+
examples: compareOptionalSchemaValues,
|
|
269
|
+
exclusiveMaximum: compareOptionalSameTypeSchemaPrimitives,
|
|
270
|
+
exclusiveMinimum: compareOptionalSameTypeSchemaPrimitives,
|
|
271
|
+
format: compareOptionalSameTypeSchemaPrimitives,
|
|
272
|
+
if: compareOptionalSchemaDefinitions,
|
|
273
|
+
maximum: compareOptionalSameTypeSchemaPrimitives,
|
|
274
|
+
maxItems: compareOptionalSameTypeSchemaPrimitives,
|
|
275
|
+
maxLength: compareOptionalSameTypeSchemaPrimitives,
|
|
276
|
+
maxProperties: compareOptionalSameTypeSchemaPrimitives,
|
|
277
|
+
minimum: compareOptionalSameTypeSchemaPrimitives,
|
|
278
|
+
multipleOf: compareOptionalSameTypeSchemaPrimitives,
|
|
279
|
+
not: compareOptionalSchemaDefinitions,
|
|
280
|
+
pattern: compareOptionalSameTypeSchemaPrimitives,
|
|
281
|
+
propertyNames: compareOptionalSchemaDefinitions,
|
|
282
|
+
readOnly: compareOptionalSameTypeSchemaPrimitives,
|
|
283
|
+
then: compareOptionalSchemaDefinitions,
|
|
284
|
+
title: compareOptionalSameTypeSchemaPrimitives,
|
|
285
|
+
writeOnly: compareOptionalSameTypeSchemaPrimitives,
|
|
286
|
+
uniqueItems: createNarrowingOptionalComparator(
|
|
287
|
+
(v): v is false => v === false,
|
|
288
|
+
zero
|
|
289
|
+
),
|
|
290
|
+
minLength: compareNumbersWithZeroDefault,
|
|
291
|
+
minItems: compareNumbersWithZeroDefault,
|
|
292
|
+
minProperties: compareNumbersWithZeroDefault,
|
|
293
|
+
required: createNarrowingOptionalComparator(
|
|
294
|
+
isArrayEmpty,
|
|
295
|
+
compareArrayOfSameTypePrimitivesWithDeduplication
|
|
296
|
+
),
|
|
297
|
+
enum: createNarrowingOptionalComparator(
|
|
298
|
+
isArrayEmpty,
|
|
299
|
+
createArrayComparatorWithDeduplication(compareSchemaValues)
|
|
300
|
+
),
|
|
301
|
+
type: createOptionalComparator((a, b) => {
|
|
302
|
+
const isAArr = Array.isArray(a);
|
|
303
|
+
const isBArr = Array.isArray(b);
|
|
304
|
+
if (!isAArr && !isBArr) {
|
|
305
|
+
return ascComparator(a, b);
|
|
306
|
+
}
|
|
307
|
+
return compareArrayOfSameTypePrimitivesWithDeduplication(
|
|
308
|
+
isAArr ? a : [a],
|
|
309
|
+
isBArr ? b : [b]
|
|
310
|
+
);
|
|
311
|
+
}),
|
|
312
|
+
items: createNarrowingOptionalComparator(
|
|
313
|
+
(v): v is true | JSONSchema7 => !Array.isArray(v) && isAllowAnySchema(v),
|
|
314
|
+
createArrayOrItemComparator(
|
|
315
|
+
compareSchemaDefinitions,
|
|
316
|
+
createArrayComparator(compareSchemaDefinitions)
|
|
317
|
+
)
|
|
318
|
+
),
|
|
319
|
+
anyOf: compareOptionalArrayOfSchemasWithDeduplication,
|
|
320
|
+
allOf: compareOptionalArrayOfSchemasWithDeduplication,
|
|
321
|
+
oneOf: compareOptionalArrayOfSchemasWithDeduplication,
|
|
322
|
+
properties: compareRecordOfOptionalSchemasWithEmptyRecordDefault,
|
|
323
|
+
patternProperties: compareRecordOfOptionalSchemasWithEmptyRecordDefault,
|
|
324
|
+
additionalProperties: compareSchemaDefinitionsWithEmptyDefinitionDefault,
|
|
325
|
+
additionalItems: compareSchemaDefinitionsWithEmptyDefinitionDefault,
|
|
326
|
+
dependencies: createNarrowingOptionalComparator(
|
|
327
|
+
isRecordEmpty,
|
|
328
|
+
createRecordsComparator(
|
|
329
|
+
createOptionalComparator(
|
|
330
|
+
createArrayOrItemComparator(
|
|
331
|
+
compareSchemaDefinitions,
|
|
332
|
+
compareArrayOfSameTypePrimitivesWithDeduplication
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
),
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
compareSchemaValues,
|
|
341
|
+
compareSchemaDefinitions,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./compare.ts";
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
JSONSchema7,
|
|
3
|
+
JSONSchema7TypeName,
|
|
4
|
+
JSONSchema7Definition,
|
|
5
|
+
} from "json-schema";
|
|
6
|
+
|
|
7
|
+
import { isRecordEmpty } from "../object.ts";
|
|
8
|
+
|
|
9
|
+
export const JSON_SCHEMA_TYPE_NAMES: string[] = [
|
|
10
|
+
"array",
|
|
11
|
+
"boolean",
|
|
12
|
+
"integer",
|
|
13
|
+
"null",
|
|
14
|
+
"number",
|
|
15
|
+
"object",
|
|
16
|
+
"string",
|
|
17
|
+
] as const satisfies JSONSchema7TypeName[];
|
|
18
|
+
|
|
19
|
+
export const SET_OF_JSON_SCHEMA_TYPE_NAMES = new Set(JSON_SCHEMA_TYPE_NAMES);
|
|
20
|
+
|
|
21
|
+
export function isJsonSchemaType(type: string): type is JSONSchema7TypeName {
|
|
22
|
+
return SET_OF_JSON_SCHEMA_TYPE_NAMES.has(type);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type SchemaKeys = Array<keyof JSONSchema7>;
|
|
26
|
+
|
|
27
|
+
// WARN: Order is important
|
|
28
|
+
export const RECORDS_OF_SUB_SCHEMAS = [
|
|
29
|
+
"$defs",
|
|
30
|
+
"definitions",
|
|
31
|
+
"properties",
|
|
32
|
+
"patternProperties",
|
|
33
|
+
"dependencies",
|
|
34
|
+
] as const satisfies SchemaKeys;
|
|
35
|
+
|
|
36
|
+
export const SET_OF_RECORDS_OF_SUB_SCHEMAS = new Set(RECORDS_OF_SUB_SCHEMAS);
|
|
37
|
+
|
|
38
|
+
export type SubSchemasRecordKey = (typeof RECORDS_OF_SUB_SCHEMAS)[number];
|
|
39
|
+
|
|
40
|
+
// WARN: Order is important
|
|
41
|
+
export const ARRAYS_OF_SUB_SCHEMAS = [
|
|
42
|
+
"items",
|
|
43
|
+
"allOf",
|
|
44
|
+
"oneOf",
|
|
45
|
+
"anyOf",
|
|
46
|
+
] as const satisfies SchemaKeys;
|
|
47
|
+
|
|
48
|
+
export const SET_OF_ARRAYS_OF_SUB_SCHEMAS = new Set(ARRAYS_OF_SUB_SCHEMAS);
|
|
49
|
+
|
|
50
|
+
export type SubSchemasArrayKey = (typeof ARRAYS_OF_SUB_SCHEMAS)[number];
|
|
51
|
+
|
|
52
|
+
// WARN: Order is important
|
|
53
|
+
export const SUB_SCHEMAS = [
|
|
54
|
+
"items",
|
|
55
|
+
"additionalItems",
|
|
56
|
+
"additionalProperties",
|
|
57
|
+
"propertyNames",
|
|
58
|
+
"contains",
|
|
59
|
+
"if",
|
|
60
|
+
"then",
|
|
61
|
+
"else",
|
|
62
|
+
"not",
|
|
63
|
+
] as const satisfies SchemaKeys;
|
|
64
|
+
|
|
65
|
+
export const SET_OF_SUB_SCHEMAS = new Set(SUB_SCHEMAS);
|
|
66
|
+
|
|
67
|
+
export type SubSchemaKey = (typeof SUB_SCHEMAS)[number];
|
|
68
|
+
|
|
69
|
+
// WARN: Order is important
|
|
70
|
+
export const ALL_SUB_SCHEMA_KEYS = [
|
|
71
|
+
...RECORDS_OF_SUB_SCHEMAS,
|
|
72
|
+
...ARRAYS_OF_SUB_SCHEMAS,
|
|
73
|
+
...SUB_SCHEMAS,
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export type AnySubSchemaKey = (typeof ALL_SUB_SCHEMA_KEYS)[number];
|
|
77
|
+
|
|
78
|
+
export type TransformedSchema<R, S> = Omit<S, AnySubSchemaKey> & {
|
|
79
|
+
items?: R | R[] | undefined;
|
|
80
|
+
additionalItems?: R | undefined;
|
|
81
|
+
contains?: R | undefined;
|
|
82
|
+
additionalProperties?: R | undefined;
|
|
83
|
+
propertyNames?: R | undefined;
|
|
84
|
+
if?: R | undefined;
|
|
85
|
+
then?: R | undefined;
|
|
86
|
+
else?: R | undefined;
|
|
87
|
+
not?: R | undefined;
|
|
88
|
+
// Records
|
|
89
|
+
$defs?: Record<string, R> | undefined;
|
|
90
|
+
properties?: Record<string, R> | undefined;
|
|
91
|
+
patternProperties?: Record<string, R> | undefined;
|
|
92
|
+
dependencies?: Record<string, R | string[]> | undefined;
|
|
93
|
+
definitions?: Record<string, R> | undefined;
|
|
94
|
+
// Arrays
|
|
95
|
+
allOf?: R[] | undefined;
|
|
96
|
+
anyOf?: R[] | undefined;
|
|
97
|
+
oneOf?: R[] | undefined;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type TransformedSchemaDefinition<R, S> =
|
|
101
|
+
TransformedSchema<R, S> | boolean;
|
|
102
|
+
|
|
103
|
+
export function isSchemaObject<D extends JSONSchema7Definition>(
|
|
104
|
+
schemaDef: D
|
|
105
|
+
): schemaDef is Exclude<D, boolean> {
|
|
106
|
+
return typeof schemaDef === "object";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function isAllowAnySchema(
|
|
110
|
+
def: JSONSchema7Definition
|
|
111
|
+
): def is true | Record<string, never> {
|
|
112
|
+
return isSchemaObject(def) ? isRecordEmpty(def) : def === true;
|
|
113
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { JSONSchema7Definition } from "json-schema";
|
|
2
|
+
|
|
3
|
+
import { transformSchemaDefinition } from "../transform.ts";
|
|
4
|
+
|
|
5
|
+
function getAllOfSchemas(
|
|
6
|
+
schema: JSONSchema7Definition
|
|
7
|
+
): JSONSchema7Definition[] {
|
|
8
|
+
const result: JSONSchema7Definition[] = [];
|
|
9
|
+
const stack: JSONSchema7Definition[] = [schema];
|
|
10
|
+
while (stack.length > 0) {
|
|
11
|
+
const current = stack.pop()!;
|
|
12
|
+
if (typeof current === "boolean" || current.allOf === undefined) {
|
|
13
|
+
result.push(current);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const { allOf, ...rest } = current;
|
|
17
|
+
result.push(rest);
|
|
18
|
+
for (let i = allOf.length - 1; i >= 0; i--) {
|
|
19
|
+
stack.push(allOf[i]!);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createShallowAllOfMerge(
|
|
26
|
+
mergeArrayOfSchemaDefinitions: (
|
|
27
|
+
defs: JSONSchema7Definition[]
|
|
28
|
+
) => JSONSchema7Definition
|
|
29
|
+
) {
|
|
30
|
+
return (schema: JSONSchema7Definition) =>
|
|
31
|
+
mergeArrayOfSchemaDefinitions(getAllOfSchemas(schema));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createDeepAllOfMerge(
|
|
35
|
+
shallowMerge: (def: JSONSchema7Definition) => JSONSchema7Definition
|
|
36
|
+
) {
|
|
37
|
+
return (schemaDef: JSONSchema7Definition) =>
|
|
38
|
+
transformSchemaDefinition<JSONSchema7Definition>(schemaDef, (def) => {
|
|
39
|
+
if (typeof def === "boolean" || def.allOf === undefined) {
|
|
40
|
+
return def;
|
|
41
|
+
}
|
|
42
|
+
return shallowMerge(def);
|
|
43
|
+
});
|
|
44
|
+
}
|