effect 3.11.6 → 3.11.8
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/cjs/Arbitrary.js +46 -4
- package/dist/cjs/Arbitrary.js.map +1 -1
- package/dist/cjs/JSONSchema.js +157 -38
- package/dist/cjs/JSONSchema.js.map +1 -1
- package/dist/cjs/Schema.js +63 -51
- package/dist/cjs/Schema.js.map +1 -1
- package/dist/cjs/internal/schema/{filters.js → schemaId.js} +4 -2
- package/dist/cjs/internal/schema/schemaId.js.map +1 -0
- package/dist/cjs/internal/version.js +1 -1
- package/dist/dts/Arbitrary.d.ts +5 -1
- package/dist/dts/Arbitrary.d.ts.map +1 -1
- package/dist/dts/JSONSchema.d.ts +11 -2
- package/dist/dts/JSONSchema.d.ts.map +1 -1
- package/dist/dts/Schema.d.ts +16 -6
- package/dist/dts/Schema.d.ts.map +1 -1
- package/dist/dts/internal/schema/schemaId.d.ts +2 -0
- package/dist/dts/internal/schema/schemaId.d.ts.map +1 -0
- package/dist/esm/Arbitrary.js +44 -3
- package/dist/esm/Arbitrary.js.map +1 -1
- package/dist/esm/JSONSchema.js +157 -38
- package/dist/esm/JSONSchema.js.map +1 -1
- package/dist/esm/Schema.js +55 -43
- package/dist/esm/Schema.js.map +1 -1
- package/dist/esm/internal/schema/{filters.js → schemaId.js} +3 -1
- package/dist/esm/internal/schema/schemaId.js.map +1 -0
- package/dist/esm/internal/version.js +1 -1
- package/package.json +1 -1
- package/src/Arbitrary.ts +64 -13
- package/src/JSONSchema.ts +185 -40
- package/src/Schema.ts +65 -50
- package/src/internal/schema/{filters.ts → schemaId.ts} +5 -0
- package/src/internal/version.ts +1 -1
- package/dist/cjs/internal/schema/filters.js.map +0 -1
- package/dist/dts/internal/schema/filters.d.ts +0 -2
- package/dist/dts/internal/schema/filters.d.ts.map +0 -1
- package/dist/esm/internal/schema/filters.js.map +0 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
+
export const DateFromSelfSchemaId = /*#__PURE__*/Symbol.for("effect/SchemaId/DateFromSelf");
|
|
3
|
+
/** @internal */
|
|
2
4
|
export const GreaterThanSchemaId = /*#__PURE__*/Symbol.for("effect/SchemaId/GreaterThan");
|
|
3
5
|
/** @internal */
|
|
4
6
|
export const GreaterThanOrEqualToSchemaId = /*#__PURE__*/Symbol.for("effect/SchemaId/GreaterThanOrEqualTo");
|
|
@@ -38,4 +40,4 @@ export const MinItemsSchemaId = /*#__PURE__*/Symbol.for("effect/SchemaId/MinItem
|
|
|
38
40
|
export const MaxItemsSchemaId = /*#__PURE__*/Symbol.for("effect/SchemaId/MaxItems");
|
|
39
41
|
/** @internal */
|
|
40
42
|
export const ItemsCountSchemaId = /*#__PURE__*/Symbol.for("effect/SchemaId/ItemsCount");
|
|
41
|
-
//# sourceMappingURL=
|
|
43
|
+
//# sourceMappingURL=schemaId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaId.js","names":["DateFromSelfSchemaId","Symbol","for","GreaterThanSchemaId","GreaterThanOrEqualToSchemaId","LessThanSchemaId","LessThanOrEqualToSchemaId","IntSchemaId","NonNaNSchemaId","FiniteSchemaId","JsonNumberSchemaId","BetweenSchemaId","GreaterThanBigintSchemaId","GreaterThanOrEqualToBigIntSchemaId","LessThanBigIntSchemaId","LessThanOrEqualToBigIntSchemaId","BetweenBigintSchemaId","MinLengthSchemaId","MaxLengthSchemaId","LengthSchemaId","MinItemsSchemaId","MaxItemsSchemaId","ItemsCountSchemaId"],"sources":["../../../../src/internal/schema/schemaId.ts"],"sourcesContent":[null],"mappings":"AAEA;AACA,OAAO,MAAMA,oBAAoB,gBAAgCC,MAAM,CAACC,GAAG,CACzE,8BAA8B,CACA;AAEhC;AACA,OAAO,MAAMC,mBAAmB,gBAA+BF,MAAM,CAACC,GAAG,CACvE,6BAA6B,CACA;AAE/B;AACA,OAAO,MAAME,4BAA4B,gBAAwCH,MAAM,CAACC,GAAG,CACzF,sCAAsC,CACA;AAExC;AACA,OAAO,MAAMG,gBAAgB,gBAA4BJ,MAAM,CAACC,GAAG,CACjE,0BAA0B,CACA;AAE5B;AACA,OAAO,MAAMI,yBAAyB,gBAAqCL,MAAM,CAACC,GAAG,CACnF,mCAAmC,CACA;AAErC;AACA,OAAO,MAAMK,WAAW,gBAAuBN,MAAM,CAACC,GAAG,CACvD,qBAAqB,CACA;AAEvB;AACA,OAAO,MAAMM,cAAc,gBAA0BP,MAAM,CAACC,GAAG,CAC7D,wBAAwB,CACA;AAE1B;AACA,OAAO,MAAMO,cAAc,gBAA0BR,MAAM,CAACC,GAAG,CAC7D,wBAAwB,CACA;AAE1B;AACA,OAAO,MAAMQ,kBAAkB,gBAA8BT,MAAM,CAACC,GAAG,CACrE,4BAA4B,CACA;AAE9B;AACA,OAAO,MAAMS,eAAe,gBAA2BV,MAAM,CAACC,GAAG,CAC/D,yBAAyB,CACA;AAE3B;AACA,OAAO,MAAMU,yBAAyB,gBAAqCX,MAAM,CAACC,GAAG,CACnF,mCAAmC,CACA;AAErC;AACA,OAAO,MAAMW,kCAAkC,gBAA8CZ,MAAM,CAACC,GAAG,CACrG,4CAA4C,CACA;AAE9C;AACA,OAAO,MAAMY,sBAAsB,gBAAkCb,MAAM,CAACC,GAAG,CAC7E,gCAAgC,CACA;AAElC;AACA,OAAO,MAAMa,+BAA+B,gBAA2Cd,MAAM,CAACC,GAAG,CAC/F,yCAAyC,CACA;AAE3C;AACA,OAAO,MAAMc,qBAAqB,gBAAiCf,MAAM,CAACC,GAAG,CAC3E,+BAA+B,CACA;AAEjC;AACA,OAAO,MAAMe,iBAAiB,gBAA6BhB,MAAM,CAACC,GAAG,CACnE,2BAA2B,CACA;AAE7B;AACA,OAAO,MAAMgB,iBAAiB,gBAA6BjB,MAAM,CAACC,GAAG,CACnE,2BAA2B,CACA;AAE7B;AACA,OAAO,MAAMiB,cAAc,gBAA0BlB,MAAM,CAACC,GAAG,CAC7D,wBAAwB,CACA;AAE1B;AACA,OAAO,MAAMkB,gBAAgB,gBAA4BnB,MAAM,CAACC,GAAG,CACjE,0BAA0B,CACA;AAE5B;AACA,OAAO,MAAMmB,gBAAgB,gBAA4BpB,MAAM,CAACC,GAAG,CACjE,0BAA0B,CACA;AAE5B;AACA,OAAO,MAAMoB,kBAAkB,gBAA8BrB,MAAM,CAACC,GAAG,CACrE,4BAA4B,CACA","ignoreList":[]}
|
package/package.json
CHANGED
package/src/Arbitrary.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import * as Arr from "./Array.js"
|
|
6
6
|
import * as FastCheck from "./FastCheck.js"
|
|
7
7
|
import * as errors_ from "./internal/schema/errors.js"
|
|
8
|
-
import * as
|
|
8
|
+
import * as schemaId_ from "./internal/schema/schemaId.js"
|
|
9
9
|
import * as util_ from "./internal/schema/util.js"
|
|
10
10
|
import * as Option from "./Option.js"
|
|
11
11
|
import * as Predicate from "./Predicate.js"
|
|
@@ -28,7 +28,7 @@ export interface LazyArbitrary<A> {
|
|
|
28
28
|
export interface ArbitraryGenerationContext {
|
|
29
29
|
readonly maxDepth: number
|
|
30
30
|
readonly depthIdentifier?: string
|
|
31
|
-
readonly constraints?: StringConstraints | NumberConstraints | BigIntConstraints | ArrayConstraints
|
|
31
|
+
readonly constraints?: StringConstraints | NumberConstraints | BigIntConstraints | DateConstraints | ArrayConstraints
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -110,6 +110,8 @@ class Deferred {
|
|
|
110
110
|
}
|
|
111
111
|
case "BigIntConstraints":
|
|
112
112
|
return (fc) => fc.bigInt(config.constraints)
|
|
113
|
+
case "DateConstraints":
|
|
114
|
+
return (fc) => fc.date(config.constraints)
|
|
113
115
|
case "ArrayConstraints":
|
|
114
116
|
return goTupleType(config.ast, ctx, path, config.constraints)
|
|
115
117
|
}
|
|
@@ -232,6 +234,32 @@ export const makeArrayConstraints = (options: {
|
|
|
232
234
|
return out
|
|
233
235
|
}
|
|
234
236
|
|
|
237
|
+
interface DateConstraints {
|
|
238
|
+
readonly _tag: "DateConstraints"
|
|
239
|
+
readonly constraints: FastCheck.DateConstraints
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** @internal */
|
|
243
|
+
export const makeDateConstraints = (options: {
|
|
244
|
+
readonly min?: Date | undefined
|
|
245
|
+
readonly max?: Date | undefined
|
|
246
|
+
readonly noInvalidDate?: boolean | undefined
|
|
247
|
+
}): DateConstraints => {
|
|
248
|
+
const out: Types.Mutable<DateConstraints> = {
|
|
249
|
+
_tag: "DateConstraints",
|
|
250
|
+
constraints: {
|
|
251
|
+
noInvalidDate: options.noInvalidDate ?? false
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (Predicate.isDate(options.min)) {
|
|
255
|
+
out.constraints.min = options.min
|
|
256
|
+
}
|
|
257
|
+
if (Predicate.isDate(options.max)) {
|
|
258
|
+
out.constraints.max = options.max
|
|
259
|
+
}
|
|
260
|
+
return out
|
|
261
|
+
}
|
|
262
|
+
|
|
235
263
|
interface ArrayConfig extends ArrayConstraints {
|
|
236
264
|
readonly ast: AST.TupleType
|
|
237
265
|
}
|
|
@@ -246,7 +274,7 @@ const makeArrayConfig = (options: {
|
|
|
246
274
|
}
|
|
247
275
|
}
|
|
248
276
|
|
|
249
|
-
type Config = StringConstraints | NumberConstraints | BigIntConstraints | ArrayConfig
|
|
277
|
+
type Config = StringConstraints | NumberConstraints | BigIntConstraints | DateConstraints | ArrayConfig
|
|
250
278
|
|
|
251
279
|
const go = (
|
|
252
280
|
ast: AST.AST,
|
|
@@ -268,6 +296,9 @@ const go = (
|
|
|
268
296
|
return hook.value(ctx)
|
|
269
297
|
}
|
|
270
298
|
}
|
|
299
|
+
if (AST.isDeclaration(ast)) {
|
|
300
|
+
throw new Error(errors_.getArbitraryMissingAnnotationErrorMessage(path, ast))
|
|
301
|
+
}
|
|
271
302
|
const op = toOp(ast, ctx, path)
|
|
272
303
|
switch (op._tag) {
|
|
273
304
|
case "Succeed":
|
|
@@ -288,8 +319,14 @@ export const toOp = (
|
|
|
288
319
|
path: ReadonlyArray<PropertyKey>
|
|
289
320
|
): Op => {
|
|
290
321
|
switch (ast._tag) {
|
|
291
|
-
case "Declaration":
|
|
292
|
-
|
|
322
|
+
case "Declaration": {
|
|
323
|
+
const TypeAnnotationId: any = ast.annotations[AST.SchemaIdAnnotationId]
|
|
324
|
+
switch (TypeAnnotationId) {
|
|
325
|
+
case schemaId_.DateFromSelfSchemaId:
|
|
326
|
+
return new Deferred(makeDateConstraints(ast.annotations[TypeAnnotationId] as any))
|
|
327
|
+
}
|
|
328
|
+
return new Succeed(go(ast, ctx, path))
|
|
329
|
+
}
|
|
293
330
|
case "Literal":
|
|
294
331
|
return new Succeed((fc) => fc.constant(ast.literal))
|
|
295
332
|
case "UniqueSymbol":
|
|
@@ -479,7 +516,7 @@ const goTupleType = (
|
|
|
479
516
|
}
|
|
480
517
|
}
|
|
481
518
|
|
|
482
|
-
type Constraints = StringConstraints | NumberConstraints | BigIntConstraints | ArrayConstraints
|
|
519
|
+
type Constraints = StringConstraints | NumberConstraints | BigIntConstraints | DateConstraints | ArrayConstraints
|
|
483
520
|
|
|
484
521
|
const getConstraints = (_tag: Constraints["_tag"], ast: AST.Refinement): Constraints | undefined => {
|
|
485
522
|
const TypeAnnotationId: any = ast.annotations[AST.SchemaIdAnnotationId]
|
|
@@ -490,7 +527,7 @@ const getConstraints = (_tag: Constraints["_tag"], ast: AST.Refinement): Constra
|
|
|
490
527
|
return makeStringConstraints(jsonSchema)
|
|
491
528
|
case "NumberConstraints": {
|
|
492
529
|
switch (TypeAnnotationId) {
|
|
493
|
-
case
|
|
530
|
+
case schemaId_.NonNaNSchemaId:
|
|
494
531
|
return makeNumberConstraints({ noNaN: true })
|
|
495
532
|
default:
|
|
496
533
|
return makeNumberConstraints({
|
|
@@ -506,6 +543,8 @@ const getConstraints = (_tag: Constraints["_tag"], ast: AST.Refinement): Constra
|
|
|
506
543
|
}
|
|
507
544
|
case "BigIntConstraints":
|
|
508
545
|
return makeBigIntConstraints(ast.annotations[TypeAnnotationId] as any)
|
|
546
|
+
case "DateConstraints":
|
|
547
|
+
return makeDateConstraints(ast.annotations[TypeAnnotationId] as any)
|
|
509
548
|
case "ArrayConstraints":
|
|
510
549
|
return makeArrayConstraints({
|
|
511
550
|
minLength: jsonSchema.minItems,
|
|
@@ -514,21 +553,23 @@ const getConstraints = (_tag: Constraints["_tag"], ast: AST.Refinement): Constra
|
|
|
514
553
|
}
|
|
515
554
|
}
|
|
516
555
|
|
|
556
|
+
function getMax(n1: Date | undefined, n2: Date | undefined): Date | undefined
|
|
517
557
|
function getMax(n1: bigint | undefined, n2: bigint | undefined): bigint | undefined
|
|
518
558
|
function getMax(n1: number | undefined, n2: number | undefined): number | undefined
|
|
519
559
|
function getMax(
|
|
520
|
-
n1: bigint | number | undefined,
|
|
521
|
-
n2: bigint | number | undefined
|
|
522
|
-
): bigint | number | undefined {
|
|
560
|
+
n1: bigint | number | Date | undefined,
|
|
561
|
+
n2: bigint | number | Date | undefined
|
|
562
|
+
): bigint | number | Date | undefined {
|
|
523
563
|
return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n2 : n1
|
|
524
564
|
}
|
|
525
565
|
|
|
566
|
+
function getMin(n1: Date | undefined, n2: Date | undefined): Date | undefined
|
|
526
567
|
function getMin(n1: bigint | undefined, n2: bigint | undefined): bigint | undefined
|
|
527
568
|
function getMin(n1: number | undefined, n2: number | undefined): number | undefined
|
|
528
569
|
function getMin(
|
|
529
|
-
n1: bigint | number | undefined,
|
|
530
|
-
n2: bigint | number | undefined
|
|
531
|
-
): bigint | number | undefined {
|
|
570
|
+
n1: bigint | number | Date | undefined,
|
|
571
|
+
n2: bigint | number | Date | undefined
|
|
572
|
+
): bigint | number | Date | undefined {
|
|
532
573
|
return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n1 : n2
|
|
533
574
|
}
|
|
534
575
|
|
|
@@ -572,6 +613,16 @@ const merge = (c1: Config, c2: Constraints | undefined): Config => {
|
|
|
572
613
|
}
|
|
573
614
|
break
|
|
574
615
|
}
|
|
616
|
+
case "DateConstraints": {
|
|
617
|
+
if (c2._tag === "DateConstraints") {
|
|
618
|
+
return makeDateConstraints({
|
|
619
|
+
min: getMax(c1.constraints.min, c2.constraints.min),
|
|
620
|
+
max: getMin(c1.constraints.max, c2.constraints.max),
|
|
621
|
+
noInvalidDate: getOr(c1.constraints.noInvalidDate, c2.constraints.noInvalidDate)
|
|
622
|
+
})
|
|
623
|
+
}
|
|
624
|
+
break
|
|
625
|
+
}
|
|
575
626
|
case "ArrayConstraints": {
|
|
576
627
|
if (c2._tag === "ArrayConstraints") {
|
|
577
628
|
return makeArrayConfig({
|
package/src/JSONSchema.ts
CHANGED
|
@@ -85,6 +85,14 @@ export interface JsonSchema7Ref extends JsonSchemaAnnotations {
|
|
|
85
85
|
$ref: string
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* @category model
|
|
90
|
+
* @since 3.11.7
|
|
91
|
+
*/
|
|
92
|
+
export interface JsonSchema7Null extends JsonSchemaAnnotations {
|
|
93
|
+
type: "null"
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
/**
|
|
89
97
|
* @category model
|
|
90
98
|
* @since 3.10.0
|
|
@@ -163,7 +171,8 @@ export interface JsonSchema7Array extends JsonSchemaAnnotations {
|
|
|
163
171
|
* @since 3.10.0
|
|
164
172
|
*/
|
|
165
173
|
export interface JsonSchema7Enum extends JsonSchemaAnnotations {
|
|
166
|
-
|
|
174
|
+
type?: "string" | "number" | "boolean"
|
|
175
|
+
enum: Array<string | number | boolean>
|
|
167
176
|
}
|
|
168
177
|
|
|
169
178
|
/**
|
|
@@ -173,6 +182,7 @@ export interface JsonSchema7Enum extends JsonSchemaAnnotations {
|
|
|
173
182
|
export interface JsonSchema7Enums extends JsonSchemaAnnotations {
|
|
174
183
|
$comment: "/schemas/enums"
|
|
175
184
|
anyOf: Array<{
|
|
185
|
+
type: "string" | "number"
|
|
176
186
|
title: string
|
|
177
187
|
enum: [string | number]
|
|
178
188
|
}>
|
|
@@ -211,6 +221,7 @@ export type JsonSchema7 =
|
|
|
211
221
|
| JsonSchema7object
|
|
212
222
|
| JsonSchema7empty
|
|
213
223
|
| JsonSchema7Ref
|
|
224
|
+
| JsonSchema7Null
|
|
214
225
|
| JsonSchema7String
|
|
215
226
|
| JsonSchema7Number
|
|
216
227
|
| JsonSchema7Integer
|
|
@@ -240,11 +251,17 @@ export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): JsonSchema7Root =
|
|
|
240
251
|
// Special case top level `parseJson` transformations
|
|
241
252
|
? schema.ast.to
|
|
242
253
|
: schema.ast
|
|
243
|
-
const
|
|
254
|
+
const jsonSchema = fromAST(ast, {
|
|
244
255
|
definitions
|
|
245
256
|
})
|
|
246
|
-
out
|
|
247
|
-
|
|
257
|
+
const out: JsonSchema7Root = {
|
|
258
|
+
$schema,
|
|
259
|
+
$defs: {},
|
|
260
|
+
...jsonSchema
|
|
261
|
+
}
|
|
262
|
+
if (Record.isEmptyRecord(definitions)) {
|
|
263
|
+
delete out.$defs
|
|
264
|
+
} else {
|
|
248
265
|
out.$defs = definitions
|
|
249
266
|
}
|
|
250
267
|
return out
|
|
@@ -405,10 +422,31 @@ const isOverrideAnnotation = (jsonSchema: JsonSchema7): boolean => {
|
|
|
405
422
|
("enum" in jsonSchema) || ("$ref" in jsonSchema)
|
|
406
423
|
}
|
|
407
424
|
|
|
408
|
-
// Returns true if the schema is an enum with no other properties
|
|
409
|
-
// This is used to merge enums together.
|
|
410
|
-
const
|
|
411
|
-
|
|
425
|
+
// Returns true if the schema is an enum with no other properties other than the
|
|
426
|
+
// optional "type". This is used to merge enums together.
|
|
427
|
+
const isMergeableEnum = (jsonSchema: JsonSchema7): jsonSchema is JsonSchema7Enum => {
|
|
428
|
+
const len = Object.keys(jsonSchema).length
|
|
429
|
+
return "enum" in jsonSchema && (len === 1 || ("type" in jsonSchema && len === 2))
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Some validators do not support enums without a type keyword. This function
|
|
433
|
+
// adds a type keyword to the schema if it is missing and the enum values are
|
|
434
|
+
// homogeneous.
|
|
435
|
+
const addEnumType = (jsonSchema: JsonSchema7): JsonSchema7 => {
|
|
436
|
+
if ("enum" in jsonSchema && !("type" in jsonSchema)) {
|
|
437
|
+
const type: "string" | "number" | "boolean" | undefined = jsonSchema.enum.every(Predicate.isString) ?
|
|
438
|
+
"string" :
|
|
439
|
+
jsonSchema.enum.every(Predicate.isNumber) ?
|
|
440
|
+
"number" :
|
|
441
|
+
jsonSchema.enum.every(Predicate.isBoolean) ?
|
|
442
|
+
"boolean" :
|
|
443
|
+
undefined
|
|
444
|
+
if (type !== undefined) {
|
|
445
|
+
return { type, ...jsonSchema }
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return jsonSchema
|
|
449
|
+
}
|
|
412
450
|
|
|
413
451
|
const mergeRefinements = (from: any, jsonSchema: any, annotations: any): any => {
|
|
414
452
|
const out: any = { ...from, ...annotations, ...jsonSchema }
|
|
@@ -438,15 +476,54 @@ const mergeRefinements = (from: any, jsonSchema: any, annotations: any): any =>
|
|
|
438
476
|
return out
|
|
439
477
|
}
|
|
440
478
|
|
|
479
|
+
type Options = {
|
|
480
|
+
readonly getRef: (id: string) => string
|
|
481
|
+
readonly target: Target
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
type Path = ReadonlyArray<PropertyKey>
|
|
485
|
+
|
|
486
|
+
const isContentSchemaSupported = (options: Options) => options.target !== "jsonSchema7"
|
|
487
|
+
|
|
488
|
+
const isNullTypeKeywordSupported = (options: Options) => options.target !== "openApi3.1"
|
|
489
|
+
|
|
490
|
+
// https://swagger.io/docs/specification/v3_0/data-models/data-types/#null
|
|
491
|
+
const isNullableKeywordSupported = (options: Options) => options.target === "openApi3.1"
|
|
492
|
+
|
|
493
|
+
const isNeverJSONSchema = (jsonSchema: JsonSchema7): jsonSchema is JsonSchema7Never =>
|
|
494
|
+
"$id" in jsonSchema && jsonSchema.$id === "/schemas/never"
|
|
495
|
+
|
|
496
|
+
const isAnyJSONSchema = (jsonSchema: JsonSchema7): jsonSchema is JsonSchema7Any =>
|
|
497
|
+
"$id" in jsonSchema && jsonSchema.$id === "/schemas/any"
|
|
498
|
+
|
|
499
|
+
const isUnknownJSONSchema = (jsonSchema: JsonSchema7): jsonSchema is JsonSchema7Unknown =>
|
|
500
|
+
"$id" in jsonSchema && jsonSchema.$id === "/schemas/unknown"
|
|
501
|
+
|
|
502
|
+
const isVoidJSONSchema = (jsonSchema: JsonSchema7): jsonSchema is JsonSchema7Void =>
|
|
503
|
+
"$id" in jsonSchema && jsonSchema.$id === "/schemas/void"
|
|
504
|
+
|
|
505
|
+
const shrink = (members: Array<JsonSchema7>): Array<JsonSchema7> => {
|
|
506
|
+
let i = members.findIndex(isAnyJSONSchema)
|
|
507
|
+
if (i !== -1) {
|
|
508
|
+
members = [members[i]]
|
|
509
|
+
}
|
|
510
|
+
i = members.findIndex(isUnknownJSONSchema)
|
|
511
|
+
if (i !== -1) {
|
|
512
|
+
members = [members[i]]
|
|
513
|
+
}
|
|
514
|
+
i = members.findIndex(isVoidJSONSchema)
|
|
515
|
+
if (i !== -1) {
|
|
516
|
+
members = [members[i]]
|
|
517
|
+
}
|
|
518
|
+
return members
|
|
519
|
+
}
|
|
520
|
+
|
|
441
521
|
const go = (
|
|
442
522
|
ast: AST.AST,
|
|
443
523
|
$defs: Record<string, JsonSchema7>,
|
|
444
524
|
handleIdentifier: boolean,
|
|
445
|
-
path:
|
|
446
|
-
options:
|
|
447
|
-
readonly getRef: (id: string) => string
|
|
448
|
-
readonly target: Target
|
|
449
|
-
}
|
|
525
|
+
path: Path,
|
|
526
|
+
options: Options
|
|
450
527
|
): JsonSchema7 => {
|
|
451
528
|
if (handleIdentifier) {
|
|
452
529
|
const identifier = AST.getJSONIdentifier(ast)
|
|
@@ -487,9 +564,25 @@ const go = (
|
|
|
487
564
|
case "Literal": {
|
|
488
565
|
const literal = ast.literal
|
|
489
566
|
if (literal === null) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
567
|
+
if (isNullTypeKeywordSupported(options)) {
|
|
568
|
+
// https://json-schema.org/draft-07/draft-handrews-json-schema-validation-00.pdf
|
|
569
|
+
// Section 6.1.1
|
|
570
|
+
return { type: "null", ...getJsonSchemaAnnotations(ast) }
|
|
571
|
+
} else {
|
|
572
|
+
// OpenAPI 3.1 does not support the "null" type keyword
|
|
573
|
+
// https://swagger.io/docs/specification/v3_0/data-models/data-types/#null
|
|
574
|
+
return {
|
|
575
|
+
// @ts-expect-error
|
|
576
|
+
enum: [null],
|
|
577
|
+
...getJsonSchemaAnnotations(ast)
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
} else if (Predicate.isString(literal)) {
|
|
581
|
+
return { type: "string", enum: [literal], ...getJsonSchemaAnnotations(ast) }
|
|
582
|
+
} else if (Predicate.isNumber(literal)) {
|
|
583
|
+
return { type: "number", enum: [literal], ...getJsonSchemaAnnotations(ast) }
|
|
584
|
+
} else if (Predicate.isBoolean(literal)) {
|
|
585
|
+
return { type: "boolean", enum: [literal], ...getJsonSchemaAnnotations(ast) }
|
|
493
586
|
}
|
|
494
587
|
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast))
|
|
495
588
|
}
|
|
@@ -636,38 +729,90 @@ const go = (
|
|
|
636
729
|
return { ...output, ...getJsonSchemaAnnotations(ast) }
|
|
637
730
|
}
|
|
638
731
|
case "Union": {
|
|
639
|
-
const
|
|
732
|
+
const members: Array<JsonSchema7> = []
|
|
640
733
|
for (const type of ast.types) {
|
|
641
|
-
const
|
|
642
|
-
if (
|
|
643
|
-
|
|
644
|
-
|
|
734
|
+
const jsonSchema = go(type, $defs, true, path, options)
|
|
735
|
+
if (!isNeverJSONSchema(jsonSchema)) {
|
|
736
|
+
const last = members[members.length - 1]
|
|
737
|
+
if (isMergeableEnum(jsonSchema) && last !== undefined && isMergeableEnum(last)) {
|
|
738
|
+
members[members.length - 1] = { enum: last.enum.concat(jsonSchema.enum) }
|
|
645
739
|
} else {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
740
|
+
members.push(jsonSchema)
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const anyOf = shrink(members)
|
|
746
|
+
|
|
747
|
+
const finalize = (anyOf: Array<JsonSchema7>) => {
|
|
748
|
+
switch (anyOf.length) {
|
|
749
|
+
case 0:
|
|
750
|
+
return {
|
|
751
|
+
...constNever,
|
|
752
|
+
...getJsonSchemaAnnotations(ast)
|
|
753
|
+
}
|
|
754
|
+
case 1: {
|
|
755
|
+
return {
|
|
756
|
+
...addEnumType(anyOf[0]),
|
|
757
|
+
...getJsonSchemaAnnotations(ast)
|
|
653
758
|
}
|
|
654
759
|
}
|
|
655
|
-
|
|
656
|
-
|
|
760
|
+
default:
|
|
761
|
+
return {
|
|
762
|
+
anyOf: anyOf.map(addEnumType),
|
|
763
|
+
...getJsonSchemaAnnotations(ast)
|
|
764
|
+
}
|
|
657
765
|
}
|
|
658
766
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
767
|
+
|
|
768
|
+
if (isNullableKeywordSupported(options)) {
|
|
769
|
+
let nullable = false
|
|
770
|
+
const nonNullables: Array<JsonSchema7> = []
|
|
771
|
+
for (const s of anyOf) {
|
|
772
|
+
if ("nullable" in s) {
|
|
773
|
+
nullable = true
|
|
774
|
+
const nn = { ...s }
|
|
775
|
+
delete nn.nullable
|
|
776
|
+
nonNullables.push(nn)
|
|
777
|
+
} else if (isMergeableEnum(s)) {
|
|
778
|
+
const nnes = s.enum.filter((e) => e !== null)
|
|
779
|
+
if (nnes.length < s.enum.length) {
|
|
780
|
+
nullable = true
|
|
781
|
+
if (nnes.length === 0) {
|
|
782
|
+
continue
|
|
783
|
+
}
|
|
784
|
+
const nn = { ...s }
|
|
785
|
+
nn.enum = nnes
|
|
786
|
+
nonNullables.push(nn)
|
|
787
|
+
}
|
|
788
|
+
} else {
|
|
789
|
+
nonNullables.push(s)
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (nullable) {
|
|
793
|
+
const out = finalize(nonNullables)
|
|
794
|
+
if (!isAnyJSONSchema(out) && !isUnknownJSONSchema(out)) {
|
|
795
|
+
// @ts-expect-error
|
|
796
|
+
out.nullable = nullable
|
|
797
|
+
}
|
|
798
|
+
return out
|
|
799
|
+
}
|
|
663
800
|
}
|
|
801
|
+
|
|
802
|
+
return finalize(anyOf)
|
|
664
803
|
}
|
|
665
804
|
case "Enums": {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
805
|
+
const anyOf = ast.enums.map((e) => addEnumType({ title: e[0], enum: [e[1]] }))
|
|
806
|
+
return anyOf.length >= 1 ?
|
|
807
|
+
{
|
|
808
|
+
$comment: "/schemas/enums",
|
|
809
|
+
anyOf,
|
|
810
|
+
...getJsonSchemaAnnotations(ast)
|
|
811
|
+
} :
|
|
812
|
+
{
|
|
813
|
+
...constNever,
|
|
814
|
+
...getJsonSchemaAnnotations(ast)
|
|
815
|
+
}
|
|
671
816
|
}
|
|
672
817
|
case "Refinement": {
|
|
673
818
|
// The jsonSchema annotation is required only if the refinement does not have a transformation
|
|
@@ -699,7 +844,7 @@ const go = (
|
|
|
699
844
|
"type": "string",
|
|
700
845
|
"contentMediaType": "application/json"
|
|
701
846
|
}
|
|
702
|
-
if (options
|
|
847
|
+
if (isContentSchemaSupported(options)) {
|
|
703
848
|
out["contentSchema"] = go(ast.to, $defs, handleIdentifier, path, options)
|
|
704
849
|
}
|
|
705
850
|
return out
|