diff --git a/docs/api.md b/docs/api.md
index 5dfb0c3090..2a06807309 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -251,7 +251,7 @@ Add validation keyword to Ajv instance.
Keyword should be different from all standard JSON Schema keywords and different from previously defined keywords. There is no way to redefine keywords or to remove keyword definition from the instance.
-Keyword must start with a letter, `_` or `$`, and may continue with letters, numbers, `_`, `$`, or `-`.
+Keyword must start with an ASCII letter, `_` or `$`, and may continue with ASCII letters, numbers, `_`, `$`, `-`, or `:`.
It is recommended to use an application-specific prefix for keywords to avoid current and future name collisions.
Example Keywords:
diff --git a/docs/guide/managing-schemas.md b/docs/guide/managing-schemas.md
index eb007b5394..4c860755c9 100644
--- a/docs/guide/managing-schemas.md
+++ b/docs/guide/managing-schemas.md
@@ -186,7 +186,7 @@ In the example above, the key passed to the `addSchema` method was used to retri
### Pre-adding all schemas vs adding on demand
-In the example above all schemas were added in advance. It is also possible, to add schemas as they are used - it can be helpful if there is many schemas. In this case, you need to check first whether the schema is already added by calling `getSchema` method - it would return `undefined` if not:
+In the example above all schemas were added in advance. It is also possible, to add schemas as they are used - it can be helpful if there are many schemas. In this case, you need to check first whether the schema is already added by calling `getSchema` method - it would return `undefined` if not:
```javascript
const schema_user = require("./schema_user.json")
diff --git a/docs/guide/modifying-data.md b/docs/guide/modifying-data.md
index 2705c0bedf..5b1d893bd3 100644
--- a/docs/guide/modifying-data.md
+++ b/docs/guide/modifying-data.md
@@ -210,7 +210,7 @@ With `useDefaults` option `default` keywords throws exception during schema comp
The strict mode option can change the behaviour for these unsupported defaults (`strict: false` to ignore them, `"log"` to log a warning).
-See [Strict mode](./strict-mode.md).
+See [Strict mode](../strict-mode.md).
::: tip Default with discriminator keyword
Defaults will be assigned in schemas inside `oneOf` in case [discriminator](../json-schema.md#discriminator) keyword is used.
diff --git a/docs/json-schema.md b/docs/json-schema.md
index c888c636d6..0b7659ef56 100644
--- a/docs/json-schema.md
+++ b/docs/json-schema.md
@@ -478,11 +478,11 @@ To create and equivalent schema in draft-2020-12 use keywords [prefixItems](#pre
The value of the keyword should be a boolean or an object.
-If `items` keyword is not present or it is an object, `additionalItems` keyword should be ignored regardless of its value. By default Ajv will throw exception in this case - see [Strict mode](./strict-mode.md)
+`additionalItems` keyword is ignored if `items` keyword is not present or is an object. By default Ajv will throw exception in this case - see [Strict mode](./strict-mode.md)
-If `items` keyword is an array and data array has not more items than the length of `items` keyword value, `additionalItems` keyword is also ignored.
+`additionalItems` keyword is ignored if `items` keyword has more elements than data array.
-If the length of data array is bigger than the length of "items" keyword value than the result of the validation depends on the value of `additionalItems` keyword:
+If the data array has more elements than the `items` keyword value then the result of the validation depends on the value of `additionalItems` keyword:
- `false`: data is invalid
- `true`: data is valid
diff --git a/docs/options.md b/docs/options.md
index 6f74b02b39..fdce7f571b 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -344,7 +344,7 @@ Include human-readable messages in errors. `true` by default. `false` can be pas
### uriResolver
-By default `uriResolver` is undefined and relies on the embedded uriResolver [uri-js](https://github.com/garycourt/uri-js). Pass an object that satisfies the interface [UriResolver](https://github.com/ajv-validator/ajv/blob/master/lib/types/index.ts) to be used in replacement. One alternative is [fast-uri](https://github.com/fastify/fast-uri).
+By default `uriResolver` is undefined and relies on the embedded uriResolver [fast-uri](https://github.com/fastify/fast-uri). Pass an object that satisfies the interface [UriResolver](https://github.com/ajv-validator/ajv/blob/master/lib/types/index.ts) to be used in replacement. One alternative is [uri-js](https://github.com/garycourt/uri-js).
### code
diff --git a/docs/strict-mode.md b/docs/strict-mode.md
index d067d9d5ab..5aed953efc 100644
--- a/docs/strict-mode.md
+++ b/docs/strict-mode.md
@@ -42,10 +42,10 @@ By default Ajv fails schema compilation when unknown keywords are used. Users ca
ajv.addKeyword("allowedKeyword")
```
-or
+or use the convenience method `addVocabulary` for multiple keywords
```javascript
-ajv.addVocabulary(["allowed1", "allowed2"])
+ajv.addVocabulary(["allowed1", "allowed2"]) // simply calls addKeyword multiple times
```
#### Ignored "additionalItems" keyword
diff --git a/lib/compile/codegen/code.ts b/lib/compile/codegen/code.ts
index b17701973e..9d4de6149f 100644
--- a/lib/compile/codegen/code.ts
+++ b/lib/compile/codegen/code.ts
@@ -1,3 +1,4 @@
+// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export abstract class _CodeOrName {
abstract readonly str: string
abstract readonly names: UsedNames
diff --git a/lib/compile/index.ts b/lib/compile/index.ts
index 3dac2699b2..bfc3934552 100644
--- a/lib/compile/index.ts
+++ b/lib/compile/index.ts
@@ -14,7 +14,7 @@ import N from "./names"
import {LocalRefs, getFullPath, _getFullPath, inlineRef, normalizeId, resolveUrl} from "./resolve"
import {schemaHasRulesButRef, unescapeFragment} from "./util"
import {validateFunctionCode} from "./validate"
-import * as URI from "uri-js"
+import {URIComponent} from "fast-uri"
import {JSONType} from "./rules"
export type SchemaRefs = {
@@ -295,7 +295,7 @@ const PREVENT_SCOPE_CHANGE = new Set([
function getJsonPointer(
this: Ajv,
- parsedRef: URI.URIComponents,
+ parsedRef: URIComponent,
{baseId, schema, root}: SchemaEnv
): SchemaEnv | undefined {
if (parsedRef.fragment?.[0] !== "/") return
diff --git a/lib/compile/resolve.ts b/lib/compile/resolve.ts
index be283866ca..b8c4aca394 100644
--- a/lib/compile/resolve.ts
+++ b/lib/compile/resolve.ts
@@ -1,6 +1,6 @@
import type {AnySchema, AnySchemaObject, UriResolver} from "../types"
import type Ajv from "../ajv"
-import type {URIComponents} from "uri-js"
+import type {URIComponent} from "fast-uri"
import {eachItem} from "./util"
import * as equal from "fast-deep-equal"
import * as traverse from "json-schema-traverse"
@@ -73,7 +73,7 @@ export function getFullPath(resolver: UriResolver, id = "", normalize?: boolean)
return _getFullPath(resolver, p)
}
-export function _getFullPath(resolver: UriResolver, p: URIComponents): string {
+export function _getFullPath(resolver: UriResolver, p: URIComponent): string {
const serialized = resolver.serialize(p)
return serialized.split("#")[0] + "#"
}
diff --git a/lib/runtime/uri.ts b/lib/runtime/uri.ts
index 7dd35f9d17..5450549cd5 100644
--- a/lib/runtime/uri.ts
+++ b/lib/runtime/uri.ts
@@ -1,4 +1,4 @@
-import * as uri from "uri-js"
+import * as uri from "fast-uri"
type URI = typeof uri & {code: string}
;(uri as URI).code = 'require("ajv/dist/runtime/uri").default'
diff --git a/lib/types/index.ts b/lib/types/index.ts
index b5ef53eebd..39bc51b0b9 100644
--- a/lib/types/index.ts
+++ b/lib/types/index.ts
@@ -1,4 +1,4 @@
-import * as URI from "uri-js"
+import {URIComponent} from "fast-uri"
import type {CodeGen, Code, Name, ScopeValueSets, ValueScopeName} from "../compile/codegen"
import type {SchemaEnv, SchemaCxt, SchemaObjCxt} from "../compile"
import type {JSONType} from "../compile/rules"
@@ -238,7 +238,7 @@ export interface RegExpLike {
}
export interface UriResolver {
- parse(uri: string): URI.URIComponents
+ parse(uri: string): URIComponent
resolve(base: string, path: string): string
- serialize(component: URI.URIComponents): string
+ serialize(component: URIComponent): string
}
diff --git a/lib/types/json-schema.ts b/lib/types/json-schema.ts
index 281a38bdb0..065c972e54 100644
--- a/lib/types/json-schema.ts
+++ b/lib/types/json-schema.ts
@@ -108,25 +108,25 @@ type UncheckedJSONSchemaType = (
: UncheckedPropertiesSchema
patternProperties?: Record>
propertyNames?: Omit, "type"> & {type?: "string"}
- dependencies?: {[K in keyof T]?: Readonly<(keyof T)[]> | UncheckedPartialSchema}
- dependentRequired?: {[K in keyof T]?: Readonly<(keyof T)[]>}
+ dependencies?: {[K in keyof T]?: readonly (keyof T)[] | UncheckedPartialSchema}
+ dependentRequired?: {[K in keyof T]?: readonly (keyof T)[]}
dependentSchemas?: {[K in keyof T]?: UncheckedPartialSchema}
minProperties?: number
maxProperties?: number
} & (IsPartial extends true // "required" is not necessary if it's a non-partial type with no required keys // are listed it only asserts that optional cannot be listed. // "required" type does not guarantee that all required properties
- ? {required: Readonly<(keyof T)[]>}
+ ? {required: readonly (keyof T)[]}
: [UncheckedRequiredMembers] extends [never]
- ? {required?: Readonly[]>}
- : {required: Readonly[]>})
+ ? {required?: readonly UncheckedRequiredMembers[]}
+ : {required: readonly UncheckedRequiredMembers[]})
: T extends null
? {
type: JSONType<"null", IsPartial>
nullable: true
}
: never) & {
- allOf?: Readonly[]>
- anyOf?: Readonly[]>
- oneOf?: Readonly[]>
+ allOf?: readonly UncheckedPartialSchema[]
+ anyOf?: readonly UncheckedPartialSchema[]
+ oneOf?: readonly UncheckedPartialSchema[]
if?: UncheckedPartialSchema
then?: UncheckedPartialSchema
else?: UncheckedPartialSchema
@@ -176,12 +176,12 @@ type Nullable = undefined extends T
? {
nullable: true
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
+ enum?: readonly (T | null)[] // `null` must be explicitly included in "enum" for `null` to pass
default?: T | null
}
: {
nullable?: false
const?: T
- enum?: Readonly
+ enum?: readonly T[]
default?: T
}
diff --git a/package.json b/package.json
index ca56231a8f..17df7b1473 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ajv",
- "version": "8.16.0",
+ "version": "8.17.1",
"description": "Another JSON Schema Validator",
"main": "dist/ajv.js",
"types": "dist/ajv.d.ts",
@@ -59,9 +59,9 @@
"runkitExampleFilename": ".runkit_example.js",
"dependencies": {
"fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.4.1"
+ "require-from-string": "^2.0.2"
},
"devDependencies": {
"@ajv-validator/config": "^0.5.0",
@@ -83,7 +83,6 @@
"dayjs-plugin-utc": "^0.1.2",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
- "fast-uri": "^2.3.0",
"glob": "^10.3.10",
"husky": "^9.0.11",
"if-node-version": "^1.1.1",
@@ -104,7 +103,8 @@
"rollup-plugin-terser": "^7.0.2",
"ts-node": "^10.9.2",
"tsify": "^5.0.4",
- "typescript": "5.3.3"
+ "typescript": "5.3.3",
+ "uri-js": "^4.4.1"
},
"collective": {
"type": "opencollective",
diff --git a/spec/resolve.spec.ts b/spec/resolve.spec.ts
index 2fe5b10417..032f99ff83 100644
--- a/spec/resolve.spec.ts
+++ b/spec/resolve.spec.ts
@@ -4,17 +4,17 @@ import _Ajv from "./ajv"
import type {AnyValidateFunction} from "../dist/types"
import type MissingRefError from "../dist/compile/ref_error"
import chai from "./chai"
-import * as fastUri from "fast-uri"
+import * as uriJs from "uri-js"
const should = chai.should()
-const uriResolvers = [undefined, fastUri]
+const uriResolvers = [undefined, uriJs]
uriResolvers.forEach((resolver) => {
let describeTitle: string
if (resolver !== undefined) {
- describeTitle = "fast-uri resolver"
- } else {
describeTitle = "uri-js resolver"
+ } else {
+ describeTitle = "fast-uri resolver"
}
describe(describeTitle, () => {
describe("resolve", () => {
@@ -180,6 +180,41 @@ uriResolvers.forEach((resolver) => {
})
})
+ describe("URIs with encoded characters (issue #2447)", () => {
+ it("should resolve the ref", () => {
+ const schema = {
+ $ref: "#/definitions/Record%3Cstring%2CPerson%3E",
+ $schema: "http://json-schema.org/draft-07/schema#",
+ definitions: {
+ Person: {
+ type: "object",
+ properties: {
+ firstName: {
+ type: "string",
+ description: "The person's first name.",
+ },
+ },
+ },
+ "Record": {
+ type: "object",
+ additionalProperties: {
+ $ref: "#/definitions/Person",
+ },
+ },
+ },
+ }
+ const data = {
+ joe: {
+ firstName: "Joe",
+ },
+ }
+ instances.forEach((ajv) => {
+ const validate = ajv.compile(schema)
+ validate(data).should.equal(true)
+ })
+ })
+ })
+
describe("missing schema error", function () {
this.timeout(4000)