Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ajv

Package Overview
Dependencies
Maintainers
2
Versions
355
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ajv - npm Package Compare versions

Comparing version 7.0.0-beta.3 to 7.0.0-beta.4

dist/refs/json-schema-2019-09/index.d.ts

8

dist/ajv.d.ts

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

validateFormats?: boolean;
next?: boolean;
unevaluated?: boolean;
draft2019?: boolean;
$data?: boolean;

@@ -35,3 +34,3 @@ allErrors?: boolean;

formats?: {
[name: string]: Format;
[Name in string]?: Format;
};

@@ -47,2 +46,5 @@ keywords?: Vocabulary;

coerceTypes?: boolean | "array";
next?: boolean;
unevaluated?: boolean;
dynamicRef?: boolean;
meta?: SchemaObject | boolean;

@@ -49,0 +51,0 @@ defaultMeta?: string | AnySchemaObject;

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

const unevaluated_1 = __importDefault(require("./vocabularies/unevaluated"));
const dynamic_1 = __importDefault(require("./vocabularies/dynamic"));
const util_1 = require("./compile/util");

@@ -94,2 +95,3 @@ const data_json_1 = __importDefault(require("./refs/data.json"));

constructor(opts = {}) {
var _a, _b, _c;
// shared external scope values for compiled functions

@@ -107,2 +109,7 @@ this.scope = new codegen_2.ValueScope({ scope: {}, prefixes: EXT_SCOPE_NAMES });

};
if (opts.draft2019) {
(_a = opts.next) !== null && _a !== void 0 ? _a : (opts.next = true);
(_b = opts.unevaluated) !== null && _b !== void 0 ? _b : (opts.unevaluated = true);
(_c = opts.dynamicRef) !== null && _c !== void 0 ? _c : (opts.dynamicRef = true);
}
this.logger = getLogger(opts.logger);

@@ -118,2 +125,4 @@ const formatOpt = opts.validateFormats;

this.addVocabulary(["$async"]);
if (opts.dynamicRef)
this.addVocabulary(dynamic_1.default);
this.addVocabulary(core_1.default);

@@ -525,3 +534,4 @@ this.addVocabulary(validation_1.default);

const format = this.opts.formats[name];
this.addFormat(name, format);
if (format)
this.addFormat(name, format);
}

@@ -528,0 +538,0 @@ }

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

baseId: string;
dynamicAnchors: {
[Ref in string]?: true;
};
readonly schemaPath: Code;

@@ -64,5 +67,5 @@ readonly errSchemaPath: string;

export declare function compileSchema(this: Ajv, sch: SchemaEnv): SchemaEnv;
export declare function resolveRef(this: Ajv, root: SchemaEnv, baseId: string, ref: string): AnySchema | AnyValidateFunction | SchemaEnv | undefined;
export declare function resolveRef(this: Ajv, root: SchemaEnv, baseId: string, ref: string): AnySchema | SchemaEnv | undefined;
export declare function resolveSchema(this: Ajv, root: SchemaEnv, // root object with properties schema, refs TODO below SchemaEnv is assigned to it
ref: string): SchemaEnv | undefined;
export {};

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

baseId: sch.baseId || rootId,
dynamicAnchors: {},
schemaPath: codegen_1.nil,

@@ -72,0 +73,0 @@ errSchemaPath: "#",

import { Name } from "./codegen";
declare const names: {
data: Name;
dataCxt: Name;
valCxt: Name;
dataPath: Name;

@@ -9,2 +9,3 @@ parentData: Name;

rootData: Name;
dynamicAnchors: Name;
vErrors: Name;

@@ -11,0 +12,0 @@ errors: Name;

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

// args passed from referencing schema
dataCxt: new codegen_1.Name("dataCxt"),
valCxt: new codegen_1.Name("valCxt"),
dataPath: new codegen_1.Name("dataPath"),

@@ -14,2 +14,3 @@ parentData: new codegen_1.Name("parentData"),

rootData: new codegen_1.Name("rootData"),
dynamicAnchors: new codegen_1.Name("dynamicAnchors"),
// function scoped variables

@@ -16,0 +17,0 @@ vErrors: new codegen_1.Name("vErrors"),

@@ -40,5 +40,12 @@ "use strict";

exports.inlineRef = inlineRef;
const REF_KEYWORDS = new Set([
"$ref",
"$recursiveRef",
"$recursiveAnchor",
"$dynamicRef",
"$dynamicAnchor",
]);
function hasRef(schema) {
for (const key in schema) {
if (key === "$ref")
if (REF_KEYWORDS.has(key))
return true;

@@ -90,2 +97,3 @@ const sch = schema[key];

exports.resolveUrl = resolveUrl;
const ANCHOR = /^[a-z_][-a-z0-9._]*$/i;
function getSchemaRefs(schema) {

@@ -98,2 +106,3 @@ if (typeof schema == "boolean")

const localRefs = {};
const schemaRefs = new Set();
json_schema_traverse_1.default(schema, { allKeys: true }, (sch, jsonPtr, _, parentJsonPtr) => {

@@ -103,32 +112,48 @@ if (parentJsonPtr === undefined)

const fullPath = pathPrefix + jsonPtr;
let id = sch.$id;
let baseId = baseIds[parentJsonPtr];
if (typeof id == "string") {
id = baseId = normalizeId(baseId ? URI.resolve(baseId, id) : id);
let schOrRef = this.refs[id];
if (typeof sch.$id == "string")
baseId = addRef.call(this, sch.$id);
addAnchor.call(this, sch.$anchor);
addAnchor.call(this, sch.$dynamicAnchor);
baseIds[jsonPtr] = baseId;
function addRef(ref) {
ref = normalizeId(baseId ? URI.resolve(baseId, ref) : ref);
if (schemaRefs.has(ref))
throw ambiguos(ref);
schemaRefs.add(ref);
let schOrRef = this.refs[ref];
if (typeof schOrRef == "string")
schOrRef = this.refs[schOrRef];
if (typeof schOrRef == "object") {
checkAmbiguosId(sch, schOrRef.schema, id);
checkAmbiguosRef(sch, schOrRef.schema, ref);
}
else if (id !== normalizeId(fullPath)) {
if (id[0] === "#") {
checkAmbiguosId(sch, localRefs[id], id);
localRefs[id] = sch;
else if (ref !== normalizeId(fullPath)) {
if (ref[0] === "#") {
checkAmbiguosRef(sch, localRefs[ref], ref);
localRefs[ref] = sch;
}
else {
this.refs[id] = fullPath;
this.refs[ref] = fullPath;
}
}
return ref;
}
baseIds[jsonPtr] = baseId;
function addAnchor(anchor) {
if (typeof anchor == "string") {
if (!ANCHOR.test(anchor))
throw new Error(`invalid anchor "${anchor}"`);
addRef.call(this, `#${anchor}`);
}
}
});
return localRefs;
function checkAmbiguosId(sch1, sch2, id) {
if (sch2 !== undefined && !fast_deep_equal_1.default(sch1, sch2)) {
throw new Error(`id "${id}" resolves to more than one schema`);
}
function checkAmbiguosRef(sch1, sch2, ref) {
if (sch2 !== undefined && !fast_deep_equal_1.default(sch1, sch2))
throw ambiguos(ref);
}
function ambiguos(ref) {
return new Error(`reference "${ref}" resolves to more than one schema`);
}
}
exports.getSchemaRefs = getSchemaRefs;
//# sourceMappingURL=resolve.js.map

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

types: { ...groups, integer: true, boolean: true, null: true },
rules: [groups.number, groups.string, { rules: [] }, groups.array, groups.object],
rules: [{ rules: [] }, groups.number, groups.string, groups.array, groups.object],
all: { type: true, $comment: true },

@@ -22,0 +22,0 @@ keywords: { type: true, $comment: true },

@@ -28,15 +28,20 @@ "use strict";

gen.return(() => opts.code.es5
? gen.func(validateName, codegen_1._ `${names_1.default.data}, ${names_1.default.dataCxt}`, schemaEnv.$async, () => {
? gen.func(validateName, codegen_1._ `${names_1.default.data}, ${names_1.default.valCxt}`, schemaEnv.$async, () => {
gen.code(codegen_1._ `"use strict"; ${funcSourceUrl(schema, opts)}`);
destructureDataCxtES5(gen);
destructureValCxtES5(gen, opts);
gen.code(body);
})
: gen.func(validateName, codegen_1._ `${names_1.default.data}, {${names_1.default.dataPath}="", ${names_1.default.parentData}, ${names_1.default.parentDataProperty}, ${names_1.default.rootData}=${names_1.default.data}}={}`, schemaEnv.$async, () => gen.code(funcSourceUrl(schema, opts)).code(body)));
: gen.func(validateName, codegen_1._ `${names_1.default.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () => gen.code(funcSourceUrl(schema, opts)).code(body)));
}
function destructureDataCxtES5(gen) {
gen.if(names_1.default.dataCxt, () => {
gen.var(names_1.default.dataPath, codegen_1._ `${names_1.default.dataCxt}.${names_1.default.dataPath}`);
gen.var(names_1.default.parentData, codegen_1._ `${names_1.default.dataCxt}.${names_1.default.parentData}`);
gen.var(names_1.default.parentDataProperty, codegen_1._ `${names_1.default.dataCxt}.${names_1.default.parentDataProperty}`);
gen.var(names_1.default.rootData, codegen_1._ `${names_1.default.dataCxt}.${names_1.default.rootData}`);
function destructureValCxt(opts) {
return codegen_1._ `{${names_1.default.dataPath}="", ${names_1.default.parentData}, ${names_1.default.parentDataProperty}, ${names_1.default.rootData}=${names_1.default.data}${opts.dynamicRef ? codegen_1._ `, ${names_1.default.dynamicAnchors}={}` : codegen_1.nil}}={}`;
}
function destructureValCxtES5(gen, opts) {
gen.if(names_1.default.valCxt, () => {
gen.var(names_1.default.dataPath, codegen_1._ `${names_1.default.valCxt}.${names_1.default.dataPath}`);
gen.var(names_1.default.parentData, codegen_1._ `${names_1.default.valCxt}.${names_1.default.parentData}`);
gen.var(names_1.default.parentDataProperty, codegen_1._ `${names_1.default.valCxt}.${names_1.default.parentDataProperty}`);
gen.var(names_1.default.rootData, codegen_1._ `${names_1.default.valCxt}.${names_1.default.rootData}`);
if (opts.dynamicRef)
gen.var(names_1.default.dynamicAnchors, codegen_1._ `${names_1.default.valCxt}.${names_1.default.dynamicAnchors}`);
}, () => {

@@ -47,2 +52,4 @@ gen.var(names_1.default.dataPath, codegen_1._ `""`);

gen.var(names_1.default.rootData, names_1.default.data);
if (opts.dynamicRef)
gen.var(names_1.default.dynamicAnchors, codegen_1._ `{}`);
});

@@ -49,0 +56,0 @@ }

@@ -35,2 +35,5 @@ import type { CodeGen, Code, Name, Scope } from "../compile/codegen";

rootData: Record<string, any> | any[];
dynamicAnchors: {
[Ref in string]?: ValidateFunction;
};
}

@@ -37,0 +40,0 @@ export interface ValidateFunction<T = unknown> {

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

if (!Array.isArray(items)) {
validate_1.checkStrictMode(it, '"additionalItems" without "items" is ignored');
validate_1.checkStrictMode(it, '"additionalItems" is ignored when "items" is not an array of schemas');
return;

@@ -24,0 +24,0 @@ }

@@ -50,4 +50,11 @@ "use strict";

const dataAndSchema = passSchema ? codegen_1._ `${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data;
const dataCxt = gen.object([names_1.default.dataPath, codegen_1.strConcat(names_1.default.dataPath, errorPath)], [names_1.default.parentData, it.parentData], [names_1.default.parentDataProperty, it.parentDataProperty], [names_1.default.rootData, names_1.default.rootData]);
const args = codegen_1._ `${dataAndSchema}, ${dataCxt}`;
const valCxt = [
[names_1.default.dataPath, codegen_1.strConcat(names_1.default.dataPath, errorPath)],
[names_1.default.parentData, it.parentData],
[names_1.default.parentDataProperty, it.parentDataProperty],
[names_1.default.rootData, names_1.default.rootData],
];
if (it.opts.dynamicRef)
valCxt.push([names_1.default.dynamicAnchors, names_1.default.dynamicAnchors]);
const args = codegen_1._ `${dataAndSchema}, ${gen.object(...valCxt)}`;
return context !== codegen_1.nil ? codegen_1._ `${func}.call(${context}, ${args})` : codegen_1._ `${func}(${args})`;

@@ -54,0 +61,0 @@ }

@@ -8,4 +8,12 @@ "use strict";

const ref_1 = __importDefault(require("./ref"));
const core = ["$schema", "$id", "$defs", "definitions", id_1.default, ref_1.default];
const core = [
"$schema",
"$id",
"$defs",
"$vocabulary",
"definitions",
id_1.default,
ref_1.default,
];
exports.default = core;
//# sourceMappingURL=index.js.map
import type { CodeKeywordDefinition } from "../../types";
import type KeywordCxt from "../../compile/context";
import { Code } from "../../compile/codegen";
import { SchemaEnv } from "../../compile";
declare const def: CodeKeywordDefinition;
export declare function callRef(cxt: KeywordCxt, v: Code, sch?: SchemaEnv, $async?: boolean): void;
export default def;

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

Object.defineProperty(exports, "__esModule", { value: true });
exports.callRef = void 0;
const error_classes_1 = require("../../compile/error_classes");

@@ -18,17 +19,16 @@ const code_1 = require("../code");

const { gen, schema, it } = cxt;
const { allErrors, baseId, schemaEnv: env, opts, validateName, self } = it;
const passCxt = opts.passContext ? names_1.default.this : codegen_1.nil;
const { baseId, schemaEnv: env, validateName, self } = it;
if (schema === "#" || schema === "#/")
return callRootRef();
const schOrFunc = compile_1.resolveRef.call(self, env.root, baseId, schema);
if (schOrFunc === undefined)
const schOrEnv = compile_1.resolveRef.call(self, env.root, baseId, schema);
if (schOrEnv === undefined)
throw new error_classes_1.MissingRefError(baseId, schema);
if (schOrFunc instanceof compile_1.SchemaEnv)
return callValidate(schOrFunc);
return inlineRefSchema(schOrFunc);
if (schOrEnv instanceof compile_1.SchemaEnv)
return callValidate(schOrEnv);
return inlineRefSchema(schOrEnv);
function callRootRef() {
if (env === env.root)
return callRef(validateName, env, env.$async);
return callRef(cxt, validateName, env, env.$async);
const rootName = gen.scopeValue("root", { ref: env.root });
return callRef(codegen_1._ `${rootName}.validate`, env.root, env.root.$async);
return callRef(cxt, codegen_1._ `${rootName}.validate`, env.root, env.root.$async);
}

@@ -45,10 +45,4 @@ function callValidate(sch) {

}
callRef(v, sch, sch.$async);
callRef(cxt, v, sch, sch.$async);
}
function callRef(v, sch, $async) {
if ($async)
callAsyncRef(v, sch);
else
callSyncRef(v, sch);
}
function inlineRefSchema(sch) {

@@ -68,59 +62,69 @@ const schName = gen.scopeValue("schema", { ref: sch });

}
function callAsyncRef(v, sch) {
if (!env.$async)
throw new Error("async schema referenced by sync schema");
const valid = gen.let("valid");
gen.try(() => {
gen.code(codegen_1._ `await ${code_1.callValidateCode(cxt, v, passCxt)}`);
addEvaluatedFrom(v, sch);
if (!allErrors)
gen.assign(valid, true);
}, (e) => {
gen.if(codegen_1._ `!(${e} instanceof ${it.ValidationError})`, () => gen.throw(e));
addErrorsFrom(e);
if (!allErrors)
gen.assign(valid, false);
});
cxt.ok(valid);
}
function callSyncRef(v, sch) {
cxt.result(code_1.callValidateCode(cxt, v, passCxt), () => addEvaluatedFrom(v, sch), () => addErrorsFrom(v));
}
function addErrorsFrom(source) {
const errs = codegen_1._ `${source}.errors`;
gen.assign(names_1.default.vErrors, codegen_1._ `${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`); // TODO tagged
gen.assign(names_1.default.errors, codegen_1._ `${names_1.default.vErrors}.length`);
}
function addEvaluatedFrom(source, sch) {
var _a;
if (!it.opts.unevaluated)
return;
const schEvaluated = (_a = sch.validate) === null || _a === void 0 ? void 0 : _a.evaluated;
// TODO refactor
if (it.props !== true) {
if (schEvaluated && !schEvaluated.dynamicProps) {
if (schEvaluated.props !== undefined) {
it.props = util_1.mergeEvaluated.props(gen, schEvaluated.props, it.props);
}
},
};
function callRef(cxt, v, sch, $async) {
const { gen, it } = cxt;
const { allErrors, schemaEnv: env, opts } = it;
const passCxt = opts.passContext ? names_1.default.this : codegen_1.nil;
if ($async)
callAsyncRef();
else
callSyncRef();
function callAsyncRef() {
if (!env.$async)
throw new Error("async schema referenced by sync schema");
const valid = gen.let("valid");
gen.try(() => {
gen.code(codegen_1._ `await ${code_1.callValidateCode(cxt, v, passCxt)}`);
addEvaluatedFrom(v); // TODO will not work with async, it has to be returned with the result
if (!allErrors)
gen.assign(valid, true);
}, (e) => {
gen.if(codegen_1._ `!(${e} instanceof ${it.ValidationError})`, () => gen.throw(e));
addErrorsFrom(e);
if (!allErrors)
gen.assign(valid, false);
});
cxt.ok(valid);
}
function callSyncRef() {
cxt.result(code_1.callValidateCode(cxt, v, passCxt), () => addEvaluatedFrom(v), () => addErrorsFrom(v));
}
function addErrorsFrom(source) {
const errs = codegen_1._ `${source}.errors`;
gen.assign(names_1.default.vErrors, codegen_1._ `${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`); // TODO tagged
gen.assign(names_1.default.errors, codegen_1._ `${names_1.default.vErrors}.length`);
}
function addEvaluatedFrom(source) {
var _a;
if (!it.opts.unevaluated)
return;
const schEvaluated = (_a = sch === null || sch === void 0 ? void 0 : sch.validate) === null || _a === void 0 ? void 0 : _a.evaluated;
// TODO refactor
if (it.props !== true) {
if (schEvaluated && !schEvaluated.dynamicProps) {
if (schEvaluated.props !== undefined) {
it.props = util_1.mergeEvaluated.props(gen, schEvaluated.props, it.props);
}
else {
const props = gen.var("props", codegen_1._ `${source}.evaluated.props`);
it.props = util_1.mergeEvaluated.props(gen, props, it.props, codegen_1.Name);
}
}
if (it.items !== true) {
if (schEvaluated && !schEvaluated.dynamicItems) {
if (schEvaluated.items !== undefined) {
it.items = util_1.mergeEvaluated.items(gen, schEvaluated.items, it.items);
}
else {
const props = gen.var("props", codegen_1._ `${source}.evaluated.props`);
it.props = util_1.mergeEvaluated.props(gen, props, it.props, codegen_1.Name);
}
}
if (it.items !== true) {
if (schEvaluated && !schEvaluated.dynamicItems) {
if (schEvaluated.items !== undefined) {
it.items = util_1.mergeEvaluated.items(gen, schEvaluated.items, it.items);
}
else {
const items = gen.var("items", codegen_1._ `${source}.evaluated.items`);
it.items = util_1.mergeEvaluated.items(gen, items, it.items, codegen_1.Name);
}
}
else {
const items = gen.var("items", codegen_1._ `${source}.evaluated.items`);
it.items = util_1.mergeEvaluated.items(gen, items, it.items, codegen_1.Name);
}
}
},
};
}
}
exports.callRef = callRef;
exports.default = def;
//# sourceMappingURL=ref.js.map

@@ -5,5 +5,5 @@ import type { TypeError } from "../compile/validate/dataType";

import type { FormatError } from "./format/format";
import type { UnevaluatedPropertiesError } from "./applicator/unevaluatedProperties";
import type { UnevaluatedItemsError } from "./applicator/unevaluatedItems";
import type { UnevaluatedPropertiesError } from "./unevaluated/unevaluatedProperties";
import type { UnevaluatedItemsError } from "./unevaluated/unevaluatedItems";
import type { DependentRequiredError } from "./validation/dependentRequired";
export declare type DefinedError = TypeError | ApplicatorKeywordError | ValidationKeywordError | FormatError | UnevaluatedPropertiesError | UnevaluatedItemsError | DependentRequiredError;

@@ -252,3 +252,3 @@ # API Reference

const defaultOptions = {
// strict mode options
// strict mode options (NEW)
strict: true,

@@ -261,4 +261,3 @@ strictTypes: "log",

// validation and reporting options:
next: false,
unevaluated: false,
draft2019: false // NEW
$data: false,

@@ -278,2 +277,5 @@ allErrors: false,

// advanced options:
next: false, // NEW
unevaluated: false, // NEW
dynamicRef: false, // NEW
meta: true,

@@ -285,7 +287,7 @@ validateSchema: true,

loopRequired: Infinity,
loopEnum: Infinity,
loopEnum: Infinity, // NEW
ownProperties: false,
multipleOfPrecision: false,
messages: true,
code: {
code: { // NEW
es5: false,

@@ -322,4 +324,3 @@ lines: false,

- _next_: add support for the keywords from the next JSON-Schema draft (currently it is draft 2019-09): [`dependentRequired`](./json-schema.md#dependentrequired), [`dependentSchemas`](./json-schema.md#dependentschemas), [`maxContains`/`minContain`](./json-schema.md#maxcontains--mincontains). This option will be removed once the next draft is fully supported.
- _unevaluated_: to track evaluated properties/items and support keywords [`unevaluatedProperties`](./json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./json-schema.md#unevaluateditems). Supporting these keywords may add additional validation-time logic even to validation functions where these keywords are not used. When possible, Ajv determines which properties/items are "unevaluated" at compilation time.
- _draft2019_ (NEW in v7): add JSON Schema draft-2019-09 support. This option is equivalent to the combination of options `next`, `unevaluated` and `dynamicRef` (see [Advanced options](#advanced-options)). This option enables support for additional keywords (including `unevaluatedProperties` and `unevaluatedItems`) and for dynamic recursive references (see [Extending recursive schemas](./validation.md#extending-recursive-schemas)), but it does not add draft-2019-09 meta-schema - the default JSON Schema draft remains drat-07. See code example how to add it in [JSON Schema draft-2019-09](./validation.md#json-schema-draft-2019-09)
- _\$data_: support [\$data references](./validation.md#data-reference). Draft 6 meta-schema that is added by default will be extended to allow them. If you want to use another meta-schema you need to use $dataMetaSchema method to add support for $data reference. See [API](#ajv-constructor-and-methods).

@@ -358,2 +359,5 @@ - _allErrors_: check all rules collecting all errors. Default is to return after the first error.

- _next_ (NEW in v7): add support for the keywords from the next JSON-Schema draft (currently it is draft 2019-09): [`dependentRequired`](./json-schema.md#dependentrequired), [`dependentSchemas`](./json-schema.md#dependentschemas), [`maxContains`/`minContain`](./json-schema.md#maxcontains--mincontains). This option will be removed once the next draft is fully supported.
- _unevaluated_ (NEW in v7): to track evaluated properties/items and support keywords [`unevaluatedProperties`](./json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./json-schema.md#unevaluateditems). Supporting these keywords may add additional validation-time logic even to validation functions where these keywords are not used. When possible, Ajv determines which properties/items are "unevaluated" at compilation time.
- _dynamicRef_ (NEW in v7): to support `recursiveRef`/`recursiveAnchor` keywords (JSON Schema draft-2019-09) and `dynamicRef`/`dynamicAnchor` keywords (the upcoming JSON Schema draft). See [Extending recursive schemas](./validation.md#extending-recursive-schemas)
- _meta_: add [meta-schema](http://json-schema.org/documentation.html) so it can be used by other schemas (true by default). If an object is passed, it will be used as the default meta-schema for schemas that have no `$schema` keyword. This default meta-schema MUST have `$schema` keyword.

@@ -360,0 +364,0 @@ - _validateSchema_: validate added/compiled schemas against meta-schema (true by default). `$schema` property in the schema can be http://json-schema.org/draft-07/schema or absent (draft-07 meta-schema will be used) or can be a reference to the schema previously added with `addMetaSchema` method. Option values:

@@ -7,4 +7,17 @@ # JSON Schema validation keywords

## Keywords
## JSON Schema draft-2019-09
v7 added support for all new keywords in draft-2019-09:
- [unevaluatedProperties](#unevaluatedproperties)
- [unevaluatedItems](#unevaluateditems)
- [dependentRequired](#dependentrequired)
- [dependentSchemas](#dependentschemas)
- [maxContains/minContains](#maxcontains--mincontains)
- [$recursiveAnchor/$recursiveRef](./validation.md#extending-recursive-schemas)
There is also support for [$dynamicAnchor/$dynamicRef](./validation.md#extending-recursive-schemas) from the next version of JSON Schema draft that will replace `$recursiveAnchor`/`$recursiveRef`.
## Included keywords
- [type](#type)

@@ -11,0 +24,0 @@ - [Keywords for numbers](#keywords-for-numbers)

# Data validation
- [Data validation](#data-validation)
- [JSON Schema draft-2019-09](#json-schema-draft-2019-09)
- [Validation basics](#validation-basics)

@@ -10,2 +11,3 @@ - [JSON Schema validation keywords](#json-schema-validation-keywords)

- [<a name="ref"></a>Combining schemas with \$ref](#combining-schemas-with-ref)
- [Extending recursive schemas](#extending-recursive-schemas)
- [\$data reference](#data-reference)

@@ -22,2 +24,21 @@ - [$merge and $patch keywords](#merge-and-patch-keywords)

## JSON Schema draft-2019-09
To enable JSON Schema draft-2019-09 support:
```javascript
const ajv = new Ajv({draft2019: true})
const addMetaSchema2019 = require("ajv/dist/refs/json-schema-2019-09")
addMetaSchema2019(ajv) // to add draft-2019-09 meta-schema without making it default
// addMetaSchema2019(ajv, true) // to add it and make default
```
Option `draft2019: true` enables the following features:
- keywords [`unevaluatedProperties`](./json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./json-schema.md#unevaluateditems)
- keywords [`dependentRequired`](./json-schema.md#dependentrequired), [`dependentSchemas`](./json-schema.md#dependentschemas), [`maxContains`/`minContain`](./json-schema.md#maxcontains--mincontains)
- dynamic recursive references with [`recursiveAnchor`/`recursiveReference`] - see [Extending recursive schemas](#extending-recursive-schemas)
**Please note**: option `draft2019` is off by default because both `unevaluated*` keywords and dynamic recursive references may add additional code to compiled validation functions, depending on the schema, even if they are not used - so unless these features are used it is better to have them disabled. They can also be enabled separately - see [Advanced options](./api.md#advanced-options).
## Validation basics

@@ -140,2 +161,70 @@

### Extending recursive schemas
While statically defined `$ref` keyword allows to split schemas to multiple files, it is difficult to extend recursive schemas - the recursive reference(s) in the original schema points to the original schema, and not to the extended one. So in JSON Schema draft-07 the only available solution to extend the recursive schema was to redifine all sections of the original schema that have recursion.
It was particularly repetitive when extending meta-schema, as it has many recursive references, but even in a schema with a single recursive reference extending it was very verbose.
JSON Schema draft-2019-09 and the upcoming draft defined the mechanism for dynamic recursion using keywords `$recursiveRef`/`$recursiveAnchor` (draft-2019-09) or `$dynamicRef`/`$dynamicAnchor` (the next JSON Schema draft) that is somewhat similar to "open recursion" in functional programming.
Consider this recursive schema with static recursion:
```javascript
const treeSchema = {
$id: "https://example.com/tree",
type: "object",
required: ["data"],
properties: {
data: true,
children: {
type: "array",
items: {$ref: "#"},
},
},
}
```
The only way to extend this schema to prohibit additional properties is by adding `additionalProperties` keyword right in the schema - this approach can be impossible if you do not control the source of the original schema. Ajv also provided the additional keywords in [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) package to extend schemas by treating them as plain JSON data. While this approach works, it is non-standard.
The new keywords for dynamic recursive references allow extending this schema without modifying it:
```javascript
const treeSchema = {
$id: "https://example.com/tree",
$recursiveAnchor: true,
type: "object",
required: ["data"],
properties: {
data: true,
children: {
type: "array",
items: {$recursiveRef: "#"},
},
},
}
const strictTreeSchema = {
$id: "https://example.com/strict-tree",
$recursiveAnchor: true,
$ref: "tree",
unevaluatedProperties: false,
}
const ajv = new Ajv({
dynamicRef: true, // to support dynamic recursive references
unevaluated: true, // to support unevaluatedProperties
schemas: [treeSchema, strictTreeSchema],
})
const validate = ajv.getSchema("https://example.com/strict-tree")
```
See [dynamic-refs](../spec/dynamic-ref.spec.ts) test for the example using `$dynamicAnchor`/`$dynamicRef`.
At the moment Ajv implements the spec for dynamic recursive references with these limitations:
- `$recursiveAnchor`/`$dynamicAnchor` can only be used in the schema root.
- `$recursiveRef`/`$dynamicRef` can only be hash fragments, without URI.
Ajv also does not support dynamic references in [asynchronous schemas](#asynchronous-validation) (Ajv spec extension), it is assumed that the referenced schema is synchronous - there is no validation-time check.
### \$data reference

@@ -142,0 +231,0 @@

@@ -64,2 +64,3 @@ export {

import unevaluatedVocabulary from "./vocabularies/unevaluated"
import dynamicVocabulary from "./vocabularies/dynamic"
import {eachItem} from "./compile/util"

@@ -89,3 +90,3 @@ import $dataRefSchema from "./refs/data.json"

interface CurrentOptions {
// strict mode options
// strict mode options (NEW)
strict?: boolean | "log"

@@ -98,4 +99,3 @@ strictTypes?: boolean | "log"

// validation and reporting options:
next?: boolean
unevaluated?: boolean
draft2019?: boolean // NEW
$data?: boolean

@@ -107,3 +107,3 @@ allErrors?: boolean

| ((comment: string, schemaPath?: string, rootSchema?: AnySchemaObject) => unknown)
formats?: {[name: string]: Format}
formats?: {[Name in string]?: Format}
keywords?: Vocabulary

@@ -118,2 +118,5 @@ schemas?: AnySchema[] | {[key: string]: AnySchema}

// advanced options:
next?: boolean // NEW
unevaluated?: boolean // NEW
dynamicRef?: boolean // NEW
meta?: SchemaObject | boolean

@@ -126,7 +129,7 @@ defaultMeta?: string | AnySchemaObject

loopRequired?: number
loopEnum?: number
loopEnum?: number // NEW
ownProperties?: boolean
multipleOfPrecision?: boolean | number
messages?: boolean
code?: CodeOptions
code?: CodeOptions // NEW
}

@@ -271,2 +274,7 @@

}
if (opts.draft2019) {
opts.next ??= true
opts.unevaluated ??= true
opts.dynamicRef ??= true
}
this.logger = getLogger(opts.logger)

@@ -283,2 +291,3 @@ const formatOpt = opts.validateFormats

this.addVocabulary(["$async"])
if (opts.dynamicRef) this.addVocabulary(dynamicVocabulary)
this.addVocabulary(coreVocabulary)

@@ -742,3 +751,3 @@ this.addVocabulary(validationVocabulary)

const format = this.opts.formats[name]
this.addFormat(name, format)
if (format) this.addFormat(name, format)
}

@@ -745,0 +754,0 @@ }

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

baseId: string // the current schema base URI that should be used as the base for resolving URIs in references (\$ref)
dynamicAnchors: {[Ref in string]?: true}
readonly schemaPath: Code // the run-time expression that evaluates to the property name of the current schema

@@ -136,2 +137,3 @@ readonly errSchemaPath: string // this is actual string, should not be changed to Code

baseId: sch.baseId || rootId,
dynamicAnchors: {},
schemaPath: nil,

@@ -195,3 +197,3 @@ errSchemaPath: "#",

ref: string
): AnySchema | AnyValidateFunction | SchemaEnv | undefined {
): AnySchema | SchemaEnv | undefined {
ref = resolveUrl(baseId, ref)

@@ -198,0 +200,0 @@ const schOrFunc = root.refs[ref]

@@ -7,3 +7,3 @@ import {Name} from "./codegen"

// args passed from referencing schema
dataCxt: new Name("dataCxt"), // should not be used directly, it is destructured to the names below
valCxt: new Name("valCxt"), // validation/data context - should not be used directly, it is destructured to the names below
dataPath: new Name("dataPath"),

@@ -13,2 +13,3 @@ parentData: new Name("parentData"),

rootData: new Name("rootData"), // root data - same as the data passed to the first/top validation function
dynamicAnchors: new Name("dynamicAnchors"), // used to support recursiveRef and dynamicRef
// function scoped variables

@@ -15,0 +16,0 @@ vErrors: new Name("vErrors"), // null or array of validation errors

@@ -40,5 +40,13 @@ import type {AnySchema, AnySchemaObject} from "../types"

const REF_KEYWORDS = new Set([
"$ref",
"$recursiveRef",
"$recursiveAnchor",
"$dynamicRef",
"$dynamicAnchor",
])
function hasRef(schema: AnySchemaObject): boolean {
for (const key in schema) {
if (key === "$ref") return true
if (REF_KEYWORDS.has(key)) return true
const sch = schema[key]

@@ -85,2 +93,4 @@ if (Array.isArray(sch) && sch.some(hasRef)) return true

const ANCHOR = /^[a-z_][-a-z0-9._]*$/i
export function getSchemaRefs(this: Ajv, schema: AnySchema): LocalRefs {

@@ -92,2 +102,3 @@ if (typeof schema == "boolean") return {}

const localRefs: LocalRefs = {}
const schemaRefs: Set<string> = new Set()

@@ -97,20 +108,33 @@ traverse(schema, {allKeys: true}, (sch, jsonPtr, _, parentJsonPtr) => {

const fullPath = pathPrefix + jsonPtr
let id = sch.$id
let baseId = baseIds[parentJsonPtr]
if (typeof id == "string") {
id = baseId = normalizeId(baseId ? URI.resolve(baseId, id) : id)
let schOrRef = this.refs[id]
if (typeof sch.$id == "string") baseId = addRef.call(this, sch.$id)
addAnchor.call(this, sch.$anchor)
addAnchor.call(this, sch.$dynamicAnchor)
baseIds[jsonPtr] = baseId
function addRef(this: Ajv, ref: string): string {
ref = normalizeId(baseId ? URI.resolve(baseId, ref) : ref)
if (schemaRefs.has(ref)) throw ambiguos(ref)
schemaRefs.add(ref)
let schOrRef = this.refs[ref]
if (typeof schOrRef == "string") schOrRef = this.refs[schOrRef]
if (typeof schOrRef == "object") {
checkAmbiguosId(sch, schOrRef.schema, id)
} else if (id !== normalizeId(fullPath)) {
if (id[0] === "#") {
checkAmbiguosId(sch, localRefs[id], id)
localRefs[id] = sch
checkAmbiguosRef(sch, schOrRef.schema, ref)
} else if (ref !== normalizeId(fullPath)) {
if (ref[0] === "#") {
checkAmbiguosRef(sch, localRefs[ref], ref)
localRefs[ref] = sch
} else {
this.refs[id] = fullPath
this.refs[ref] = fullPath
}
}
return ref
}
baseIds[jsonPtr] = baseId
function addAnchor(this: Ajv, anchor: unknown): void {
if (typeof anchor == "string") {
if (!ANCHOR.test(anchor)) throw new Error(`invalid anchor "${anchor}"`)
addRef.call(this, `#${anchor}`)
}
}
})

@@ -120,7 +144,9 @@

function checkAmbiguosId(sch1: AnySchema, sch2: AnySchema | undefined, id: string): void {
if (sch2 !== undefined && !equal(sch1, sch2)) {
throw new Error(`id "${id}" resolves to more than one schema`)
}
function checkAmbiguosRef(sch1: AnySchema, sch2: AnySchema | undefined, ref: string): void {
if (sch2 !== undefined && !equal(sch1, sch2)) throw ambiguos(ref)
}
function ambiguos(ref: string): Error {
return new Error(`reference "${ref}" resolves to more than one schema`)
}
}

@@ -44,3 +44,3 @@ import type {AddedKeywordDefinition} from "../types"

types: {...groups, integer: true, boolean: true, null: true},
rules: [groups.number, groups.string, {rules: []}, groups.array, groups.object],
rules: [{rules: []}, groups.number, groups.string, groups.array, groups.object],
all: {type: true, $comment: true},

@@ -47,0 +47,0 @@ keywords: {type: true, $comment: true},

@@ -30,12 +30,9 @@ import type {AnySchema} from "../../types"

opts.code.es5
? gen.func(validateName, _`${N.data}, ${N.dataCxt}`, schemaEnv.$async, () => {
? gen.func(validateName, _`${N.data}, ${N.valCxt}`, schemaEnv.$async, () => {
gen.code(_`"use strict"; ${funcSourceUrl(schema, opts)}`)
destructureDataCxtES5(gen)
destructureValCxtES5(gen, opts)
gen.code(body)
})
: gen.func(
validateName,
_`${N.data}, {${N.dataPath}="", ${N.parentData}, ${N.parentDataProperty}, ${N.rootData}=${N.data}}={}`,
schemaEnv.$async,
() => gen.code(funcSourceUrl(schema, opts)).code(body)
: gen.func(validateName, _`${N.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () =>
gen.code(funcSourceUrl(schema, opts)).code(body)
)

@@ -45,10 +42,17 @@ )

function destructureDataCxtES5(gen: CodeGen): void {
function destructureValCxt(opts: InstanceOptions): Code {
return _`{${N.dataPath}="", ${N.parentData}, ${N.parentDataProperty}, ${N.rootData}=${N.data}${
opts.dynamicRef ? _`, ${N.dynamicAnchors}={}` : nil
}}={}`
}
function destructureValCxtES5(gen: CodeGen, opts: InstanceOptions): void {
gen.if(
N.dataCxt,
N.valCxt,
() => {
gen.var(N.dataPath, _`${N.dataCxt}.${N.dataPath}`)
gen.var(N.parentData, _`${N.dataCxt}.${N.parentData}`)
gen.var(N.parentDataProperty, _`${N.dataCxt}.${N.parentDataProperty}`)
gen.var(N.rootData, _`${N.dataCxt}.${N.rootData}`)
gen.var(N.dataPath, _`${N.valCxt}.${N.dataPath}`)
gen.var(N.parentData, _`${N.valCxt}.${N.parentData}`)
gen.var(N.parentDataProperty, _`${N.valCxt}.${N.parentDataProperty}`)
gen.var(N.rootData, _`${N.valCxt}.${N.rootData}`)
if (opts.dynamicRef) gen.var(N.dynamicAnchors, _`${N.valCxt}.${N.dynamicAnchors}`)
},

@@ -60,2 +64,3 @@ () => {

gen.var(N.rootData, N.data)
if (opts.dynamicRef) gen.var(N.dynamicAnchors, _`{}`)
}

@@ -62,0 +67,0 @@ )

@@ -44,2 +44,3 @@ import type {CodeGen, Code, Name, Scope} from "../compile/codegen"

rootData: Record<string, any> | any[]
dynamicAnchors: {[Ref in string]?: ValidateFunction}
}

@@ -46,0 +47,0 @@

@@ -25,3 +25,3 @@ import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"

if (!Array.isArray(items)) {
checkStrictMode(it, '"additionalItems" without "items" is ignored')
checkStrictMode(it, '"additionalItems" is ignored when "items" is not an array of schemas')
return

@@ -28,0 +28,0 @@ }

@@ -68,9 +68,10 @@ import type {AnySchema, SchemaMap} from "../types"

const dataAndSchema = passSchema ? _`${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data
const dataCxt = gen.object(
const valCxt: [Name, Code | number][] = [
[N.dataPath, strConcat(N.dataPath, errorPath)],
[N.parentData, it.parentData],
[N.parentDataProperty, it.parentDataProperty],
[N.rootData, N.rootData]
)
const args = _`${dataAndSchema}, ${dataCxt}`
[N.rootData, N.rootData],
]
if (it.opts.dynamicRef) valCxt.push([N.dynamicAnchors, N.dynamicAnchors])
const args = _`${dataAndSchema}, ${gen.object(...valCxt)}`
return context !== nil ? _`${func}.call(${context}, ${args})` : _`${func}(${args})`

@@ -77,0 +78,0 @@ }

@@ -5,4 +5,12 @@ import type {Vocabulary} from "../../types"

const core: Vocabulary = ["$schema", "$id", "$defs", "definitions", idKeyword, refKeyword]
const core: Vocabulary = [
"$schema",
"$id",
"$defs",
"$vocabulary",
"definitions",
idKeyword,
refKeyword,
]
export default core

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

const {gen, schema, it} = cxt
const {allErrors, baseId, schemaEnv: env, opts, validateName, self} = it
const passCxt = opts.passContext ? N.this : nil
const {baseId, schemaEnv: env, validateName, self} = it
if (schema === "#" || schema === "#/") return callRootRef()
const schOrFunc = resolveRef.call(self, env.root, baseId, schema)
if (schOrFunc === undefined) throw new MissingRefError(baseId, schema)
if (schOrFunc instanceof SchemaEnv) return callValidate(schOrFunc)
return inlineRefSchema(schOrFunc)
const schOrEnv = resolveRef.call(self, env.root, baseId, schema)
if (schOrEnv === undefined) throw new MissingRefError(baseId, schema)
if (schOrEnv instanceof SchemaEnv) return callValidate(schOrEnv)
return inlineRefSchema(schOrEnv)
function callRootRef(): void {
if (env === env.root) return callRef(validateName, env, env.$async)
if (env === env.root) return callRef(cxt, validateName, env, env.$async)
const rootName = gen.scopeValue("root", {ref: env.root})
return callRef(_`${rootName}.validate`, env.root, env.root.$async)
return callRef(cxt, _`${rootName}.validate`, env.root, env.root.$async)
}

@@ -39,10 +38,5 @@

}
callRef(v, sch, sch.$async)
callRef(cxt, v, sch, sch.$async)
}
function callRef(v: Code, sch: SchemaEnv, $async?: boolean): void {
if ($async) callAsyncRef(v, sch)
else callSyncRef(v, sch)
}
function inlineRefSchema(sch: AnySchema): void {

@@ -65,63 +59,71 @@ const schName = gen.scopeValue("schema", {ref: sch})

}
},
}
function callAsyncRef(v: Code, sch: SchemaEnv): void {
if (!env.$async) throw new Error("async schema referenced by sync schema")
const valid = gen.let("valid")
gen.try(
() => {
gen.code(_`await ${callValidateCode(cxt, v, passCxt)}`)
addEvaluatedFrom(v, sch)
if (!allErrors) gen.assign(valid, true)
},
(e) => {
gen.if(_`!(${e} instanceof ${it.ValidationError as Name})`, () => gen.throw(e))
addErrorsFrom(e)
if (!allErrors) gen.assign(valid, false)
}
)
cxt.ok(valid)
}
export function callRef(cxt: KeywordCxt, v: Code, sch?: SchemaEnv, $async?: boolean): void {
const {gen, it} = cxt
const {allErrors, schemaEnv: env, opts} = it
const passCxt = opts.passContext ? N.this : nil
if ($async) callAsyncRef()
else callSyncRef()
function callSyncRef(v: Code, sch: SchemaEnv): void {
cxt.result(
callValidateCode(cxt, v, passCxt),
() => addEvaluatedFrom(v, sch),
() => addErrorsFrom(v)
)
}
function callAsyncRef(): void {
if (!env.$async) throw new Error("async schema referenced by sync schema")
const valid = gen.let("valid")
gen.try(
() => {
gen.code(_`await ${callValidateCode(cxt, v, passCxt)}`)
addEvaluatedFrom(v) // TODO will not work with async, it has to be returned with the result
if (!allErrors) gen.assign(valid, true)
},
(e) => {
gen.if(_`!(${e} instanceof ${it.ValidationError as Name})`, () => gen.throw(e))
addErrorsFrom(e)
if (!allErrors) gen.assign(valid, false)
}
)
cxt.ok(valid)
}
function addErrorsFrom(source: Code): void {
const errs = _`${source}.errors`
gen.assign(N.vErrors, _`${N.vErrors} === null ? ${errs} : ${N.vErrors}.concat(${errs})`) // TODO tagged
gen.assign(N.errors, _`${N.vErrors}.length`)
}
function callSyncRef(): void {
cxt.result(
callValidateCode(cxt, v, passCxt),
() => addEvaluatedFrom(v),
() => addErrorsFrom(v)
)
}
function addEvaluatedFrom(source: Code, sch: SchemaEnv): void {
if (!it.opts.unevaluated) return
const schEvaluated = sch.validate?.evaluated
// TODO refactor
if (it.props !== true) {
if (schEvaluated && !schEvaluated.dynamicProps) {
if (schEvaluated.props !== undefined) {
it.props = mergeEvaluated.props(gen, schEvaluated.props, it.props)
}
} else {
const props = gen.var("props", _`${source}.evaluated.props`)
it.props = mergeEvaluated.props(gen, props, it.props, Name)
function addErrorsFrom(source: Code): void {
const errs = _`${source}.errors`
gen.assign(N.vErrors, _`${N.vErrors} === null ? ${errs} : ${N.vErrors}.concat(${errs})`) // TODO tagged
gen.assign(N.errors, _`${N.vErrors}.length`)
}
function addEvaluatedFrom(source: Code): void {
if (!it.opts.unevaluated) return
const schEvaluated = sch?.validate?.evaluated
// TODO refactor
if (it.props !== true) {
if (schEvaluated && !schEvaluated.dynamicProps) {
if (schEvaluated.props !== undefined) {
it.props = mergeEvaluated.props(gen, schEvaluated.props, it.props)
}
} else {
const props = gen.var("props", _`${source}.evaluated.props`)
it.props = mergeEvaluated.props(gen, props, it.props, Name)
}
if (it.items !== true) {
if (schEvaluated && !schEvaluated.dynamicItems) {
if (schEvaluated.items !== undefined) {
it.items = mergeEvaluated.items(gen, schEvaluated.items, it.items)
}
} else {
const items = gen.var("items", _`${source}.evaluated.items`)
it.items = mergeEvaluated.items(gen, items, it.items, Name)
}
if (it.items !== true) {
if (schEvaluated && !schEvaluated.dynamicItems) {
if (schEvaluated.items !== undefined) {
it.items = mergeEvaluated.items(gen, schEvaluated.items, it.items)
}
} else {
const items = gen.var("items", _`${source}.evaluated.items`)
it.items = mergeEvaluated.items(gen, items, it.items, Name)
}
}
},
}
}
export default def

@@ -5,4 +5,4 @@ import type {TypeError} from "../compile/validate/dataType"

import type {FormatError} from "./format/format"
import type {UnevaluatedPropertiesError} from "./applicator/unevaluatedProperties"
import type {UnevaluatedItemsError} from "./applicator/unevaluatedItems"
import type {UnevaluatedPropertiesError} from "./unevaluated/unevaluatedProperties"
import type {UnevaluatedItemsError} from "./unevaluated/unevaluatedItems"
import type {DependentRequiredError} from "./validation/dependentRequired"

@@ -9,0 +9,0 @@

{
"name": "ajv",
"version": "7.0.0-beta.3",
"version": "7.0.0-beta.4",
"description": "Another JSON Schema Validator",

@@ -23,3 +23,3 @@ "main": "dist/ajv.js",

"bundle": "rm -rf bundle && node ./scripts/bundle.js",
"build": "rm -rf dist && tsc && cp -r lib/refs dist",
"build": "rm -rf dist && tsc && cp -r lib/refs dist && rm dist/refs/json-schema-2019-09/index.ts",
"json-tests": "rm -rf spec/_json/*.js && node scripts/jsontests",

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

"@typescript-eslint/parser": "^3.8.0",
"ajv-formats": "^0.3.2",
"ajv-formats": "^0.5.0",
"browserify": "^16.2.0",

@@ -80,0 +80,0 @@ "chai": "^4.0.1",

@@ -9,3 +9,3 @@ <img align="right" alt="Ajv logo" width="160" src="https://ajv.js.org/images/ajv_logo.png">

[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv)
[![npm (beta)](https://img.shields.io/npm/v/ajv/beta)](https://www.npmjs.com/package/ajv/v/7.0.0-beta.3)
[![npm (beta)](https://img.shields.io/npm/v/ajv/beta)](https://www.npmjs.com/package/ajv/v/7.0.0-beta.4)
[![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv)

@@ -20,6 +20,7 @@ [![Coverage Status](https://coveralls.io/repos/github/ajv-validator/ajv/badge.svg?branch=master)](https://coveralls.io/github/ajv-validator/ajv?branch=master)

- support of JSON Schema draft-2019-09 features: [`unevaluatedProperties`](./json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./json-schema.md#unevaluateditems), [dynamic recursive references](./validation.md#extending-recursive-schemas) and other [additional keywords](./json-schema.md#json-schema-draft-2019-09).
- 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.
- 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%).
- to simplify Ajv extensions, the new keyword API that is used by pre-defined keywords is available to user-defined keywords - it is much easier to define any keywords now, especially with subschemas.
- schemas are compiled to ES6 code (ES5 code generation is supported with an option).
- to simplify Ajv extensions, the new keyword API that is used by pre-defined keywords is available to user-defined keywords - it is much easier to define any keywords now, especially with subschemas. [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package was updated to use the new API (in [v4.0.0-beta.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v4.0.0-beta.0))
- schemas are compiled to ES6 code (ES5 code generation is also supported with an option).
- to improve reliability and maintainability the code is migrated to TypeScript.

@@ -26,0 +27,0 @@

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
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc