lib/vocabularies/applicator/items.ts
import type {CodeKeywordDefinition, AnySchema, AnySchemaObject} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_} from "../../compile/codegen"
import {alwaysValidSchema, mergeEvaluated, checkStrictMode} from "../../compile/util"
import {validateArray} from "../code"
const def: CodeKeywordDefinition = {
keyword: "items",
type: "array",
schemaType: ["object", "array", "boolean"],
before: "uniqueItems",
code(cxt: KeywordCxt) {
const {schema, it} = cxt
if (Array.isArray(schema)) return validateTuple(cxt, "additionalItems", schema)
it.items = true
if (alwaysValidSchema(it, schema)) return
cxt.ok(validateArray(cxt))
},
}
export function validateTuple(
cxt: KeywordCxt,
extraItems: string,
schArr: AnySchema[] = cxt.schema
): void {
const {gen, parentSchema, data, keyword, it} = cxt
checkStrictTuple(parentSchema)
if (it.opts.unevaluated && schArr.length && it.items !== true) {
it.items = mergeEvaluated.items(gen, schArr.length, it.items)
}
const valid = gen.name("valid")
const len = gen.const("len", _`${data}.length`)
schArr.forEach((sch: AnySchema, i: number) => {
if (alwaysValidSchema(it, sch)) return
gen.if(_`${len} > ${i}`, () =>
cxt.subschema(
{
keyword,
schemaProp: i,
dataProp: i,
},
valid
)
)
cxt.ok(valid)
})
function checkStrictTuple(sch: AnySchemaObject): void {
const {opts, errSchemaPath} = it
const l = schArr.length
const fullTuple = l === sch.minItems && (l === sch.maxItems || sch[extraItems] === false)
if (opts.strictTuples && !fullTuple) {
const msg = `"${keyword}" is ${l}-tuple, but minItems or maxItems/${extraItems} are not specified or different at path "${errSchemaPath}"`
checkStrictMode(it, msg, opts.strictTuples)
}
}
}
export default def