effect 3.11.5 → 3.11.7
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 +357 -343
- 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 +49 -33
- package/dist/cjs/Schema.js.map +1 -1
- package/dist/cjs/internal/schema/filters.js +24 -18
- package/dist/cjs/internal/schema/filters.js.map +1 -1
- package/dist/cjs/internal/version.js +1 -1
- package/dist/dts/Arbitrary.d.ts +21 -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 +46 -18
- package/dist/dts/Schema.d.ts.map +1 -1
- package/dist/esm/Arbitrary.js +351 -335
- 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 +49 -33
- package/dist/esm/Schema.js.map +1 -1
- package/dist/esm/internal/schema/filters.js +23 -17
- package/dist/esm/internal/schema/filters.js.map +1 -1
- package/dist/esm/internal/version.js +1 -1
- package/package.json +1 -1
- package/src/Arbitrary.ts +410 -361
- package/src/JSONSchema.ts +185 -40
- package/src/Schema.ts +161 -74
- package/src/internal/schema/filters.ts +32 -17
- package/src/internal/version.ts +1 -1
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
|