Skip to content

Commit

Permalink
Merge branch 'master' into integerSubSchemaNarrowing
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Jan 2, 2023
2 parents 1d664d9 + a211e8d commit ad187e1
Show file tree
Hide file tree
Showing 20 changed files with 156 additions and 49 deletions.
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Supports JSON Schema draft-04/06/07/2019-09/2020-12 ([draft-04 support](https://
[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv)
[![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv)
[![Coverage Status](https://coveralls.io/repos/github/ajv-validator/ajv/badge.svg?branch=master)](https://coveralls.io/github/ajv-validator/ajv?branch=master)
[![SimpleX](https://img.shields.io/badge/chat-on%20SimpleX-%2307b4b9)](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2Fap4lMFzfXF8Hzmh-Vz0WNxp_1jKiOa-h%23MCowBQYDK2VuAyEAcdefddRvDfI8iAuBpztm_J3qFucj8MDZoVs_2EcMTzU%3D)
[![SimpleX](https://img.shields.io/badge/chat-on%20SimpleX-%2307b4b9)](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FV-6t4hoy_SsvKMi9KekdGX-VKQOhDeAe%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAm98gjwvrAEiiz_YgBoaQB9dtKTl5Om1pborUyevQwzg%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22wYrTFafovkymjUtc2vUjCQ%3D%3D%22%7D)
[![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv)
[![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin)

Expand All @@ -35,6 +35,7 @@ Please review [Contributing guidelines](./CONTRIBUTING.md) and [Code components]
All documentation is available on the [Ajv website](https://ajv.js.org).

Some useful site links:

- [Getting started](https://ajv.js.org/guide/getting-started.html)
- [JSON Schema vs JSON Type Definition](https://ajv.js.org/guide/schema-language.html)
- [API reference](https://ajv.js.org/api.html)
Expand All @@ -53,7 +54,7 @@ Your continuing support is very important - the funds will be used to develop an
Please sponsor Ajv via:

- [GitHub sponsors page](https://github.com/sponsors/epoberezkin) (GitHub will match it)
- [Ajv Open Collective️](https://opencollective.com/ajv)
- [Ajv Open Collective](https://opencollective.com/ajv)

Thank you.

Expand All @@ -73,6 +74,19 @@ Thank you.
<a href="https://opencollective.com/ajv/organization/9/website"><img src="https://opencollective.com/ajv/organization/9/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/10/website"><img src="https://opencollective.com/ajv/organization/10/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/11/website"><img src="https://opencollective.com/ajv/organization/11/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/12/website"><img src="https://opencollective.com/ajv/organization/12/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/13/website"><img src="https://opencollective.com/ajv/organization/13/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/14/website"><img src="https://opencollective.com/ajv/organization/14/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/15/website"><img src="https://opencollective.com/ajv/organization/15/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/16/website"><img src="https://opencollective.com/ajv/organization/16/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/17/website"><img src="https://opencollective.com/ajv/organization/17/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/18/website"><img src="https://opencollective.com/ajv/organization/18/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/19/website"><img src="https://opencollective.com/ajv/organization/19/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/20/website"><img src="https://opencollective.com/ajv/organization/20/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/21/website"><img src="https://opencollective.com/ajv/organization/21/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/22/website"><img src="https://opencollective.com/ajv/organization/22/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/23/website"><img src="https://opencollective.com/ajv/organization/23/avatar.svg"></a>
<a href="https://opencollective.com/ajv/organization/24/website"><img src="https://opencollective.com/ajv/organization/24/avatar.svg"></a>

## Performance

Expand All @@ -87,7 +101,7 @@ Currently Ajv is the fastest and the most standard compliant validator according

Performance of different validators by [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark):

[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=62,4,1&chs=600x416&chxl=-1:|ajv|@exodus&#x2F;schemasafe|is-my-json-valid|djv|@cfworker&#x2F;json-schema|jsonschema&chd=t:100,69.2,51.5,13.1,5.1,1.2)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance)
[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=62,4,1&chs=600x416&chxl=-1:|ajv|@exodus/schemasafe|is-my-json-valid|djv|@cfworker/json-schema|jsonschema/=t:100,69.2,51.5,13.1,5.1,1.2)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance)

## Features

Expand Down Expand Up @@ -144,15 +158,15 @@ const schema = {
type: "object",
properties: {
foo: {type: "integer"},
bar: {type: "string"}
bar: {type: "string"},
},
required: ["foo"],
additionalProperties: false,
}

const data = {
foo: 1,
bar: "abc"
bar: "abc",
}

const validate = ajv.compile(schema)
Expand Down
6 changes: 4 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ Every time this method is called the errors are overwritten so you need to copy

If the schema is asynchronous (has `$async` keyword on the top level) this method returns a Promise. See [Asynchronous validation](./guide/async-validation.md).

<a name="add-schema"></a>
<a id="add-schema"></a>

### ajv.addSchema(schema: object | object[], key?: string): Ajv

Expand Down Expand Up @@ -245,7 +245,7 @@ Formats can be also added via `formats` option.

<a name="api-addkeyword"></a>

### ajv.addKeyword(definition: object): Ajv
### ajv.addKeyword(definition: string | object): Ajv

Add validation keyword to Ajv instance.

Expand Down Expand Up @@ -297,6 +297,8 @@ interface KeywordDefinition {
}
```

If only the property `keyword` is provided in the definition object, you can also pass the keyword name as the argument.

`compile`, `macro` and `code` are mutually exclusive, only one should be used at a time. `validate` can be used separately or in addition to `compile` or `macro` to support [\$data reference](./guide/combining-schemas.md#data-reference).

::: tip Keyword is validated only for applicable data types
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/combining-schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const ajv = new Ajv()
const validate = ajv.addSchema(defsSchema).compile(schema)
```

See [Options](./api.md#options) and [addSchema](./api.md#add-schema) method.
See [Options](../options.md) and [addSchema](../api.md#add-schema) method.

::: tip Reference resolution
- `$ref` is resolved as the uri-reference using schema \$id as the base URI (see the example).
Expand Down
6 changes: 3 additions & 3 deletions docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
You can try Ajv without installing it in the Node.js REPL: [https://runkit.com/npm/ajv](https://runkit.com/npm/ajv)
:::

To install Ajv version 7:
To install Ajv version 8:

```bash
npm install ajv
Expand Down Expand Up @@ -140,8 +140,8 @@ const parse = ajv.compileParser(schema)
const json = '{"foo": 1, "bar": "abc"}'
const invalidJson = '{"unknown": "abc"}'

console.log(parseAndLog(json)) // logs {foo: 1, bar: "abc"}
console.log(parseAndLog(invalidJson)) // logs error and position
parseAndLog(json) // logs {foo: 1, bar: "abc"}
parseAndLog(invalidJson) // logs error and position

function parseAndLog(json) {
const data = parse(json)
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/modifying-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ See [discriminator](../json-schema.md#discriminator) keyword.

## Assigning defaults

With [option `useDefaults`](./api.md#options) Ajv will assign values from `default` keyword in the schemas of `properties` and `items` (when it is the array of schemas) to the missing properties and items.
With [option `useDefaults`](./options.md#options) Ajv will assign values from `default` keyword in the schemas of `properties` and `items` (when it is the array of schemas) to the missing properties and items.

With the option value `"empty"` properties and items equal to `null` or `""` (empty string) will be considered missing and assigned defaults.

Expand Down
4 changes: 4 additions & 0 deletions docs/json-type-definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,10 @@ Any user-defined keywords that can be used in JSON Schema schemas can also be us
It is strongly recommended to only use it to simplify migration from JSON Schema to JTD and not to use non-standard keywords in the new schemas, as these keywords are not supported by any other tools.
:::

::: warning Parsing does NOT support non-standard JTD keywords
compileParser method does not support non-standard JTD keywords, you will have to use JSON.parse and then validates.
:::

## Validation errors

TODO
2 changes: 1 addition & 1 deletion docs/keywords.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ ajv.addKeyword({
})
```

Macro keywords an be recursive - i.e. return schemas containing the same keyword. See the example of defining a recursive macro keyword `deepProperties` in the [test](https://github.com/ajv-validator/ajv/blob/master/spec/keyword.spec.ts#L316).
Macro keywords can be recursive - i.e. return schemas containing the same keyword. See the example of defining a recursive macro keyword `deepProperties` in the [test](https://github.com/ajv-validator/ajv/blob/master/spec/keyword.spec.ts#L316).

## Schema compilation context

Expand Down
2 changes: 1 addition & 1 deletion docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ To use a third-party regex engine in Ajv, set the ajv.opts.code.regExp property
```
const Ajv = require("ajv")
const RE2 = require("re2")
const ajv = new Ajv({regExp: RE2})
const ajv = new Ajv({code: {regExp: RE2}})
```

For details about the interface of the `regexp` option, see options.md under the docs folder.
Expand Down
2 changes: 2 additions & 0 deletions lib/2019.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,5 @@ export {DefinedError} from "./vocabularies/errors"
export {JSONType} from "./compile/rules"
export {JSONSchemaType} from "./types/json-schema"
export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen"
export {default as ValidationError} from "./runtime/validation_error"
export {default as MissingRefError} from "./compile/ref_error"
2 changes: 2 additions & 0 deletions lib/2020.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,5 @@ export {DefinedError} from "./vocabularies/errors"
export {JSONType} from "./compile/rules"
export {JSONSchemaType} from "./types/json-schema"
export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen"
export {default as ValidationError} from "./runtime/validation_error"
export {default as MissingRefError} from "./compile/ref_error"
2 changes: 2 additions & 0 deletions lib/ajv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,5 @@ export {DefinedError} from "./vocabularies/errors"
export {JSONType} from "./compile/rules"
export {JSONSchemaType} from "./types/json-schema"
export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen"
export {default as ValidationError} from "./runtime/validation_error"
export {default as MissingRefError} from "./compile/ref_error"
34 changes: 20 additions & 14 deletions lib/compile/jtd/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ function serializeValues(cxt: SerializeCxt): void {
gen.add(N.json, str`}`)
}

function serializeKeyValue(cxt: SerializeCxt, key: Name, schema: SchemaObject, first: Name): void {
function serializeKeyValue(cxt: SerializeCxt, key: Name, schema: SchemaObject, first?: Name): void {
const {gen, data} = cxt
addComma(cxt, first)
serializeString({...cxt, data: key})
Expand Down Expand Up @@ -156,20 +156,24 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v
const optProps = keys(optionalProperties)
const allProps = allProperties(props.concat(optProps))
let first = !discriminator
let firstProp: Name | undefined

for (const key of props) {
if (first) first = false
else gen.add(N.json, str`,`)
serializeProperty(key, properties[key], keyValue(key))
}
if (first) firstProp = gen.let("first", true)
for (const key of optProps) {
const value = keyValue(key)
gen.if(and(_`${value} !== undefined`, isOwnProperty(gen, data, key)), () =>
gen.if(and(_`${value} !== undefined`, isOwnProperty(gen, data, key)), () => {
addComma(cxt, firstProp)
serializeProperty(key, optionalProperties[key], value)
)
})
}
if (schema.additionalProperties) {
gen.forIn("key", data, (key) =>
gen.if(isAdditional(key, allProps), () =>
serializeKeyValue(cxt, key, {}, gen.let("first", first))
)
gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, firstProp))
)
}

Expand All @@ -190,8 +194,6 @@ function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): v
}

function serializeProperty(key: string, propSchema: SchemaObject, value: Name): void {
if (first) first = false
else gen.add(N.json, str`,`)
gen.add(N.json, str`${JSON.stringify(key)}:`)
serializeCode({...cxt, schema: propSchema, data: value})
}
Expand Down Expand Up @@ -251,10 +253,14 @@ function serializeEmpty({gen, data}: SerializeCxt): void {
gen.add(N.json, _`JSON.stringify(${data})`)
}

function addComma({gen}: SerializeCxt, first: Name): void {
gen.if(
first,
() => gen.assign(first, false),
() => gen.add(N.json, str`,`)
)
function addComma({gen}: SerializeCxt, first?: Name): void {
if (first) {
gen.if(
first,
() => gen.assign(first, false),
() => gen.add(N.json, str`,`)
)
} else {
gen.add(N.json, str`,`)
}
}
2 changes: 2 additions & 0 deletions lib/jtd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,5 @@ export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./com

export {JTDSchemaType, SomeJTDSchemaType, JTDDataType}
export {JTDOptions}
export {default as ValidationError} from "./runtime/validation_error"
export {default as MissingRefError} from "./compile/ref_error"
1 change: 1 addition & 0 deletions lib/types/json-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ type Nullable<T> = undefined extends T
default?: T | null
}
: {
nullable?: false
const?: T
enum?: Readonly<T[]>
default?: T
Expand Down
17 changes: 10 additions & 7 deletions lib/vocabularies/jtd/values.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {CodeKeywordDefinition, SchemaObject} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {alwaysValidSchema, Type} from "../../compile/util"
import {not, Name} from "../../compile/codegen"
import {not, or, Name} from "../../compile/codegen"
import {checkMetadata} from "./metadata"
import {checkNullableObject} from "./nullable"
import {typeError, _JTDTypeError} from "./error"
Expand All @@ -15,13 +15,16 @@ const def: CodeKeywordDefinition = {
code(cxt: KeywordCxt) {
checkMetadata(cxt)
const {gen, data, schema, it} = cxt
if (alwaysValidSchema(it, schema)) return
const [valid, cond] = checkNullableObject(cxt, data)
gen.if(cond)
gen.assign(valid, validateMap())
gen.elseIf(not(valid))
cxt.error()
gen.endIf()
if (alwaysValidSchema(it, schema)) {
gen.if(not(or(cond, valid)), () => cxt.error())
} else {
gen.if(cond)
gen.assign(valid, validateMap())
gen.elseIf(not(valid))
cxt.error()
gen.endIf()
}
cxt.ok(valid)

function validateMap(): Name | boolean {
Expand Down
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ajv",
"version": "8.11.0",
"version": "8.11.2",
"description": "Another JSON Schema Validator",
"main": "dist/ajv.js",
"types": "dist/ajv.d.ts",
Expand Down Expand Up @@ -65,13 +65,13 @@
},
"devDependencies": {
"@ajv-validator/config": "^0.3.0",
"@rollup/plugin-commonjs": "^21.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-commonjs": "^23.0.2",
"@rollup/plugin-json": "^5.0.1",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-typescript": "^8.2.1",
"@types/chai": "^4.2.12",
"@types/mocha": "^9.0.0",
"@types/node": "^17.0.0",
"@types/mocha": "^10.0.0",
"@types/node": "^18.11.9",
"@types/require-from-string": "^1.2.0",
"@typescript-eslint/eslint-plugin": "^3.8.0",
"@typescript-eslint/parser": "^3.8.0",
Expand All @@ -83,18 +83,18 @@
"dayjs-plugin-utc": "^0.1.2",
"eslint": "^7.8.1",
"eslint-config-prettier": "^7.0.0",
"fast-uri": "^1.0.0",
"glob": "^7.0.0",
"husky": "^7.0.1",
"fast-uri": "^2.1.0",
"glob": "^8.0.2",
"husky": "^8.0.2",
"if-node-version": "^1.0.0",
"jimp": "^0.16.1",
"js-beautify": "^1.7.3",
"json-schema-test": "^2.0.0",
"karma": "^6.0.0",
"karma-chrome-launcher": "^3.0.0",
"karma-mocha": "^2.0.0",
"lint-staged": "^12.1.1",
"mocha": "^9.0.2",
"lint-staged": "^13.0.3",
"mocha": "^10.0.0",
"module-from-string": "^3.1.3",
"node-fetch": "^3.0.0",
"nyc": "^15.0.0",
Expand All @@ -104,7 +104,7 @@
"rollup-plugin-terser": "^7.0.2",
"ts-node": "^10.0.0",
"tsify": "^5.0.2",
"typescript": "^4.2.0"
"typescript": "^4.8.0"
},
"collective": {
"type": "opencollective",
Expand Down
28 changes: 28 additions & 0 deletions spec/issues/1949_jtd_empty_values.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import _Ajv from "../ajv_jtd"
import * as assert from "assert"

describe("JTD values with empty schema (issue #1949)", () => {
const ajv = new _Ajv()

it("should correctly validate empty values form", () => {
const schema = {values: {}}
const validate = ajv.compile(schema)
assert.strictEqual(validate({prop1: 1, prop2: 2}), true)
assert.strictEqual(validate({}), true)
assert.strictEqual(validate(null), false)
assert.strictEqual(validate(1), false)
assert.strictEqual(validate("foo"), false)
assert.strictEqual(validate(undefined), false)
})

it("should correctly validate nullable empty values form", () => {
const schema = {values: {}, nullable: true}
const validate = ajv.compile(schema)
assert.strictEqual(validate({prop1: 1, prop2: 2}), true)
assert.strictEqual(validate({}), true)
assert.strictEqual(validate(null), true)
assert.strictEqual(validate(1), false)
assert.strictEqual(validate("foo"), false)
assert.strictEqual(validate(undefined), false)
})
})

0 comments on commit ad187e1

Please sign in to comment.