@orpc/json-schema 2.0.0-beta.47 → 2.0.0-beta.48

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 CHANGED
@@ -191,15 +191,14 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
191
191
  <td align="center"><a href="https://kellychan.im/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Kelly Peilin Chan"><img src="https://avatars.githubusercontent.com/u/520852?u=6b0f7105f694e7b5cacf410a3f04c7044b469dc8&amp;v=4" width="139" alt="Kelly Peilin Chan"/><br />Kelly Peilin Chan</a></td>
192
192
  <td align="center"><a href="https://guyariely.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Guy Ariely"><img src="https://avatars.githubusercontent.com/u/42813496?u=edb6b7f563bf28e160a290832e7da57c0506f8ca&amp;v=4" width="139" alt="Guy Ariely"/><br />Guy Ariely</a></td>
193
193
  <td align="center"><a href="https://paulsenon.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="PaulSenon"><img src="https://avatars.githubusercontent.com/u/19531087?u=6385741eb91d200b8c513ec6045482e301767ca6&amp;v=4" width="139" alt="PaulSenon"/><br />PaulSenon</a></td>
194
- <td align="center"><a href="https://nakasyou.how/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Shotaro Nakamura"><img src="https://avatars.githubusercontent.com/u/79000684?u=f644df3f29f0e8677a90967115774564f1d9d6ab&amp;v=4" width="139" alt="Shotaro Nakamura"/><br />Shotaro Nakamura</a></td>
194
+ <td align="center"><a href="https://piscis.dev/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Alex"><img src="https://avatars.githubusercontent.com/u/326163?u=b245f368bd940cf51d08c0b6bf55f8257f359437&amp;v=4" width="139" alt="Alex"/><br />Alex</a></td>
195
195
  </tr>
196
196
  <tr>
197
- <td align="center"><a href="https://piscis.dev/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Alex"><img src="https://avatars.githubusercontent.com/u/326163?u=b245f368bd940cf51d08c0b6bf55f8257f359437&amp;v=4" width="139" alt="Alex"/><br />Alex</a></td>
198
197
  <td align="center"><a href="https://opensource.gubanov.eu/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Andrey Gubanov"><img src="https://avatars.githubusercontent.com/u/1082083?u=c5f2daf7ebece498e85c83367bb37b4e10e2649d&amp;v=4" width="139" alt="Andrey Gubanov"/><br />Andrey Gubanov</a></td>
199
198
  </tr>
200
199
  </table>
201
200
 
202
- With thanks to [39 past sponsors](https://orpc.dev/sponsors#past-sponsors) who helped get oRPC here.
201
+ With thanks to [40 past sponsors](https://orpc.dev/sponsors#past-sponsors) who helped get oRPC here.
203
202
 
204
203
  ## Open Source Programs
205
204
 
@@ -0,0 +1,29 @@
1
+ import type { JsonSchema } from "./types.mjs";
2
+ export interface JsonSchemaCoercerOptions {
3
+ /**
4
+ * Maximum digits, excluding a leading `-`, of a string coerced to `bigint`. Longer strings are left unchanged.
5
+ * `Number.POSITIVE_INFINITY` disables it.
6
+ *
7
+ * @default 4300
8
+ */
9
+ maxBigIntDigits?: number | undefined;
10
+ /**
11
+ * Maximum coercion steps per input value, where a step applies one schema to one value.
12
+ * Inputs with fewer than 100 values are budgeted as 100. Past the budget the whole input is left uncoerced,
13
+ * so validation still runs and reports any mismatch.
14
+ *
15
+ * @default 1000
16
+ */
17
+ maxStepsPerValue?: number | undefined;
18
+ }
19
+ export declare class JsonSchemaCoercer {
20
+ private readonly nativeCoerceOptions;
21
+ private readonly maxStepsPerValue;
22
+ /** Reused between calls, since building a context is slow until V8 warms up. */
23
+ private idleContext;
24
+ constructor(options?: JsonSchemaCoercerOptions);
25
+ coerce([schema, optional]: [schema: JsonSchema, optional: boolean], value: unknown): unknown;
26
+ private coerceInternal;
27
+ private coerceRef;
28
+ private extendBudget;
29
+ }
@@ -0,0 +1,465 @@
1
+ import { DEFAULT_MAX_BIGINT_DIGITS, get, getOwn, isPlainObject, isTypescriptObject, parseBigIntString, toArray, tryOrUndefined } from "@orpc/shared";
2
+ import { decodeJsonPointerSegment } from "./ref-utils.mjs";
3
+ import { JsonSchemaXNativeType } from "./types.mjs";
4
+
5
+ const UNSATISFIED = 0;
6
+
7
+ const LOOSELY_SATISFIED = 1;
8
+
9
+ const SATISFIED = 2;
10
+
11
+ const NATIVE_TYPES = /* @__PURE__ */ (() => new Map([
12
+ [JsonSchemaXNativeType.Date, {
13
+ types: ["string"],
14
+ is: (value) => value instanceof Date,
15
+ coerce: (value) => typeof value === "string" ? stringToDate(value) : value
16
+ }],
17
+ [JsonSchemaXNativeType.BigInt, {
18
+ types: [
19
+ "string",
20
+ "integer",
21
+ "number"
22
+ ],
23
+ is: (value) => typeof value === "bigint",
24
+ coerce: (value, { maxBigIntDigits }) => typeof value === "string" ? stringToBigInt(value, maxBigIntDigits) : typeof value === "number" ? numberToBigInt(value) : value
25
+ }],
26
+ [JsonSchemaXNativeType.Url, {
27
+ types: ["string"],
28
+ is: (value) => value instanceof URL,
29
+ coerce: (value) => typeof value === "string" ? stringToURL(value) : value
30
+ }],
31
+ [JsonSchemaXNativeType.Set, {
32
+ types: ["array"],
33
+ is: (value) => value instanceof Set,
34
+ coerce: (value) => Array.isArray(value) ? arrayToSet(value) : value,
35
+ afterTypeCheck: true
36
+ }],
37
+ [JsonSchemaXNativeType.Map, {
38
+ types: ["array"],
39
+ is: (value) => value instanceof Map,
40
+ coerce: (value) => Array.isArray(value) ? arrayToMap(value) : value,
41
+ afterTypeCheck: true
42
+ }]
43
+ ]))();
44
+ function minSatisfaction(a, b) {
45
+ return a < b ? a : b;
46
+ }
47
+ const MIN_BUDGETED_VALUES = 100;
48
+ function createContext() {
49
+ return {
50
+ rootSchema: true,
51
+ memo: undefined,
52
+ memoize: false,
53
+ steps: 0,
54
+ maxSteps: 0,
55
+ input: undefined
56
+ };
57
+ }
58
+ class CoercionBudgetExceededError extends Error {}
59
+ export class JsonSchemaCoercer {
60
+ nativeCoerceOptions;
61
+ maxStepsPerValue;
62
+
63
+ idleContext = createContext();
64
+ constructor(options = {}) {
65
+ this.nativeCoerceOptions = { maxBigIntDigits: options.maxBigIntDigits ?? DEFAULT_MAX_BIGINT_DIGITS };
66
+ this.maxStepsPerValue = options.maxStepsPerValue ?? 1e3;
67
+ }
68
+ coerce([schema, optional], value) {
69
+ if (optional && value === undefined) {
70
+ return value;
71
+ }
72
+ const context = this.idleContext ?? createContext();
73
+ this.idleContext = undefined;
74
+ context.rootSchema = schema;
75
+ context.memoize = false;
76
+ context.steps = 0;
77
+ context.maxSteps = this.maxStepsPerValue * MIN_BUDGETED_VALUES;
78
+ context.input = value;
79
+ try {
80
+ const [, coerced] = this.coerceInternal(context, schema, value);
81
+ return coerced;
82
+ } catch (error) {
83
+
84
+ if (error instanceof CoercionBudgetExceededError) {
85
+ return value;
86
+ }
87
+ throw error;
88
+ } finally {
89
+
90
+ context.memo = undefined;
91
+ context.input = undefined;
92
+ this.idleContext = context;
93
+ }
94
+ }
95
+ coerceInternal(context, schema, value, appliedRefs) {
96
+ if (++context.steps > context.maxSteps) {
97
+ this.extendBudget(context);
98
+ }
99
+ if (typeof schema === "boolean") {
100
+ return [schema ? SATISFIED : UNSATISFIED, value];
101
+ }
102
+ if (Array.isArray(schema.type)) {
103
+ return this.coerceInternal(context, { anyOf: schema.type.map((type) => ({
104
+ ...schema,
105
+ type
106
+ })) }, value, appliedRefs);
107
+ }
108
+ let coerced = value;
109
+ let satisfied = SATISFIED;
110
+ if (typeof schema.$ref === "string") {
111
+ const resolved = schema.$ref.startsWith("#/") ? get(context.rootSchema, schema.$ref.slice("#/".length).split("/").map(decodeJsonPointerSegment)) : schema.$ref === "#" ? context.rootSchema : undefined;
112
+ if (resolved !== undefined && !appliedRefs?.has(resolved)) {
113
+ if (schema.type === "object" || schema.type === "array") {
114
+ context.memoize = true;
115
+ }
116
+ const [subSatisfied, subCoerced] = this.coerceRef(context, resolved, coerced, appliedRefs);
117
+ coerced = subCoerced;
118
+ satisfied = minSatisfaction(satisfied, subSatisfied);
119
+ }
120
+ }
121
+ const enumValues = schema.const !== undefined ? [schema.const] : schema.enum;
122
+ if (enumValues !== undefined && !enumValues.includes(coerced)) {
123
+ if (typeof coerced === "string") {
124
+ const numberValue = stringToNumber(coerced);
125
+ if (enumValues.includes(numberValue)) {
126
+ coerced = numberValue;
127
+ } else {
128
+ const booleanValue = stringToBoolean(coerced);
129
+ if (enumValues.includes(booleanValue)) {
130
+ coerced = booleanValue;
131
+ } else {
132
+ satisfied = UNSATISFIED;
133
+ }
134
+ }
135
+ } else {
136
+ satisfied = UNSATISFIED;
137
+ }
138
+ }
139
+ const declaredNativeType = NATIVE_TYPES.get(schema["x-native-type"]);
140
+ const nativeType = schema.type === undefined || declaredNativeType?.types.includes(schema.type) ? declaredNativeType : undefined;
141
+ if (nativeType && !nativeType.afterTypeCheck) {
142
+ coerced = nativeType.coerce(coerced, this.nativeCoerceOptions);
143
+ }
144
+ if (schema.type && !nativeType?.is(coerced)) {
145
+ switch (schema.type) {
146
+ case "null": {
147
+ if (coerced !== null) {
148
+ satisfied = UNSATISFIED;
149
+ }
150
+ break;
151
+ }
152
+ case "string": {
153
+ if (typeof coerced !== "string") {
154
+ satisfied = UNSATISFIED;
155
+ }
156
+ break;
157
+ }
158
+ case "number": {
159
+ if (typeof coerced === "string") {
160
+ coerced = stringToNumber(coerced);
161
+ }
162
+ if (typeof coerced !== "number") {
163
+ satisfied = UNSATISFIED;
164
+ }
165
+ break;
166
+ }
167
+ case "integer": {
168
+ if (typeof coerced === "string") {
169
+ coerced = stringToInteger(coerced);
170
+ }
171
+ if (typeof coerced !== "number" || !Number.isInteger(coerced)) {
172
+ satisfied = UNSATISFIED;
173
+ }
174
+ break;
175
+ }
176
+ case "boolean": {
177
+ if (typeof coerced === "string") {
178
+ coerced = stringToBoolean(coerced);
179
+ }
180
+ if (typeof coerced !== "boolean") {
181
+ satisfied = UNSATISFIED;
182
+ }
183
+ break;
184
+ }
185
+ case "array": {
186
+ if (Array.isArray(coerced)) {
187
+ const prefixItemSchemas = "prefixItems" in schema ? toArray(schema.prefixItems) : Array.isArray(schema.items) ? schema.items : [];
188
+ const itemSchema = Array.isArray(schema.items) ? schema.additionalItems : schema.items;
189
+ let shouldUseCoercedItems = false;
190
+ const coercedItems = coerced.map((item, i) => {
191
+ const subSchema = prefixItemSchemas[i] ?? itemSchema;
192
+ if (subSchema === undefined) {
193
+ satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
194
+ return item;
195
+ }
196
+ const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, item);
197
+ satisfied = minSatisfaction(satisfied, subSatisfied);
198
+ if (subCoerced !== item) {
199
+ shouldUseCoercedItems = true;
200
+ }
201
+ return subCoerced;
202
+ });
203
+ if (typeof schema.minItems === "number" && coercedItems.length < schema.minItems) {
204
+ satisfied = UNSATISFIED;
205
+ }
206
+ if (shouldUseCoercedItems) {
207
+ coerced = coercedItems;
208
+ }
209
+ } else {
210
+ satisfied = UNSATISFIED;
211
+ }
212
+ break;
213
+ }
214
+ case "object": {
215
+ if (Array.isArray(coerced)) {
216
+ coerced = { ...coerced };
217
+ }
218
+ if (isPlainObject(coerced)) {
219
+ let shouldUseCoercedItems = false;
220
+
221
+ const coercedItems = { ...coerced };
222
+ const patternProperties = Object.entries(schema.patternProperties ?? {}).flatMap(([key, value]) => {
223
+
224
+ const pattern = tryOrUndefined(() => new RegExp(key));
225
+ return pattern ? [[pattern, value]] : [];
226
+ });
227
+ const propertySchemas = schema.properties ?? {};
228
+ for (const key of Object.keys(coerced)) {
229
+ const value = coerced[key];
230
+ const subSchema = getOwn(propertySchemas, key) ?? patternProperties.find(([pattern]) => pattern.test(key))?.[1] ?? schema.additionalProperties;
231
+ if (value !== undefined || schema.required?.includes(key)) {
232
+ if (subSchema === undefined) {
233
+ satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
234
+ } else {
235
+ const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, value);
236
+ coercedItems[key] = subCoerced;
237
+ satisfied = minSatisfaction(satisfied, subSatisfied);
238
+ if (subCoerced !== value) {
239
+ shouldUseCoercedItems = true;
240
+ }
241
+ }
242
+ }
243
+ }
244
+ if (schema.required?.some((key) => !Object.hasOwn(coercedItems, key))) {
245
+ satisfied = UNSATISFIED;
246
+ }
247
+ if (shouldUseCoercedItems) {
248
+ coerced = coercedItems;
249
+ }
250
+ } else {
251
+ satisfied = UNSATISFIED;
252
+ }
253
+ break;
254
+ }
255
+ }
256
+ }
257
+ if (nativeType) {
258
+ if (nativeType.afterTypeCheck) {
259
+ coerced = nativeType.coerce(coerced, this.nativeCoerceOptions);
260
+ }
261
+ if (!nativeType.is(coerced)) {
262
+ satisfied = UNSATISFIED;
263
+ }
264
+ }
265
+ if (schema.allOf) {
266
+ context.memoize = true;
267
+ for (const subSchema of schema.allOf) {
268
+ const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, coerced, appliedRefs);
269
+ coerced = subCoerced;
270
+ satisfied = minSatisfaction(satisfied, subSatisfied);
271
+ }
272
+ }
273
+ for (const key of ["anyOf", "oneOf"]) {
274
+ if (schema[key]) {
275
+ context.memoize = true;
276
+
277
+ let best;
278
+ for (const subSchema of schema[key]) {
279
+ const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, coerced, appliedRefs);
280
+ if (subSatisfied === UNSATISFIED) {
281
+ continue;
282
+ }
283
+ if (subCoerced === coerced) {
284
+ best = {
285
+ satisfied: subSatisfied,
286
+ value: subCoerced
287
+ };
288
+ break;
289
+ }
290
+ if (!best || subSatisfied > best.satisfied) {
291
+ best = {
292
+ satisfied: subSatisfied,
293
+ value: subCoerced
294
+ };
295
+ }
296
+ }
297
+
298
+ if (best) {
299
+ coerced = best.value;
300
+ satisfied = minSatisfaction(satisfied, best.satisfied);
301
+ } else {
302
+ satisfied = UNSATISFIED;
303
+ }
304
+ }
305
+ }
306
+ if (typeof schema.not !== "undefined") {
307
+ context.memoize = true;
308
+ const [notSatisfied] = this.coerceInternal(context, schema.not, coerced, appliedRefs);
309
+ if (notSatisfied !== UNSATISFIED) {
310
+ satisfied = UNSATISFIED;
311
+ }
312
+ }
313
+ return [satisfied, coerced];
314
+ }
315
+ coerceRef(context, resolved, value, appliedRefs) {
316
+ let memo;
317
+
318
+ if (context.memoize && !appliedRefs?.size && isTypescriptObject(value)) {
319
+ context.memo ??= new Map();
320
+ memo = context.memo.get(resolved);
321
+ if (!memo) {
322
+ memo = new Map();
323
+ context.memo.set(resolved, memo);
324
+ }
325
+ const cached = memo.get(value);
326
+ if (cached) {
327
+ return cached;
328
+ }
329
+ }
330
+ appliedRefs ??= new Set();
331
+ appliedRefs.add(resolved);
332
+ const result = this.coerceInternal(context, resolved, value, appliedRefs);
333
+ appliedRefs.delete(resolved);
334
+ memo?.set(value, result);
335
+ return result;
336
+ }
337
+ extendBudget(context) {
338
+ const limit = 2 * context.steps / this.maxStepsPerValue;
339
+ context.maxSteps = this.maxStepsPerValue * Math.max(countValues(context.input, limit), MIN_BUDGETED_VALUES);
340
+ if (context.steps > context.maxSteps) {
341
+ throw new CoercionBudgetExceededError();
342
+ }
343
+ }
344
+ }
345
+ function countValues(input, limit) {
346
+ const walked = new Set();
347
+ const stack = [input];
348
+ let count = 1;
349
+ while (stack.length) {
350
+ const value = stack.pop();
351
+ if ((Array.isArray(value) || isPlainObject(value)) && !walked.has(value)) {
352
+ walked.add(value);
353
+ const items = Array.isArray(value) ? value : Object.values(value);
354
+ for (let i = 0; i < items.length; i++) {
355
+ if (++count >= limit) {
356
+ return count;
357
+ }
358
+ const item = items[i];
359
+ if (isTypescriptObject(item)) {
360
+ stack.push(item);
361
+ }
362
+ }
363
+ }
364
+ }
365
+ return count;
366
+ }
367
+ const NUMBER_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
368
+ function stringToNumber(value) {
369
+ if (!NUMBER_PATTERN.test(value)) {
370
+ return value;
371
+ }
372
+ const num = Number(value);
373
+
374
+ if (num < Number.MIN_SAFE_INTEGER || num > Number.MAX_SAFE_INTEGER) {
375
+ return value;
376
+ }
377
+ return num;
378
+ }
379
+ const INTEGER_PATTERN = /^-?(?:0|[1-9]\d*)$/;
380
+ function stringToInteger(value) {
381
+ if (!INTEGER_PATTERN.test(value)) {
382
+ return value;
383
+ }
384
+ const num = Number(value);
385
+ if (!Number.isSafeInteger(num)) {
386
+ return value;
387
+ }
388
+ return num;
389
+ }
390
+ function stringToBigInt(value, maxDigits) {
391
+ return parseBigIntString(value, maxDigits) ?? value;
392
+ }
393
+ function numberToBigInt(value) {
394
+ if (!Number.isInteger(value)) {
395
+ return value;
396
+ }
397
+ return BigInt(value);
398
+ }
399
+ function stringToBoolean(value) {
400
+ const lower = value.toLowerCase();
401
+ if (lower === "false" || lower === "off") {
402
+ return false;
403
+ }
404
+ if (lower === "true" || lower === "on") {
405
+ return true;
406
+ }
407
+ return value;
408
+ }
409
+ const DATE_TIME_PATTERN = /^(?!-0{6})(?:[+-]\d{2})?\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
410
+ const DAYS_IN_MONTH = [
411
+ 31,
412
+ 29,
413
+ 31,
414
+ 30,
415
+ 31,
416
+ 30,
417
+ 31,
418
+ 31,
419
+ 30,
420
+ 31,
421
+ 30,
422
+ 31
423
+ ];
424
+ function stringToDate(value) {
425
+ if (!DATE_TIME_PATTERN.test(value)) {
426
+ return value;
427
+ }
428
+ const yearEnd = value.indexOf("-", 1);
429
+ const month = readTwoDigits(value, yearEnd + 1);
430
+ const day = readTwoDigits(value, yearEnd + 4);
431
+ if (day < 1 || day > (DAYS_IN_MONTH[month - 1] ?? 0) || month === 2 && day === 29 && !isLeapYear(Number(value.slice(0, yearEnd)))) {
432
+ return value;
433
+ }
434
+ const date = new Date(value);
435
+ if (Number.isNaN(date.getTime())) {
436
+ return value;
437
+ }
438
+ return date;
439
+ }
440
+ function readTwoDigits(value, index) {
441
+ return (value.charCodeAt(index) - 48) * 10 + value.charCodeAt(index + 1) - 48;
442
+ }
443
+ function isLeapYear(year) {
444
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
445
+ }
446
+ function stringToURL(value) {
447
+ return tryOrUndefined(() => new URL(value)) ?? value;
448
+ }
449
+ function arrayToSet(value) {
450
+ const set = new Set(value);
451
+ if (set.size !== value.length) {
452
+ return value;
453
+ }
454
+ return set;
455
+ }
456
+ function arrayToMap(value) {
457
+ if (value.some((item) => !Array.isArray(item) || item.length !== 2)) {
458
+ return value;
459
+ }
460
+ const result = new Map(value);
461
+ if (result.size !== value.length) {
462
+ return value;
463
+ }
464
+ return result;
465
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * These utilities assume the schema has only one root-level `$defs` object
3
+ * and exclusively use absolute JSON pointers for `$ref` values.
4
+ */
5
+ import type { JsonSchema } from "./types.mjs";
6
+ import type { JsonArraySchema, JsonObjectSchema } from "./utils.mjs";
7
+ /**
8
+ * Combines multiple schemas under the requested composition keyword, promoting branch `$defs` to the root.
9
+ */
10
+ export declare function combineJsonSchemasWithComposition(keyword: "allOf" | "anyOf" | "oneOf", schemas: JsonSchema[]): JsonSchema;
11
+ export type JsonObjectSchemaEntry = [name: string, schema: JsonSchema, optional: boolean];
12
+ /**
13
+ * Combines object property entries back into a single object schema.
14
+ * When given, `additionalProperties` constrains every property the entries do not name.
15
+ */
16
+ export declare function combineJsonObjectSchemaEntries(entries: JsonObjectSchemaEntry[], additionalProperties?: JsonSchema): JsonObjectSchema;
17
+ /**
18
+ * Parses an object schema, or a composition of object schemas, into property entries.
19
+ */
20
+ export declare function extractJsonObjectSchemaEntries(schema: JsonSchema): JsonObjectSchemaEntry[] | undefined;
21
+ /**
22
+ * Returns the `additionalProperties` an object schema declares at its root, following root-local refs,
23
+ * with the root `$defs` attached so it can be passed to {@link combineJsonObjectSchemaEntries}.
24
+ * Composition branches are not inspected, since their keyword only constrains that branch.
25
+ */
26
+ export declare function extractJsonObjectSchemaAdditionalProperties(schema: JsonSchema): JsonSchema | undefined;
27
+ /**
28
+ * Returns true when an object can stand for every value the schema describes: the schema constrains objects,
29
+ * by `type` or object keywords, composes only such schemas, or is unconstrained, since an object satisfies it.
30
+ * Unlike {@link extractJsonObjectSchemaEntries}, an unconstrained schema counts as object-like.
31
+ */
32
+ export declare function isJsonObjectLikeSchema(schema: JsonSchema): boolean;
33
+ /**
34
+ * Flattens `anyOf` and `oneOf` unions when they are the only active constraints.
35
+ */
36
+ export declare function flattenJsonUnionSchema(schema: JsonSchema): JsonSchema[];
37
+ /**
38
+ * Matches a union made of a single item schema and its array form.
39
+ */
40
+ export declare function matchArrayableJsonSchema(schema: JsonSchema): undefined | [itemSchema: JsonSchema, arraySchema: JsonArraySchema];
41
+ export declare function deduplicateJsonSchemas(schemas: JsonSchema[]): JsonSchema[];