diff --git a/src/hyperjump-json-schema.test.js b/src/hyperjump-json-schema.test.js index 2db0653..8887885 100644 --- a/src/hyperjump-json-schema.test.js +++ b/src/hyperjump-json-schema.test.js @@ -6,6 +6,7 @@ import { unregisterSchema, validate } from "@hyperjump/json-schema/draft-2020-12"; +import "@hyperjump/json-schema/draft-2019-09"; import "@hyperjump/json-schema/formats"; import { BASIC } from "@hyperjump/json-schema/experimental"; import { jsonSchemaErrors } from "../src/index.js"; @@ -180,7 +181,7 @@ const getMessage = await (async function () { }()); runTests("https://json-schema.org/draft/2020-12/schema", 2020); -// runTests("https://json-schema.org/draft/2019-09/schema", 2019); +runTests("https://json-schema.org/draft/2019-09/schema", 2019); // runTests("http://json-schema.org/draft-07/schema", 7); // runTests("http://json-schema.org/draft-06/schema", 6); // runTests("http://json-schema.org/draft-04/schema", 4); diff --git a/src/index.js b/src/index.js index 2af47e0..8017fe1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ import { addErrorHandler, setNormalizationHandler } from "./json-schema-errors.js"; // Normalization Handlers +import additionalItemsNormalizationHandler from "./normalization-handlers/draft-04/additionalItems.js"; import additionalPropertiesNormalizationHandler from "./normalization-handlers/additionalProperties.js"; import allOfNormalizationHandler from "./normalization-handlers/allOf.js"; import anyOfNormalizationHandler from "./normalization-handlers/anyOf.js"; @@ -16,6 +17,7 @@ import exclusiveMaximumNormalizationHandler from "./normalization-handlers/exclu import exclusiveMinimumNormalizationHandler from "./normalization-handlers/exclusiveMinimum.js"; import formatNormalizationHandler from "./normalization-handlers/format.js"; import ifNormalizationHandler from "./normalization-handlers/if.js"; +import itemsDraft04NormalizationHandler from "./normalization-handlers/draft-04/items.js"; import itemsNormalizationHandler from "./normalization-handlers/items.js"; import maximumNormalizationHandler from "./normalization-handlers/maximum.js"; import maxContainsNormalizationHandler from "./normalization-handlers/maxContains.js"; @@ -71,6 +73,7 @@ import typeErrorHandler from "./error-handlers/type.js"; import uniqueItemsErrorHandler from "./error-handlers/uniqueItems.js"; import unknownErrorHandler from "./error-handlers/unknown.js"; +setNormalizationHandler("https://json-schema.org/keyword/draft-04/additionalItems", additionalItemsNormalizationHandler); setNormalizationHandler("https://json-schema.org/keyword/additionalProperties", additionalPropertiesNormalizationHandler); setNormalizationHandler("https://json-schema.org/keyword/allOf", allOfNormalizationHandler); setNormalizationHandler("https://json-schema.org/keyword/anyOf", anyOfNormalizationHandler); @@ -90,6 +93,7 @@ setNormalizationHandler("https://json-schema.org/keyword/draft-07/format", forma setNormalizationHandler("https://json-schema.org/keyword/draft-06/format", formatNormalizationHandler); setNormalizationHandler("https://json-schema.org/keyword/draft-04/format", formatNormalizationHandler); setNormalizationHandler("https://json-schema.org/keyword/if", ifNormalizationHandler); +setNormalizationHandler("https://json-schema.org/keyword/draft-04/items", itemsDraft04NormalizationHandler); setNormalizationHandler("https://json-schema.org/keyword/items", itemsNormalizationHandler); setNormalizationHandler("https://json-schema.org/keyword/exclusiveMaximum", exclusiveMaximumNormalizationHandler); setNormalizationHandler("https://json-schema.org/keyword/exclusiveMinimum", exclusiveMinimumNormalizationHandler); diff --git a/src/normalization-handlers/draft-04/additionalItems.js b/src/normalization-handlers/draft-04/additionalItems.js new file mode 100644 index 0000000..0a85eb7 --- /dev/null +++ b/src/normalization-handlers/draft-04/additionalItems.js @@ -0,0 +1,28 @@ +import { evaluateSchema } from "../../json-schema-errors.js"; +import * as Instance from "@hyperjump/json-schema/instance/experimental"; +import * as Pact from "@hyperjump/pact"; + +/** + * @import { NormalizationHandler, NormalizedOutput } from "../../index.d.ts" + */ + +/** @type NormalizationHandler<[number, string]> */ +const additionalItemsNormalizationHandler = { + evaluate([numberOfItems, additionalItems], instance, context) { + /** @type NormalizedOutput[] */ + const outputs = []; + + if (Instance.typeOf(instance) !== "array") { + return outputs; + } + + for (const itemNode of Pact.drop(numberOfItems, Instance.iter(instance))) { + outputs.push(evaluateSchema(additionalItems, itemNode, context)); + } + + return outputs; + }, + simpleApplicator: true +}; + +export default additionalItemsNormalizationHandler; diff --git a/src/normalization-handlers/draft-04/items.js b/src/normalization-handlers/draft-04/items.js new file mode 100644 index 0000000..8ae00d2 --- /dev/null +++ b/src/normalization-handlers/draft-04/items.js @@ -0,0 +1,36 @@ +import { evaluateSchema } from "../../json-schema-errors.js"; +import * as Instance from "@hyperjump/json-schema/instance/experimental"; + +/** + * @import { NormalizationHandler, NormalizedOutput } from "../../index.d.ts" + */ + +/** @type NormalizationHandler */ +const itemsDraft04NormalizationHandler = { + evaluate(items, instance, context) { + /** @type NormalizedOutput[] */ + const outputs = []; + + if (Instance.typeOf(instance) !== "array") { + return outputs; + } + + if (typeof items === "string") { + for (const itemNode of Instance.iter(instance)) { + outputs.push(evaluateSchema(items, itemNode, context)); + } + } else { + for (const [index, schemaLocation] of items.entries()) { + const itemNode = Instance.step(String(index), instance); + if (itemNode) { + outputs.push(evaluateSchema(schemaLocation, itemNode, context)); + } + } + } + + return outputs; + }, + simpleApplicator: true +}; + +export default itemsDraft04NormalizationHandler; diff --git a/src/test-suite/tests/items.json b/src/test-suite/tests/items.json index 6b98fb2..09ddba9 100644 --- a/src/test-suite/tests/items.json +++ b/src/test-suite/tests/items.json @@ -4,7 +4,24 @@ "description": "The items keyword", "tests": [ { - "description": "items with false schema", + "description": "all items", + "schema": { + "items": { "type": "number" } + }, + "instance": [42, "foo"], + "errors": [ + { + "messageId": "type-message", + "messageParams": { + "expectedTypes": { "or": ["number"] } + }, + "instanceLocation": "#/1", + "schemaLocations": ["#/items/type"] + } + ] + }, + { + "description": "prefixItems/items with false schema", "compatibility": "2020", "schema": { "prefixItems": [{ "type": "number" }], @@ -21,7 +38,24 @@ ] }, { - "description": "items with object schema", + "description": "items/additionalItems with false schema", + "compatibility": "<=2019", + "schema": { + "items": [{ "type": "number" }], + "additionalItems": false + }, + "instance": [42, "foo"], + "errors": [ + { + "messageId": "boolean-schema-message", + "messageParams": {}, + "instanceLocation": "#/1", + "schemaLocations": ["#/additionalItems"] + } + ] + }, + { + "description": "prefix/items with object schema", "compatibility": "2020", "schema": { "prefixItems": [{ "type": "number" }], @@ -39,24 +73,57 @@ } ] }, + { + "description": "items/additionalItems with object schema", + "compatibility": "<=2019", + "schema": { + "items": [{ "type": "number" }], + "additionalItems": { "type": "string" } + }, + "instance": [42, null], + "errors": [ + { + "messageId": "type-message", + "messageParams": { + "expectedTypes": { "or": ["string"] } + }, + "instanceLocation": "#/1", + "schemaLocations": ["#/additionalItems/type"] + } + ] + }, { "description": "items on a non-object", - "compatibility": "2020", "schema": { - "prefixItems": [{ "type": "number" }], "items": { "type": "string" } }, "instance": 42, "errors": [] }, + { + "description": "additionalItems on a non-array", + "compatibility": "<=2019", + "schema": { + "additionalItems": { "type": "string" } + }, + "instance": 42, + "errors": [] + }, { "description": "items pass", - "compatibility": "2020", "schema": { - "prefixItems": [{ "type": "number" }], - "items": { "type": "string" } + "items": { "type": "number" } }, - "instance": [42, "foo"], + "instance": [42, 24], + "errors": [] + }, + { + "description": "additionalItems pass", + "compatibility": "<=2019", + "schema": { + "additionalItems": { "type": "number" } + }, + "instance": [42, 24], "errors": [] } ] diff --git a/src/test-suite/tests/prefixItems.json b/src/test-suite/tests/prefixItems.json index 1088d69..6666c29 100644 --- a/src/test-suite/tests/prefixItems.json +++ b/src/test-suite/tests/prefixItems.json @@ -21,6 +21,24 @@ } ] }, + { + "description": "array-form items", + "compatibility": "<=2019", + "schema": { + "items": [{ "type": "number" }] + }, + "instance": ["foo"], + "errors": [ + { + "messageId": "type-message", + "messageParams": { + "expectedTypes": { "or": ["number"] } + }, + "instanceLocation": "#/0", + "schemaLocations": ["#/items/0/type"] + } + ] + }, { "description": "prefixItems with fewer items than are defined", "compatibility": "2020", @@ -43,7 +61,28 @@ ] }, { - "description": "prefixItems on an non-object", + "description": "array-form items with fewer items than are defined", + "compatibility": "<=2019", + "schema": { + "items": [ + { "type": "number" }, + { "type": "string" } + ] + }, + "instance": ["foo"], + "errors": [ + { + "messageId": "type-message", + "messageParams": { + "expectedTypes": { "or": ["number"] } + }, + "instanceLocation": "#/0", + "schemaLocations": ["#/items/0/type"] + } + ] + }, + { + "description": "prefixItems on an non-array", "compatibility": "2020", "schema": { "prefixItems": [{ "type": "number" }] @@ -51,6 +90,15 @@ "instance": 42, "errors": [] }, + { + "description": "array-form items on an non-array", + "compatibility": "<=2019", + "schema": { + "items": [{ "type": "number" }] + }, + "instance": 42, + "errors": [] + }, { "description": "prefixItems pass", "schema": { @@ -58,6 +106,15 @@ }, "instance": { "foo": 42 }, "errors": [] + }, + { + "description": "array-form items pass", + "compatibility": "<=2019", + "schema": { + "items": [{ "type": "number" }] + }, + "instance": { "foo": 42 }, + "errors": [] } ] }