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.4.0
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.5.0
Choose a head ref
  • 11 commits
  • 10 files changed
  • 3 contributors

Commits on May 16, 2021

  1. Copy the full SHA
    73e4936 View commit details
  2. Copy the full SHA
    dc28feb View commit details

Commits on May 19, 2021

  1. Copy the full SHA
    6cd2493 View commit details

Commits on May 20, 2021

  1. add schemaId option to support JSON Schema draft-04 with ajv-draft-04 (

    …#1618)
    
    * add schemaId option to support JSON Schema draft-04 with ajv-draft-04 package
    
    * add schemaId arg
    epoberezkin authored May 20, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f54f1b7 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
    6e7e5a1 View commit details
  3. code style

    epoberezkin committed May 20, 2021
    Copy the full SHA
    cbb79c6 View commit details
  4. Copy the full SHA
    81a2526 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
    cf3ba81 View commit details
  6. docs: update links

    epoberezkin committed May 20, 2021
    Copy the full SHA
    e37db1a View commit details
  7. Copy the full SHA
    0b76b8f View commit details
  8. 8.5.0

    epoberezkin committed May 20, 2021
    Copy the full SHA
    be07d3d View commit details
3 changes: 2 additions & 1 deletion docs/guide/environments.md
Original file line number Diff line number Diff line change
@@ -77,7 +77,8 @@ The browser bundles are available on [cdnjs](https://cdnjs.com/libraries/ajv).
Some frameworks, e.g. Dojo, may redefine global require in a way that is not compatible with CommonJS module format. In this case Ajv bundle has to be loaded before the framework and then you can use global `ajv` (see issue [#234](https://github.com/ajv-validator/ajv/issues/234)).
:::

::: warning Ajv v8 in Internet Explorer 11 (IE11) will not work straight out of the box. To use it either [recompile it](https://ajv.js.org/standalone.html), or set the options [unicodeRegExp](https://ajv.js.org/options.html#unicoderegexp) to `false` and `code: { es5: true }`, and transpile the Ajv node module (see issue [#1585](https://github.com/ajv-validator/ajv/issues/1585#issuecomment-832486204)).
::: warning Internet Explorer 11
Ajv v8 in IE 11 will not work straight out of the box. To use it either [recompile it](../standalone.md), or set the options [unicodeRegExp](../options.md#unicoderegexp) to `false` and `code: { es5: true }`, and transpile the Ajv node module (see issue [#1585](https://github.com/ajv-validator/ajv/issues/1585#issuecomment-832486204)).
:::

## ES5 environments
22 changes: 15 additions & 7 deletions lib/compile/index.ts
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@ export interface SchemaObjCxt extends SchemaCxt {
}
interface SchemaEnvArgs {
readonly schema: AnySchema
readonly schemaId?: "$id" | "id"
readonly root?: SchemaEnv
readonly baseId?: string
readonly schemaPath?: string
@@ -72,6 +73,7 @@ interface SchemaEnvArgs {

export class SchemaEnv implements SchemaEnvArgs {
readonly schema: AnySchema
readonly schemaId?: "$id" | "id"
readonly root: SchemaEnv
baseId: string // TODO possibly, it should be readonly
schemaPath?: string
@@ -91,8 +93,9 @@ export class SchemaEnv implements SchemaEnvArgs {
let schema: AnySchemaObject | undefined
if (typeof env.schema == "object") schema = env.schema
this.schema = env.schema
this.schemaId = env.schemaId
this.root = env.root || this
this.baseId = env.baseId ?? normalizeId(schema?.$id)
this.baseId = env.baseId ?? normalizeId(schema?.[env.schemaId || "$id"])
this.schemaPath = env.schemaPath
this.localRefs = env.localRefs
this.meta = env.meta
@@ -212,7 +215,8 @@ export function resolveRef(
let _sch = resolve.call(this, root, ref)
if (_sch === undefined) {
const schema = root.localRefs?.[ref] // TODO maybe localRefs should hold SchemaEnv
if (schema) _sch = new SchemaEnv({schema, root, baseId})
const {schemaId} = this.opts
if (schema) _sch = new SchemaEnv({schema, schemaId, root, baseId})
}

if (_sch === undefined) return
@@ -273,8 +277,10 @@ export function resolveSchema(
if (!schOrRef.validate) compileSchema.call(this, schOrRef)
if (id === normalizeId(ref)) {
const {schema} = schOrRef
if (schema.$id) baseId = resolveUrl(baseId, schema.$id)
return new SchemaEnv({schema, root, baseId})
const {schemaId} = this.opts
const schId = schema[schemaId]
if (schId) baseId = resolveUrl(baseId, schId)
return new SchemaEnv({schema, schemaId, root, baseId})
}
return getJsonPointer.call(this, p, schOrRef)
}
@@ -298,8 +304,9 @@ function getJsonPointer(
schema = schema[unescapeFragment(part)]
if (schema === undefined) return
// TODO PREVENT_SCOPE_CHANGE could be defined in keyword def?
if (!PREVENT_SCOPE_CHANGE.has(part) && typeof schema == "object" && schema.$id) {
baseId = resolveUrl(baseId, schema.$id)
const schId = typeof schema == "object" && schema[this.opts.schemaId]
if (!PREVENT_SCOPE_CHANGE.has(part) && schId) {
baseId = resolveUrl(baseId, schId)
}
}
let env: SchemaEnv | undefined
@@ -309,7 +316,8 @@ function getJsonPointer(
}
// even though resolution failed we need to return SchemaEnv to throw exception
// so that compileAsync loads missing schema.
env = env || new SchemaEnv({schema, root, baseId})
const {schemaId} = this.opts
env = env || new SchemaEnv({schema, schemaId, root, baseId})
if (env.schema !== env.root.schema) return env
return undefined
}
9 changes: 5 additions & 4 deletions lib/compile/resolve.ts
Original file line number Diff line number Diff line change
@@ -91,17 +91,18 @@ const ANCHOR = /^[a-z_][-a-z0-9._]*$/i

export function getSchemaRefs(this: Ajv, schema: AnySchema): LocalRefs {
if (typeof schema == "boolean") return {}
const schemaId = normalizeId(schema.$id)
const baseIds: {[JsonPtr in string]?: string} = {"": schemaId}
const pathPrefix = getFullPath(schemaId, false)
const {schemaId} = this.opts
const schId = normalizeId(schema[schemaId])
const baseIds: {[JsonPtr in string]?: string} = {"": schId}
const pathPrefix = getFullPath(schId, false)
const localRefs: LocalRefs = {}
const schemaRefs: Set<string> = new Set()

traverse(schema, {allKeys: true}, (sch, jsonPtr, _, parentJsonPtr) => {
if (parentJsonPtr === undefined) return
const fullPath = pathPrefix + jsonPtr
let baseId = baseIds[parentJsonPtr]
if (typeof sch.$id == "string") baseId = addRef.call(this, sch.$id)
if (typeof sch[schemaId] == "string") baseId = addRef.call(this, sch[schemaId])
addAnchor.call(this, sch.$anchor)
addAnchor.call(this, sch.$dynamicAnchor)
baseIds[jsonPtr] = baseId
8 changes: 4 additions & 4 deletions lib/compile/validate/index.ts
Original file line number Diff line number Diff line change
@@ -112,9 +112,8 @@ function resetEvaluated(it: SchemaObjCxt): void {
}

function funcSourceUrl(schema: AnySchema, opts: InstanceOptions): Code {
return typeof schema == "object" && schema.$id && (opts.code.source || opts.code.process)
? _`/*# sourceURL=${schema.$id} */`
: nil
const schId = typeof schema == "object" && schema[opts.schemaId]
return schId && (opts.code.source || opts.code.process) ? _`/*# sourceURL=${schId} */` : nil
}

// schema compilation - this function is used recursively to generate code for sub-schemas
@@ -177,7 +176,8 @@ function checkNoDefault(it: SchemaObjCxt): void {
}

function updateContext(it: SchemaObjCxt): void {
if (it.schema.$id) it.baseId = resolveUrl(it.baseId, it.schema.$id)
const schId = it.schema[it.opts.schemaId]
if (schId) it.baseId = resolveUrl(it.baseId, schId)
}

function checkAsyncSchema(it: SchemaObjCxt): void {
36 changes: 24 additions & 12 deletions lib/core.ts
Original file line number Diff line number Diff line change
@@ -117,6 +117,7 @@ export interface CurrentOptions {
next?: boolean // NEW
unevaluated?: boolean // NEW
dynamicRef?: boolean // NEW
schemaId?: "id" | "$id"
jtd?: boolean // NEW
meta?: SchemaObject | boolean
defaultMeta?: string | AnySchemaObject
@@ -163,7 +164,6 @@ interface RemovedOptions {
missingRefs?: true | "ignore" | "fail"
processCode?: (code: string, schema?: SchemaEnv) => string
sourceCode?: boolean
schemaId?: string
strictDefaults?: boolean
strictKeywords?: boolean
uniqueItems?: boolean
@@ -186,7 +186,6 @@ const removedOptions: OptionsInfo<RemovedOptions> = {
missingRefs: "Pass empty schema with $id that should be ignored to ajv.addSchema.",
processCode: "Use option `code: {process: (code, schemaEnv: object) => string}`",
sourceCode: "Use option `code: {source: true}`",
schemaId: "JSON Schema draft-04 is not supported in Ajv v7/8.",
strictDefaults: "It is default now, see option `strict`.",
strictKeywords: "It is default now, see option `strict`.",
uniqueItems: '"uniqueItems" keyword is always validated.',
@@ -214,6 +213,7 @@ type RequiredInstanceOptions = {
| "loopEnum"
| "meta"
| "messages"
| "schemaId"
| "addUsedSchema"
| "validateSchema"
| "validateFormats"
@@ -241,6 +241,7 @@ function requiredOptions(o: Options): RequiredInstanceOptions {
meta: o.meta ?? true,
messages: o.messages ?? true,
inlineRefs: o.inlineRefs ?? true,
schemaId: o.schemaId ?? "$id",
addUsedSchema: o.addUsedSchema ?? true,
validateSchema: o.validateSchema ?? true,
validateFormats: o.validateFormats ?? true,
@@ -299,13 +300,19 @@ export default class Ajv {
}

_addDefaultMetaSchema(): void {
const {$data, meta} = this.opts
if (meta && $data) this.addMetaSchema($dataRefSchema, $dataRefSchema.$id, false)
const {$data, meta, schemaId} = this.opts
let _dataRefSchema: SchemaObject = $dataRefSchema
if (schemaId === "id") {
_dataRefSchema = {...$dataRefSchema}
_dataRefSchema.id = _dataRefSchema.$id
delete _dataRefSchema.$id
}
if (meta && $data) this.addMetaSchema(_dataRefSchema, _dataRefSchema[schemaId], false)
}

defaultMeta(): string | AnySchemaObject | undefined {
const {meta} = this.opts
return (this.opts.defaultMeta = typeof meta == "object" ? meta.$id || meta : undefined)
const {meta, schemaId} = this.opts
return (this.opts.defaultMeta = typeof meta == "object" ? meta[schemaId] || meta : undefined)
}

// Validate data using schema
@@ -450,8 +457,11 @@ export default class Ajv {
}
let id: string | undefined
if (typeof schema === "object") {
id = schema.$id
if (id !== undefined && typeof id != "string") throw new Error("schema $id must be string")
const {schemaId} = this.opts
id = schema[schemaId]
if (id !== undefined && typeof id != "string") {
throw new Error(`schema ${schemaId} must be string`)
}
}
key = normalizeId(key || id)
this._checkUnique(key)
@@ -499,7 +509,8 @@ export default class Ajv {
let sch
while (typeof (sch = getSchEnv.call(this, keyRef)) == "string") keyRef = sch
if (sch === undefined) {
const root = new SchemaEnv({schema: {}})
const {schemaId} = this.opts
const root = new SchemaEnv({schema: {}, schemaId})
sch = resolveSchema.call(this, root, keyRef)
if (!sch) return
this.refs[keyRef] = sch
@@ -533,7 +544,7 @@ export default class Ajv {
case "object": {
const cacheKey = schemaKeyRef
this._cache.delete(cacheKey)
let id = schemaKeyRef.$id
let id = schemaKeyRef[this.opts.schemaId]
if (id) {
id = normalizeId(id)
delete this.schemas[id]
@@ -670,8 +681,9 @@ export default class Ajv {
addSchema = this.opts.addUsedSchema
): SchemaEnv {
let id: string | undefined
const {schemaId} = this.opts
if (typeof schema == "object") {
id = schema.$id
id = schema[schemaId]
} else {
if (this.opts.jtd) throw new Error("schema must be object")
else if (typeof schema != "boolean") throw new Error("schema must be object or boolean")
@@ -681,7 +693,7 @@ export default class Ajv {

const localRefs = getSchemaRefs.call(this, schema)
baseId = normalizeId(id || baseId)
sch = new SchemaEnv({schema, meta, baseId, localRefs})
sch = new SchemaEnv({schema, schemaId, meta, baseId, localRefs})
this._cache.set(sch.schema, sch)
if (addSchema && !baseId.startsWith("#")) {
// TODO atm it is allowed to overwrite schemas without id (instead of not adding them)
2 changes: 2 additions & 0 deletions lib/types/index.ts
Original file line number Diff line number Diff line change
@@ -5,12 +5,14 @@ import type {KeywordCxt} from "../compile/validate"
import type Ajv from "../core"

interface _SchemaObject {
id?: string
$id?: string
$schema?: string
[x: string]: any // TODO
}

export interface SchemaObject extends _SchemaObject {
id?: string
$id?: string
$schema?: string
$async?: false
3 changes: 2 additions & 1 deletion lib/vocabularies/dynamic/dynamicAnchor.ts
Original file line number Diff line number Diff line change
@@ -22,7 +22,8 @@ export function dynamicAnchor(cxt: KeywordCxt, anchor: string): void {
function _getValidate(cxt: KeywordCxt): Code {
const {schemaEnv, schema, self} = cxt.it
const {root, baseId, localRefs, meta} = schemaEnv.root
const sch = new SchemaEnv({schema, root, baseId, localRefs, meta})
const {schemaId} = self.opts
const sch = new SchemaEnv({schema, schemaId, root, baseId, localRefs, meta})
compileSchema.call(self, sch)
return getValidate(cxt, sch)
}
9 changes: 6 additions & 3 deletions lib/vocabularies/validation/const.ts
Original file line number Diff line number Diff line change
@@ -16,9 +16,12 @@ const def: CodeKeywordDefinition = {
$data: true,
error,
code(cxt: KeywordCxt) {
const {gen, data, schemaCode} = cxt
// TODO optimize for scalar values in schema
cxt.fail$data(_`!${useFunc(gen, equal)}(${data}, ${schemaCode})`)
const {gen, data, $data, schemaCode, schema} = cxt
if ($data || (schema && typeof schema == "object")) {
cxt.fail$data(_`!${useFunc(gen, equal)}(${data}, ${schemaCode})`)
} else {
cxt.fail(_`${schema} !== ${data}`)
}
},
}

2 changes: 1 addition & 1 deletion lib/vocabularies/validation/enum.ts
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ const def: CodeKeywordDefinition = {

function equalCode(vSchema: Name, i: number): Code {
const sch = schema[i]
return sch && typeof sch === "object"
return typeof sch === "object" && sch !== null
? _`${eql}(${data}, ${vSchema}[${i}])`
: _`${data} === ${sch}`
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ajv",
"version": "8.4.0",
"version": "8.5.0",
"description": "Another JSON Schema Validator",
"main": "dist/ajv.js",
"types": "dist/ajv.d.ts",