Socket
Socket
Sign inDemoInstall

ajv

Package Overview
Dependencies
4
Maintainers
2
Versions
354
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 7.0.4 to 7.1.0

dist/compile/timestamp.d.ts

5

dist/ajv.js

@@ -25,6 +25,5 @@ "use strict";

super._addDefaultMetaSchema();
const { $data, meta } = this.opts;
if (!meta)
if (!this.opts.meta)
return;
const metaSchema = $data
const metaSchema = this.opts.$data
? this.$dataMetaSchema(draft7MetaSchema, META_SUPPORT_DATA)

@@ -31,0 +30,0 @@ : draft7MetaSchema;

2

dist/compile/context.d.ts

@@ -12,3 +12,3 @@ import type { AddedKeywordDefinition, KeywordErrorCxt, KeywordCxtParams, AnySchemaObject } from "../types";

readonly $data?: string | false;
readonly schema: any;
schema: any;
readonly schemaValue: Code | number | boolean;

@@ -15,0 +15,0 @@ readonly schemaCode: Code | number | boolean;

@@ -82,3 +82,3 @@ "use strict";

;
(append ? errors_1.reportExtraError : errors_1.reportError)(this, this.def.error || errors_1.keywordError);
(append ? errors_1.reportExtraError : errors_1.reportError)(this, this.def.error);
}

@@ -85,0 +85,0 @@ $dataError() {

@@ -11,3 +11,3 @@ import type { ErrorObject } from "../types";

readonly missingSchema: string;
constructor(baseId: string, ref: string);
constructor(baseId: string, ref: string, msg?: string);
}

@@ -14,4 +14,4 @@ "use strict";

class MissingRefError extends Error {
constructor(baseId, ref) {
super(`can't resolve reference ${ref} from id ${baseId}`);
constructor(baseId, ref, msg) {
super(msg || `can't resolve reference ${ref} from id ${baseId}`);
this.missingRef = resolve_1.resolveUrl(baseId, ref);

@@ -18,0 +18,0 @@ this.missingSchema = resolve_1.normalizeId(resolve_1.getFullPath(this.missingRef));

@@ -5,5 +5,5 @@ import type { KeywordErrorCxt, KeywordErrorDefinition } from "../types";

export declare const keyword$DataError: KeywordErrorDefinition;
export declare function reportError(cxt: KeywordErrorCxt, error: KeywordErrorDefinition, overrideAllErrors?: boolean): void;
export declare function reportExtraError(cxt: KeywordErrorCxt, error: KeywordErrorDefinition): void;
export declare function reportError(cxt: KeywordErrorCxt, error?: KeywordErrorDefinition, overrideAllErrors?: boolean): void;
export declare function reportExtraError(cxt: KeywordErrorCxt, error?: KeywordErrorDefinition): void;
export declare function resetErrorsCount(gen: CodeGen, errsCount: Name): void;
export declare function extendErrors({ gen, keyword, schemaValue, data, errsCount, it, }: KeywordErrorCxt): void;

@@ -14,3 +14,3 @@ "use strict";

};
function reportError(cxt, error, overrideAllErrors) {
function reportError(cxt, error = exports.keywordError, overrideAllErrors) {
const { it } = cxt;

@@ -27,3 +27,3 @@ const { gen, compositeRule, allErrors } = it;

exports.reportError = reportError;
function reportExtraError(cxt, error) {
function reportExtraError(cxt, error = exports.keywordError) {
const { it } = cxt;

@@ -82,7 +82,26 @@ const { gen, compositeRule, allErrors } = it;

parentSchema: new codegen_1.Name("parentSchema"),
// JTD error properties
instancePath: new codegen_1.Name("instancePath"),
};
function errorObjectCode(cxt, error) {
const { keyword, data, schemaValue, it: { gen, createErrors, topSchemaRef, schemaPath, errorPath, errSchemaPath, propertyName, opts }, } = cxt;
const { createErrors, opts } = cxt.it;
if (createErrors === false)
return codegen_1._ `{}`;
return (opts.jtd && !opts.ajvErrors ? jtdErrorObject : ajvErrorObject)(cxt, error);
}
function jtdErrorObject(cxt, { message }) {
const { gen, keyword, it } = cxt;
const { errorPath, errSchemaPath, opts } = it;
const keyValues = [
[E.instancePath, codegen_1.strConcat(names_1.default.dataPath, errorPath)],
[E.schemaPath, codegen_1.str `${errSchemaPath}/${keyword}`],
];
if (opts.messages) {
keyValues.push([E.message, typeof message == "function" ? message(cxt) : message]);
}
return gen.object(...keyValues);
}
function ajvErrorObject(cxt, error) {
const { gen, keyword, data, schemaValue, it } = cxt;
const { topSchemaRef, schemaPath, errorPath, errSchemaPath, propertyName, opts } = it;
const { params, message } = error;

@@ -97,5 +116,4 @@ const keyValues = [

keyValues.push([E.propertyName, propertyName]);
if (opts.messages !== false) {
const msg = typeof message == "function" ? message(cxt) : message;
keyValues.push([E.message, msg]);
if (opts.messages) {
keyValues.push([E.message, typeof message == "function" ? message(cxt) : message]);
}

@@ -102,0 +120,0 @@ if (opts.verbose) {

@@ -26,3 +26,2 @@ import type { AnySchema, AnySchemaObject, AnyValidateFunction, EvaluatedProperties, EvaluatedItems } from "../types";

readonly schemaEnv: SchemaEnv;
readonly strictSchema?: boolean;
readonly rootId: string;

@@ -37,2 +36,4 @@ baseId: string;

items?: EvaluatedItems | Name;
jtdDiscriminator?: string;
jtdMetadata?: boolean;
readonly createErrors?: boolean;

@@ -39,0 +40,0 @@ readonly opts: InstanceOptions;

@@ -67,7 +67,6 @@ "use strict";

schemaEnv: sch,
strictSchema: true,
rootId,
baseId: sch.baseId || rootId,
schemaPath: codegen_1.nil,
errSchemaPath: "#",
errSchemaPath: this.opts.jtd ? "" : "#",
errorPath: codegen_1._ `""`,

@@ -74,0 +73,0 @@ opts: this.opts,

@@ -21,4 +21,4 @@ "use strict";

post: { rules: [] },
all: { type: true, $comment: true },
keywords: { type: true, $comment: true },
all: {},
keywords: {},
};

@@ -25,0 +25,0 @@ }

@@ -13,3 +13,2 @@ import type { AnySchema } from "../types";

schema: AnySchema;
strictSchema: boolean;
schemaPath: Code;

@@ -23,2 +22,4 @@ errSchemaPath: string;

dataPropType: Type;
jtdDiscriminator: string;
jtdMetadata: boolean;
compositeRule: true;

@@ -25,0 +26,0 @@ createErrors: boolean;

@@ -21,3 +21,3 @@ "use strict";

exports.applySubschema = applySubschema;
function getSubschema(it, { keyword, schemaProp, schema, strictSchema, schemaPath, errSchemaPath, topSchemaRef, }) {
function getSubschema(it, { keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef }) {
if (keyword !== undefined && schema !== undefined) {

@@ -46,3 +46,2 @@ throw new Error('both "keyword" and "schema" passed, only one allowed');

schema,
strictSchema,
schemaPath,

@@ -85,3 +84,3 @@ topSchemaRef,

}
function extendSubschemaMode(subschema, { compositeRule, createErrors, allErrors, strictSchema }) {
function extendSubschemaMode(subschema, { jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors }) {
if (compositeRule !== undefined)

@@ -93,3 +92,4 @@ subschema.compositeRule = compositeRule;

subschema.allErrors = allErrors;
subschema.strictSchema = strictSchema; // not inherited
subschema.jtdDiscriminator = jtdDiscriminator; // not inherited
subschema.jtdMetadata = jtdMetadata; // not inherited
}

@@ -96,0 +96,0 @@ function getErrorPath(dataProp, dataPropType, jsPropertySyntax) {

@@ -122,2 +122,4 @@ "use strict";

function typeAndKeywords(it, errsCount) {
if (it.opts.jtd)
return iterate_1.schemaKeywords(it, [], false, errsCount);
const types = dataType_1.getSchemaTypes(it.schema);

@@ -124,0 +126,0 @@ const checkedTypes = dataType_1.coerceAndCheckDataType(it, types);

@@ -19,3 +19,4 @@ "use strict";

}
checkStrictTypes(it, types);
if (!opts.jtd)
checkStrictTypes(it, types);
gen.block(() => {

@@ -22,0 +23,0 @@ for (const group of RULES.rules)

@@ -20,3 +20,3 @@ export { Format, FormatDefinition, AsyncFormatDefinition, KeywordDefinition, KeywordErrorDefinition, CodeKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition, Vocabulary, Schema, SchemaObject, AnySchemaObject, AsyncSchema, AnySchema, ValidateFunction, AsyncValidateFunction, AnyValidateFunction, ErrorObject, ErrorNoParams, } from "./types";

export declare type Options = CurrentOptions & DeprecatedOptions;
interface CurrentOptions {
export interface CurrentOptions {
strict?: boolean | "log";

@@ -47,2 +47,3 @@ strictTypes?: boolean | "log";

dynamicRef?: boolean;
jtd?: boolean;
meta?: SchemaObject | boolean;

@@ -60,2 +61,3 @@ defaultMeta?: string | AnySchemaObject;

code?: CodeOptions;
ajvErrors?: boolean;
}

@@ -62,0 +64,0 @@ export interface CodeOptions {

@@ -412,4 +412,7 @@ "use strict";

_addSchema(schema, meta, validateSchema = this.opts.validateSchema, addSchema = this.opts.addUsedSchema) {
if (typeof schema != "object" && typeof schema != "boolean") {
throw new Error("schema must be object or boolean");
if (typeof schema != "object") {
if (this.opts.jtd)
throw new Error("schema must be object");
else if (typeof schema != "boolean")
throw new Error("schema must be object or boolean");
}

@@ -519,3 +522,3 @@ let sch = this._cache.get(schema);

}
const KEYWORD_NAME = /^[a-z_$][a-z0-9_$-]*$/i;
const KEYWORD_NAME = /^[a-z_$][a-z0-9_$-:]*$/i;
function checkKeyword(keyword, def) {

@@ -522,0 +525,0 @@ const { RULES } = this;

@@ -69,2 +69,3 @@ import type { CodeGen, Code, Name, ScopeValueSets, ValueScopeName } from "../compile/codegen";

dataPath: string;
instancePath?: string;
schemaPath: string;

@@ -71,0 +72,0 @@ params: P;

@@ -44,4 +44,9 @@ "use strict";

// TODO maybe an option instead of hard-coded 8?
const hasProp = gen.scopeValue("func", {
// eslint-disable-next-line @typescript-eslint/unbound-method
ref: Object.prototype.hasOwnProperty,
code: codegen_1._ `Object.prototype.hasOwnProperty`,
});
const propsSchema = util_1.schemaRefOrVal(it, parentSchema.properties, "properties");
definedProp = codegen_1._ `${propsSchema}.hasOwnProperty(${key})`;
definedProp = codegen_1._ `${hasProp}.call(${propsSchema}, ${key})`;
}

@@ -57,3 +62,3 @@ else if (props.length) {

}
return codegen_1._ `!(${definedProp})`;
return codegen_1.not(definedProp);
}

@@ -96,3 +101,2 @@ function deleteAdditional(key) {

dataPropType: subschema_1.Type.Str,
strictSchema: it.strictSchema,
};

@@ -99,0 +103,0 @@ if (errors === false) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const codegen_1 = require("../../compile/codegen");
const util_1 = require("../../compile/util");
const code_1 = require("../code");
const def = {

@@ -9,27 +8,3 @@ keyword: "anyOf",

trackErrors: true,
code(cxt) {
const { gen, schema, it } = cxt;
/* istanbul ignore if */
if (!Array.isArray(schema))
throw new Error("ajv implementation error");
const alwaysValid = schema.some((sch) => util_1.alwaysValidSchema(it, sch));
if (alwaysValid && !it.opts.unevaluated)
return;
const valid = gen.let("valid", false);
const schValid = gen.name("_valid");
gen.block(() => schema.forEach((_sch, i) => {
const schCxt = cxt.subschema({
keyword: "anyOf",
schemaProp: i,
compositeRule: true,
}, schValid);
gen.assign(valid, codegen_1._ `${valid} || ${schValid}`);
const merged = cxt.mergeValidEvaluated(schCxt, schValid);
// can short-circuit if `unevaluatedProperties/Items` not supported (opts.unevaluated !== true)
// or if all properties and items were evaluated (it.props === true && it.items === true)
if (!merged)
gen.if(codegen_1.not(valid));
}));
cxt.result(valid, () => cxt.reset(), () => cxt.error(true));
},
code: code_1.validateUnion,
error: {

@@ -36,0 +11,0 @@ message: "should match some schema in anyOf",

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const codegen_1 = require("../../compile/codegen");
const subschema_1 = require("../../compile/subschema");
const util_1 = require("../../compile/util");
const validate_1 = require("../../compile/validate");
const code_1 = require("../code");
const def = {

@@ -13,4 +13,3 @@ keyword: "items",

code(cxt) {
const { gen, schema, parentSchema, data, it } = cxt;
const len = gen.const("len", codegen_1._ `${data}.length`);
const { gen, schema, it } = cxt;
if (Array.isArray(schema)) {

@@ -24,7 +23,9 @@ if (it.opts.unevaluated && schema.length && it.items !== true) {

it.items = true;
if (!util_1.alwaysValidSchema(it, schema))
validateArray();
if (util_1.alwaysValidSchema(it, schema))
return;
cxt.ok(code_1.validateArray(cxt));
}
function validateTuple(schArr) {
if (it.opts.strictTuples && !fullTupleSchema(schema.length, parentSchema)) {
const { parentSchema, data } = cxt;
if (it.opts.strictTuples && !fullTupleSchema(schArr.length, parentSchema)) {
const msg = `"items" is ${schArr.length}-tuple, but minItems or maxItems/additionalItems are not specified or different`;

@@ -34,2 +35,3 @@ validate_1.checkStrictMode(it, msg, it.opts.strictTuples);

const valid = gen.name("valid");
const len = gen.const("len", codegen_1._ `${data}.length`);
schArr.forEach((sch, i) => {

@@ -42,3 +44,2 @@ if (util_1.alwaysValidSchema(it, sch))

dataProp: i,
strictSchema: it.strictSchema,
}, valid));

@@ -48,16 +49,2 @@ cxt.ok(valid);

}
function validateArray() {
const valid = gen.name("valid");
gen.forRange("i", 0, len, (i) => {
cxt.subschema({
keyword: "items",
dataProp: i,
dataPropType: subschema_1.Type.Num,
strictSchema: it.strictSchema,
}, valid);
if (!it.allErrors)
gen.if(codegen_1.not(valid), () => gen.break());
});
cxt.ok(valid);
}
},

@@ -64,0 +51,0 @@ };

@@ -55,3 +55,2 @@ "use strict";

dataPropType: subschema_1.Type.Str,
strictSchema: it.strictSchema,
}, valid);

@@ -58,0 +57,0 @@ if (it.opts.unevaluated && props !== true) {

@@ -45,3 +45,2 @@ "use strict";

dataProp: prop,
strictSchema: it.strictSchema,
}, valid);

@@ -48,0 +47,0 @@ }

@@ -27,3 +27,2 @@ "use strict";

compositeRule: true,
strictSchema: it.strictSchema,
}, valid);

@@ -30,0 +29,0 @@ gen.if(codegen_1.not(valid), () => {

@@ -14,1 +14,3 @@ import type { SchemaMap } from "../types";

export declare function usePattern(gen: CodeGen, pattern: string): Name;
export declare function validateArray(cxt: KeywordCxt): Name;
export declare function validateUnion(cxt: KeywordCxt): void;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = void 0;
exports.validateUnion = exports.validateArray = exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = void 0;
const codegen_1 = require("../compile/codegen");
const util_1 = require("../compile/util");
const subschema_1 = require("../compile/subschema");
const names_1 = require("../compile/names");

@@ -67,2 +68,52 @@ function checkReportMissingProp(cxt, prop) {

exports.usePattern = usePattern;
function validateArray(cxt) {
const { gen, data, keyword, it } = cxt;
const valid = gen.name("valid");
if (it.allErrors) {
const validArr = gen.let("valid", true);
validateItems(() => gen.assign(validArr, false));
return validArr;
}
gen.var(valid, true);
validateItems(() => gen.break());
return valid;
function validateItems(notValid) {
const len = gen.const("len", codegen_1._ `${data}.length`);
gen.forRange("i", 0, len, (i) => {
cxt.subschema({
keyword,
dataProp: i,
dataPropType: subschema_1.Type.Num,
}, valid);
gen.if(codegen_1.not(valid), notValid);
});
}
}
exports.validateArray = validateArray;
function validateUnion(cxt) {
const { gen, schema, keyword, it } = cxt;
/* istanbul ignore if */
if (!Array.isArray(schema))
throw new Error("ajv implementation error");
const alwaysValid = schema.some((sch) => util_1.alwaysValidSchema(it, sch));
if (alwaysValid && !it.opts.unevaluated)
return;
const valid = gen.let("valid", false);
const schValid = gen.name("_valid");
gen.block(() => schema.forEach((_sch, i) => {
const schCxt = cxt.subschema({
keyword,
schemaProp: i,
compositeRule: true,
}, schValid);
gen.assign(valid, codegen_1._ `${valid} || ${schValid}`);
const merged = cxt.mergeValidEvaluated(schCxt, schValid);
// can short-circuit if `unevaluatedProperties/Items` not supported (opts.unevaluated !== true)
// or if all properties and items were evaluated (it.props === true && it.items === true)
if (!merged)
gen.if(codegen_1.not(valid));
}));
cxt.result(valid, () => cxt.reset(), () => cxt.error(true));
}
exports.validateUnion = validateUnion;
//# sourceMappingURL=code.js.map

@@ -10,2 +10,3 @@ "use strict";

"$vocabulary",
{ keyword: "$comment" },
"definitions",

@@ -12,0 +13,0 @@ id_1.default,

@@ -41,3 +41,2 @@ "use strict";

schema: sch,
strictSchema: true,
dataTypes: [],

@@ -44,0 +43,0 @@ schemaPath: codegen_1.nil,

@@ -47,3 +47,2 @@ "use strict";

dataPropType: subschema_1.Type.Str,
strictSchema: it.strictSchema,
}, valid);

@@ -50,0 +49,0 @@ if (!allErrors)

@@ -27,2 +27,3 @@ "use strict";

// any
{ keyword: "type", schemaType: ["string", "array"] },
{ keyword: "nullable", schemaType: "boolean" },

@@ -29,0 +30,0 @@ const_1.default,

@@ -254,12 +254,12 @@ # API Reference

strict: true,
strictTypes: "log",
strictTuples: "log",
allowUnionTypes: false,
allowMatchingProperties: false,
validateFormats: true,
strictTypes: "log", // *
strictTuples: "log", // *
allowUnionTypes: false, // *
allowMatchingProperties: false, // *
validateFormats: true, // *
// validation and reporting options:
$data: false,
$data: false, // *
allErrors: false,
verbose: false,
$comment: false,
verbose: false, // *
$comment: false, // *
formats: {},

@@ -269,7 +269,7 @@ keywords: {},

logger: undefined,
loadSchema: undefined, // function(uri: string): Promise {}
loadSchema: undefined, // *, function(uri: string): Promise {}
// options to modify validated data:
removeAdditional: false,
useDefaults: false,
coerceTypes: false,
useDefaults: false, // *
coerceTypes: false, // *
// advanced options:

@@ -281,7 +281,8 @@ meta: true,

passContext: false,
loopRequired: Infinity,
loopRequired: Infinity, // *
loopEnum: Infinity, // NEW
ownProperties: false,
multipleOfPrecision: undefined,
messages: true,
multipleOfPrecision: undefined, // *
messages: true, // false with JTD
ajvErrors: false // only with JTD
code: {

@@ -298,2 +299,4 @@ // NEW

<sup>\*</sup> these options are not supported with JSON Type Definition schemas
#### Strict mode options (NEW in v7)

@@ -370,2 +373,3 @@

- _messages_: Include human-readable messages in errors. `true` by default. `false` can be passed when messages are generated outside of Ajv code (e.g. with [ajv-i18n](https://github.com/ajv-validator/ajv-i18n)).
- _ajvErrors_: this option is only supported with JTD schemas to generate error objects with the properties described in the first part of [Validation errors](#validation-errors) section, otherwise JTD errors are generated when JTD schemas are used (see the second part of [the same section](#validation-errors)).
- _code_ (new in v7): code generation options:

@@ -401,3 +405,3 @@

Each error is an object with the following properties:
Each error reported when validating against JSON Schema (also when validating against JTD schema with option `ajvErrors`) is an object with the following properties:

@@ -422,2 +426,15 @@ ```typescript

[JTD specification](./json-type-definition.md) defines strict format for validation errors, where each error is an object with the following properties:
```typescript
interface JTDErrorObject {
instancePath: string // JSON Pointer to the location in the data instance
schemaPath: string // JSON Pointer to the location in the schema
}
```
This error format is used when using JTD schemas. To simplify usage, you may still generate Ajv error objects using `ajvErrors` option. You can also add a human-readable error message to error objects using option `messages`.
**Please note**: Ajv is not fully consistent with JTD regarding the error objects in some scenarios - it will be consistent by the time Ajv version 8 is released. Therefore it is not recommended yet to use error objects for any advanced application logic.
### Error parameters

@@ -424,0 +441,0 @@

@@ -1,23 +0,42 @@

## Strict mode
# Strict mode
Strict mode intends to prevent any unexpected behaviours or silently ignored mistakes in user schemas. It does not change any validation results compared with JSON Schema specification, but it makes some schemas invalid and throws exception or logs warning (with `strict: "log"` option) in case any restriction is violated.
Strict mode intends to prevent any unexpected behaviours or silently ignored mistakes in user schemas. It does not change any validation results compared with the specification, but it makes some schemas invalid and throws exception or logs warning (with `strict: "log"` option) in case any restriction is violated.
To disable all strict mode restrictions use option `strict: false`. Some of the restrictions can be changed with their own options
- [Prohibit ignored keywords](#prohibit-ignored-keywords)
- unknown keywords
- ignored "additionalItems" keyword
- ignored "if", "then", "else" keywords
- ignored "contains", "maxContains" and "minContains" keywords
- unknown formats
- ignored defaults
- [Prevent unexpected validation](#prevent-unexpected-validation)
- overlap between "properties" and "patternProperties" keywords (also `allowMatchingProperties` option)
- unconstrained tuples (also `strictTuples` option)
- [Strict types](#strict-types) (also `strictTypes` option)
- union types (also `allowUnionTypes` option)
- contradictory types
- require applicable types
- [Strict number validation](#strict-number-validation)
- [JSON Type Definition schemas](#json-type-definition-schemas)
- [JSON Schema schemas](#json-schema-schemas)
- [Prohibit ignored keywords](#prohibit-ignored-keywords)
- unknown keywords
- ignored "additionalItems" keyword
- ignored "if", "then", "else" keywords
- ignored "contains", "maxContains" and "minContains" keywords
- unknown formats
- ignored defaults
- [Prevent unexpected validation](#prevent-unexpected-validation)
- overlap between "properties" and "patternProperties" keywords (also `allowMatchingProperties` option)
- unconstrained tuples (also `strictTuples` option)
- [Strict types](#strict-types) (also `strictTypes` option)
- union types (also `allowUnionTypes` option)
- contradictory types
- require applicable types
- [Strict number validation](#strict-number-validation)
## JSON Type Definition schemas
JTD specification is strict - whether Ajv strict mode is enabled or not it will not allow schemas with ignored or ambiguous elements, including:
- unknown schema keywords
- combining multiple schema forms in one schema
- defining the same property as both required and optional
- re-defining discriminator tag inside properties, even if the definition is non-contradictory
See [JSON Type Definition](./json-type-definition.md) for informal and [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) for formal specification descriptions.
The only change that strict mode introduces to JTD schemas, without changing their syntax or semantics, is the requirement that all members that are present in optional `metadata` members are defined as Ajv keywords. This restriction can be disabled with `strict: false` option, without any impact to other JTD features.
## JSON Schema schemas
JSON Schema specification is very permissive and allows many elements in the schema to be quietly ignored or be ambiguous. It is recommended to use JSON Schema with strict mode.
### Prohibit ignored keywords

@@ -24,0 +43,0 @@

@@ -104,3 +104,3 @@ # Data validation

- `contentEncoding`: [RFC 2045](https://tools.ietf.org/html/rfc2045#section-6.1), e.g., "base64".
- `contentMediaType`: [RFC 2046](https://tools.ietf.org/html/rfc2046), e.g., "image/png".
- `contentMediaType`: [RFC 2046](https://datatracker.ietf.org/doc/rfc2046/), e.g., "image/png".

@@ -137,3 +137,3 @@ **Please note**: Ajv does not implement validation of the keywords `examples`, `contentEncoding` and `contentMediaType` but it reserves them. If you want to create a plugin that implements any of them, it should remove these keywords from the instance.

- _uri-reference_: URI reference, including full and relative URIs.
- _uri-template_: URI template according to [RFC6570](https://tools.ietf.org/html/rfc6570)
- _uri-template_: URI template according to [RFC6570](https://datatracker.ietf.org/doc/rfc6570/)
- _url_ (deprecated): [URL record](https://url.spec.whatwg.org/#concept-url).

@@ -145,4 +145,4 @@ - _email_: email address.

- _regex_: tests whether a string is a valid regular expression by passing it to RegExp constructor.
- _uuid_: Universally Unique IDentifier according to [RFC4122](http://tools.ietf.org/html/rfc4122).
- _json-pointer_: JSON-pointer according to [RFC6901](https://tools.ietf.org/html/rfc6901).
- _uuid_: Universally Unique Identifier according to [RFC4122](https://datatracker.ietf.org/doc/rfc4122/).
- _json-pointer_: JSON-pointer according to [RFC6901](https://datatracker.ietf.org/doc/rfc6901/).
- _relative-json-pointer_: relative JSON-pointer according to [this draft](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00).

@@ -281,3 +281,3 @@

The value of "$data" should be a [JSON-pointer](https://tools.ietf.org/html/rfc6901) to the data (the root is always the top level data object, even if the $data reference is inside a referenced subschema) or a [relative JSON-pointer](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00) (it is relative to the current point in data; if the \$data reference is inside a referenced subschema it cannot point to the data outside of the root level for this subschema).
The value of "$data" should be a [JSON-pointer](https://datatracker.ietf.org/doc/rfc6901/) to the data (the root is always the top level data object, even if the $data reference is inside a referenced subschema) or a [relative JSON-pointer](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00) (it is relative to the current point in data; if the \$data reference is inside a referenced subschema it cannot point to the data outside of the root level for this subschema).

@@ -329,3 +329,3 @@ Examples.

With the package [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) you can use the keywords `$merge` and `$patch` that allow extending JSON Schemas with patches using formats [JSON Merge Patch (RFC 7396)](https://tools.ietf.org/html/rfc7396) and [JSON Patch (RFC 6902)](https://tools.ietf.org/html/rfc6902).
With the package [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) you can use the keywords `$merge` and `$patch` that allow extending JSON Schemas with patches using formats [JSON Merge Patch (RFC 7396)](https://datatracker.ietf.org/doc/rfc7396/) and [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/rfc6902/).

@@ -332,0 +332,0 @@ To add keywords `$merge` and `$patch` to Ajv instance use this code:

@@ -48,5 +48,4 @@ export {

super._addDefaultMetaSchema()
const {$data, meta} = this.opts
if (!meta) return
const metaSchema = $data
if (!this.opts.meta) return
const metaSchema = this.opts.$data
? this.$dataMetaSchema(draft7MetaSchema, META_SUPPORT_DATA)

@@ -53,0 +52,0 @@ : draft7MetaSchema

@@ -11,9 +11,3 @@ import type {

import {schemaRefOrVal, unescapeJsonPointer, mergeEvaluated} from "./util"
import {
reportError,
reportExtraError,
resetErrorsCount,
keywordError,
keyword$DataError,
} from "./errors"
import {reportError, reportExtraError, resetErrorsCount, keyword$DataError} from "./errors"
import {CodeGen, _, nil, or, not, getProperty, Code, Name} from "./codegen"

@@ -29,3 +23,3 @@ import N from "./names"

readonly $data?: string | false
readonly schema: any // keyword value in the schema
schema: any // keyword value in the schema
readonly schemaValue: Code | number | boolean // Code reference to keyword schema value or primitive value

@@ -38,3 +32,3 @@ readonly schemaCode: Code | number | boolean // Code reference to resolved schema value (different if schema is $data)

params: KeywordCxtParams // object to pass parameters to error messages from keyword code
readonly it: SchemaObjCxt // schema compilation context (schema is guaranted to be an object, not boolean)
readonly it: SchemaObjCxt // schema compilation context (schema is guaranteed to be an object, not boolean)
readonly def: AddedKeywordDefinition

@@ -108,3 +102,3 @@

error(append?: true): void {
;(append ? reportExtraError : reportError)(this, this.def.error || keywordError)
;(append ? reportExtraError : reportError)(this, this.def.error)
}

@@ -111,0 +105,0 @@

@@ -20,4 +20,4 @@ import type {ErrorObject} from "../types"

constructor(baseId: string, ref: string) {
super(`can't resolve reference ${ref} from id ${baseId}`)
constructor(baseId: string, ref: string, msg?: string) {
super(msg || `can't resolve reference ${ref} from id ${baseId}`)
this.missingRef = resolveUrl(baseId, ref)

@@ -24,0 +24,0 @@ this.missingSchema = normalizeId(getFullPath(this.missingRef))

@@ -20,3 +20,3 @@ import type {KeywordErrorCxt, KeywordErrorDefinition} from "../types"

cxt: KeywordErrorCxt,
error: KeywordErrorDefinition,
error: KeywordErrorDefinition = keywordError,
overrideAllErrors?: boolean

@@ -34,3 +34,6 @@ ): void {

export function reportExtraError(cxt: KeywordErrorCxt, error: KeywordErrorDefinition): void {
export function reportExtraError(
cxt: KeywordErrorCxt,
error: KeywordErrorDefinition = keywordError
): void {
const {it} = cxt

@@ -102,3 +105,3 @@ const {gen, compositeRule, allErrors} = it

keyword: new Name("keyword"),
schemaPath: new Name("schemaPath"),
schemaPath: new Name("schemaPath"), // also used in JTD errors
params: new Name("params"),

@@ -109,12 +112,28 @@ propertyName: new Name("propertyName"),

parentSchema: new Name("parentSchema"),
// JTD error properties
instancePath: new Name("instancePath"),
}
function errorObjectCode(cxt: KeywordErrorCxt, error: KeywordErrorDefinition): Code {
const {
keyword,
data,
schemaValue,
it: {gen, createErrors, topSchemaRef, schemaPath, errorPath, errSchemaPath, propertyName, opts},
} = cxt
const {createErrors, opts} = cxt.it
if (createErrors === false) return _`{}`
return (opts.jtd && !opts.ajvErrors ? jtdErrorObject : ajvErrorObject)(cxt, error)
}
function jtdErrorObject(cxt: KeywordErrorCxt, {message}: KeywordErrorDefinition): Code {
const {gen, keyword, it} = cxt
const {errorPath, errSchemaPath, opts} = it
const keyValues: [Name, SafeExpr | string][] = [
[E.instancePath, strConcat(N.dataPath, errorPath)],
[E.schemaPath, str`${errSchemaPath}/${keyword}`],
]
if (opts.messages) {
keyValues.push([E.message, typeof message == "function" ? message(cxt) : message])
}
return gen.object(...keyValues)
}
function ajvErrorObject(cxt: KeywordErrorCxt, error: KeywordErrorDefinition): Code {
const {gen, keyword, data, schemaValue, it} = cxt
const {topSchemaRef, schemaPath, errorPath, errSchemaPath, propertyName, opts} = it
const {params, message} = error

@@ -128,5 +147,4 @@ const keyValues: [Name, SafeExpr | string][] = [

if (propertyName) keyValues.push([E.propertyName, propertyName])
if (opts.messages !== false) {
const msg = typeof message == "function" ? message(cxt) : message
keyValues.push([E.message, msg])
if (opts.messages) {
keyValues.push([E.message, typeof message == "function" ? message(cxt) : message])
}

@@ -133,0 +151,0 @@ if (opts.verbose) {

@@ -41,3 +41,2 @@ import type {

readonly schemaEnv: SchemaEnv
readonly strictSchema?: boolean
readonly rootId: string

@@ -55,2 +54,4 @@ baseId: string // the current schema base URI that should be used as the base for resolving URIs in references (\$ref)

items?: EvaluatedItems | Name // last item evaluated by this schema - used by parent schema or assigned to validation function
jtdDiscriminator?: string
jtdMetadata?: boolean
readonly createErrors?: boolean

@@ -140,7 +141,6 @@ readonly opts: InstanceOptions // Ajv instance option.

schemaEnv: sch,
strictSchema: true,
rootId,
baseId: sch.baseId || rootId,
schemaPath: nil,
errSchemaPath: "#",
errSchemaPath: this.opts.jtd ? "" : "#",
errorPath: _`""`,

@@ -147,0 +147,0 @@ opts: this.opts,

@@ -47,5 +47,5 @@ import type {AddedKeywordDefinition} from "../types"

post: {rules: []},
all: {type: true, $comment: true},
keywords: {type: true, $comment: true},
all: {},
keywords: {},
}
}

@@ -11,3 +11,2 @@ import type {AnySchema} from "../types"

schema: AnySchema
strictSchema?: boolean
schemaPath: Code

@@ -25,2 +24,4 @@ errSchemaPath: string

propertyName?: Name
jtdDiscriminator?: string
jtdMetadata?: boolean
compositeRule?: true

@@ -40,3 +41,2 @@ createErrors?: boolean

schema: AnySchema
strictSchema: boolean
schemaPath: Code

@@ -50,2 +50,4 @@ errSchemaPath: string

dataPropType: Type
jtdDiscriminator: string
jtdMetadata: boolean
compositeRule: true

@@ -67,11 +69,3 @@ createErrors: boolean

it: SchemaObjCxt,
{
keyword,
schemaProp,
schema,
strictSchema,
schemaPath,
errSchemaPath,
topSchemaRef,
}: SubschemaArgs
{keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef}: SubschemaArgs
): SubschemaContext {

@@ -103,3 +97,2 @@ if (keyword !== undefined && schema !== undefined) {

schema,
strictSchema,
schemaPath,

@@ -154,3 +147,3 @@ topSchemaRef,

subschema: SubschemaContext,
{compositeRule, createErrors, allErrors, strictSchema}: SubschemaArgs
{jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors}: SubschemaArgs
): void {

@@ -160,3 +153,4 @@ if (compositeRule !== undefined) subschema.compositeRule = compositeRule

if (allErrors !== undefined) subschema.allErrors = allErrors
subschema.strictSchema = strictSchema // not inherited
subschema.jtdDiscriminator = jtdDiscriminator // not inherited
subschema.jtdMetadata = jtdMetadata // not inherited
}

@@ -163,0 +157,0 @@

@@ -134,2 +134,3 @@ import type {AnySchema} from "../../types"

function typeAndKeywords(it: SchemaObjCxt, errsCount?: Name): void {
if (it.opts.jtd) return schemaKeywords(it, [], false, errsCount)
const types = getSchemaTypes(it.schema)

@@ -136,0 +137,0 @@ const checkedTypes = coerceAndCheckDataType(it, types)

@@ -24,3 +24,3 @@ import type {SchemaObjCxt} from ".."

}
checkStrictTypes(it, types)
if (!opts.jtd) checkStrictTypes(it, types)
gen.block(() => {

@@ -27,0 +27,0 @@ for (const group of RULES.rules) groupKeywords(group)

@@ -80,3 +80,3 @@ export {

interface CurrentOptions {
export interface CurrentOptions {
// strict mode options (NEW)

@@ -109,2 +109,3 @@ strict?: boolean | "log"

dynamicRef?: boolean // NEW
jtd?: boolean // NEW
meta?: SchemaObject | boolean

@@ -122,2 +123,3 @@ defaultMeta?: string | AnySchemaObject

code?: CodeOptions // NEW
ajvErrors?: boolean
}

@@ -632,4 +634,5 @@

): SchemaEnv {
if (typeof schema != "object" && typeof schema != "boolean") {
throw new Error("schema must be object or boolean")
if (typeof schema != "object") {
if (this.opts.jtd) throw new Error("schema must be object")
else if (typeof schema != "boolean") throw new Error("schema must be object or boolean")
}

@@ -746,3 +749,3 @@ let sch = this._cache.get(schema)

const KEYWORD_NAME = /^[a-z_$][a-z0-9_$-]*$/i
const KEYWORD_NAME = /^[a-z_$][a-z0-9_$-:]*$/i

@@ -749,0 +752,0 @@ function checkKeyword(this: Ajv, keyword: string | string[], def?: KeywordDefinition): void {

@@ -79,2 +79,3 @@ import type {CodeGen, Code, Name, ScopeValueSets, ValueScopeName} from "../compile/codegen"

dataPath: string
instancePath?: string
schemaPath: string

@@ -81,0 +82,0 @@ params: P

@@ -55,4 +55,9 @@ import type {

// TODO maybe an option instead of hard-coded 8?
const hasProp = gen.scopeValue("func", {
// eslint-disable-next-line @typescript-eslint/unbound-method
ref: Object.prototype.hasOwnProperty,
code: _`Object.prototype.hasOwnProperty`,
})
const propsSchema = schemaRefOrVal(it, parentSchema.properties, "properties")
definedProp = _`${propsSchema}.hasOwnProperty(${key})`
definedProp = _`${hasProp}.call(${propsSchema}, ${key})`
} else if (props.length) {

@@ -66,3 +71,3 @@ definedProp = or(...props.map((p) => _`${key} === ${p}`))

}
return _`!(${definedProp})`
return not(definedProp)
}

@@ -107,3 +112,2 @@

dataPropType: Type.Str,
strictSchema: it.strictSchema,
}

@@ -110,0 +114,0 @@ if (errors === false) {

import type {CodeKeywordDefinition, ErrorNoParams, AnySchema} from "../../types"
import type KeywordCxt from "../../compile/context"
import {_, not} from "../../compile/codegen"
import {alwaysValidSchema} from "../../compile/util"
import {validateUnion} from "../code"

@@ -12,36 +10,3 @@ export type AnyOfError = ErrorNoParams<"anyOf", AnySchema[]>

trackErrors: true,
code(cxt: KeywordCxt) {
const {gen, schema, it} = cxt
/* istanbul ignore if */
if (!Array.isArray(schema)) throw new Error("ajv implementation error")
const alwaysValid = schema.some((sch: AnySchema) => alwaysValidSchema(it, sch))
if (alwaysValid && !it.opts.unevaluated) return
const valid = gen.let("valid", false)
const schValid = gen.name("_valid")
gen.block(() =>
schema.forEach((_sch: AnySchema, i: number) => {
const schCxt = cxt.subschema(
{
keyword: "anyOf",
schemaProp: i,
compositeRule: true,
},
schValid
)
gen.assign(valid, _`${valid} || ${schValid}`)
const merged = cxt.mergeValidEvaluated(schCxt, schValid)
// can short-circuit if `unevaluatedProperties/Items` not supported (opts.unevaluated !== true)
// or if all properties and items were evaluated (it.props === true && it.items === true)
if (!merged) gen.if(not(valid))
})
)
cxt.result(
valid,
() => cxt.reset(),
() => cxt.error(true)
)
},
code: validateUnion,
error: {

@@ -48,0 +13,0 @@ message: "should match some schema in anyOf",

import type {CodeKeywordDefinition, AnySchema} from "../../types"
import type KeywordCxt from "../../compile/context"
import {_, not} from "../../compile/codegen"
import {Type} from "../../compile/subschema"
import {_} from "../../compile/codegen"
import {alwaysValidSchema, mergeEvaluated} from "../../compile/util"
import {checkStrictMode} from "../../compile/validate"
import {validateArray} from "../code"

@@ -14,4 +14,3 @@ const def: CodeKeywordDefinition = {

code(cxt: KeywordCxt) {
const {gen, schema, parentSchema, data, it} = cxt
const len = gen.const("len", _`${data}.length`)
const {gen, schema, it} = cxt
if (Array.isArray(schema)) {

@@ -24,7 +23,9 @@ if (it.opts.unevaluated && schema.length && it.items !== true) {

it.items = true
if (!alwaysValidSchema(it, schema)) validateArray()
if (alwaysValidSchema(it, schema)) return
cxt.ok(validateArray(cxt))
}
function validateTuple(schArr: AnySchema[]): void {
if (it.opts.strictTuples && !fullTupleSchema(schema.length, parentSchema)) {
const {parentSchema, data} = cxt
if (it.opts.strictTuples && !fullTupleSchema(schArr.length, parentSchema)) {
const msg = `"items" is ${schArr.length}-tuple, but minItems or maxItems/additionalItems are not specified or different`

@@ -34,2 +35,3 @@ checkStrictMode(it, msg, it.opts.strictTuples)

const valid = gen.name("valid")
const len = gen.const("len", _`${data}.length`)
schArr.forEach((sch: AnySchema, i: number) => {

@@ -43,3 +45,2 @@ if (alwaysValidSchema(it, sch)) return

dataProp: i,
strictSchema: it.strictSchema,
},

@@ -52,19 +53,2 @@ valid

}
function validateArray(): void {
const valid = gen.name("valid")
gen.forRange("i", 0, len, (i) => {
cxt.subschema(
{
keyword: "items",
dataProp: i,
dataPropType: Type.Num,
strictSchema: it.strictSchema,
},
valid
)
if (!it.allErrors) gen.if(not(valid), () => gen.break())
})
cxt.ok(valid)
}
},

@@ -71,0 +55,0 @@ }

@@ -60,3 +60,2 @@ import type {CodeKeywordDefinition} from "../../types"

dataPropType: Type.Str,
strictSchema: it.strictSchema,
},

@@ -63,0 +62,0 @@ valid

@@ -46,3 +46,2 @@ import type {CodeKeywordDefinition} from "../../types"

dataProp: prop,
strictSchema: it.strictSchema,
},

@@ -49,0 +48,0 @@ valid

@@ -37,3 +37,2 @@ import type {

compositeRule: true,
strictSchema: it.strictSchema,
},

@@ -40,0 +39,0 @@ valid

import type {AnySchema, SchemaMap} from "../types"
import type {SchemaCxt} from "../compile"
import type KeywordCxt from "../compile/context"
import {CodeGen, _, or, nil, strConcat, getProperty, Code, Name} from "../compile/codegen"
import {CodeGen, _, or, not, nil, strConcat, getProperty, Code, Name} from "../compile/codegen"
import {alwaysValidSchema} from "../compile/util"
import {Type} from "../compile/subschema"
import N from "../compile/names"

@@ -86,1 +87,64 @@

}
export function validateArray(cxt: KeywordCxt): Name {
const {gen, data, keyword, it} = cxt
const valid = gen.name("valid")
if (it.allErrors) {
const validArr = gen.let("valid", true)
validateItems(() => gen.assign(validArr, false))
return validArr
}
gen.var(valid, true)
validateItems(() => gen.break())
return valid
function validateItems(notValid: () => void): void {
const len = gen.const("len", _`${data}.length`)
gen.forRange("i", 0, len, (i) => {
cxt.subschema(
{
keyword,
dataProp: i,
dataPropType: Type.Num,
},
valid
)
gen.if(not(valid), notValid)
})
}
}
export function validateUnion(cxt: KeywordCxt): void {
const {gen, schema, keyword, it} = cxt
/* istanbul ignore if */
if (!Array.isArray(schema)) throw new Error("ajv implementation error")
const alwaysValid = schema.some((sch: AnySchema) => alwaysValidSchema(it, sch))
if (alwaysValid && !it.opts.unevaluated) return
const valid = gen.let("valid", false)
const schValid = gen.name("_valid")
gen.block(() =>
schema.forEach((_sch: AnySchema, i: number) => {
const schCxt = cxt.subschema(
{
keyword,
schemaProp: i,
compositeRule: true,
},
schValid
)
gen.assign(valid, _`${valid} || ${schValid}`)
const merged = cxt.mergeValidEvaluated(schCxt, schValid)
// can short-circuit if `unevaluatedProperties/Items` not supported (opts.unevaluated !== true)
// or if all properties and items were evaluated (it.props === true && it.items === true)
if (!merged) gen.if(not(valid))
})
)
cxt.result(
valid,
() => cxt.reset(),
() => cxt.error(true)
)
}

@@ -10,2 +10,3 @@ import type {Vocabulary} from "../../types"

"$vocabulary",
{keyword: "$comment"},
"definitions",

@@ -12,0 +13,0 @@ idKeyword,

@@ -13,3 +13,3 @@ import type {CodeKeywordDefinition, AnySchema} from "../../types"

schemaType: "string",
code(cxt: KeywordCxt) {
code(cxt: KeywordCxt): void {
const {gen, schema, it} = cxt

@@ -45,3 +45,2 @@ const {baseId, schemaEnv: env, validateName, opts, self} = it

schema: sch,
strictSchema: true,
dataTypes: [],

@@ -48,0 +47,0 @@ schemaPath: nil,

@@ -65,3 +65,2 @@ import type {

dataPropType: Type.Str,
strictSchema: it.strictSchema,
},

@@ -68,0 +67,0 @@ valid

@@ -27,2 +27,3 @@ import type {ErrorObject, Vocabulary} from "../../types"

// any
{keyword: "type", schemaType: ["string", "array"]},
{keyword: "nullable", schemaType: "boolean"},

@@ -29,0 +30,0 @@ constKeyword,

{
"name": "ajv",
"version": "7.0.4",
"version": "7.1.0",
"description": "Another JSON Schema Validator",

@@ -22,4 +22,4 @@ "main": "dist/ajv.js",

"test-cov": "nyc npm run test-spec",
"bundle": "rm -rf bundle && node ./scripts/bundle.js ajv ajv7 ajv7 && node ./scripts/bundle.js 2019 ajv2019 ajv2019",
"build": "rm -rf dist && tsc && cp -r lib/refs dist && rm dist/refs/json-schema-2019-09/index.ts",
"bundle": "rm -rf bundle && node ./scripts/bundle.js ajv ajv7 ajv7 && node ./scripts/bundle.js 2019 ajv2019 ajv2019 && node ./scripts/bundle.js jtd ajvJTD ajvJTD",
"build": "rm -rf dist && tsc && cp -r lib/refs dist && rm dist/refs/json-schema-2019-09/index.ts && rm dist/refs/jtd-schema.ts",
"json-tests": "rm -rf spec/_json/*.js && node scripts/jsontests",

@@ -85,3 +85,3 @@ "test-karma": "karma start",

"glob": "^7.0.0",
"husky": "^4.2.5",
"husky": "^5.0.9",
"if-node-version": "^1.0.0",

@@ -88,0 +88,0 @@ "js-beautify": "^1.7.3",

<img align="right" alt="Ajv logo" width="160" src="https://ajv.js.org/images/ajv_logo.png">
# Ajv: Another JSON Schema Validator
# Ajv: Another JSON schema validator
The fastest JSON Schema validator for Node.js and browser. Supports draft-06/07/2019-09 (draft-04 is supported in [version 6](https://github.com/ajv-validator/ajv/tree/v6)).
The fastest JSON schema validator for Node.js and browser. Supports JSON Schema draft-06/07/2019-09 (draft-04 is supported in [version 6](https://github.com/ajv-validator/ajv/tree/v6)) and JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/).

@@ -20,5 +20,6 @@ [![build](https://github.com/ajv-validator/ajv/workflows/build/badge.svg)](https://github.com/ajv-validator/ajv/actions?query=workflow%3Abuild)

Ajv version 7 is released with these changes:
Ajv version 7 has these new features:
- support of JSON Schema draft-2019-09 features: [`unevaluatedProperties`](./docs/json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./docs/json-schema.md#unevaluateditems), [dynamic recursive references](./docs/validation.md#extending-recursive-schemas) and other [additional keywords](./docs/json-schema.md#json-schema-draft-2019-09).
- NEW: support of JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) (from [v7.1.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v7.1.0))
- to reduce the mistakes in JSON schemas and unexpected validation results, [strict mode](./docs/strict-mode.md) is added - it prohibits ignored or ambiguous JSON Schema elements.

@@ -75,2 +76,5 @@ - to make code injection from untrusted schemas impossible, [code generation](./docs/codegen.md) is fully re-written to be safe and to allow code optimization (compiled schema code size is reduced by more than 10%).

- [Getting started](#usage)
- [Choosing schema language](#choosing-schema-language)
- [JSON Schema](#json-schema)
- [JSON Type Definition](#json-type-definition)
- [Frequently Asked Questions](./docs/faq.md)

@@ -171,3 +175,3 @@ - [Using in browser](#using-in-browser)

- Ajv implements full JSON Schema [draft-06/07](http://json-schema.org/) standards (draft-04 is supported in v6):
- Ajv implements JSON Schema [draft-06/07/2019-09](http://json-schema.org/) standards (draft-04 is supported in v6):
- all validation keywords (see [JSON Schema validation keywords](./docs/json-schema.md))

@@ -180,2 +184,6 @@ - keyword "nullable" from [Open API 3 specification](https://swagger.io/docs/specification/data-models/data-types/).

- [validates schemas against meta-schema](./docs/api.md#api-validateschema)
- NEW: supports [JSON Type Definition](https://datatracker.ietf.org/doc/rfc8927/):
- all forms (see [JSON Type Definition schema forms](./docs/json-type-definition.md))
- meta-schema for JTD schemas
- "union" keyword and user-defined keywords (can be used inside "metadata" member of the schema)
- supports [browsers](#using-in-browser) and Node.js 0.10-14.x

@@ -269,2 +277,22 @@ - [asynchronous loading](./docs/validation.md#asynchronous-schema-compilation) of referenced schemas during compilation

With JSON Type Definition schema:
```javascript
const Ajv = require("ajv").default
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
properties: {
foo: {type: "float64"},
},
}
const validate = ajv.compile(schema)
const valid = validate({foo: 1}) // true
if (!valid) console.log(validate.errors)
// Unlike JSON Schema:
const valid1 = validate(1) // false, bot an object
const valid2 = validate({}) // false, foo is required
const valid3 = validate({foo: 1, bar: 2}) // false, bar is additional
```
See [this test](./spec/types/json-schema.spec.ts) for an advanced example, [API reference](./docs/api.md) and [Options](./docs/api.md#options) for more details.

@@ -298,3 +326,3 @@

or to load the bundle that supports JSONSchema draft-2019-09:
To load the bundle that supports JSON Schema draft-2019-09:

@@ -311,2 +339,14 @@ ```html

To load the bundle that supports JSON Type Definition:
```html
<script src="bundle/ajvJTD.min.js"></script>
<script>
;(function () {
const Ajv = window.ajvJTD.default
const ajv = new Ajv()
})()
</script>
```
This bundle can be used with different module systems; it creates global `ajv` (or `ajv2019`) if no module system is found.

@@ -318,2 +358,53 @@

## Choosing schema language
Both JSON Schema and JSON Type Definition are cross-platform specifications with implementations in multiple programming languages that help you define the shape and requirements to your JSON data.
This section compares their pros/cons to help decide which specification fits your application better.
### JSON Schema
- Pros
- Wide specification adoption.
- Used as part of OpenAPI specification.
- Support of complex validation scenarios:
- untagged unions and boolean logic
- conditional schemas and dependencies
- restrictions on the number ranges and the size of strings, arrays and objects
- semantic validation with formats, patterns and content keywords
- distribute strict record definitions across multiple schemas (with unevaluatedProperties)
- Can be effectively used for validation of any JavaScript objects and configuration files.
- Cons
- Defines the collection of restrictions on the data, rather than the shape of the data.
- No standard support for tagged unions.
- Complex and error prone for the new users (Ajv has [strict mode](./docs/strict-mode) to compensate for it, but it is not cross-platform).
- Some parts of specification are difficult to implement, creating the risk of implementations divergence:
- reference resolution model
- unevaluatedProperties/unevaluatedItems
- dynamic recursive references
- Internet draft status (rather than RFC)
See [JSON Schema](./docs/json-schema.md) for the list of defined keywords.
### JSON Type Definition
- Pros:
- Aligned with type systems of many languages - can be used to generate type definitions and efficient parsers and serializers to/from these types.
- Very simple, enforcing the best practices for cross-platform JSON API modelling.
- Simple to implement, ensuring consistency across implementations.
- Defines the shape of JSON data via strictly defined schema forms (rather than the collection of restrictions).
- Effective support for tagged unions.
- Designed to protect against user mistakes.
- Approved as [RFC8927](https://datatracker.ietf.org/doc/rfc8927/)
- Cons:
- Limited, compared with JSON Schema - no support for untagged unions<sup>\*</sup>, conditionals, references between different schema files<sup>\*\*</sup>, etc.
- No meta-schema in the specification<sup>\*</sup>.
- Brand new - limited industry adoption (as of January 2021).
<sup>\*</sup> Ajv defines meta-schema for JTD schemas and non-standard keyword "union" that can be used inside "metadata" object.
<sup>\*\*</sup> You can still combine schemas from multiple files in the application code.
See [JSON Type Definition](./docs/json-type-definition.md) for the list of defined schema forms.
## Using in ES5 environment

@@ -345,3 +436,3 @@

- all Ajv options
- reporting changes in data after validation in [JSON-patch](https://tools.ietf.org/html/rfc6902) format
- reporting changes in data after validation in [JSON-patch](https://datatracker.ietf.org/doc/rfc6902/) format

@@ -348,0 +439,0 @@ ## Extending Ajv

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc