This commit is contained in:
2024-03-22 03:47:51 +05:30
parent 8bcf3d211e
commit 89819f6fe2
28440 changed files with 3211033 additions and 2 deletions

View File

@@ -0,0 +1,56 @@
import type {
CodeKeywordDefinition,
ErrorObject,
KeywordErrorDefinition,
AnySchema,
} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_, str, not, Name} from "../../compile/codegen"
import {alwaysValidSchema, checkStrictMode, Type} from "../../compile/util"
export type AdditionalItemsError = ErrorObject<"additionalItems", {limit: number}, AnySchema>
const error: KeywordErrorDefinition = {
message: ({params: {len}}) => str`must NOT have more than ${len} items`,
params: ({params: {len}}) => _`{limit: ${len}}`,
}
const def: CodeKeywordDefinition = {
keyword: "additionalItems" as const,
type: "array",
schemaType: ["boolean", "object"],
before: "uniqueItems",
error,
code(cxt: KeywordCxt) {
const {parentSchema, it} = cxt
const {items} = parentSchema
if (!Array.isArray(items)) {
checkStrictMode(it, '"additionalItems" is ignored when "items" is not an array of schemas')
return
}
validateAdditionalItems(cxt, items)
},
}
export function validateAdditionalItems(cxt: KeywordCxt, items: AnySchema[]): void {
const {gen, schema, data, keyword, it} = cxt
it.items = true
const len = gen.const("len", _`${data}.length`)
if (schema === false) {
cxt.setParams({len: items.length})
cxt.pass(_`${len} <= ${items.length}`)
} else if (typeof schema == "object" && !alwaysValidSchema(it, schema)) {
const valid = gen.var("valid", _`${len} <= ${items.length}`) // TODO var
gen.if(not(valid), () => validateItems(valid))
cxt.ok(valid)
}
function validateItems(valid: Name): void {
gen.forRange("i", items.length, len, (i) => {
cxt.subschema({keyword, dataProp: i, dataPropType: Type.Num}, valid)
if (!it.allErrors) gen.if(not(valid), () => gen.break())
})
}
}
export default def

View File

@@ -0,0 +1,118 @@
import type {
CodeKeywordDefinition,
AddedKeywordDefinition,
ErrorObject,
KeywordErrorDefinition,
AnySchema,
} from "../../types"
import {allSchemaProperties, usePattern, isOwnProperty} from "../code"
import {_, nil, or, not, Code, Name} from "../../compile/codegen"
import N from "../../compile/names"
import type {SubschemaArgs} from "../../compile/validate/subschema"
import {alwaysValidSchema, schemaRefOrVal, Type} from "../../compile/util"
export type AdditionalPropertiesError = ErrorObject<
"additionalProperties",
{additionalProperty: string},
AnySchema
>
const error: KeywordErrorDefinition = {
message: "must NOT have additional properties",
params: ({params}) => _`{additionalProperty: ${params.additionalProperty}}`,
}
const def: CodeKeywordDefinition & AddedKeywordDefinition = {
keyword: "additionalProperties",
type: ["object"],
schemaType: ["boolean", "object"],
allowUndefined: true,
trackErrors: true,
error,
code(cxt) {
const {gen, schema, parentSchema, data, errsCount, it} = cxt
/* istanbul ignore if */
if (!errsCount) throw new Error("ajv implementation error")
const {allErrors, opts} = it
it.props = true
if (opts.removeAdditional !== "all" && alwaysValidSchema(it, schema)) return
const props = allSchemaProperties(parentSchema.properties)
const patProps = allSchemaProperties(parentSchema.patternProperties)
checkAdditionalProperties()
cxt.ok(_`${errsCount} === ${N.errors}`)
function checkAdditionalProperties(): void {
gen.forIn("key", data, (key: Name) => {
if (!props.length && !patProps.length) additionalPropertyCode(key)
else gen.if(isAdditional(key), () => additionalPropertyCode(key))
})
}
function isAdditional(key: Name): Code {
let definedProp: Code
if (props.length > 8) {
// TODO maybe an option instead of hard-coded 8?
const propsSchema = schemaRefOrVal(it, parentSchema.properties, "properties")
definedProp = isOwnProperty(gen, propsSchema as Code, key)
} else if (props.length) {
definedProp = or(...props.map((p) => _`${key} === ${p}`))
} else {
definedProp = nil
}
if (patProps.length) {
definedProp = or(definedProp, ...patProps.map((p) => _`${usePattern(cxt, p)}.test(${key})`))
}
return not(definedProp)
}
function deleteAdditional(key: Name): void {
gen.code(_`delete ${data}[${key}]`)
}
function additionalPropertyCode(key: Name): void {
if (opts.removeAdditional === "all" || (opts.removeAdditional && schema === false)) {
deleteAdditional(key)
return
}
if (schema === false) {
cxt.setParams({additionalProperty: key})
cxt.error()
if (!allErrors) gen.break()
return
}
if (typeof schema == "object" && !alwaysValidSchema(it, schema)) {
const valid = gen.name("valid")
if (opts.removeAdditional === "failing") {
applyAdditionalSchema(key, valid, false)
gen.if(not(valid), () => {
cxt.reset()
deleteAdditional(key)
})
} else {
applyAdditionalSchema(key, valid)
if (!allErrors) gen.if(not(valid), () => gen.break())
}
}
}
function applyAdditionalSchema(key: Name, valid: Name, errors?: false): void {
const subschema: SubschemaArgs = {
keyword: "additionalProperties",
dataProp: key,
dataPropType: Type.Str,
}
if (errors === false) {
Object.assign(subschema, {
compositeRule: true,
createErrors: false,
allErrors: false,
})
}
cxt.subschema(subschema, valid)
}
},
}
export default def

22
node_modules/ajv/lib/vocabularies/applicator/allOf.ts generated vendored Normal file
View File

@@ -0,0 +1,22 @@
import type {CodeKeywordDefinition, AnySchema} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {alwaysValidSchema} from "../../compile/util"
const def: CodeKeywordDefinition = {
keyword: "allOf",
schemaType: "array",
code(cxt: KeywordCxt) {
const {gen, schema, it} = cxt
/* istanbul ignore if */
if (!Array.isArray(schema)) throw new Error("ajv implementation error")
const valid = gen.name("valid")
schema.forEach((sch: AnySchema, i: number) => {
if (alwaysValidSchema(it, sch)) return
const schCxt = cxt.subschema({keyword: "allOf", schemaProp: i}, valid)
cxt.ok(valid)
cxt.mergeEvaluated(schCxt)
})
},
}
export default def

14
node_modules/ajv/lib/vocabularies/applicator/anyOf.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import type {CodeKeywordDefinition, ErrorNoParams, AnySchema} from "../../types"
import {validateUnion} from "../code"
export type AnyOfError = ErrorNoParams<"anyOf", AnySchema[]>
const def: CodeKeywordDefinition = {
keyword: "anyOf",
schemaType: "array",
trackErrors: true,
code: validateUnion,
error: {message: "must match a schema in anyOf"},
}
export default def

View File

@@ -0,0 +1,109 @@
import type {
CodeKeywordDefinition,
KeywordErrorDefinition,
ErrorObject,
AnySchema,
} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_, str, Name} from "../../compile/codegen"
import {alwaysValidSchema, checkStrictMode, Type} from "../../compile/util"
export type ContainsError = ErrorObject<
"contains",
{minContains: number; maxContains?: number},
AnySchema
>
const error: KeywordErrorDefinition = {
message: ({params: {min, max}}) =>
max === undefined
? str`must contain at least ${min} valid item(s)`
: str`must contain at least ${min} and no more than ${max} valid item(s)`,
params: ({params: {min, max}}) =>
max === undefined ? _`{minContains: ${min}}` : _`{minContains: ${min}, maxContains: ${max}}`,
}
const def: CodeKeywordDefinition = {
keyword: "contains",
type: "array",
schemaType: ["object", "boolean"],
before: "uniqueItems",
trackErrors: true,
error,
code(cxt: KeywordCxt) {
const {gen, schema, parentSchema, data, it} = cxt
let min: number
let max: number | undefined
const {minContains, maxContains} = parentSchema
if (it.opts.next) {
min = minContains === undefined ? 1 : minContains
max = maxContains
} else {
min = 1
}
const len = gen.const("len", _`${data}.length`)
cxt.setParams({min, max})
if (max === undefined && min === 0) {
checkStrictMode(it, `"minContains" == 0 without "maxContains": "contains" keyword ignored`)
return
}
if (max !== undefined && min > max) {
checkStrictMode(it, `"minContains" > "maxContains" is always invalid`)
cxt.fail()
return
}
if (alwaysValidSchema(it, schema)) {
let cond = _`${len} >= ${min}`
if (max !== undefined) cond = _`${cond} && ${len} <= ${max}`
cxt.pass(cond)
return
}
it.items = true
const valid = gen.name("valid")
if (max === undefined && min === 1) {
validateItems(valid, () => gen.if(valid, () => gen.break()))
} else if (min === 0) {
gen.let(valid, true)
if (max !== undefined) gen.if(_`${data}.length > 0`, validateItemsWithCount)
} else {
gen.let(valid, false)
validateItemsWithCount()
}
cxt.result(valid, () => cxt.reset())
function validateItemsWithCount(): void {
const schValid = gen.name("_valid")
const count = gen.let("count", 0)
validateItems(schValid, () => gen.if(schValid, () => checkLimits(count)))
}
function validateItems(_valid: Name, block: () => void): void {
gen.forRange("i", 0, len, (i) => {
cxt.subschema(
{
keyword: "contains",
dataProp: i,
dataPropType: Type.Num,
compositeRule: true,
},
_valid
)
block()
})
}
function checkLimits(count: Name): void {
gen.code(_`${count}++`)
if (max === undefined) {
gen.if(_`${count} >= ${min}`, () => gen.assign(valid, true).break())
} else {
gen.if(_`${count} > ${max}`, () => gen.assign(valid, false).break())
if (min === 1) gen.assign(valid, true)
else gen.if(_`${count} >= ${min}`, () => gen.assign(valid, true))
}
}
},
}
export default def

View File

@@ -0,0 +1,112 @@
import type {
CodeKeywordDefinition,
ErrorObject,
KeywordErrorDefinition,
SchemaMap,
AnySchema,
} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_, str} from "../../compile/codegen"
import {alwaysValidSchema} from "../../compile/util"
import {checkReportMissingProp, checkMissingProp, reportMissingProp, propertyInData} from "../code"
export type PropertyDependencies = {[K in string]?: string[]}
export interface DependenciesErrorParams {
property: string
missingProperty: string
depsCount: number
deps: string // TODO change to string[]
}
type SchemaDependencies = SchemaMap
export type DependenciesError = ErrorObject<
"dependencies",
DependenciesErrorParams,
{[K in string]?: string[] | AnySchema}
>
export const error: KeywordErrorDefinition = {
message: ({params: {property, depsCount, deps}}) => {
const property_ies = depsCount === 1 ? "property" : "properties"
return str`must have ${property_ies} ${deps} when property ${property} is present`
},
params: ({params: {property, depsCount, deps, missingProperty}}) =>
_`{property: ${property},
missingProperty: ${missingProperty},
depsCount: ${depsCount},
deps: ${deps}}`, // TODO change to reference
}
const def: CodeKeywordDefinition = {
keyword: "dependencies",
type: "object",
schemaType: "object",
error,
code(cxt: KeywordCxt) {
const [propDeps, schDeps] = splitDependencies(cxt)
validatePropertyDeps(cxt, propDeps)
validateSchemaDeps(cxt, schDeps)
},
}
function splitDependencies({schema}: KeywordCxt): [PropertyDependencies, SchemaDependencies] {
const propertyDeps: PropertyDependencies = {}
const schemaDeps: SchemaDependencies = {}
for (const key in schema) {
if (key === "__proto__") continue
const deps = Array.isArray(schema[key]) ? propertyDeps : schemaDeps
deps[key] = schema[key]
}
return [propertyDeps, schemaDeps]
}
export function validatePropertyDeps(
cxt: KeywordCxt,
propertyDeps: {[K in string]?: string[]} = cxt.schema
): void {
const {gen, data, it} = cxt
if (Object.keys(propertyDeps).length === 0) return
const missing = gen.let("missing")
for (const prop in propertyDeps) {
const deps = propertyDeps[prop] as string[]
if (deps.length === 0) continue
const hasProperty = propertyInData(gen, data, prop, it.opts.ownProperties)
cxt.setParams({
property: prop,
depsCount: deps.length,
deps: deps.join(", "),
})
if (it.allErrors) {
gen.if(hasProperty, () => {
for (const depProp of deps) {
checkReportMissingProp(cxt, depProp)
}
})
} else {
gen.if(_`${hasProperty} && (${checkMissingProp(cxt, deps, missing)})`)
reportMissingProp(cxt, missing)
gen.else()
}
}
}
export function validateSchemaDeps(cxt: KeywordCxt, schemaDeps: SchemaMap = cxt.schema): void {
const {gen, data, keyword, it} = cxt
const valid = gen.name("valid")
for (const prop in schemaDeps) {
if (alwaysValidSchema(it, schemaDeps[prop] as AnySchema)) continue
gen.if(
propertyInData(gen, data, prop, it.opts.ownProperties),
() => {
const schCxt = cxt.subschema({keyword, schemaProp: prop}, valid)
cxt.mergeValidEvaluated(schCxt, valid)
},
() => gen.var(valid, true) // TODO var
)
cxt.ok(valid)
}
}
export default def

View File

@@ -0,0 +1,11 @@
import type {CodeKeywordDefinition} from "../../types"
import {validateSchemaDeps} from "./dependencies"
const def: CodeKeywordDefinition = {
keyword: "dependentSchemas",
type: "object",
schemaType: "object",
code: (cxt) => validateSchemaDeps(cxt),
}
export default def

80
node_modules/ajv/lib/vocabularies/applicator/if.ts generated vendored Normal file
View File

@@ -0,0 +1,80 @@
import type {
CodeKeywordDefinition,
ErrorObject,
KeywordErrorDefinition,
AnySchema,
} from "../../types"
import type {SchemaObjCxt} from "../../compile"
import type {KeywordCxt} from "../../compile/validate"
import {_, str, not, Name} from "../../compile/codegen"
import {alwaysValidSchema, checkStrictMode} from "../../compile/util"
export type IfKeywordError = ErrorObject<"if", {failingKeyword: string}, AnySchema>
const error: KeywordErrorDefinition = {
message: ({params}) => str`must match "${params.ifClause}" schema`,
params: ({params}) => _`{failingKeyword: ${params.ifClause}}`,
}
const def: CodeKeywordDefinition = {
keyword: "if",
schemaType: ["object", "boolean"],
trackErrors: true,
error,
code(cxt: KeywordCxt) {
const {gen, parentSchema, it} = cxt
if (parentSchema.then === undefined && parentSchema.else === undefined) {
checkStrictMode(it, '"if" without "then" and "else" is ignored')
}
const hasThen = hasSchema(it, "then")
const hasElse = hasSchema(it, "else")
if (!hasThen && !hasElse) return
const valid = gen.let("valid", true)
const schValid = gen.name("_valid")
validateIf()
cxt.reset()
if (hasThen && hasElse) {
const ifClause = gen.let("ifClause")
cxt.setParams({ifClause})
gen.if(schValid, validateClause("then", ifClause), validateClause("else", ifClause))
} else if (hasThen) {
gen.if(schValid, validateClause("then"))
} else {
gen.if(not(schValid), validateClause("else"))
}
cxt.pass(valid, () => cxt.error(true))
function validateIf(): void {
const schCxt = cxt.subschema(
{
keyword: "if",
compositeRule: true,
createErrors: false,
allErrors: false,
},
schValid
)
cxt.mergeEvaluated(schCxt)
}
function validateClause(keyword: string, ifClause?: Name): () => void {
return () => {
const schCxt = cxt.subschema({keyword}, schValid)
gen.assign(valid, schValid)
cxt.mergeValidEvaluated(schCxt, valid)
if (ifClause) gen.assign(ifClause, _`${keyword}`)
else cxt.setParams({ifClause: keyword})
}
}
},
}
function hasSchema(it: SchemaObjCxt, keyword: string): boolean {
const schema = it.schema[keyword]
return schema !== undefined && !alwaysValidSchema(it, schema)
}
export default def

53
node_modules/ajv/lib/vocabularies/applicator/index.ts generated vendored Normal file
View File

@@ -0,0 +1,53 @@
import type {ErrorNoParams, Vocabulary} from "../../types"
import additionalItems, {AdditionalItemsError} from "./additionalItems"
import prefixItems from "./prefixItems"
import items from "./items"
import items2020, {ItemsError} from "./items2020"
import contains, {ContainsError} from "./contains"
import dependencies, {DependenciesError} from "./dependencies"
import propertyNames, {PropertyNamesError} from "./propertyNames"
import additionalProperties, {AdditionalPropertiesError} from "./additionalProperties"
import properties from "./properties"
import patternProperties from "./patternProperties"
import notKeyword, {NotKeywordError} from "./not"
import anyOf, {AnyOfError} from "./anyOf"
import oneOf, {OneOfError} from "./oneOf"
import allOf from "./allOf"
import ifKeyword, {IfKeywordError} from "./if"
import thenElse from "./thenElse"
export default function getApplicator(draft2020 = false): Vocabulary {
const applicator = [
// any
notKeyword,
anyOf,
oneOf,
allOf,
ifKeyword,
thenElse,
// object
propertyNames,
additionalProperties,
dependencies,
properties,
patternProperties,
]
// array
if (draft2020) applicator.push(prefixItems, items2020)
else applicator.push(additionalItems, items)
applicator.push(contains)
return applicator
}
export type ApplicatorKeywordError =
| ErrorNoParams<"false schema">
| AdditionalItemsError
| ItemsError
| ContainsError
| AdditionalPropertiesError
| DependenciesError
| IfKeywordError
| AnyOfError
| OneOfError
| NotKeywordError
| PropertyNamesError

59
node_modules/ajv/lib/vocabularies/applicator/items.ts generated vendored Normal file
View File

@@ -0,0 +1,59 @@
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

View File

@@ -0,0 +1,36 @@
import type {
CodeKeywordDefinition,
KeywordErrorDefinition,
ErrorObject,
AnySchema,
} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_, str} from "../../compile/codegen"
import {alwaysValidSchema} from "../../compile/util"
import {validateArray} from "../code"
import {validateAdditionalItems} from "./additionalItems"
export type ItemsError = ErrorObject<"items", {limit: number}, AnySchema>
const error: KeywordErrorDefinition = {
message: ({params: {len}}) => str`must NOT have more than ${len} items`,
params: ({params: {len}}) => _`{limit: ${len}}`,
}
const def: CodeKeywordDefinition = {
keyword: "items",
type: "array",
schemaType: ["object", "boolean"],
before: "uniqueItems",
error,
code(cxt: KeywordCxt) {
const {schema, parentSchema, it} = cxt
const {prefixItems} = parentSchema
it.items = true
if (alwaysValidSchema(it, schema)) return
if (prefixItems) validateAdditionalItems(cxt, prefixItems)
else cxt.ok(validateArray(cxt))
},
}
export default def

38
node_modules/ajv/lib/vocabularies/applicator/not.ts generated vendored Normal file
View File

@@ -0,0 +1,38 @@
import type {CodeKeywordDefinition, ErrorNoParams, AnySchema} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {alwaysValidSchema} from "../../compile/util"
export type NotKeywordError = ErrorNoParams<"not", AnySchema>
const def: CodeKeywordDefinition = {
keyword: "not",
schemaType: ["object", "boolean"],
trackErrors: true,
code(cxt: KeywordCxt) {
const {gen, schema, it} = cxt
if (alwaysValidSchema(it, schema)) {
cxt.fail()
return
}
const valid = gen.name("valid")
cxt.subschema(
{
keyword: "not",
compositeRule: true,
createErrors: false,
allErrors: false,
},
valid
)
cxt.failResult(
valid,
() => cxt.reset(),
() => cxt.error()
)
},
error: {message: "must NOT be valid"},
}
export default def

82
node_modules/ajv/lib/vocabularies/applicator/oneOf.ts generated vendored Normal file
View File

@@ -0,0 +1,82 @@
import type {
CodeKeywordDefinition,
ErrorObject,
KeywordErrorDefinition,
AnySchema,
} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_, Name} from "../../compile/codegen"
import {alwaysValidSchema} from "../../compile/util"
import {SchemaCxt} from "../../compile"
export type OneOfError = ErrorObject<
"oneOf",
{passingSchemas: [number, number] | null},
AnySchema[]
>
const error: KeywordErrorDefinition = {
message: "must match exactly one schema in oneOf",
params: ({params}) => _`{passingSchemas: ${params.passing}}`,
}
const def: CodeKeywordDefinition = {
keyword: "oneOf",
schemaType: "array",
trackErrors: true,
error,
code(cxt: KeywordCxt) {
const {gen, schema, parentSchema, it} = cxt
/* istanbul ignore if */
if (!Array.isArray(schema)) throw new Error("ajv implementation error")
if (it.opts.discriminator && parentSchema.discriminator) return
const schArr: AnySchema[] = schema
const valid = gen.let("valid", false)
const passing = gen.let("passing", null)
const schValid = gen.name("_valid")
cxt.setParams({passing})
// TODO possibly fail straight away (with warning or exception) if there are two empty always valid schemas
gen.block(validateOneOf)
cxt.result(
valid,
() => cxt.reset(),
() => cxt.error(true)
)
function validateOneOf(): void {
schArr.forEach((sch: AnySchema, i: number) => {
let schCxt: SchemaCxt | undefined
if (alwaysValidSchema(it, sch)) {
gen.var(schValid, true)
} else {
schCxt = cxt.subschema(
{
keyword: "oneOf",
schemaProp: i,
compositeRule: true,
},
schValid
)
}
if (i > 0) {
gen
.if(_`${schValid} && ${valid}`)
.assign(valid, false)
.assign(passing, _`[${passing}, ${i}]`)
.else()
}
gen.if(schValid, () => {
gen.assign(valid, true)
gen.assign(passing, i)
if (schCxt) cxt.mergeEvaluated(schCxt, Name)
})
})
}
},
}
export default def

View File

@@ -0,0 +1,91 @@
import type {CodeKeywordDefinition} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {allSchemaProperties, usePattern} from "../code"
import {_, not, Name} from "../../compile/codegen"
import {alwaysValidSchema, checkStrictMode} from "../../compile/util"
import {evaluatedPropsToName, Type} from "../../compile/util"
import {AnySchema} from "../../types"
const def: CodeKeywordDefinition = {
keyword: "patternProperties",
type: "object",
schemaType: "object",
code(cxt: KeywordCxt) {
const {gen, schema, data, parentSchema, it} = cxt
const {opts} = it
const patterns = allSchemaProperties(schema)
const alwaysValidPatterns = patterns.filter((p) =>
alwaysValidSchema(it, schema[p] as AnySchema)
)
if (
patterns.length === 0 ||
(alwaysValidPatterns.length === patterns.length &&
(!it.opts.unevaluated || it.props === true))
) {
return
}
const checkProperties =
opts.strictSchema && !opts.allowMatchingProperties && parentSchema.properties
const valid = gen.name("valid")
if (it.props !== true && !(it.props instanceof Name)) {
it.props = evaluatedPropsToName(gen, it.props)
}
const {props} = it
validatePatternProperties()
function validatePatternProperties(): void {
for (const pat of patterns) {
if (checkProperties) checkMatchingProperties(pat)
if (it.allErrors) {
validateProperties(pat)
} else {
gen.var(valid, true) // TODO var
validateProperties(pat)
gen.if(valid)
}
}
}
function checkMatchingProperties(pat: string): void {
for (const prop in checkProperties) {
if (new RegExp(pat).test(prop)) {
checkStrictMode(
it,
`property ${prop} matches pattern ${pat} (use allowMatchingProperties)`
)
}
}
}
function validateProperties(pat: string): void {
gen.forIn("key", data, (key) => {
gen.if(_`${usePattern(cxt, pat)}.test(${key})`, () => {
const alwaysValid = alwaysValidPatterns.includes(pat)
if (!alwaysValid) {
cxt.subschema(
{
keyword: "patternProperties",
schemaProp: pat,
dataProp: key,
dataPropType: Type.Str,
},
valid
)
}
if (it.opts.unevaluated && props !== true) {
gen.assign(_`${props}[${key}]`, true)
} else if (!alwaysValid && !it.allErrors) {
// can short-circuit if `unevaluatedProperties` is not supported (opts.next === false)
// or if all properties were evaluated (props === true)
gen.if(not(valid), () => gen.break())
}
})
})
}
},
}
export default def

View File

@@ -0,0 +1,12 @@
import type {CodeKeywordDefinition} from "../../types"
import {validateTuple} from "./items"
const def: CodeKeywordDefinition = {
keyword: "prefixItems",
type: "array",
schemaType: ["array"],
before: "uniqueItems",
code: (cxt) => validateTuple(cxt, "items"),
}
export default def

View File

@@ -0,0 +1,57 @@
import type {CodeKeywordDefinition} from "../../types"
import {KeywordCxt} from "../../compile/validate"
import {propertyInData, allSchemaProperties} from "../code"
import {alwaysValidSchema, toHash, mergeEvaluated} from "../../compile/util"
import apDef from "./additionalProperties"
const def: CodeKeywordDefinition = {
keyword: "properties",
type: "object",
schemaType: "object",
code(cxt: KeywordCxt) {
const {gen, schema, parentSchema, data, it} = cxt
if (it.opts.removeAdditional === "all" && parentSchema.additionalProperties === undefined) {
apDef.code(new KeywordCxt(it, apDef, "additionalProperties"))
}
const allProps = allSchemaProperties(schema)
for (const prop of allProps) {
it.definedProperties.add(prop)
}
if (it.opts.unevaluated && allProps.length && it.props !== true) {
it.props = mergeEvaluated.props(gen, toHash(allProps), it.props)
}
const properties = allProps.filter((p) => !alwaysValidSchema(it, schema[p]))
if (properties.length === 0) return
const valid = gen.name("valid")
for (const prop of properties) {
if (hasDefault(prop)) {
applyPropertySchema(prop)
} else {
gen.if(propertyInData(gen, data, prop, it.opts.ownProperties))
applyPropertySchema(prop)
if (!it.allErrors) gen.else().var(valid, true)
gen.endIf()
}
cxt.it.definedProperties.add(prop)
cxt.ok(valid)
}
function hasDefault(prop: string): boolean | undefined {
return it.opts.useDefaults && !it.compositeRule && schema[prop].default !== undefined
}
function applyPropertySchema(prop: string): void {
cxt.subschema(
{
keyword: "properties",
schemaProp: prop,
dataProp: prop,
},
valid
)
}
},
}
export default def

View File

@@ -0,0 +1,50 @@
import type {
CodeKeywordDefinition,
ErrorObject,
KeywordErrorDefinition,
AnySchema,
} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_, not} from "../../compile/codegen"
import {alwaysValidSchema} from "../../compile/util"
export type PropertyNamesError = ErrorObject<"propertyNames", {propertyName: string}, AnySchema>
const error: KeywordErrorDefinition = {
message: "property name must be valid",
params: ({params}) => _`{propertyName: ${params.propertyName}}`,
}
const def: CodeKeywordDefinition = {
keyword: "propertyNames",
type: "object",
schemaType: ["object", "boolean"],
error,
code(cxt: KeywordCxt) {
const {gen, schema, data, it} = cxt
if (alwaysValidSchema(it, schema)) return
const valid = gen.name("valid")
gen.forIn("key", data, (key) => {
cxt.setParams({propertyName: key})
cxt.subschema(
{
keyword: "propertyNames",
data: key,
dataTypes: ["string"],
propertyName: key,
compositeRule: true,
},
valid
)
gen.if(not(valid), () => {
cxt.error(true)
if (!it.allErrors) gen.break()
})
})
cxt.ok(valid)
},
}
export default def

View File

@@ -0,0 +1,13 @@
import type {CodeKeywordDefinition} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {checkStrictMode} from "../../compile/util"
const def: CodeKeywordDefinition = {
keyword: ["then", "else"],
schemaType: ["object", "boolean"],
code({keyword, parentSchema, it}: KeywordCxt) {
if (parentSchema.if === undefined) checkStrictMode(it, `"${keyword}" without "if" is ignored`)
},
}
export default def