2023-10-03 11:14:36 +08:00
"use strict" ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . default = void 0 ;
const {
stringHints ,
numberHints
} = require ( "./util/hints" ) ;
/** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
/** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */
/** @typedef {import("./validate").Schema} Schema */
/** @typedef {import("./validate").ValidationErrorConfiguration} ValidationErrorConfiguration */
/** @typedef {import("./validate").PostFormatter} PostFormatter */
/** @typedef {import("./validate").SchemaUtilErrorObject} SchemaUtilErrorObject */
/** @enum {number} */
const SPECIFICITY = {
type : 1 ,
not : 1 ,
oneOf : 1 ,
anyOf : 1 ,
if : 1 ,
enum : 1 ,
const : 1 ,
instanceof : 1 ,
required : 2 ,
pattern : 2 ,
patternRequired : 2 ,
format : 2 ,
formatMinimum : 2 ,
formatMaximum : 2 ,
minimum : 2 ,
exclusiveMinimum : 2 ,
maximum : 2 ,
exclusiveMaximum : 2 ,
multipleOf : 2 ,
uniqueItems : 2 ,
contains : 2 ,
minLength : 2 ,
maxLength : 2 ,
minItems : 2 ,
maxItems : 2 ,
minProperties : 2 ,
maxProperties : 2 ,
dependencies : 2 ,
propertyNames : 2 ,
additionalItems : 2 ,
additionalProperties : 2 ,
absolutePath : 2
} ;
/ * *
*
* @ param { Array < SchemaUtilErrorObject > } array
* @ param { ( item : SchemaUtilErrorObject ) => number } fn
* @ returns { Array < SchemaUtilErrorObject > }
* /
function filterMax ( array , fn ) {
const evaluatedMax = array . reduce ( ( max , item ) => Math . max ( max , fn ( item ) ) , 0 ) ;
return array . filter ( item => fn ( item ) === evaluatedMax ) ;
}
/ * *
*
* @ param { Array < SchemaUtilErrorObject > } children
* @ returns { Array < SchemaUtilErrorObject > }
* /
function filterChildren ( children ) {
let newChildren = children ;
newChildren = filterMax ( newChildren ,
/ * *
*
* @ param { SchemaUtilErrorObject } error
* @ returns { number }
* /
error => error . dataPath ? error . dataPath . length : 0 ) ;
newChildren = filterMax ( newChildren ,
/ * *
* @ param { SchemaUtilErrorObject } error
* @ returns { number }
* /
error => SPECIFICITY [
/** @type {keyof typeof SPECIFICITY} */
error . keyword ] || 2 ) ;
return newChildren ;
}
/ * *
* Find all children errors
* @ param { Array < SchemaUtilErrorObject > } children
* @ param { Array < string > } schemaPaths
* @ return { number } returns index of first child
* /
function findAllChildren ( children , schemaPaths ) {
let i = children . length - 1 ;
const predicate =
/ * *
* @ param { string } schemaPath
* @ returns { boolean }
* /
schemaPath => children [ i ] . schemaPath . indexOf ( schemaPath ) !== 0 ;
while ( i > - 1 && ! schemaPaths . every ( predicate ) ) {
if ( children [ i ] . keyword === "anyOf" || children [ i ] . keyword === "oneOf" ) {
const refs = extractRefs ( children [ i ] ) ;
const childrenStart = findAllChildren ( children . slice ( 0 , i ) , refs . concat ( children [ i ] . schemaPath ) ) ;
i = childrenStart - 1 ;
} else {
i -= 1 ;
}
}
return i + 1 ;
}
/ * *
* Extracts all refs from schema
* @ param { SchemaUtilErrorObject } error
* @ return { Array < string > }
* /
function extractRefs ( error ) {
const {
schema
} = error ;
if ( ! Array . isArray ( schema ) ) {
return [ ] ;
}
return schema . map ( ( {
$ref
} ) => $ref ) . filter ( s => s ) ;
}
/ * *
* Groups children by their first level parent ( assuming that error is root )
* @ param { Array < SchemaUtilErrorObject > } children
* @ return { Array < SchemaUtilErrorObject > }
* /
function groupChildrenByFirstChild ( children ) {
const result = [ ] ;
let i = children . length - 1 ;
while ( i > 0 ) {
const child = children [ i ] ;
if ( child . keyword === "anyOf" || child . keyword === "oneOf" ) {
const refs = extractRefs ( child ) ;
const childrenStart = findAllChildren ( children . slice ( 0 , i ) , refs . concat ( child . schemaPath ) ) ;
if ( childrenStart !== i ) {
result . push ( Object . assign ( { } , child , {
children : children . slice ( childrenStart , i )
} ) ) ;
i = childrenStart ;
} else {
result . push ( child ) ;
}
} else {
result . push ( child ) ;
}
i -= 1 ;
}
if ( i === 0 ) {
result . push ( children [ i ] ) ;
}
return result . reverse ( ) ;
}
/ * *
* @ param { string } str
* @ param { string } prefix
* @ returns { string }
* /
function indent ( str , prefix ) {
return str . replace ( /\n(?!$)/g , ` \n ${ prefix } ` ) ;
}
/ * *
* @ param { Schema } schema
* @ returns { schema is ( Schema & { not : Schema } ) }
* /
function hasNotInSchema ( schema ) {
return ! ! schema . not ;
}
/ * *
* @ param { Schema } schema
* @ return { Schema }
* /
function findFirstTypedSchema ( schema ) {
if ( hasNotInSchema ( schema ) ) {
return findFirstTypedSchema ( schema . not ) ;
}
return schema ;
}
/ * *
* @ param { Schema } schema
* @ return { boolean }
* /
function canApplyNot ( schema ) {
const typedSchema = findFirstTypedSchema ( schema ) ;
return likeNumber ( typedSchema ) || likeInteger ( typedSchema ) || likeString ( typedSchema ) || likeNull ( typedSchema ) || likeBoolean ( typedSchema ) ;
}
/ * *
* @ param { any } maybeObj
* @ returns { boolean }
* /
function isObject ( maybeObj ) {
return typeof maybeObj === "object" && maybeObj !== null ;
}
/ * *
* @ param { Schema } schema
* @ returns { boolean }
* /
function likeNumber ( schema ) {
return schema . type === "number" || typeof schema . minimum !== "undefined" || typeof schema . exclusiveMinimum !== "undefined" || typeof schema . maximum !== "undefined" || typeof schema . exclusiveMaximum !== "undefined" || typeof schema . multipleOf !== "undefined" ;
}
/ * *
* @ param { Schema } schema
* @ returns { boolean }
* /
function likeInteger ( schema ) {
return schema . type === "integer" || typeof schema . minimum !== "undefined" || typeof schema . exclusiveMinimum !== "undefined" || typeof schema . maximum !== "undefined" || typeof schema . exclusiveMaximum !== "undefined" || typeof schema . multipleOf !== "undefined" ;
}
/ * *
* @ param { Schema } schema
* @ returns { boolean }
* /
function likeString ( schema ) {
return schema . type === "string" || typeof schema . minLength !== "undefined" || typeof schema . maxLength !== "undefined" || typeof schema . pattern !== "undefined" || typeof schema . format !== "undefined" || typeof schema . formatMinimum !== "undefined" || typeof schema . formatMaximum !== "undefined" ;
}
/ * *
* @ param { Schema } schema
* @ returns { boolean }
* /
function likeBoolean ( schema ) {
return schema . type === "boolean" ;
}
/ * *
* @ param { Schema } schema
* @ returns { boolean }
* /
function likeArray ( schema ) {
return schema . type === "array" || typeof schema . minItems === "number" || typeof schema . maxItems === "number" || typeof schema . uniqueItems !== "undefined" || typeof schema . items !== "undefined" || typeof schema . additionalItems !== "undefined" || typeof schema . contains !== "undefined" ;
}
/ * *
* @ param { Schema & { patternRequired ? : Array < string > } } schema
* @ returns { boolean }
* /
function likeObject ( schema ) {
return schema . type === "object" || typeof schema . minProperties !== "undefined" || typeof schema . maxProperties !== "undefined" || typeof schema . required !== "undefined" || typeof schema . properties !== "undefined" || typeof schema . patternProperties !== "undefined" || typeof schema . additionalProperties !== "undefined" || typeof schema . dependencies !== "undefined" || typeof schema . propertyNames !== "undefined" || typeof schema . patternRequired !== "undefined" ;
}
/ * *
* @ param { Schema } schema
* @ returns { boolean }
* /
function likeNull ( schema ) {
return schema . type === "null" ;
}
/ * *
* @ param { string } type
* @ returns { string }
* /
function getArticle ( type ) {
if ( /^[aeiou]/i . test ( type ) ) {
return "an" ;
}
return "a" ;
}
/ * *
* @ param { Schema = } schema
* @ returns { string }
* /
function getSchemaNonTypes ( schema ) {
if ( ! schema ) {
return "" ;
}
if ( ! schema . type ) {
if ( likeNumber ( schema ) || likeInteger ( schema ) ) {
return " | should be any non-number" ;
}
if ( likeString ( schema ) ) {
return " | should be any non-string" ;
}
if ( likeArray ( schema ) ) {
return " | should be any non-array" ;
}
if ( likeObject ( schema ) ) {
return " | should be any non-object" ;
}
}
return "" ;
}
/ * *
* @ param { Array < string > } hints
* @ returns { string }
* /
function formatHints ( hints ) {
return hints . length > 0 ? ` ( ${ hints . join ( ", " ) } ) ` : "" ;
}
/ * *
* @ param { Schema } schema
* @ param { boolean } logic
* @ returns { string [ ] }
* /
function getHints ( schema , logic ) {
if ( likeNumber ( schema ) || likeInteger ( schema ) ) {
return numberHints ( schema , logic ) ;
} else if ( likeString ( schema ) ) {
return stringHints ( schema , logic ) ;
}
return [ ] ;
}
class ValidationError extends Error {
/ * *
* @ param { Array < SchemaUtilErrorObject > } errors
* @ param { Schema } schema
* @ param { ValidationErrorConfiguration } configuration
* /
constructor ( errors , schema , configuration = { } ) {
super ( ) ;
/** @type {string} */
this . name = "ValidationError" ;
/** @type {Array<SchemaUtilErrorObject>} */
this . errors = errors ;
/** @type {Schema} */
this . schema = schema ;
let headerNameFromSchema ;
let baseDataPathFromSchema ;
if ( schema . title && ( ! configuration . name || ! configuration . baseDataPath ) ) {
const splittedTitleFromSchema = schema . title . match ( /^(.+) (.+)$/ ) ;
if ( splittedTitleFromSchema ) {
if ( ! configuration . name ) {
[ , headerNameFromSchema ] = splittedTitleFromSchema ;
}
if ( ! configuration . baseDataPath ) {
[ , , baseDataPathFromSchema ] = splittedTitleFromSchema ;
}
}
}
/** @type {string} */
this . headerName = configuration . name || headerNameFromSchema || "Object" ;
/** @type {string} */
this . baseDataPath = configuration . baseDataPath || baseDataPathFromSchema || "configuration" ;
/** @type {PostFormatter | null} */
this . postFormatter = configuration . postFormatter || null ;
const header = ` Invalid ${ this . baseDataPath } object. ${ this . headerName } has been initialized using ${ getArticle ( this . baseDataPath ) } ${ this . baseDataPath } object that does not match the API schema. \n ` ;
/** @type {string} */
this . message = ` ${ header } ${ this . formatValidationErrors ( errors ) } ` ;
Error . captureStackTrace ( this , this . constructor ) ;
}
/ * *
* @ param { string } path
* @ returns { Schema }
* /
getSchemaPart ( path ) {
const newPath = path . split ( "/" ) ;
let schemaPart = this . schema ;
for ( let i = 1 ; i < newPath . length ; i ++ ) {
const inner = schemaPart [
/** @type {keyof Schema} */
newPath [ i ] ] ;
if ( ! inner ) {
break ;
}
schemaPart = inner ;
}
return schemaPart ;
}
/ * *
* @ param { Schema } schema
* @ param { boolean } logic
* @ param { Array < Object > } prevSchemas
* @ returns { string }
* /
formatSchema ( schema , logic = true , prevSchemas = [ ] ) {
let newLogic = logic ;
const formatInnerSchema =
/ * *
*
* @ param { Object } innerSchema
* @ param { boolean = } addSelf
* @ returns { string }
* /
( innerSchema , addSelf ) => {
if ( ! addSelf ) {
return this . formatSchema ( innerSchema , newLogic , prevSchemas ) ;
}
if ( prevSchemas . includes ( innerSchema ) ) {
return "(recursive)" ;
}
return this . formatSchema ( innerSchema , newLogic , prevSchemas . concat ( schema ) ) ;
} ;
if ( hasNotInSchema ( schema ) && ! likeObject ( schema ) ) {
if ( canApplyNot ( schema . not ) ) {
newLogic = ! logic ;
return formatInnerSchema ( schema . not ) ;
}
const needApplyLogicHere = ! schema . not . not ;
const prefix = logic ? "" : "non " ;
newLogic = ! logic ;
return needApplyLogicHere ? prefix + formatInnerSchema ( schema . not ) : formatInnerSchema ( schema . not ) ;
}
if (
/** @type {Schema & {instanceof: string | Array<string>}} */
schema . instanceof ) {
const {
instanceof : value
} =
/** @type {Schema & {instanceof: string | Array<string>}} */
schema ;
const values = ! Array . isArray ( value ) ? [ value ] : value ;
return values . map (
/ * *
* @ param { string } item
* @ returns { string }
* /
item => item === "Function" ? "function" : item ) . join ( " | " ) ;
}
if ( schema . enum ) {
const enumValues =
/** @type {Array<any>} */
schema . enum . map ( item => {
if ( item === null && schema . undefinedAsNull ) {
return ` ${ JSON . stringify ( item ) } | undefined ` ;
}
return JSON . stringify ( item ) ;
} ) . join ( " | " ) ;
return ` ${ enumValues } ` ;
}
if ( typeof schema . const !== "undefined" ) {
return JSON . stringify ( schema . const ) ;
}
if ( schema . oneOf ) {
return (
/** @type {Array<Schema>} */
schema . oneOf . map ( item => formatInnerSchema ( item , true ) ) . join ( " | " )
) ;
}
if ( schema . anyOf ) {
return (
/** @type {Array<Schema>} */
schema . anyOf . map ( item => formatInnerSchema ( item , true ) ) . join ( " | " )
) ;
}
if ( schema . allOf ) {
return (
/** @type {Array<Schema>} */
schema . allOf . map ( item => formatInnerSchema ( item , true ) ) . join ( " & " )
) ;
}
if (
/** @type {JSONSchema7} */
schema . if ) {
const {
if : ifValue ,
then : thenValue ,
else : elseValue
} =
/** @type {JSONSchema7} */
schema ;
return ` ${ ifValue ? ` if ${ formatInnerSchema ( ifValue ) } ` : "" } ${ thenValue ? ` then ${ formatInnerSchema ( thenValue ) } ` : "" } ${ elseValue ? ` else ${ formatInnerSchema ( elseValue ) } ` : "" } ` ;
}
if ( schema . $ref ) {
return formatInnerSchema ( this . getSchemaPart ( schema . $ref ) , true ) ;
}
if ( likeNumber ( schema ) || likeInteger ( schema ) ) {
const [ type , ... hints ] = getHints ( schema , logic ) ;
const str = ` ${ type } ${ hints . length > 0 ? ` ${ formatHints ( hints ) } ` : "" } ` ;
return logic ? str : hints . length > 0 ? ` non- ${ type } | ${ str } ` : ` non- ${ type } ` ;
}
if ( likeString ( schema ) ) {
const [ type , ... hints ] = getHints ( schema , logic ) ;
const str = ` ${ type } ${ hints . length > 0 ? ` ${ formatHints ( hints ) } ` : "" } ` ;
return logic ? str : str === "string" ? "non-string" : ` non-string | ${ str } ` ;
}
if ( likeBoolean ( schema ) ) {
return ` ${ logic ? "" : "non-" } boolean ` ;
}
if ( likeArray ( schema ) ) {
// not logic already applied in formatValidationError
newLogic = true ;
const hints = [ ] ;
if ( typeof schema . minItems === "number" ) {
hints . push ( ` should not have fewer than ${ schema . minItems } item ${ schema . minItems > 1 ? "s" : "" } ` ) ;
}
if ( typeof schema . maxItems === "number" ) {
hints . push ( ` should not have more than ${ schema . maxItems } item ${ schema . maxItems > 1 ? "s" : "" } ` ) ;
}
if ( schema . uniqueItems ) {
hints . push ( "should not have duplicate items" ) ;
}
const hasAdditionalItems = typeof schema . additionalItems === "undefined" || Boolean ( schema . additionalItems ) ;
let items = "" ;
if ( schema . items ) {
if ( Array . isArray ( schema . items ) && schema . items . length > 0 ) {
items = ` ${
/** @type {Array<Schema>} */
schema . items . map ( item => formatInnerSchema ( item ) ) . join ( ", " ) } ` ;
if ( hasAdditionalItems ) {
if ( schema . additionalItems && isObject ( schema . additionalItems ) && Object . keys ( schema . additionalItems ) . length > 0 ) {
hints . push ( ` additional items should be ${ formatInnerSchema ( schema . additionalItems ) } ` ) ;
}
}
} else if ( schema . items && Object . keys ( schema . items ) . length > 0 ) {
// "additionalItems" is ignored
items = ` ${ formatInnerSchema ( schema . items ) } ` ;
} else {
// Fallback for empty `items` value
items = "any" ;
}
} else {
// "additionalItems" is ignored
items = "any" ;
}
if ( schema . contains && Object . keys ( schema . contains ) . length > 0 ) {
hints . push ( ` should contains at least one ${ this . formatSchema ( schema . contains ) } item ` ) ;
}
return ` [ ${ items } ${ hasAdditionalItems ? ", ..." : "" } ] ${ hints . length > 0 ? ` ( ${ hints . join ( ", " ) } ) ` : "" } ` ;
}
if ( likeObject ( schema ) ) {
// not logic already applied in formatValidationError
newLogic = true ;
const hints = [ ] ;
if ( typeof schema . minProperties === "number" ) {
hints . push ( ` should not have fewer than ${ schema . minProperties } ${ schema . minProperties > 1 ? "properties" : "property" } ` ) ;
}
if ( typeof schema . maxProperties === "number" ) {
hints . push ( ` should not have more than ${ schema . maxProperties } ${ schema . minProperties && schema . minProperties > 1 ? "properties" : "property" } ` ) ;
}
if ( schema . patternProperties && Object . keys ( schema . patternProperties ) . length > 0 ) {
const patternProperties = Object . keys ( schema . patternProperties ) ;
hints . push ( ` additional property names should match pattern ${ patternProperties . length > 1 ? "s" : "" } ${ patternProperties . map ( pattern => JSON . stringify ( pattern ) ) . join ( " | " ) } ` ) ;
}
const properties = schema . properties ? Object . keys ( schema . properties ) : [ ] ;
const required = schema . required ? schema . required : [ ] ;
const allProperties = [ ... new Set (
/** @type {Array<string>} */
[ ] . concat ( required ) . concat ( properties ) ) ] ;
const objectStructure = allProperties . map ( property => {
const isRequired = required . includes ( property ) ; // Some properties need quotes, maybe we should add check
// Maybe we should output type of property (`foo: string`), but it is looks very unreadable
return ` ${ property } ${ isRequired ? "" : "?" } ` ;
} ) . concat ( typeof schema . additionalProperties === "undefined" || Boolean ( schema . additionalProperties ) ? schema . additionalProperties && isObject ( schema . additionalProperties ) ? [ ` <key>: ${ formatInnerSchema ( schema . additionalProperties ) } ` ] : [ "…" ] : [ ] ) . join ( ", " ) ;
const {
dependencies ,
propertyNames ,
patternRequired
} =
/** @type {Schema & {patternRequired?: Array<string>;}} */
schema ;
if ( dependencies ) {
Object . keys ( dependencies ) . forEach ( dependencyName => {
const dependency = dependencies [ dependencyName ] ;
if ( Array . isArray ( dependency ) ) {
hints . push ( ` should have ${ dependency . length > 1 ? "properties" : "property" } ${ dependency . map ( dep => ` ' ${ dep } ' ` ) . join ( ", " ) } when property ' ${ dependencyName } ' is present ` ) ;
} else {
hints . push ( ` should be valid according to the schema ${ formatInnerSchema ( dependency ) } when property ' ${ dependencyName } ' is present ` ) ;
}
} ) ;
}
if ( propertyNames && Object . keys ( propertyNames ) . length > 0 ) {
hints . push ( ` each property name should match format ${ JSON . stringify ( schema . propertyNames . format ) } ` ) ;
}
if ( patternRequired && patternRequired . length > 0 ) {
hints . push ( ` should have property matching pattern ${ patternRequired . map (
/ * *
* @ param { string } item
* @ returns { string }
* /
item => JSON . stringify ( item ) ) } ` );
}
return ` object { ${ objectStructure ? ` ${ objectStructure } ` : "" } } ${ hints . length > 0 ? ` ( ${ hints . join ( ", " ) } ) ` : "" } ` ;
}
if ( likeNull ( schema ) ) {
return ` ${ logic ? "" : "non-" } null ` ;
}
if ( Array . isArray ( schema . type ) ) {
// not logic already applied in formatValidationError
return ` ${ schema . type . join ( " | " ) } ` ;
} // Fallback for unknown keywords
// not logic already applied in formatValidationError
/* istanbul ignore next */
return JSON . stringify ( schema , null , 2 ) ;
}
/ * *
* @ param { Schema = } schemaPart
* @ param { ( boolean | Array < string > ) = } additionalPath
* @ param { boolean = } needDot
* @ param { boolean = } logic
* @ returns { string }
* /
getSchemaPartText ( schemaPart , additionalPath , needDot = false , logic = true ) {
if ( ! schemaPart ) {
return "" ;
}
if ( Array . isArray ( additionalPath ) ) {
for ( let i = 0 ; i < additionalPath . length ; i ++ ) {
/** @type {Schema | undefined} */
const inner = schemaPart [
/** @type {keyof Schema} */
additionalPath [ i ] ] ;
if ( inner ) {
// eslint-disable-next-line no-param-reassign
schemaPart = inner ;
} else {
break ;
}
}
}
while ( schemaPart . $ref ) {
// eslint-disable-next-line no-param-reassign
schemaPart = this . getSchemaPart ( schemaPart . $ref ) ;
}
let schemaText = ` ${ this . formatSchema ( schemaPart , logic ) } ${ needDot ? "." : "" } ` ;
if ( schemaPart . description ) {
schemaText += ` \n -> ${ schemaPart . description } ` ;
}
if ( schemaPart . link ) {
schemaText += ` \n -> Read more at ${ schemaPart . link } ` ;
}
return schemaText ;
}
/ * *
* @ param { Schema = } schemaPart
* @ returns { string }
* /
getSchemaPartDescription ( schemaPart ) {
if ( ! schemaPart ) {
return "" ;
}
while ( schemaPart . $ref ) {
// eslint-disable-next-line no-param-reassign
schemaPart = this . getSchemaPart ( schemaPart . $ref ) ;
}
let schemaText = "" ;
if ( schemaPart . description ) {
schemaText += ` \n -> ${ schemaPart . description } ` ;
}
if ( schemaPart . link ) {
schemaText += ` \n -> Read more at ${ schemaPart . link } ` ;
}
return schemaText ;
}
/ * *
* @ param { SchemaUtilErrorObject } error
* @ returns { string }
* /
formatValidationError ( error ) {
const {
keyword ,
dataPath : errorDataPath
} = error ;
const dataPath = ` ${ this . baseDataPath } ${ errorDataPath } ` ;
switch ( keyword ) {
case "type" :
{
const {
parentSchema ,
params
} = error ; // eslint-disable-next-line default-case
switch (
/** @type {import("ajv").TypeParams} */
params . type ) {
case "number" :
return ` ${ dataPath } should be a ${ this . getSchemaPartText ( parentSchema , false , true ) } ` ;
case "integer" :
return ` ${ dataPath } should be an ${ this . getSchemaPartText ( parentSchema , false , true ) } ` ;
case "string" :
return ` ${ dataPath } should be a ${ this . getSchemaPartText ( parentSchema , false , true ) } ` ;
case "boolean" :
return ` ${ dataPath } should be a ${ this . getSchemaPartText ( parentSchema , false , true ) } ` ;
case "array" :
return ` ${ dataPath } should be an array: \n ${ this . getSchemaPartText ( parentSchema ) } ` ;
case "object" :
return ` ${ dataPath } should be an object: \n ${ this . getSchemaPartText ( parentSchema ) } ` ;
case "null" :
return ` ${ dataPath } should be a ${ this . getSchemaPartText ( parentSchema , false , true ) } ` ;
default :
return ` ${ dataPath } should be: \n ${ this . getSchemaPartText ( parentSchema ) } ` ;
}
}
case "instanceof" :
{
const {
parentSchema
} = error ;
return ` ${ dataPath } should be an instance of ${ this . getSchemaPartText ( parentSchema , false , true ) } ` ;
}
case "pattern" :
{
const {
params ,
parentSchema
} = error ;
const {
pattern
} =
/** @type {import("ajv").PatternParams} */
params ;
return ` ${ dataPath } should match pattern ${ JSON . stringify ( pattern ) } ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "format" :
{
const {
params ,
parentSchema
} = error ;
const {
format
} =
/** @type {import("ajv").FormatParams} */
params ;
return ` ${ dataPath } should match format ${ JSON . stringify ( format ) } ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "formatMinimum" :
case "formatMaximum" :
{
const {
params ,
parentSchema
} = error ;
const {
comparison ,
limit
} =
/** @type {import("ajv").ComparisonParams} */
params ;
return ` ${ dataPath } should be ${ comparison } ${ JSON . stringify ( limit ) } ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "minimum" :
case "maximum" :
case "exclusiveMinimum" :
case "exclusiveMaximum" :
{
const {
parentSchema ,
params
} = error ;
const {
comparison ,
limit
} =
/** @type {import("ajv").ComparisonParams} */
params ;
const [ , ... hints ] = getHints (
/** @type {Schema} */
parentSchema , true ) ;
if ( hints . length === 0 ) {
hints . push ( ` should be ${ comparison } ${ limit } ` ) ;
}
return ` ${ dataPath } ${ hints . join ( " " ) } ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "multipleOf" :
{
const {
params ,
parentSchema
} = error ;
const {
multipleOf
} =
/** @type {import("ajv").MultipleOfParams} */
params ;
return ` ${ dataPath } should be multiple of ${ multipleOf } ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "patternRequired" :
{
const {
params ,
parentSchema
} = error ;
const {
missingPattern
} =
/** @type {import("ajv").PatternRequiredParams} */
params ;
return ` ${ dataPath } should have property matching pattern ${ JSON . stringify ( missingPattern ) } ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "minLength" :
{
const {
params ,
parentSchema
} = error ;
const {
limit
} =
/** @type {import("ajv").LimitParams} */
params ;
if ( limit === 1 ) {
return ` ${ dataPath } should be a non-empty string ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
const length = limit - 1 ;
return ` ${ dataPath } should be longer than ${ length } character ${ length > 1 ? "s" : "" } ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "minItems" :
{
const {
params ,
parentSchema
} = error ;
const {
limit
} =
/** @type {import("ajv").LimitParams} */
params ;
if ( limit === 1 ) {
return ` ${ dataPath } should be a non-empty array ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
return ` ${ dataPath } should not have fewer than ${ limit } items ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "minProperties" :
{
const {
params ,
parentSchema
} = error ;
const {
limit
} =
/** @type {import("ajv").LimitParams} */
params ;
if ( limit === 1 ) {
return ` ${ dataPath } should be a non-empty object ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
return ` ${ dataPath } should not have fewer than ${ limit } properties ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "maxLength" :
{
const {
params ,
parentSchema
} = error ;
const {
limit
} =
/** @type {import("ajv").LimitParams} */
params ;
const max = limit + 1 ;
return ` ${ dataPath } should be shorter than ${ max } character ${ max > 1 ? "s" : "" } ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "maxItems" :
{
const {
params ,
parentSchema
} = error ;
const {
limit
} =
/** @type {import("ajv").LimitParams} */
params ;
return ` ${ dataPath } should not have more than ${ limit } items ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "maxProperties" :
{
const {
params ,
parentSchema
} = error ;
const {
limit
} =
/** @type {import("ajv").LimitParams} */
params ;
return ` ${ dataPath } should not have more than ${ limit } properties ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "uniqueItems" :
{
const {
params ,
parentSchema
} = error ;
const {
i
} =
/** @type {import("ajv").UniqueItemsParams} */
params ;
return ` ${ dataPath } should not contain the item ' ${ error . data [ i ] } ' twice ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "additionalItems" :
{
const {
params ,
parentSchema
} = error ;
const {
limit
} =
/** @type {import("ajv").LimitParams} */
params ;
return ` ${ dataPath } should not have more than ${ limit } items ${ getSchemaNonTypes ( parentSchema ) } . These items are valid: \n ${ this . getSchemaPartText ( parentSchema ) } ` ;
}
case "contains" :
{
const {
parentSchema
} = error ;
return ` ${ dataPath } should contains at least one ${ this . getSchemaPartText ( parentSchema , [ "contains" ] ) } item ${ getSchemaNonTypes ( parentSchema ) } . ` ;
}
case "required" :
{
const {
parentSchema ,
params
} = error ;
const missingProperty =
/** @type {import("ajv").DependenciesParams} */
params . missingProperty . replace ( /^\./ , "" ) ;
const hasProperty = parentSchema && Boolean (
/** @type {Schema} */
parentSchema . properties &&
/** @type {Schema} */
parentSchema . properties [ missingProperty ] ) ;
return ` ${ dataPath } misses the property ' ${ missingProperty } ' ${ getSchemaNonTypes ( parentSchema ) } . ${ hasProperty ? ` Should be: \n ${ this . getSchemaPartText ( parentSchema , [ "properties" , missingProperty ] ) } ` : this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "additionalProperties" :
{
const {
params ,
parentSchema
} = error ;
const {
additionalProperty
} =
/** @type {import("ajv").AdditionalPropertiesParams} */
params ;
return ` ${ dataPath } has an unknown property ' ${ additionalProperty } ' ${ getSchemaNonTypes ( parentSchema ) } . These properties are valid: \n ${ this . getSchemaPartText ( parentSchema ) } ` ;
}
case "dependencies" :
{
const {
params ,
parentSchema
} = error ;
const {
property ,
deps
} =
/** @type {import("ajv").DependenciesParams} */
params ;
const dependencies = deps . split ( "," ) . map (
/ * *
* @ param { string } dep
* @ returns { string }
* /
dep => ` ' ${ dep . trim ( ) } ' ` ) . join ( ", " ) ;
return ` ${ dataPath } should have properties ${ dependencies } when property ' ${ property } ' is present ${ getSchemaNonTypes ( parentSchema ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "propertyNames" :
{
const {
params ,
parentSchema ,
schema
} = error ;
const {
propertyName
} =
/** @type {import("ajv").PropertyNamesParams} */
params ;
return ` ${ dataPath } property name ' ${ propertyName } ' is invalid ${ getSchemaNonTypes ( parentSchema ) } . Property names should be match format ${ JSON . stringify ( schema . format ) } . ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
case "enum" :
{
const {
parentSchema
} = error ;
if ( parentSchema &&
/** @type {Schema} */
parentSchema . enum &&
/** @type {Schema} */
parentSchema . enum . length === 1 ) {
return ` ${ dataPath } should be ${ this . getSchemaPartText ( parentSchema , false , true ) } ` ;
}
return ` ${ dataPath } should be one of these: \n ${ this . getSchemaPartText ( parentSchema ) } ` ;
}
case "const" :
{
const {
parentSchema
} = error ;
return ` ${ dataPath } should be equal to constant ${ this . getSchemaPartText ( parentSchema , false , true ) } ` ;
}
case "not" :
{
const postfix = likeObject (
/** @type {Schema} */
error . parentSchema ) ? ` \n ${ this . getSchemaPartText ( error . parentSchema ) } ` : "" ;
const schemaOutput = this . getSchemaPartText ( error . schema , false , false , false ) ;
if ( canApplyNot ( error . schema ) ) {
return ` ${ dataPath } should be any ${ schemaOutput } ${ postfix } . ` ;
}
const {
schema ,
parentSchema
} = error ;
return ` ${ dataPath } should not be ${ this . getSchemaPartText ( schema , false , true ) } ${ parentSchema && likeObject ( parentSchema ) ? ` \n ${ this . getSchemaPartText ( parentSchema ) } ` : "" } ` ;
}
case "oneOf" :
case "anyOf" :
{
const {
parentSchema ,
children
} = error ;
if ( children && children . length > 0 ) {
if ( error . schema . length === 1 ) {
const lastChild = children [ children . length - 1 ] ;
const remainingChildren = children . slice ( 0 , children . length - 1 ) ;
return this . formatValidationError ( Object . assign ( { } , lastChild , {
children : remainingChildren ,
parentSchema : Object . assign ( { } , parentSchema , lastChild . parentSchema )
} ) ) ;
}
let filteredChildren = filterChildren ( children ) ;
if ( filteredChildren . length === 1 ) {
return this . formatValidationError ( filteredChildren [ 0 ] ) ;
}
filteredChildren = groupChildrenByFirstChild ( filteredChildren ) ;
return ` ${ dataPath } should be one of these: \n ${ this . getSchemaPartText ( parentSchema ) } \n Details: \n ${ filteredChildren . map (
/ * *
* @ param { SchemaUtilErrorObject } nestedError
* @ returns { string }
* /
nestedError => ` * ${ indent ( this . formatValidationError ( nestedError ) , " " ) } ` ) . join ( "\n" ) } ` ;
}
return ` ${ dataPath } should be one of these: \n ${ this . getSchemaPartText ( parentSchema ) } ` ;
}
case "if" :
{
const {
params ,
parentSchema
} = error ;
const {
failingKeyword
} =
/** @type {import("ajv").IfParams} */
params ;
return ` ${ dataPath } should match " ${ failingKeyword } " schema: \n ${ this . getSchemaPartText ( parentSchema , [ failingKeyword ] ) } ` ;
}
case "absolutePath" :
{
const {
message ,
parentSchema
} = error ;
return ` ${ dataPath } : ${ message } ${ this . getSchemaPartDescription ( parentSchema ) } ` ;
}
/* istanbul ignore next */
default :
{
const {
message ,
parentSchema
} = error ;
const ErrorInJSON = JSON . stringify ( error , null , 2 ) ; // For `custom`, `false schema`, `$ref` keywords
// Fallback for unknown keywords
return ` ${ dataPath } ${ message } ( ${ ErrorInJSON } ). \n ${ this . getSchemaPartText ( parentSchema , false ) } ` ;
}
}
}
/ * *
* @ param { Array < SchemaUtilErrorObject > } errors
* @ returns { string }
* /
formatValidationErrors ( errors ) {
return errors . map ( error => {
let formattedError = this . formatValidationError ( error ) ;
if ( this . postFormatter ) {
formattedError = this . postFormatter ( formattedError , error ) ;
}
return ` - ${ indent ( formattedError , " " ) } ` ;
} ) . join ( "\n" ) ;
}
}
var _default = ValidationError ;
2023-09-25 15:58:56 +08:00
exports . default = _default ;