Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ajv-validator/ajv
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v8.6.2
Choose a base ref
...
head repository: ajv-validator/ajv
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v8.6.3
Choose a head ref

Commits on Apr 29, 2021

  1. Copy the full SHA
    d5a683b View commit details

Commits on Jul 18, 2021

  1. Unverified

    The email in this signature doesn’t match the committer email.
    Copy the full SHA
    f662548 View commit details
  2. Merge pull request #1580 from ajv-validator/dependabot/add-v2-config-…

    …file
    
    Upgrade to GitHub-native Dependabot
    erikbrinkman authored Jul 18, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c2a7f1f View commit details
  3. json-schema.md remove extra quotation mark (#1696)

    Co-authored-by: Erik Brinkman <erik.brinkman@gmail.com>
    qwertyforce and erikbrinkman authored Jul 18, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    176d7a0 View commit details
  4. build(deps-dev): bump mocha from 8.4.0 to 9.0.2 (#1672)

    Bumps [mocha](https://github.com/mochajs/mocha) from 8.4.0 to 9.0.2.
    - [Release notes](https://github.com/mochajs/mocha/releases)
    - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)
    - [Commits](mochajs/mocha@v8.4.0...v9.0.2)
    
    Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
    
    Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
    Co-authored-by: Erik Brinkman <erik.brinkman@gmail.com>
    dependabot-preview[bot] and erikbrinkman authored Jul 18, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    500c8e6 View commit details

Commits on Aug 1, 2021

  1. docs: Fix data is not defined (#1723)

    The `const data = {...}` defined after `serialize(data)` that will throw error: `Reference Error: data is not defined`.
    mxsgx authored Aug 1, 2021
    Copy the full SHA
    14bfde3 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    af0d015 View commit details
  3. build(deps-dev): bump @types/mocha from 8.2.3 to 9.0.0 (#1714)

    Bumps [@types/mocha](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mocha) from 8.2.3 to 9.0.0.
    - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
    - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/mocha)
    
    ---
    updated-dependencies:
    - dependency-name: "@types/mocha"
      dependency-type: direct:development
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
    dependabot[bot] and epoberezkin authored Aug 1, 2021
    Copy the full SHA
    e055175 View commit details
  4. update types (#1699)

    Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
    erikbrinkman and epoberezkin authored Aug 1, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    02e175a View commit details
  5. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a9b3cbf View commit details
  6. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    e62c905 View commit details

Commits on Aug 14, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    2eb5f99 View commit details
  2. Copy the full SHA
    84caa20 View commit details
  3. news

    epoberezkin committed Aug 14, 2021
    Copy the full SHA
    8ffe5fa View commit details

Commits on Sep 1, 2021

  1. Copy the full SHA
    e403421 View commit details

Commits on Sep 12, 2021

  1. update code for breaking typescript change (catched error now has typ…

    …e `unknown`, not `any` as before) (#1760)
    epoberezkin authored Sep 12, 2021
    Copy the full SHA
    658e7a1 View commit details
  2. Fix broken links (#1753)

    asaid-0 authored Sep 12, 2021
    Copy the full SHA
    d95fc92 View commit details
  3. Copy the full SHA
    efb3d6d View commit details
  4. build(deps-dev): bump node-fetch from 2.6.1 to 3.0.0 (#1748)

    Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.1 to 3.0.0.
    - [Release notes](https://github.com/node-fetch/node-fetch/releases)
    - [Changelog](https://github.com/node-fetch/node-fetch/blob/main/docs/CHANGELOG.md)
    - [Commits](node-fetch/node-fetch@v2.6.1...v3.0.0)
    
    ---
    updated-dependencies:
    - dependency-name: node-fetch
      dependency-type: direct:development
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Sep 12, 2021
    Copy the full SHA
    a9f38cd View commit details
  5. Copy the full SHA
    87d1901 View commit details
  6. Copy the full SHA
    ae5c1fe View commit details
  7. build(deps-dev): bump @rollup/plugin-commonjs from 19.0.2 to 20.0.0 (#…

    …1726)
    
    Bumps [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/HEAD/packages/commonjs) from 19.0.2 to 20.0.0.
    - [Release notes](https://github.com/rollup/plugins/releases)
    - [Changelog](https://github.com/rollup/plugins/blob/master/packages/commonjs/CHANGELOG.md)
    - [Commits](https://github.com/rollup/plugins/commits/commonjs-v20.0.0/packages/commonjs)
    
    ---
    updated-dependencies:
    - dependency-name: "@rollup/plugin-commonjs"
      dependency-type: direct:development
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Sep 12, 2021
    Copy the full SHA
    7419ec3 View commit details
  8. Merge branch 'refResolveBaseURI' of https://github.com/rbuckton/ajv i…

    …nto rbuckton-refResolveBaseURI
    epoberezkin committed Sep 12, 2021
    Copy the full SHA
    831cb1c View commit details
  9. fix prettier

    epoberezkin committed Sep 12, 2021
    Copy the full SHA
    d9bc534 View commit details
  10. Copy the full SHA
    9a9656f View commit details
  11. fix(standalone/index.ts): makes standalone module importable to esm m…

    …odules without explicitly accessing default property. (#1757)
    
    fix(lib/runtime/parseJson.ts): added `any` to error object in order to fix the type checking error.
    
    Co-authored-by: Bhavin <bhavinkamani@gmail.com>
    bhvngt and bhavinkamani authored Sep 12, 2021
    Copy the full SHA
    6ef0c66 View commit details
  12. 8.6.3

    epoberezkin committed Sep 12, 2021
    Copy the full SHA
    760fd10 View commit details
26 changes: 26 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
ignore:
- dependency-name: "@types/node"
versions:
- 15.0.0
- dependency-name: eslint-config-prettier
versions:
- 8.0.0
- 8.1.0
- 8.2.0
- dependency-name: karma
versions:
- 6.0.3
- 6.0.4
- 6.1.0
- 6.1.1
- 6.1.2
- 6.2.0
- 6.3.0
- 6.3.1
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -187,6 +187,7 @@ Ajv stands out as the implementation of choice - it provides a rich API which ma
[Vega-Lite](https://vega.github.io/vega-lite/)
[![middy](./projects/middy.png)](https://middy.js.org)
[JSDoc](https://github.com/jsdoc/jsdoc)
[![Ts.ED](./projects/tsed.png)](https://tsed.io)

</Projects>
</HomeSection>
4 changes: 2 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ if (validate(data)) {
}
```

See more advanced example in [the test](../spec/types/json-schema.spec.ts).
See more advanced example in [the test](https://github.com/ajv-validator/ajv/blob/master/spec/types/json-schema.spec.ts).

<a name="jtd-serialize"></a>

@@ -385,7 +385,7 @@ if (validate(data)) {
}
```

Also see an example in [this test](../spec/types/error-parameters.spec.ts)
Also see an example in [this test](https://github.com/ajv-validator/ajv/blob/master/spec/types/error-parameters.spec.ts)

- `maxItems`, `minItems`, `maxLength`, `minLength`, `maxProperties`, `minProperties`:

4 changes: 2 additions & 2 deletions docs/codegen.md
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ if (num0 > 0) {
}
```

`.const`, `.if` and `.code` above are methods of CodeGen class that generate code inside class instance `gen` - see [source code](https://github.com/ajv-validator/ajv/blob/master/lib/compile/codegen/index.ts) for all available methods and [tests](../spec/codegen.spec.ts) for other code generation examples.
`.const`, `.if` and `.code` above are methods of CodeGen class that generate code inside class instance `gen` - see [source code](https://github.com/ajv-validator/ajv/blob/master/lib/compile/codegen/index.ts) for all available methods and [tests](https://github.com/ajv-validator/ajv/blob/master/spec/codegen.spec.ts) for other code generation examples.

These methods only accept instances of private class `_Code`, other values will be rejected by Typescript compiler - the risk to pass unsafe string is mitigated on type level.

@@ -72,7 +72,7 @@ CodeGen class generates code trees and performs several optimizations before the
These optimizations assume that the expressions in `if` conditions, `for` statement headers and assigned expressions are free of any side effects - this is the case for all pre-defined validation keywords.
:::

See [these tests](../spec/codegen.spec.ts) for examples.
See [these tests](https://github.com/ajv-validator/ajv/blob/master/spec/codegen.spec.ts) for examples.

By default Ajv does 1-pass optimization - based on the test suite it reduces the code size by 10.5% and the number of tree nodes by 16.7% (TODO benchmark the validation time). The second optimization pass changes it by less than 0.1%, so you won't need it unless you have really complex schemas or if you generate standalone code and want it to pass relevant eslint rules.

2 changes: 1 addition & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
@@ -76,7 +76,7 @@ There are several ways to implement the described logic that would allow two pro

This problem is related to the problem explained above - properties treated as additional in the sense of `additionalProperties` keyword, based on `properties`/`patternProperties` keyword in the same schema object.

See the example in [Filtering Data](https://github.com/ajv-validator/ajv#filtering-data) section of readme.
See the example in the [Removing Additional Data](https://ajv.js.org/guide/modifying-data.html#removing-additional-properties) section of the docs.

## Generating schemas with resolved references ($ref)

2 changes: 1 addition & 1 deletion docs/guide/async-validation.md
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ Example:
const ajv = new Ajv()

ajv.addKeyword({
keyword: "idExists"
keyword: "idExists",
async: true,
type: "number",
validate: checkIdExists,
2 changes: 1 addition & 1 deletion docs/guide/combining-schemas.md
Original file line number Diff line number Diff line change
@@ -112,7 +112,7 @@ const ajv = new Ajv2019({
const validate = ajv.getSchema("https://example.com/strict-tree")
```

See [dynamic-refs](../spec/dynamic-ref.spec.ts) test for the example using `$dynamicAnchor`/`$dynamicRef`.
See [dynamic-refs](https://github.com/ajv-validator/ajv/blob/master/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:

7 changes: 4 additions & 3 deletions docs/guide/getting-started.md
Original file line number Diff line number Diff line change
@@ -127,15 +127,16 @@ const schema = {
}

const serialize = ajv.compileSerializer(schema)
console.log(serialize(data))

const parse = ajv.compileParser(schema)

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

console.log(serialize(data))

const parse = ajv.compileParser(schema)

const json = '{"foo": 1, "bar": "abc"}'
const invalidJson = '{"unknown": "abc"}'

2 changes: 1 addition & 1 deletion docs/json-schema.md
Original file line number Diff line number Diff line change
@@ -290,7 +290,7 @@ The value of the keywords should be a number. The data to be valid should have l

_invalid_: `"abcdef"`

2) _schema_: `{type: "string", minLength": 2}`
2) _schema_: `{type: "string", minLength: 2}`

_valid_: `"ab"`, `"😀😀"`

2 changes: 1 addition & 1 deletion docs/json-type-definition.md
Original file line number Diff line number Diff line change
@@ -376,7 +376,7 @@ const schema: JTDSchemaType<LinkedList, {node: LinkedList}> = {

### Notable Omissions

`JTDSchemaType` currently validats that if the schema compiles it will verify an accurate type, but there are a few places with potentially unexpected behavior.
`JTDSchemaType` currently validates that if the schema compiles it will verify an accurate type, but there are a few places with potentially unexpected behavior.
`JTDSchemaType` doesn't verify the schema is correct. It won't reject schemas that definitions anywhere by the root, and it won't reject discriminator schemas that still define the descriminator in mapping properties. It also won't verify that enum schemas have every enum member as this isn't generally feasible in typescript yet.

## Extending JTD
2 changes: 1 addition & 1 deletion docs/keywords.md
Original file line number Diff line number Diff line change
@@ -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](../spec/keyword.spec.ts#L316).
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).

## Schema compilation context

10 changes: 10 additions & 0 deletions docs/news/2021-05-24-ajv-online-event-video.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
news: true
title: Ajv online event video uploaded
date: 2021-05-24
more: false
---

Huge thanks to everybody who joined, and to the speakers! The video of the event is [available on YouTube](https://www.youtube.com/watch?v=KxSKqXEBB7A).

<!-- more -->
12 changes: 12 additions & 0 deletions docs/news/2021-07-22-ajv-microsoft-foss-fund-award.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
news: true
title: Microsoft FOSS award
date: 2021-07-22
more: false
---

Ajv was awarded a sponsorship from [Microsoft FOSS fund](https://github.com/microsoft/foss-fund/blob/main/README.md#2021) - huge thanks to Microsoft and the engineers who voted to support Ajv development.

This award will contribute to a long term maintenance of Ajv.

<!-- more -->
Binary file added docs/projects/tsed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions docs/standalone.md
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ npm install ajv
```javascript
const Ajv = require("ajv") // version >= v7.0.0
const ajv = new Ajv({code: {source: true}}) // this option is required to generate standalone code
const standaloneCode = require("ajv/dist/standalone")
const standaloneCode = require("ajv/dist/standalone").default

const schema = {
$id: "https://example.com/object.json",
@@ -70,7 +70,9 @@ const requireFromString = require("require-from-string")
const standaloneValidate = requireFromString(moduleCode) // for a single default export
```

Ajv package should still be a run-time dependency for most schemas, but generated modules can only depend on code in [runtime](https://github.com/ajv-validator/ajv/tree/master/lib/runtime) folder, so the whole Ajv will not be included in the bundle (or executed) if you require the modules with standalone validation code from your application code.
### Requirement at runtime

To run the standalone generated functions, the Ajv package should still be a run-time dependency for most schemas, but generated modules can only depend on code in [runtime](https://github.com/ajv-validator/ajv/tree/master/lib/runtime) folder, so the whole Ajv will not be included in the bundle (or executed) if you require the modules with standalone validation code from your application code.

## Configuration and limitations

9 changes: 5 additions & 4 deletions lib/compile/index.ts
Original file line number Diff line number Diff line change
@@ -300,11 +300,12 @@ function getJsonPointer(
): SchemaEnv | undefined {
if (parsedRef.fragment?.[0] !== "/") return
for (const part of parsedRef.fragment.slice(1).split("/")) {
if (typeof schema == "boolean") return
schema = schema[unescapeFragment(part)]
if (schema === undefined) return
if (typeof schema === "boolean") return
const partSchema = schema[unescapeFragment(part)]
if (partSchema === undefined) return
schema = partSchema
// TODO PREVENT_SCOPE_CHANGE could be defined in keyword def?
const schId = typeof schema == "object" && schema[this.opts.schemaId]
const schId = typeof schema === "object" && schema[this.opts.schemaId]
if (!PREVENT_SCOPE_CHANGE.has(part) && schId) {
baseId = resolveUrl(baseId, schId)
}
4 changes: 2 additions & 2 deletions lib/compile/resolve.ts
Original file line number Diff line number Diff line change
@@ -89,10 +89,10 @@ export function resolveUrl(baseId: string, id: string): string {

const ANCHOR = /^[a-z_][-a-z0-9._]*$/i

export function getSchemaRefs(this: Ajv, schema: AnySchema): LocalRefs {
export function getSchemaRefs(this: Ajv, schema: AnySchema, baseId: string): LocalRefs {
if (typeof schema == "boolean") return {}
const {schemaId} = this.opts
const schId = normalizeId(schema[schemaId])
const schId = normalizeId(schema[schemaId] || baseId)
const baseIds: {[JsonPtr in string]?: string} = {"": schId}
const pathPrefix = getFullPath(schId, false)
const localRefs: LocalRefs = {}
2 changes: 1 addition & 1 deletion lib/core.ts
Original file line number Diff line number Diff line change
@@ -694,8 +694,8 @@ export default class Ajv {
let sch = this._cache.get(schema)
if (sch !== undefined) return sch

const localRefs = getSchemaRefs.call(this, schema)
baseId = normalizeId(id || baseId)
const localRefs = getSchemaRefs.call(this, schema, baseId)
sch = new SchemaEnv({schema, schemaId, meta, baseId, localRefs})
this._cache.set(sch.schema, sch)
if (addSchema && !baseId.startsWith("#")) {
13 changes: 8 additions & 5 deletions lib/runtime/parseJson.ts
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ export function parseJson(s: string, pos: number): unknown {
parseJson.position = pos + s.length
return JSON.parse(s)
} catch (e) {
matches = rxParseJson.exec(e.message)
matches = rxParseJson.exec((e as Error).message)
if (!matches) {
parseJson.message = "unexpected end"
return undefined
@@ -128,14 +128,16 @@ export function parseJsonString(s: string, pos: number): string | undefined {
let code = 0
while (count--) {
code <<= 4
c = s[pos].toLowerCase()
c = s[pos]
if (c === undefined) {
errorMessage("unexpected end")
return undefined
}
c = c.toLowerCase()
if (c >= "a" && c <= "f") {
code += c.charCodeAt(0) - CODE_A + 10
} else if (c >= "0" && c <= "9") {
code += c.charCodeAt(0) - CODE_0
} else if (c === undefined) {
errorMessage("unexpected end")
return undefined
} else {
errorMessage(`unexpected token ${c}`)
return undefined
@@ -147,6 +149,7 @@ export function parseJsonString(s: string, pos: number): string | undefined {
errorMessage(`unexpected token ${c}`)
return undefined
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
} else if (c === undefined) {
errorMessage("unexpected end")
return undefined
7 changes: 6 additions & 1 deletion lib/standalone/index.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import type {SchemaEnv} from "../compile"
import {UsedScopeValues, UsedValueState, ValueScopeName, varKinds} from "../compile/codegen/scope"
import {_, nil, _Code, Code, getProperty} from "../compile/codegen/code"

export default function standaloneCode(
function standaloneCode(
ajv: AjvCore,
refsOrFunc?: {[K in string]?: string} | AnyValidateFunction
): string {
@@ -86,3 +86,8 @@ export default function standaloneCode(
}
}
}

module.exports = exports = standaloneCode
Object.defineProperty(exports, "__esModule", {value: true})

export default standaloneCode
51 changes: 30 additions & 21 deletions lib/types/json-schema.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,10 @@ type StrictNullChecksWrapper<Name extends string, Type> = undefined extends null
? `strictNullChecks must be true in tsconfig to use ${Name}`
: Type

type UnionToIntersection<U> = (U extends any ? (_: U) => void : never) extends (_: infer I) => void
? I
: never

export type SomeJSONSchema = UncheckedJSONSchemaType<Known, true>

type UncheckedPartialSchema<T> = Partial<UncheckedJSONSchemaType<T, true>>
@@ -39,20 +43,23 @@ type UncheckedJSONSchemaType<T, IsPartial extends boolean> = (
}
// this union allows for { type: (primitive)[] } style schemas
| ({
type: (T extends number
type: readonly (T extends number
? JSONType<"number" | "integer", IsPartial>
: T extends string
? JSONType<"string", IsPartial>
: T extends boolean
? JSONType<"boolean", IsPartial>
: never)[]
} & (T extends number
? NumberKeywords
: T extends string
? StringKeywords
: T extends boolean
? unknown
: never))
} & UnionToIntersection<
T extends number
? NumberKeywords
: T extends string
? StringKeywords
: T extends boolean
? // eslint-disable-next-line @typescript-eslint/ban-types
{}
: never
>)
// this covers "normal" types; it's last so typescript looks to it first for errors
| ((T extends number
? {
@@ -64,9 +71,9 @@ type UncheckedJSONSchemaType<T, IsPartial extends boolean> = (
} & StringKeywords
: T extends boolean
? {
type: "boolean"
type: JSONType<"boolean", IsPartial>
}
: T extends [any, ...any[]]
: T extends readonly [any, ...any[]]
? {
// JSON AnySchema for tuple
type: JSONType<"array", IsPartial>
@@ -99,7 +106,7 @@ type UncheckedJSONSchemaType<T, IsPartial extends boolean> = (
properties?: IsPartial extends true
? Partial<UncheckedPropertiesSchema<T>>
: UncheckedPropertiesSchema<T>
patternProperties?: {[Pattern in string]?: UncheckedJSONSchemaType<T[string], false>}
patternProperties?: Record<string, UncheckedJSONSchemaType<T[string], false>>
propertyNames?: Omit<UncheckedJSONSchemaType<string, false>, "type"> & {type?: "string"}
dependencies?: {[K in keyof T]?: Readonly<(keyof T)[]> | UncheckedPartialSchema<T>}
dependentRequired?: {[K in keyof T]?: Readonly<(keyof T)[]>}
@@ -116,6 +123,7 @@ type UncheckedJSONSchemaType<T, IsPartial extends boolean> = (
: {required: Readonly<UncheckedRequiredMembers<T>[]>})
: T extends null
? {
type: JSONType<"null", IsPartial>
nullable: true
}
: never) & {
@@ -131,22 +139,23 @@ type UncheckedJSONSchemaType<T, IsPartial extends boolean> = (
[keyword: string]: any
$id?: string
$ref?: string
$defs?: {
[Key in string]?: UncheckedJSONSchemaType<Known, true>
}
definitions?: {
[Key in string]?: UncheckedJSONSchemaType<Known, true>
}
$defs?: Record<string, UncheckedJSONSchemaType<Known, true>>
definitions?: Record<string, UncheckedJSONSchemaType<Known, true>>
}

export type JSONSchemaType<T> = StrictNullChecksWrapper<
"JSONSchemaType",
UncheckedJSONSchemaType<T, false>
>

type Known = KnownRecord | [Known, ...Known[]] | Known[] | number | string | boolean | null

interface KnownRecord extends Record<string, Known> {}
type Known =
| {[key: string]: Known}
| [Known, ...Known[]]
| Known[]
| number
| string
| boolean
| null

type UncheckedPropertiesSchema<T> = {
[K in keyof T]-?: (UncheckedJSONSchemaType<T[K], false> & Nullable<T[K]>) | {$ref: string}
@@ -169,7 +178,7 @@ export type RequiredMembers<T> = StrictNullChecksWrapper<
type Nullable<T> = undefined extends T
? {
nullable: true
const?: never // any non-null value would fail `const: null`, `null` would fail any other value in const
const?: null // any non-null value would fail `const: null`, `null` would fail any other value in const
enum?: Readonly<(T | null)[]> // `null` must be explicitly included in "enum" for `null` to pass
default?: T | null
}
28 changes: 12 additions & 16 deletions lib/types/jtd-schema.ts
Original file line number Diff line number Diff line change
@@ -228,27 +228,23 @@ type JTDDataDef<S, D extends Record<string, unknown>> =
optionalProperties?: Record<string, unknown>
additionalProperties?: boolean
}
? {-readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D>} &
{
-readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef<
S["optionalProperties"][K],
D
>
} &
([S["additionalProperties"]] extends [true] ? Record<string, unknown> : unknown)
? {-readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D>} & {
-readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef<
S["optionalProperties"][K],
D
>
} & ([S["additionalProperties"]] extends [true] ? Record<string, unknown> : unknown)
: S extends {
properties?: Record<string, unknown>
optionalProperties: Record<string, unknown>
additionalProperties?: boolean
}
? {-readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D>} &
{
-readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef<
S["optionalProperties"][K],
D
>
} &
([S["additionalProperties"]] extends [true] ? Record<string, unknown> : unknown)
? {-readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D>} & {
-readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef<
S["optionalProperties"][K],
D
>
} & ([S["additionalProperties"]] extends [true] ? Record<string, unknown> : unknown)
: // values
S extends {values: infer V}
? Record<string, JTDDataDef<V, D>>
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ajv",
"version": "8.6.2",
"version": "8.6.3",
"description": "Another JSON Schema Validator",
"main": "dist/ajv.js",
"types": "dist/ajv.d.ts",
@@ -64,12 +64,12 @@
},
"devDependencies": {
"@ajv-validator/config": "^0.3.0",
"@rollup/plugin-commonjs": "^19.0.0",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-typescript": "^8.2.1",
"@types/chai": "^4.2.12",
"@types/mocha": "^8.0.3",
"@types/mocha": "^9.0.0",
"@types/node": "^16.3.2",
"@types/require-from-string": "^1.2.0",
"@typescript-eslint/eslint-plugin": "^3.8.0",
@@ -92,8 +92,8 @@
"karma-chrome-launcher": "^3.0.0",
"karma-mocha": "^2.0.0",
"lint-staged": "^11.0.0",
"mocha": "^8.0.1",
"node-fetch": "^2.6.1",
"mocha": "^9.0.2",
"node-fetch": "^3.0.0",
"nyc": "^15.0.0",
"prettier": "^2.3.1",
"rollup": "^2.44.0",
2 changes: 1 addition & 1 deletion spec/ajv.spec.ts
Original file line number Diff line number Diff line change
@@ -225,7 +225,7 @@ describe("Ajv", () => {
ajv.addSchema({$id: 1, type: "integer"})
throw new Error("should have throw exception")
} catch (e) {
e.message.should.equal("schema $id must be string")
;(e as Error).message.should.equal("schema $id must be string")
}
})

29 changes: 27 additions & 2 deletions spec/resolve.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type AjvCore from "../dist/core"
import getAjvInstances from "./ajv_instances"
import _Ajv from "./ajv"
import {AnyValidateFunction} from "../dist/types"
import type {AnyValidateFunction} from "../dist/types"
import type MissingRefError from "../dist/compile/ref_error"
import chai from "./chai"
const should = chai.should()

@@ -67,6 +68,29 @@ describe("resolve", () => {
})
})

it("should resolve fragment $id in schema refs when root $id not present", () => {
const schema = {
$schema: "http://json-schema.org/draft-07/schema#",
definitions: {
SeeAlso: {$id: "#SeeAlso", type: "number"},
Engine: {
$id: "#Engine",
type: "object",
properties: {
see_also: {$ref: "#SeeAlso"},
},
},
},
}

instances.forEach((ajv) => {
ajv.addSchema(schema, "yaml.json")
const data = {see_also: 1}
const validate = ajv.validate("yaml.json#/definitions/Engine", data)
validate.should.equal(true)
})
})

it("should throw if the same id resolves to two different schemas", () => {
instances.forEach((ajv) => {
ajv.compile({
@@ -200,7 +224,8 @@ describe("resolve", () => {
type: "object",
properties: {a: {$ref: opts.ref}},
})
} catch (e) {
} catch (err) {
const e = err as MissingRefError
e.missingRef.should.equal(opts.expectedMissingRef)
e.missingSchema.should.equal(opts.expectedMissingSchema)
}
60 changes: 51 additions & 9 deletions spec/types/json-schema.spec.ts
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ interface MyData {
[x: string]: string
}
boo?: true
tuple?: [number, string]
tuple?: readonly [number, string]
arr: {id: number}[]
map: {[K in string]?: number}
notBoo?: string // should not be present if "boo" is present
@@ -32,7 +32,7 @@ const arrSchema: JSONSchemaType<MyData["arr"]> = {
required: ["id"],
},
uniqueItems: true,
}
} as const

const mySchema: JSONSchemaType<MyData> & {
definitions: {
@@ -95,7 +95,7 @@ const mySchema: JSONSchemaType<MyData> & {
},
additionalProperties: false,
required: ["foo", "baz", "arr", "map"], // any other property added here won't typecheck
}
} as const

type MyUnionData = {a: boolean} | string | number

@@ -115,7 +115,7 @@ const myUnionSchema: JSONSchemaType<MyUnionData> = {
minLength: 1,
},
],
}
} as const

// because of the current definition, you can do this nested recusion
const myNestedUnionSchema: JSONSchemaType<MyUnionData> = {
@@ -138,12 +138,12 @@ const myNestedUnionSchema: JSONSchemaType<MyUnionData> = {
type: "number",
},
],
}
} as const

// @ts-expect-error can't use empty array for invalid type
const invalidSchema: JSONSchemaType<MyData> = {
// allowing union types necessarily allows this to pass :/
const emptyType: JSONSchemaType<MyData> = {
type: [],
}
} as const

type MyEnumRecord = Record<"a" | "b" | "c" | "d", number | undefined>

@@ -244,6 +244,22 @@ describe("JSONSchemaType type and validation as a type guard", () => {
}
should.not.exist(ajv.errors)
})

it("should fail for invalid unions", () => {
// @ts-expect-error extra type
const extraSchema: JSONSchemaType<string | number> = {
type: ["string", "number", "boolean"],
} as const

// @ts-expect-error extra properties
const extraProps: JSONSchemaType<string, boolean> = {
type: ["string", "boolean"],
maximum: 5, // number property
} as const

// eslint-disable-next-line no-void
void [extraSchema, extraProps]
})
})

describe("schema has type SchemaObject", () => {
@@ -286,11 +302,37 @@ describe("JSONSchemaType type and validation as a type guard", () => {
const optionalSchema: JSONSchemaType<{a?: number}> = {
type: "object",
}

// eslint-disable-next-line no-void
void optionalSchema
})
})

describe("schema works for primitives", () => {
it("allows partial boolean sub schemas", () => {
// this schema doesn't have much meaning, but it wasn't allowed before
const trueSchema: JSONSchemaType<true> = {
type: "boolean",
not: {const: false},
} as const

// eslint-disable-next-line no-void
void trueSchema
})

it("validates simple null", () => {
const nullSchema: JSONSchemaType<null> = {
type: "null",
nullable: true,
const: null,
enum: [null],
} as const
const validate = ajv.compile(nullSchema)
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
validate(null).should.be.true
})
})
})

// eslint-disable-next-line no-void
void invalidSchema
void emptyType