kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
pull/715/head
rodzic
2c26a75a14
commit
ffd783f9e4
|
@ -19,14 +19,16 @@ export function cfValidateJsonSchemaObject<
|
|||
schema,
|
||||
data,
|
||||
errorMessage,
|
||||
coerce = true
|
||||
coerce = true,
|
||||
strictAdditionalProperties = true
|
||||
}: {
|
||||
schema: any
|
||||
data: Record<string, unknown>
|
||||
errorMessage?: string
|
||||
coerce?: boolean
|
||||
strictAdditionalProperties?: boolean
|
||||
}): T {
|
||||
// Special-case check for required fields to give better error messages
|
||||
// Special-case check for required fields to give better error messages.
|
||||
if (schema.required && Array.isArray(schema.required)) {
|
||||
const missingRequiredFields: string[] = schema.required.filter(
|
||||
(field: string) => (data as T)[field] === undefined
|
||||
|
@ -40,11 +42,13 @@ export function cfValidateJsonSchemaObject<
|
|||
}
|
||||
}
|
||||
|
||||
// Special-case check for additional top-level properties, which is not
|
||||
// currently handled by `@agentic/json-schema`.
|
||||
// TODO: In the future, the underlying JSON schema validation should handle
|
||||
// this for any sub-schema, not just the top-level one.
|
||||
if (schema.properties && !schema.additionalProperties) {
|
||||
// Special-case check for additional top-level fields to give better error
|
||||
// messages.
|
||||
if (
|
||||
schema.properties &&
|
||||
(schema.additionalProperties === false ||
|
||||
(schema.additionalProperties === undefined && strictAdditionalProperties))
|
||||
) {
|
||||
const extraProperties = Object.keys(data).filter(
|
||||
(key) => !schema.properties[key]
|
||||
)
|
||||
|
@ -57,7 +61,11 @@ export function cfValidateJsonSchemaObject<
|
|||
}
|
||||
}
|
||||
|
||||
const validator = new Validator({ schema, coerce })
|
||||
const validator = new Validator({
|
||||
schema,
|
||||
coerce,
|
||||
strictAdditionalProperties
|
||||
})
|
||||
const result = validator.validate(data)
|
||||
if (result.valid) {
|
||||
// console.log('validate', {
|
||||
|
|
|
@ -84,6 +84,10 @@ export function coerceValue({
|
|||
}
|
||||
break
|
||||
}
|
||||
|
||||
if ($type === 'integer' && typeof instance === 'number') {
|
||||
instance = Math.floor(instance)
|
||||
}
|
||||
break
|
||||
|
||||
case 'string':
|
||||
|
|
|
@ -12,15 +12,30 @@ export type Evaluated = Record<string | number, boolean>
|
|||
export function validate(
|
||||
instance: any,
|
||||
schema: Schema | boolean,
|
||||
draft: SchemaDraft = '2019-09',
|
||||
lookup: Record<string, Schema | boolean> = dereference(schema),
|
||||
coerce = false,
|
||||
shortCircuit = true,
|
||||
recursiveAnchor: Schema | null = null,
|
||||
instanceLocation = '#',
|
||||
schemaLocation = '#',
|
||||
evaluated: Evaluated = Object.create(null)
|
||||
opts: {
|
||||
draft?: SchemaDraft
|
||||
lookup?: Record<string, Schema | boolean>
|
||||
coerce?: boolean
|
||||
shortCircuit?: boolean
|
||||
recursiveAnchor?: Schema | null
|
||||
instanceLocation?: string
|
||||
schemaLocation?: string
|
||||
evaluated?: Evaluated
|
||||
strictAdditionalProperties?: boolean
|
||||
} = {}
|
||||
): ValidationResult {
|
||||
const {
|
||||
draft = '2019-09',
|
||||
lookup = dereference(schema),
|
||||
coerce = false,
|
||||
shortCircuit = true,
|
||||
instanceLocation = '#',
|
||||
schemaLocation = '#',
|
||||
evaluated = Object.create(null),
|
||||
strictAdditionalProperties = false
|
||||
} = opts
|
||||
let { recursiveAnchor = null } = opts
|
||||
|
||||
if (schema === true) {
|
||||
return { valid: true, errors: [], instance }
|
||||
}
|
||||
|
@ -114,14 +129,14 @@ export function validate(
|
|||
const result = validate(
|
||||
instance,
|
||||
recursiveAnchor === null ? schema : recursiveAnchor,
|
||||
draft,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
refSchema,
|
||||
instanceLocation,
|
||||
keywordLocation,
|
||||
evaluated
|
||||
{
|
||||
...opts,
|
||||
lookup,
|
||||
recursiveAnchor: refSchema,
|
||||
instanceLocation,
|
||||
schemaLocation: keywordLocation,
|
||||
evaluated
|
||||
}
|
||||
)
|
||||
if (result.valid) {
|
||||
instance = result.instance
|
||||
|
@ -150,18 +165,14 @@ export function validate(
|
|||
throw new Error(message)
|
||||
}
|
||||
const keywordLocation = `${schemaLocation}/$ref`
|
||||
const result = validate(
|
||||
instance,
|
||||
refSchema,
|
||||
draft,
|
||||
const result = validate(instance, refSchema, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
instanceLocation,
|
||||
keywordLocation,
|
||||
schemaLocation: keywordLocation,
|
||||
evaluated
|
||||
)
|
||||
})
|
||||
if (result.valid) {
|
||||
instance = result.instance
|
||||
} else {
|
||||
|
@ -292,18 +303,14 @@ export function validate(
|
|||
// TODO: type coercion
|
||||
if ($not !== undefined) {
|
||||
const keywordLocation = `${schemaLocation}/not`
|
||||
const result = validate(
|
||||
instance,
|
||||
$not,
|
||||
draft,
|
||||
const result = validate(instance, $not, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
instanceLocation,
|
||||
keywordLocation /*,
|
||||
evaluated*/
|
||||
)
|
||||
schemaLocation: keywordLocation
|
||||
// evaluated
|
||||
})
|
||||
if (result.valid) {
|
||||
errors.push({
|
||||
instanceLocation,
|
||||
|
@ -323,18 +330,14 @@ export function validate(
|
|||
let anyValid = false
|
||||
for (const [i, subSchema] of $anyOf.entries()) {
|
||||
const subEvaluated: Evaluated = Object.create(evaluated)
|
||||
const result = validate(
|
||||
instance,
|
||||
subSchema,
|
||||
draft,
|
||||
const result = validate(instance, subSchema, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
$recursiveAnchor === true ? recursiveAnchor : null,
|
||||
recursiveAnchor: $recursiveAnchor === true ? recursiveAnchor : null,
|
||||
instanceLocation,
|
||||
`${keywordLocation}/${i}`,
|
||||
subEvaluated
|
||||
)
|
||||
schemaLocation: `${keywordLocation}/${i}`,
|
||||
evaluated: subEvaluated
|
||||
})
|
||||
errors.push(...result.errors)
|
||||
anyValid = anyValid || result.valid
|
||||
if (result.valid) {
|
||||
|
@ -360,18 +363,14 @@ export function validate(
|
|||
let allValid = true
|
||||
for (const [i, subSchema] of $allOf.entries()) {
|
||||
const subEvaluated: Evaluated = Object.create(evaluated)
|
||||
const result = validate(
|
||||
instance,
|
||||
subSchema,
|
||||
draft,
|
||||
const result = validate(instance, subSchema, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
$recursiveAnchor === true ? recursiveAnchor : null,
|
||||
recursiveAnchor: $recursiveAnchor === true ? recursiveAnchor : null,
|
||||
instanceLocation,
|
||||
`${keywordLocation}/${i}`,
|
||||
subEvaluated
|
||||
)
|
||||
schemaLocation: `${keywordLocation}/${i}`,
|
||||
evaluated: subEvaluated
|
||||
})
|
||||
errors.push(...result.errors)
|
||||
allValid = allValid && result.valid
|
||||
if (result.valid) {
|
||||
|
@ -396,18 +395,14 @@ export function validate(
|
|||
const errorsLength = errors.length
|
||||
const matches = $oneOf.filter((subSchema, i) => {
|
||||
const subEvaluated: Evaluated = Object.create(evaluated)
|
||||
const result = validate(
|
||||
instance,
|
||||
subSchema,
|
||||
draft,
|
||||
const result = validate(instance, subSchema, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
$recursiveAnchor === true ? recursiveAnchor : null,
|
||||
recursiveAnchor: $recursiveAnchor === true ? recursiveAnchor : null,
|
||||
instanceLocation,
|
||||
`${keywordLocation}/${i}`,
|
||||
subEvaluated
|
||||
)
|
||||
schemaLocation: `${keywordLocation}/${i}`,
|
||||
evaluated: subEvaluated
|
||||
})
|
||||
errors.push(...result.errors)
|
||||
if (result.valid) {
|
||||
subEvaluateds.push(subEvaluated)
|
||||
|
@ -432,32 +427,24 @@ export function validate(
|
|||
|
||||
if ($if !== undefined) {
|
||||
const keywordLocation = `${schemaLocation}/if`
|
||||
const conditionResult = validate(
|
||||
instance,
|
||||
$if,
|
||||
draft,
|
||||
const conditionResult = validate(instance, $if, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
instanceLocation,
|
||||
keywordLocation,
|
||||
schemaLocation: keywordLocation,
|
||||
evaluated
|
||||
).valid
|
||||
}).valid
|
||||
if (conditionResult) {
|
||||
if ($then !== undefined) {
|
||||
const thenResult = validate(
|
||||
instance,
|
||||
$then,
|
||||
draft,
|
||||
const thenResult = validate(instance, $then, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
instanceLocation,
|
||||
`${schemaLocation}/then`,
|
||||
schemaLocation: `${schemaLocation}/then`,
|
||||
evaluated
|
||||
)
|
||||
})
|
||||
if (thenResult.valid) {
|
||||
instance = thenResult.instance
|
||||
} else {
|
||||
|
@ -473,18 +460,14 @@ export function validate(
|
|||
}
|
||||
}
|
||||
} else if ($else !== undefined) {
|
||||
const elseResult = validate(
|
||||
instance,
|
||||
$else,
|
||||
draft,
|
||||
const elseResult = validate(instance, $else, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
instanceLocation,
|
||||
`${schemaLocation}/else`,
|
||||
schemaLocation: `${schemaLocation}/else`,
|
||||
evaluated
|
||||
)
|
||||
})
|
||||
if (elseResult.valid) {
|
||||
instance = elseResult.instance
|
||||
} else {
|
||||
|
@ -539,17 +522,13 @@ export function validate(
|
|||
const keywordLocation = `${schemaLocation}/propertyNames`
|
||||
for (const key in instance) {
|
||||
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`
|
||||
const result = validate(
|
||||
key,
|
||||
$propertyNames,
|
||||
draft,
|
||||
const result = validate(key, $propertyNames, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
subInstancePointer,
|
||||
keywordLocation
|
||||
)
|
||||
instanceLocation: subInstancePointer,
|
||||
schemaLocation: keywordLocation
|
||||
})
|
||||
if (!result.valid) {
|
||||
errors.push(
|
||||
{
|
||||
|
@ -587,18 +566,14 @@ export function validate(
|
|||
for (const key in $dependentSchemas) {
|
||||
const keywordLocation = `${schemaLocation}/dependentSchemas`
|
||||
if (key in instance) {
|
||||
const result = validate(
|
||||
instance,
|
||||
$dependentSchemas[key]!,
|
||||
draft,
|
||||
const result = validate(instance, $dependentSchemas[key]!, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
instanceLocation,
|
||||
`${keywordLocation}/${encodePointer(key)}`,
|
||||
schemaLocation: `${keywordLocation}/${encodePointer(key)}`,
|
||||
evaluated
|
||||
)
|
||||
})
|
||||
if (!result.valid) {
|
||||
errors.push(
|
||||
{
|
||||
|
@ -631,17 +606,13 @@ export function validate(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
const result = validate(
|
||||
instance,
|
||||
propsOrSchema,
|
||||
draft,
|
||||
const result = validate(instance, propsOrSchema, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
instanceLocation,
|
||||
`${keywordLocation}/${encodePointer(key)}`
|
||||
)
|
||||
schemaLocation: `${keywordLocation}/${encodePointer(key)}`
|
||||
})
|
||||
if (!result.valid) {
|
||||
errors.push(
|
||||
{
|
||||
|
@ -669,17 +640,13 @@ export function validate(
|
|||
continue
|
||||
}
|
||||
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`
|
||||
const result = validate(
|
||||
instance[key],
|
||||
$properties[key]!,
|
||||
draft,
|
||||
const result = validate(instance[key], $properties[key]!, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
subInstancePointer,
|
||||
`${keywordLocation}/${encodePointer(key)}`
|
||||
)
|
||||
instanceLocation: subInstancePointer,
|
||||
schemaLocation: `${keywordLocation}/${encodePointer(key)}`
|
||||
})
|
||||
if (result.valid) {
|
||||
evaluated[key] = thisEvaluated[key] = true
|
||||
instance[key] = result.instance
|
||||
|
@ -709,17 +676,13 @@ export function validate(
|
|||
continue
|
||||
}
|
||||
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`
|
||||
const result = validate(
|
||||
instance[key],
|
||||
subSchema!,
|
||||
draft,
|
||||
const result = validate(instance[key], subSchema!, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
subInstancePointer,
|
||||
`${keywordLocation}/${encodePointer(pattern)}`
|
||||
)
|
||||
instanceLocation: subInstancePointer,
|
||||
schemaLocation: `${keywordLocation}/${encodePointer(pattern)}`
|
||||
})
|
||||
if (result.valid) {
|
||||
evaluated[key] = thisEvaluated[key] = true
|
||||
instance[key] = result.instance
|
||||
|
@ -739,7 +702,10 @@ export function validate(
|
|||
}
|
||||
}
|
||||
|
||||
if (!stop && $additionalProperties !== undefined) {
|
||||
if (
|
||||
!stop &&
|
||||
($additionalProperties !== undefined || strictAdditionalProperties)
|
||||
) {
|
||||
const keywordLocation = `${schemaLocation}/additionalProperties`
|
||||
for (const key in instance) {
|
||||
if (thisEvaluated[key]) {
|
||||
|
@ -748,14 +714,14 @@ export function validate(
|
|||
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`
|
||||
const result = validate(
|
||||
instance[key],
|
||||
$additionalProperties,
|
||||
draft,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
subInstancePointer,
|
||||
keywordLocation
|
||||
$additionalProperties ?? !strictAdditionalProperties,
|
||||
{
|
||||
...opts,
|
||||
lookup,
|
||||
recursiveAnchor,
|
||||
instanceLocation: subInstancePointer,
|
||||
schemaLocation: keywordLocation
|
||||
}
|
||||
)
|
||||
if (result.valid) {
|
||||
evaluated[key] = true
|
||||
|
@ -778,17 +744,13 @@ export function validate(
|
|||
for (const key in instance) {
|
||||
if (!evaluated[key]) {
|
||||
const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`
|
||||
const result = validate(
|
||||
instance[key],
|
||||
$unevaluatedProperties,
|
||||
draft,
|
||||
const result = validate(instance[key], $unevaluatedProperties, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
subInstancePointer,
|
||||
keywordLocation
|
||||
)
|
||||
instanceLocation: subInstancePointer,
|
||||
schemaLocation: keywordLocation
|
||||
})
|
||||
if (result.valid) {
|
||||
evaluated[key] = true
|
||||
instance[key] = result.instance
|
||||
|
@ -833,17 +795,13 @@ export function validate(
|
|||
const keywordLocation = `${schemaLocation}/prefixItems`
|
||||
const length2 = Math.min($prefixItems.length, length)
|
||||
for (; i < length2; i++) {
|
||||
const result = validate(
|
||||
instance[i],
|
||||
$prefixItems[i]!,
|
||||
draft,
|
||||
const result = validate(instance[i], $prefixItems[i]!, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
`${instanceLocation}/${i}`,
|
||||
`${keywordLocation}/${i}`
|
||||
)
|
||||
instanceLocation: `${instanceLocation}/${i}`,
|
||||
schemaLocation: `${keywordLocation}/${i}`
|
||||
})
|
||||
evaluated[i] = true
|
||||
if (!result.valid) {
|
||||
stop = shortCircuit
|
||||
|
@ -866,17 +824,13 @@ export function validate(
|
|||
if (Array.isArray($items)) {
|
||||
const length2 = Math.min($items.length, length)
|
||||
for (; i < length2; i++) {
|
||||
const result = validate(
|
||||
instance[i],
|
||||
$items[i]!,
|
||||
draft,
|
||||
const result = validate(instance[i], $items[i]!, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
`${instanceLocation}/${i}`,
|
||||
`${keywordLocation}/${i}`
|
||||
)
|
||||
instanceLocation: `${instanceLocation}/${i}`,
|
||||
schemaLocation: `${keywordLocation}/${i}`
|
||||
})
|
||||
evaluated[i] = true
|
||||
if (result.valid) {
|
||||
instance[i] = result.instance
|
||||
|
@ -896,17 +850,13 @@ export function validate(
|
|||
}
|
||||
} else {
|
||||
for (; i < length; i++) {
|
||||
const result = validate(
|
||||
instance[i],
|
||||
$items,
|
||||
draft,
|
||||
const result = validate(instance[i], $items, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
`${instanceLocation}/${i}`,
|
||||
keywordLocation
|
||||
)
|
||||
instanceLocation: `${instanceLocation}/${i}`,
|
||||
schemaLocation: keywordLocation
|
||||
})
|
||||
evaluated[i] = true
|
||||
if (result.valid) {
|
||||
instance[i] = result.instance
|
||||
|
@ -929,17 +879,13 @@ export function validate(
|
|||
if (!stop && $additionalItems !== undefined) {
|
||||
const keywordLocation = `${schemaLocation}/additionalItems`
|
||||
for (; i < length; i++) {
|
||||
const result = validate(
|
||||
instance[i],
|
||||
$additionalItems,
|
||||
draft,
|
||||
const result = validate(instance[i], $additionalItems, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
`${instanceLocation}/${i}`,
|
||||
keywordLocation
|
||||
)
|
||||
instanceLocation: `${instanceLocation}/${i}`,
|
||||
schemaLocation: keywordLocation
|
||||
})
|
||||
evaluated[i] = true
|
||||
if (result.valid) {
|
||||
instance[i] = result.instance
|
||||
|
@ -979,17 +925,13 @@ export function validate(
|
|||
const errorsLength = errors.length
|
||||
let contained = 0
|
||||
for (let j = 0; j < length; j++) {
|
||||
const result = validate(
|
||||
instance[j],
|
||||
$contains,
|
||||
draft,
|
||||
const result = validate(instance[j], $contains, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
`${instanceLocation}/${j}`,
|
||||
keywordLocation
|
||||
)
|
||||
instanceLocation: `${instanceLocation}/${j}`,
|
||||
schemaLocation: keywordLocation
|
||||
})
|
||||
if (result.valid) {
|
||||
evaluated[j] = true
|
||||
contained++
|
||||
|
@ -1037,17 +979,13 @@ export function validate(
|
|||
if (evaluated[i]) {
|
||||
continue
|
||||
}
|
||||
const result = validate(
|
||||
instance[i],
|
||||
$unevaluatedItems,
|
||||
draft,
|
||||
const result = validate(instance[i], $unevaluatedItems, {
|
||||
...opts,
|
||||
lookup,
|
||||
coerce,
|
||||
shortCircuit,
|
||||
recursiveAnchor,
|
||||
`${instanceLocation}/${i}`,
|
||||
keywordLocation
|
||||
)
|
||||
instanceLocation: `${instanceLocation}/${i}`,
|
||||
schemaLocation: keywordLocation
|
||||
})
|
||||
evaluated[i] = true
|
||||
if (result.valid) {
|
||||
instance[i] = result.instance
|
||||
|
|
|
@ -3,39 +3,42 @@ import { dereference } from './dereference'
|
|||
import { validate } from './validate'
|
||||
|
||||
export class Validator {
|
||||
private readonly lookup: ReturnType<typeof dereference>
|
||||
private readonly schema: Schema | boolean
|
||||
private readonly draft: SchemaDraft
|
||||
private readonly shortCircuit: boolean
|
||||
private readonly coerce: boolean
|
||||
protected readonly lookup: ReturnType<typeof dereference>
|
||||
protected readonly schema: Schema | boolean
|
||||
protected readonly draft: SchemaDraft
|
||||
protected readonly shortCircuit: boolean
|
||||
protected readonly coerce: boolean
|
||||
protected readonly strictAdditionalProperties: boolean
|
||||
|
||||
constructor({
|
||||
schema,
|
||||
draft = '2019-09',
|
||||
shortCircuit = true,
|
||||
coerce = false
|
||||
coerce = false,
|
||||
strictAdditionalProperties = false
|
||||
}: {
|
||||
schema: Schema | boolean
|
||||
draft?: SchemaDraft
|
||||
shortCircuit?: boolean
|
||||
coerce?: boolean
|
||||
strictAdditionalProperties?: boolean
|
||||
}) {
|
||||
this.schema = schema
|
||||
this.draft = draft
|
||||
this.shortCircuit = shortCircuit
|
||||
this.coerce = coerce
|
||||
this.strictAdditionalProperties = strictAdditionalProperties
|
||||
this.lookup = dereference(schema)
|
||||
}
|
||||
|
||||
public validate(instance: any): ValidationResult {
|
||||
return validate(
|
||||
structuredClone(instance),
|
||||
this.schema,
|
||||
this.draft,
|
||||
this.lookup,
|
||||
this.coerce,
|
||||
this.shortCircuit
|
||||
)
|
||||
return validate(structuredClone(instance), this.schema, {
|
||||
draft: this.draft,
|
||||
lookup: this.lookup,
|
||||
coerce: this.coerce,
|
||||
shortCircuit: this.shortCircuit,
|
||||
strictAdditionalProperties: this.strictAdditionalProperties
|
||||
})
|
||||
}
|
||||
|
||||
public addSchema(schema: Schema, id?: string): void {
|
||||
|
|
|
@ -4,114 +4,143 @@ import { validate } from '../src/index'
|
|||
|
||||
describe('json-schema coercion', () => {
|
||||
it('string => number coercion', () => {
|
||||
const result = validate('7', { type: 'number' }, '2019-09', undefined, true)
|
||||
const result = validate('7', { type: 'number' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal(7)
|
||||
})
|
||||
|
||||
it('boolean => number coercion', () => {
|
||||
const result = validate(
|
||||
true,
|
||||
{ type: 'number' },
|
||||
'2019-09',
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
const result = validate(true, { type: 'number' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal(1)
|
||||
})
|
||||
|
||||
it('null => number coercion', () => {
|
||||
const result = validate(
|
||||
null,
|
||||
{ type: 'number' },
|
||||
'2019-09',
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
const result = validate(null, { type: 'number' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal(0)
|
||||
})
|
||||
|
||||
it('array => number coercion', () => {
|
||||
const result = validate([1], { type: 'number' }, '2019-09', undefined, true)
|
||||
const result = validate([1], { type: 'number' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal(1)
|
||||
})
|
||||
|
||||
it('boolean => string coercion', () => {
|
||||
const result = validate(
|
||||
true,
|
||||
{ type: 'string' },
|
||||
'2019-09',
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
const result = validate(true, { type: 'string' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal('true')
|
||||
})
|
||||
|
||||
it('number => string coercion', () => {
|
||||
const result = validate(
|
||||
72.3,
|
||||
{ type: 'string' },
|
||||
'2019-09',
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
const result = validate(72.3, { type: 'string' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal('72.3')
|
||||
})
|
||||
|
||||
it('null => string coercion', () => {
|
||||
const result = validate(
|
||||
null,
|
||||
{ type: 'string' },
|
||||
'2019-09',
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
const result = validate(null, { type: 'string' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal('')
|
||||
})
|
||||
|
||||
it('array => string coercion', () => {
|
||||
const result = validate(
|
||||
['nala'],
|
||||
{ type: 'string' },
|
||||
'2019-09',
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
const result = validate(['nala'], { type: 'string' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal('nala')
|
||||
})
|
||||
|
||||
it('string => boolean coercion', () => {
|
||||
const result = validate(
|
||||
'true',
|
||||
{ type: 'boolean' },
|
||||
'2019-09',
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
const result = validate('true', { type: 'boolean' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal(true)
|
||||
})
|
||||
|
||||
it('string => null coercion', () => {
|
||||
const result = validate('', { type: 'null' }, '2019-09', undefined, true)
|
||||
const result = validate('', { type: 'null' }, { coerce: true })
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.equal(null)
|
||||
})
|
||||
|
||||
it('object property coercion', () => {
|
||||
const result = validate(
|
||||
{
|
||||
name: null,
|
||||
cool: 'true',
|
||||
cool2: 0,
|
||||
number: '5.12',
|
||||
integer: '5.12'
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
cool: { type: 'boolean' },
|
||||
cool2: { type: 'boolean' },
|
||||
number: { type: 'number' },
|
||||
integer: { type: 'integer' }
|
||||
}
|
||||
},
|
||||
{ coerce: true }
|
||||
)
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.deep.equal({
|
||||
name: '',
|
||||
cool: true,
|
||||
cool2: false,
|
||||
number: 5.12,
|
||||
integer: 5
|
||||
})
|
||||
})
|
||||
|
||||
it('strictAdditionalProperties false', () => {
|
||||
const result = validate(
|
||||
{
|
||||
name: 'nala',
|
||||
extra: true
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
expect(result.valid).to.equal(true)
|
||||
expect(result.instance).to.deep.equal({
|
||||
name: 'nala',
|
||||
extra: true
|
||||
})
|
||||
})
|
||||
|
||||
it('strictAdditionalProperties true', () => {
|
||||
const result = validate(
|
||||
{
|
||||
name: 'nala',
|
||||
extra: true
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
},
|
||||
{ strictAdditionalProperties: true }
|
||||
)
|
||||
|
||||
expect(result.valid).to.equal(false)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -60,7 +60,7 @@ describe('json-schema', () => {
|
|||
}
|
||||
let result: ValidationResult | undefined
|
||||
try {
|
||||
result = validate(data, schema, draft, lookup)
|
||||
result = validate(data, schema, { draft, lookup })
|
||||
} catch {}
|
||||
if (result?.valid !== valid) {
|
||||
failures[name] = failures[name] ?? {}
|
||||
|
|
Ładowanie…
Reference in New Issue