3 AddedKeywordDefinition,
5 KeywordErrorDefinition,
8 import {allSchemaProperties, usePattern, isOwnProperty} from "../code"
9 import {_, nil, or, not, Code, Name} from "../../compile/codegen"
10 import N from "../../compile/names"
11 import type {SubschemaArgs} from "../../compile/validate/subschema"
12 import {alwaysValidSchema, schemaRefOrVal, Type} from "../../compile/util"
14 export type AdditionalPropertiesError = ErrorObject<
15 "additionalProperties",
16 {additionalProperty: string},
20 const error: KeywordErrorDefinition = {
21 message: "must NOT have additional properties",
22 params: ({params}) => _`{additionalProperty: ${params.additionalProperty}}`,
25 const def: CodeKeywordDefinition & AddedKeywordDefinition = {
26 keyword: "additionalProperties",
28 schemaType: ["boolean", "object"],
33 const {gen, schema, parentSchema, data, errsCount, it} = cxt
34 /* istanbul ignore if */
35 if (!errsCount) throw new Error("ajv implementation error")
36 const {allErrors, opts} = it
38 if (opts.removeAdditional !== "all" && alwaysValidSchema(it, schema)) return
39 const props = allSchemaProperties(parentSchema.properties)
40 const patProps = allSchemaProperties(parentSchema.patternProperties)
41 checkAdditionalProperties()
42 cxt.ok(_`${errsCount} === ${N.errors}`)
44 function checkAdditionalProperties(): void {
45 gen.forIn("key", data, (key: Name) => {
46 if (!props.length && !patProps.length) additionalPropertyCode(key)
47 else gen.if(isAdditional(key), () => additionalPropertyCode(key))
51 function isAdditional(key: Name): Code {
53 if (props.length > 8) {
54 // TODO maybe an option instead of hard-coded 8?
55 const propsSchema = schemaRefOrVal(it, parentSchema.properties, "properties")
56 definedProp = isOwnProperty(gen, propsSchema as Code, key)
57 } else if (props.length) {
58 definedProp = or(...props.map((p) => _`${key} === ${p}`))
62 if (patProps.length) {
63 definedProp = or(definedProp, ...patProps.map((p) => _`${usePattern(gen, p)}.test(${key})`))
65 return not(definedProp)
68 function deleteAdditional(key: Name): void {
69 gen.code(_`delete ${data}[${key}]`)
72 function additionalPropertyCode(key: Name): void {
73 if (opts.removeAdditional === "all" || (opts.removeAdditional && schema === false)) {
78 if (schema === false) {
79 cxt.setParams({additionalProperty: key})
81 if (!allErrors) gen.break()
85 if (typeof schema == "object" && !alwaysValidSchema(it, schema)) {
86 const valid = gen.name("valid")
87 if (opts.removeAdditional === "failing") {
88 applyAdditionalSchema(key, valid, false)
89 gen.if(not(valid), () => {
94 applyAdditionalSchema(key, valid)
95 if (!allErrors) gen.if(not(valid), () => gen.break())
100 function applyAdditionalSchema(key: Name, valid: Name, errors?: false): void {
101 const subschema: SubschemaArgs = {
102 keyword: "additionalProperties",
104 dataPropType: Type.Str,
106 if (errors === false) {
107 Object.assign(subschema, {
113 cxt.subschema(subschema, valid)