@x0k/json-schema-merge 1.0.4 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x0k/json-schema-merge",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "A minimal JSON Schema merging library.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -13,8 +13,13 @@
13
13
  "url": "git+https://github.com/x0k/json-schema-merge.git"
14
14
  },
15
15
  "author": "Roman Krasilnikov",
16
+ "sideEffects": false,
16
17
  "files": [
17
- "dist"
18
+ "dist",
19
+ "src",
20
+ "!src/**/*.test.ts",
21
+ "!src/**/*.bench.ts",
22
+ "!src/**/fixtures"
18
23
  ],
19
24
  "module": "./dist/index.js",
20
25
  "types": "./dist/index.d.ts",
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./lib/json-schema/index.ts";
@@ -0,0 +1,172 @@
1
+ import type { Comparator } from "./ord.ts";
2
+
3
+ export function union<T>(larger: T[], smaller: T[]): T[] {
4
+ const ll = larger.length;
5
+ if (ll === 0) {
6
+ return smaller;
7
+ }
8
+ let sl = smaller.length;
9
+ if (sl === 0) {
10
+ return larger;
11
+ }
12
+ if (ll < sl) {
13
+ const tmp = larger;
14
+ larger = smaller;
15
+ smaller = tmp;
16
+ sl = ll;
17
+ }
18
+ const data = new Set(larger);
19
+ for (let i = 0; i < sl; i++) {
20
+ data.add(smaller[i]!);
21
+ }
22
+ return Array.from(data);
23
+ }
24
+
25
+ export function intersection<T>(a: T[], b: T[]): T[] {
26
+ const result: T[] = [];
27
+
28
+ if (a.length === 0 || b.length === 0) {
29
+ return result;
30
+ }
31
+
32
+ if (a.length > b.length) {
33
+ const tmp = a;
34
+ a = b;
35
+ b = tmp;
36
+ }
37
+
38
+ const setB = new Set(b);
39
+ for (let i = 0; i < a.length && setB.size > 0; i++) {
40
+ const val = a[i]!;
41
+ if (setB.delete(val)) {
42
+ result.push(val);
43
+ }
44
+ }
45
+
46
+ return result;
47
+ }
48
+
49
+ export function isArrayEmpty<T>(arr: T[]): arr is [] {
50
+ return arr.length === 0;
51
+ }
52
+
53
+ export function createArrayComparator<T>(compare: (a: T, b: T) => number) {
54
+ return (a: T[], b: T[]) => {
55
+ const d = a.length - b.length;
56
+ if (d !== 0) {
57
+ return d;
58
+ }
59
+ for (let i = 0; i < a.length; i++) {
60
+ if (a[i] !== b[i]) {
61
+ const d = compare(a[i]!, b[i]!);
62
+ if (d !== 0) {
63
+ return d;
64
+ }
65
+ }
66
+ }
67
+ return 0;
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Function type for removing duplicates from an array.
73
+ */
74
+ export type Deduplicator<T> = (data: T[]) => T[];
75
+
76
+ export interface DeduplicatorOptions {
77
+ /** @default 12 */
78
+ threshold?: number;
79
+ }
80
+
81
+ export function createDeduplicator<T>(
82
+ compare: Comparator<T>,
83
+ { threshold = 12 }: DeduplicatorOptions = {}
84
+ ): Deduplicator<T> {
85
+ return (arr) => {
86
+ const al = arr.length;
87
+
88
+ if (al === 0) {
89
+ return arr;
90
+ }
91
+
92
+ if (al <= threshold) {
93
+ const result: T[] = [];
94
+ let rl = 0;
95
+ outer: for (let i = 0; i < al; i++) {
96
+ const item = arr[i]!;
97
+ for (let j = 0; j < rl; j++) {
98
+ if (compare(item, result[j]!) === 0) {
99
+ continue outer;
100
+ }
101
+ }
102
+ rl = result.push(item);
103
+ }
104
+ return result;
105
+ }
106
+
107
+ const sorted = arr.slice().sort(compare);
108
+ let wIndex = 0;
109
+
110
+ for (let rIndex = 1; rIndex < al; rIndex++) {
111
+ if (compare(sorted[wIndex]!, sorted[rIndex]!) !== 0) {
112
+ if (++wIndex !== rIndex) {
113
+ sorted[wIndex] = sorted[rIndex]!;
114
+ }
115
+ }
116
+ }
117
+ sorted.length = wIndex + 1;
118
+ return sorted;
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Function type for intersecting two arrays of the same type.
124
+ */
125
+ export type Intersector<T> = (a: T[], b: T[]) => T[];
126
+
127
+ export function createIntersector<T>(compare: Comparator<T>): Intersector<T> {
128
+ return (a, b) => {
129
+ const result: T[] = [];
130
+ let al = a.length;
131
+ let bl = b.length;
132
+
133
+ if (al === 0 || bl === 0) {
134
+ return result;
135
+ }
136
+
137
+ if (al > bl) {
138
+ const tmpArr = a;
139
+ a = b;
140
+ b = tmpArr;
141
+ const tmpL = al;
142
+ al = bl;
143
+ bl = tmpL;
144
+ }
145
+
146
+ const aSorted = [...a].sort(compare);
147
+ const bSorted = [...b].sort(compare);
148
+
149
+ let i = 0,
150
+ j = 0;
151
+ while (i < al && j < bl) {
152
+ const cmp = compare(aSorted[i]!, bSorted[j]!);
153
+ if (cmp === 0) {
154
+ // Only push if result is empty OR last pushed value is different
155
+ if (
156
+ result.length === 0 ||
157
+ compare(result[result.length - 1]!, aSorted[i]!) !== 0
158
+ ) {
159
+ result.push(aSorted[i]!);
160
+ }
161
+ i++;
162
+ j++;
163
+ } else if (cmp < 0) {
164
+ i++;
165
+ } else {
166
+ j++;
167
+ }
168
+ }
169
+
170
+ return result;
171
+ };
172
+ }
@@ -0,0 +1,3 @@
1
+ export function identity<T>(v: T): T {
2
+ return v;
3
+ }
@@ -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,5 @@
1
+ export * from "./json-schema.ts";
2
+ export * from "./transform.ts";
3
+ export * from "./traverse.ts";
4
+ export * from "./compare/index.ts";
5
+ export * from "./merge/index.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
+ }