From 7199125eb52ebfa6ce6f9e617d678e12cc3268f1 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 3 Feb 2026 21:19:52 +0530 Subject: [PATCH 1/8] combined required and dependentRequired error messages --- src/error-handlers/dependentRequired.js | 29 ++++++++--- src/error-handlers/required.js | 66 +++++++++++++++++++------ src/test-suite/tests/dependencies.json | 15 ++---- src/test-suite/tests/required.json | 24 ++++++++- 4 files changed, 98 insertions(+), 36 deletions(-) diff --git a/src/error-handlers/dependentRequired.js b/src/error-handlers/dependentRequired.js index d149a85..f15fdf7 100644 --- a/src/error-handlers/dependentRequired.js +++ b/src/error-handlers/dependentRequired.js @@ -8,11 +8,24 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** @type ErrorHandler */ const dependentRequiredErrorHandler = async (normalizedErrors, instance, localization) => { + if (normalizedErrors["https://json-schema.org/keyword/required"]) { + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/required"]) { + if (normalizedErrors["https://json-schema.org/keyword/required"][schemaLocation] === false) { + return []; + } + } + } + /** @type ErrorObject[] */ const errors = []; - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) { - if (normalizedErrors["https://json-schema.org/keyword/dependentRequired"][schemaLocation]) { + const dependentRequiredErrors = normalizedErrors["https://json-schema.org/keyword/dependentRequired"]; + if (!dependentRequiredErrors) { + return []; + } + + for (const schemaLocation in dependentRequiredErrors) { + if (dependentRequiredErrors[schemaLocation]) { continue; } @@ -31,11 +44,13 @@ const dependentRequiredErrorHandler = async (normalizedErrors, instance, localiz } } - errors.push({ - message: localization.getRequiredErrorMessage([...required]), - instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] - }); + if (required.size > 0) { + errors.push({ + message: localization.getRequiredErrorMessage([...required].sort()), + instanceLocation: Instance.uri(instance), + schemaLocations: [schemaLocation] + }); + } } return errors; diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index b9677a2..413ac72 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -8,32 +8,66 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** @type ErrorHandler */ const requiredErrorHandler = async (normalizedErrors, instance, localization) => { - /** @type ErrorObject[] */ - const errors = []; + const allMissingRequired = new Set(); + const allSchemaLocations = new Set(); + let hasRequiredFailure = false; - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/required"]) { - if (normalizedErrors["https://json-schema.org/keyword/required"][schemaLocation]) { - continue; + const requiredErrors = normalizedErrors["https://json-schema.org/keyword/required"]; + if (requiredErrors) { + for (const schemaLocation in requiredErrors) { + if (requiredErrors[schemaLocation] === false) { + hasRequiredFailure = true; + allSchemaLocations.add(schemaLocation); + const keyword = await getSchema(schemaLocation); + const required = /** @type string[] */ (Schema.value(keyword)); + + for (const propertyName of required) { + if (!Instance.has(propertyName, instance)) { + allMissingRequired.add(propertyName); + } + } + } } + } - const keyword = await getSchema(schemaLocation); - const required = /** @type string[] */ (Schema.value(keyword)); + if (hasRequiredFailure) { + const dependentRequiredErrors = normalizedErrors["https://json-schema.org/keyword/dependentRequired"]; + if (dependentRequiredErrors) { + for (const schemaLocation in dependentRequiredErrors) { + if (dependentRequiredErrors[schemaLocation] === false) { + allSchemaLocations.add(schemaLocation); + const keyword = await getSchema(schemaLocation); + const dependentRequired = /** @type Record */ (Schema.value(keyword)); - const missingRequired = []; - for (const propertyName of required) { - if (!Instance.has(propertyName, instance)) { - missingRequired.push(propertyName); + for (const propertyName in dependentRequired) { + if (Instance.has(propertyName, instance)) { + for (const requiredPropertyName of dependentRequired[propertyName]) { + if (!Instance.has(requiredPropertyName, instance)) { + allMissingRequired.add(requiredPropertyName); + } + } + } + } + } } } + } + + if (allMissingRequired.size > 0) { + /** @type {string[]} */ + const missingProperties = [...allMissingRequired].sort(); + + /** @type {string[]} */ + const locations = [...allSchemaLocations].sort(); - errors.push({ - message: localization.getRequiredErrorMessage(missingRequired), + return [{ + message: localization.getRequiredErrorMessage(missingProperties), instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] - }); + schemaLocations: locations + }]; } - return errors; + return []; }; export default requiredErrorHandler; diff --git a/src/test-suite/tests/dependencies.json b/src/test-suite/tests/dependencies.json index d9bd39a..855df7a 100644 --- a/src/test-suite/tests/dependencies.json +++ b/src/test-suite/tests/dependencies.json @@ -110,20 +110,11 @@ { "messageId": "required-message", "messageParams": { - "count": 1, - "required": { "and": ["baz"] } - }, - "instanceLocation": "#", - "schemaLocations": ["#/dependentSchemas/foo/required"] - }, - { - "messageId": "required-message", - "messageParams": { - "count": 1, - "required": { "and": ["bbb"] } + "count": 2, + "required": { "and": ["baz", "bbb"] } }, "instanceLocation": "#", - "schemaLocations": ["#/dependentSchemas/aaa/required"] + "schemaLocations": ["#/dependentSchemas/aaa/required", "#/dependentSchemas/foo/required"] } ] }, diff --git a/src/test-suite/tests/required.json b/src/test-suite/tests/required.json index 6ac31e6..0c3db89 100644 --- a/src/test-suite/tests/required.json +++ b/src/test-suite/tests/required.json @@ -14,13 +14,35 @@ "messageId": "required-message", "messageParams": { "count": 1, - "required": { "or": ["bar"] } + "required": { "and": ["bar"] } }, "instanceLocation": "#", "schemaLocations": ["#/required"] } ] }, + { + "description": "required and dependentRequired", + "compatibility": ">=2019", + "schema": { + "required": ["foo", "bar"], + "dependentRequired": { + "a": ["b"] + } + }, + "instance": { "a": 1 }, + "errors": [ + { + "messageId": "required-message", + "messageParams": { + "count": 3, + "required": { "and": ["b", "bar", "foo"] } + }, + "instanceLocation": "#", + "schemaLocations": ["#/dependentRequired", "#/required"] + } + ] + }, { "description": "required pass", "schema": { From 959669e576cc9af5a3db9ef9881069ab8bb9efbe Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 4 Feb 2026 15:39:03 +0530 Subject: [PATCH 2/8] unified error handler for each keyword --- src/error-handlers/dependentRequired.js | 59 ------ src/error-handlers/draft-04/dependencies.js | 22 --- src/error-handlers/required.js | 53 ++++-- src/index.js | 2 - .../draft-04/dependencies.js | 12 +- src/test-suite/tests/dependencies.json | 175 +++++++++++++----- src/test-suite/tests/required.json | 2 +- 7 files changed, 180 insertions(+), 145 deletions(-) delete mode 100644 src/error-handlers/dependentRequired.js diff --git a/src/error-handlers/dependentRequired.js b/src/error-handlers/dependentRequired.js deleted file mode 100644 index f15fdf7..0000000 --- a/src/error-handlers/dependentRequired.js +++ /dev/null @@ -1,59 +0,0 @@ -import { getSchema } from "@hyperjump/json-schema/experimental"; -import * as Schema from "@hyperjump/browser"; -import * as Instance from "@hyperjump/json-schema/instance/experimental"; - -/** - * @import { ErrorHandler, ErrorObject } from "../index.d.ts" - */ - -/** @type ErrorHandler */ -const dependentRequiredErrorHandler = async (normalizedErrors, instance, localization) => { - if (normalizedErrors["https://json-schema.org/keyword/required"]) { - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/required"]) { - if (normalizedErrors["https://json-schema.org/keyword/required"][schemaLocation] === false) { - return []; - } - } - } - - /** @type ErrorObject[] */ - const errors = []; - - const dependentRequiredErrors = normalizedErrors["https://json-schema.org/keyword/dependentRequired"]; - if (!dependentRequiredErrors) { - return []; - } - - for (const schemaLocation in dependentRequiredErrors) { - if (dependentRequiredErrors[schemaLocation]) { - continue; - } - - const keyword = await getSchema(schemaLocation); - const dependentRequired = /** @type Record */ (Schema.value(keyword)); - - /** @type Set */ - const required = new Set(); - for (const propertyName in dependentRequired) { - if (Instance.has(propertyName, instance)) { - for (const requiredPropertyName of dependentRequired[propertyName]) { - if (!Instance.has(requiredPropertyName, instance)) { - required.add(requiredPropertyName); - } - } - } - } - - if (required.size > 0) { - errors.push({ - message: localization.getRequiredErrorMessage([...required].sort()), - instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] - }); - } - } - - return errors; -}; - -export default dependentRequiredErrorHandler; diff --git a/src/error-handlers/draft-04/dependencies.js b/src/error-handlers/draft-04/dependencies.js index d16effc..c01a9f5 100644 --- a/src/error-handlers/draft-04/dependencies.js +++ b/src/error-handlers/draft-04/dependencies.js @@ -1,6 +1,3 @@ -import { getSchema } from "@hyperjump/json-schema/experimental"; -import * as Schema from "@hyperjump/browser"; -import * as Instance from "@hyperjump/json-schema/instance/experimental"; import { getErrors } from "../../json-schema-errors.js"; /** @@ -22,25 +19,6 @@ const dependenciesErrorHandler = async (normalizedErrors, instance, localization const dependentSchemaErrors = await getErrors(dependentSchemaOutput, instance, localization); errors.push(...dependentSchemaErrors); } - - const dependencies = await getSchema(schemaLocation); - for await (const [propertyName, dependency] of Schema.entries(dependencies)) { - if (!Instance.has(propertyName, instance)) { - continue; - } - - if (Schema.typeOf(dependency) !== "array") { - continue; - } - - const dependentRequired = /** @type {string[]} */ (Schema.value(dependency)); - const missing = dependentRequired.filter((required) => !Instance.has(required, instance)); - errors.push({ - message: localization.getRequiredErrorMessage(missing), - instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] - }); - } } return errors; diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index 413ac72..b06f88c 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -10,13 +10,11 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental"; const requiredErrorHandler = async (normalizedErrors, instance, localization) => { const allMissingRequired = new Set(); const allSchemaLocations = new Set(); - let hasRequiredFailure = false; const requiredErrors = normalizedErrors["https://json-schema.org/keyword/required"]; if (requiredErrors) { for (const schemaLocation in requiredErrors) { if (requiredErrors[schemaLocation] === false) { - hasRequiredFailure = true; allSchemaLocations.add(schemaLocation); const keyword = await getSchema(schemaLocation); const required = /** @type string[] */ (Schema.value(keyword)); @@ -30,18 +28,43 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => } } - if (hasRequiredFailure) { - const dependentRequiredErrors = normalizedErrors["https://json-schema.org/keyword/dependentRequired"]; - if (dependentRequiredErrors) { - for (const schemaLocation in dependentRequiredErrors) { - if (dependentRequiredErrors[schemaLocation] === false) { - allSchemaLocations.add(schemaLocation); - const keyword = await getSchema(schemaLocation); - const dependentRequired = /** @type Record */ (Schema.value(keyword)); + const dependentRequiredErrors = normalizedErrors["https://json-schema.org/keyword/dependentRequired"]; + if (dependentRequiredErrors) { + for (const schemaLocation in dependentRequiredErrors) { + if (dependentRequiredErrors[schemaLocation] === false) { + allSchemaLocations.add(schemaLocation); + const keyword = await getSchema(schemaLocation); + const dependentRequired = /** @type Record */ (Schema.value(keyword)); + + for (const propertyName in dependentRequired) { + if (Instance.has(propertyName, instance)) { + for (const requiredPropertyName of dependentRequired[propertyName]) { + if (!Instance.has(requiredPropertyName, instance)) { + allMissingRequired.add(requiredPropertyName); + } + } + } + } + } + } + } + + const dependenciesErrors = normalizedErrors["https://json-schema.org/keyword/draft-04/dependencies"]; + if (dependenciesErrors) { + for (const schemaLocation in dependenciesErrors) { + const dependencyValue = dependenciesErrors[schemaLocation]; + if (dependencyValue === false || Array.isArray(dependencyValue)) { + const keyword = await getSchema(schemaLocation); + const dependencies = /** @type Record */ (Schema.value(keyword)); - for (const propertyName in dependentRequired) { - if (Instance.has(propertyName, instance)) { - for (const requiredPropertyName of dependentRequired[propertyName]) { + let hasArrayFormDependencies = false; + for (const propertyName in dependencies) { + if (Instance.has(propertyName, instance)) { + const dependency = dependencies[propertyName]; + if (Array.isArray(dependency)) { + hasArrayFormDependencies = true; + const dependencyArray = /** @type {string[]} */ (dependency); + for (const requiredPropertyName of dependencyArray) { if (!Instance.has(requiredPropertyName, instance)) { allMissingRequired.add(requiredPropertyName); } @@ -49,6 +72,10 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => } } } + + if (hasArrayFormDependencies) { + allSchemaLocations.add(schemaLocation); + } } } } diff --git a/src/index.js b/src/index.js index 1bff19e..ce48b7f 100644 --- a/src/index.js +++ b/src/index.js @@ -58,7 +58,6 @@ import booleanSchemaErrorHandler from "./error-handlers/boolean-schema.js"; import constErrorHandler from "./error-handlers/const.js"; import containsErrorHandler from "./error-handlers/contains.js"; import dependenciesErrorHandler from "./error-handlers/draft-04/dependencies.js"; -import dependentRequiredErrorHandler from "./error-handlers/dependentRequired.js"; import enumErrorHandler from "./error-handlers/enum.js"; import exclusiveMaximumErrorHandler from "./error-handlers/exclusiveMaximum.js"; import exclusiveMinimumErrorHandler from "./error-handlers/exclusiveMinimum.js"; @@ -144,7 +143,6 @@ addErrorHandler(booleanSchemaErrorHandler); addErrorHandler(constErrorHandler); addErrorHandler(containsErrorHandler); addErrorHandler(dependenciesErrorHandler); -addErrorHandler(dependentRequiredErrorHandler); addErrorHandler(enumErrorHandler); addErrorHandler(exclusiveMaximumErrorHandler); addErrorHandler(exclusiveMinimumErrorHandler); diff --git a/src/normalization-handlers/draft-04/dependencies.js b/src/normalization-handlers/draft-04/dependencies.js index e2cf5c9..b4de39f 100644 --- a/src/normalization-handlers/draft-04/dependencies.js +++ b/src/normalization-handlers/draft-04/dependencies.js @@ -12,18 +12,18 @@ const dependenciesNormalizationHandler = { const outputs = []; if (Instance.typeOf(instance) !== "object") { - return outputs; + return undefined; } for (const [propertyName, dependency] of dependencies) { - if (!Instance.has(propertyName, instance) || typeof dependency !== "string") { + if (!Instance.has(propertyName, instance)) { continue; } - - outputs.push(evaluateSchema(dependency, instance, context)); + if (typeof dependency === "string") { + outputs.push(evaluateSchema(dependency, instance, context)); + } } - - return outputs; + return outputs.length > 0 ? outputs : undefined; } }; diff --git a/src/test-suite/tests/dependencies.json b/src/test-suite/tests/dependencies.json index 855df7a..20b57f2 100644 --- a/src/test-suite/tests/dependencies.json +++ b/src/test-suite/tests/dependencies.json @@ -1,6 +1,5 @@ { "$schema": "../test-suite.schema.json", - "description": "The dependentRequired, dependentSchemas, and dependencies keywords", "tests": [ { @@ -8,8 +7,13 @@ "compatibility": "2019", "schema": { "dependentRequired": { - "foo": ["bar", "baz"], - "aaa": ["bbb"] + "foo": [ + "bar", + "baz" + ], + "aaa": [ + "bbb" + ] } }, "instance": { @@ -22,10 +26,17 @@ "messageId": "required-message", "messageParams": { "count": 2, - "required": { "and": ["baz", "bbb"] } + "required": { + "and": [ + "baz", + "bbb" + ] + } }, "instanceLocation": "#", - "schemaLocations": ["#/dependentRequired"] + "schemaLocations": [ + "#/dependentRequired" + ] } ] }, @@ -34,8 +45,13 @@ "compatibility": "<=7", "schema": { "dependencies": { - "foo": ["bar", "baz"], - "aaa": ["bbb"] + "foo": [ + "bar", + "baz" + ], + "aaa": [ + "bbb" + ] } }, "instance": { @@ -47,20 +63,18 @@ { "messageId": "required-message", "messageParams": { - "count": 1, - "required": { "and": ["baz"] } - }, - "instanceLocation": "#", - "schemaLocations": ["#/dependencies"] - }, - { - "messageId": "required-message", - "messageParams": { - "count": 1, - "required": { "and": ["bbb"] } + "count": 2, + "required": { + "and": [ + "baz", + "bbb" + ] + } }, "instanceLocation": "#", - "schemaLocations": ["#/dependencies"] + "schemaLocations": [ + "#/dependencies" + ] } ] }, @@ -69,7 +83,9 @@ "compatibility": "2019", "schema": { "dependentRequired": { - "foo": ["bar"] + "foo": [ + "bar" + ] } }, "instance": { @@ -83,7 +99,9 @@ "compatibility": "<=7", "schema": { "dependencies": { - "foo": ["bar"] + "foo": [ + "bar" + ] } }, "instance": { @@ -97,8 +115,17 @@ "compatibility": "2019", "schema": { "dependentSchemas": { - "foo": { "required": ["bar", "baz"] }, - "aaa": { "required": ["bbb"] } + "foo": { + "required": [ + "bar", + "baz" + ] + }, + "aaa": { + "required": [ + "bbb" + ] + } } }, "instance": { @@ -111,10 +138,18 @@ "messageId": "required-message", "messageParams": { "count": 2, - "required": { "and": ["baz", "bbb"] } + "required": { + "and": [ + "baz", + "bbb" + ] + } }, "instanceLocation": "#", - "schemaLocations": ["#/dependentSchemas/aaa/required", "#/dependentSchemas/foo/required"] + "schemaLocations": [ + "#/dependentSchemas/aaa/required", + "#/dependentSchemas/foo/required" + ] } ] }, @@ -123,8 +158,17 @@ "compatibility": "<=7", "schema": { "dependencies": { - "foo": { "required": ["bar", "baz"] }, - "aaa": { "required": ["bbb"] } + "foo": { + "required": [ + "bar", + "baz" + ] + }, + "aaa": { + "required": [ + "bbb" + ] + } } }, "instance": { @@ -137,19 +181,31 @@ "messageId": "required-message", "messageParams": { "count": 1, - "required": { "and": ["baz"] } + "required": { + "and": [ + "baz" + ] + } }, "instanceLocation": "#", - "schemaLocations": ["#/dependencies/foo/required"] + "schemaLocations": [ + "#/dependencies/foo/required" + ] }, { "messageId": "required-message", "messageParams": { "count": 1, - "required": { "and": ["bbb"] } + "required": { + "and": [ + "bbb" + ] + } }, "instanceLocation": "#", - "schemaLocations": ["#/dependencies/aaa/required"] + "schemaLocations": [ + "#/dependencies/aaa/required" + ] } ] }, @@ -158,7 +214,11 @@ "compatibility": "2019", "schema": { "dependentSchemas": { - "foo": { "required": ["bar"] } + "foo": { + "required": [ + "bar" + ] + } } }, "instance": 42, @@ -169,7 +229,11 @@ "compatibility": "<=7", "schema": { "dependentSchemas": { - "foo": { "required": ["bar"] } + "foo": { + "required": [ + "bar" + ] + } } }, "instance": 42, @@ -179,7 +243,11 @@ "description": "dependentSchemas pass", "schema": { "dependentSchemas": { - "foo": { "required": ["bar"] } + "foo": { + "required": [ + "bar" + ] + } } }, "instance": { @@ -192,7 +260,11 @@ "description": "schem-form dependencies pass", "schema": { "dependencies": { - "foo": { "required": ["bar"] } + "foo": { + "required": [ + "bar" + ] + } } }, "instance": { @@ -206,8 +278,15 @@ "compatibility": "<=7", "schema": { "dependencies": { - "foo": { "required": ["bar", "baz"] }, - "aaa": ["bbb"] + "foo": { + "required": [ + "bar", + "baz" + ] + }, + "aaa": [ + "bbb" + ] } }, "instance": { @@ -220,21 +299,33 @@ "messageId": "required-message", "messageParams": { "count": 1, - "required": { "and": ["baz"] } + "required": { + "and": [ + "baz" + ] + } }, "instanceLocation": "#", - "schemaLocations": ["#/dependencies/foo/required"] + "schemaLocations": [ + "#/dependencies/foo/required" + ] }, { "messageId": "required-message", "messageParams": { "count": 1, - "required": { "and": ["bbb"] } + "required": { + "and": [ + "bbb" + ] + } }, "instanceLocation": "#", - "schemaLocations": ["#/dependencies"] + "schemaLocations": [ + "#/dependencies" + ] } ] } ] -} +} \ No newline at end of file diff --git a/src/test-suite/tests/required.json b/src/test-suite/tests/required.json index 0c3db89..1aab783 100644 --- a/src/test-suite/tests/required.json +++ b/src/test-suite/tests/required.json @@ -22,7 +22,7 @@ ] }, { - "description": "required and dependentRequired", + "description": "combined required and dependentRequired", "compatibility": ">=2019", "schema": { "required": ["foo", "bar"], From b6d9376b1479f2d1eace072becf010fc1f4388fb Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 7 Feb 2026 07:11:30 +0530 Subject: [PATCH 3/8] code style is fixed --- src/error-handlers/required.js | 9 +- .../draft-04/dependencies.js | 10 +- src/test-suite/tests/dependencies.json | 214 ++++-------------- src/test-suite/tests/required.json | 2 +- 4 files changed, 50 insertions(+), 185 deletions(-) diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index b06f88c..c2ccf02 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -9,13 +9,13 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** @type ErrorHandler */ const requiredErrorHandler = async (normalizedErrors, instance, localization) => { const allMissingRequired = new Set(); - const allSchemaLocations = new Set(); + const allSchemaLocations = []; const requiredErrors = normalizedErrors["https://json-schema.org/keyword/required"]; if (requiredErrors) { for (const schemaLocation in requiredErrors) { if (requiredErrors[schemaLocation] === false) { - allSchemaLocations.add(schemaLocation); + allSchemaLocations.push(schemaLocation); const keyword = await getSchema(schemaLocation); const required = /** @type string[] */ (Schema.value(keyword)); @@ -32,7 +32,7 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => if (dependentRequiredErrors) { for (const schemaLocation in dependentRequiredErrors) { if (dependentRequiredErrors[schemaLocation] === false) { - allSchemaLocations.add(schemaLocation); + allSchemaLocations.push(schemaLocation); const keyword = await getSchema(schemaLocation); const dependentRequired = /** @type Record */ (Schema.value(keyword)); @@ -74,7 +74,7 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => } if (hasArrayFormDependencies) { - allSchemaLocations.add(schemaLocation); + allSchemaLocations.push(schemaLocation); } } } @@ -83,7 +83,6 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => if (allMissingRequired.size > 0) { /** @type {string[]} */ const missingProperties = [...allMissingRequired].sort(); - /** @type {string[]} */ const locations = [...allSchemaLocations].sort(); diff --git a/src/normalization-handlers/draft-04/dependencies.js b/src/normalization-handlers/draft-04/dependencies.js index b4de39f..21f6908 100644 --- a/src/normalization-handlers/draft-04/dependencies.js +++ b/src/normalization-handlers/draft-04/dependencies.js @@ -12,18 +12,16 @@ const dependenciesNormalizationHandler = { const outputs = []; if (Instance.typeOf(instance) !== "object") { - return undefined; + return outputs; } for (const [propertyName, dependency] of dependencies) { - if (!Instance.has(propertyName, instance)) { + if (!Instance.has(propertyName, instance) || typeof dependency !== "string") { continue; } - if (typeof dependency === "string") { - outputs.push(evaluateSchema(dependency, instance, context)); - } + outputs.push(evaluateSchema(dependency, instance, context)); } - return outputs.length > 0 ? outputs : undefined; + return outputs; } }; diff --git a/src/test-suite/tests/dependencies.json b/src/test-suite/tests/dependencies.json index 20b57f2..e4eba2f 100644 --- a/src/test-suite/tests/dependencies.json +++ b/src/test-suite/tests/dependencies.json @@ -1,5 +1,6 @@ { "$schema": "../test-suite.schema.json", + "description": "The dependentRequired, dependentSchemas, and dependencies keywords", "tests": [ { @@ -7,36 +8,20 @@ "compatibility": "2019", "schema": { "dependentRequired": { - "foo": [ - "bar", - "baz" - ], - "aaa": [ - "bbb" - ] + "foo": ["bar", "baz"], + "aaa": ["bbb"] } }, - "instance": { - "foo": 42, - "bar": true, - "aaa": true - }, + "instance": { "foo": 42, "bar": true, "aaa": true }, "errors": [ { "messageId": "required-message", "messageParams": { "count": 2, - "required": { - "and": [ - "baz", - "bbb" - ] - } + "required": { "and": ["baz", "bbb"] } }, "instanceLocation": "#", - "schemaLocations": [ - "#/dependentRequired" - ] + "schemaLocations": ["#/dependentRequired"] } ] }, @@ -45,36 +30,20 @@ "compatibility": "<=7", "schema": { "dependencies": { - "foo": [ - "bar", - "baz" - ], - "aaa": [ - "bbb" - ] + "foo": ["bar", "baz"], + "aaa": ["bbb"] } }, - "instance": { - "foo": 42, - "bar": true, - "aaa": true - }, + "instance": { "foo": 42, "bar": true, "aaa": true }, "errors": [ { "messageId": "required-message", "messageParams": { "count": 2, - "required": { - "and": [ - "baz", - "bbb" - ] - } + "required": { "and": ["baz", "bbb"] } }, "instanceLocation": "#", - "schemaLocations": [ - "#/dependencies" - ] + "schemaLocations": ["#/dependencies"] } ] }, @@ -83,15 +52,10 @@ "compatibility": "2019", "schema": { "dependentRequired": { - "foo": [ - "bar" - ] + "foo": ["bar"] } }, - "instance": { - "foo": 42, - "bar": true - }, + "instance": { "foo": 42, "bar": true }, "errors": [] }, { @@ -99,15 +63,10 @@ "compatibility": "<=7", "schema": { "dependencies": { - "foo": [ - "bar" - ] + "foo": ["bar"] } }, - "instance": { - "foo": 42, - "bar": true - }, + "instance": { "foo": 42, "bar": true }, "errors": [] }, { @@ -115,41 +74,20 @@ "compatibility": "2019", "schema": { "dependentSchemas": { - "foo": { - "required": [ - "bar", - "baz" - ] - }, - "aaa": { - "required": [ - "bbb" - ] - } + "foo": { "required": ["bar", "baz"] }, + "aaa": { "required": ["bbb"] } } }, - "instance": { - "foo": 42, - "bar": true, - "aaa": true - }, + "instance": { "foo": 42, "bar": true, "aaa": true }, "errors": [ { "messageId": "required-message", "messageParams": { "count": 2, - "required": { - "and": [ - "baz", - "bbb" - ] - } + "required": { "and": ["baz", "bbb"] } }, "instanceLocation": "#", - "schemaLocations": [ - "#/dependentSchemas/aaa/required", - "#/dependentSchemas/foo/required" - ] + "schemaLocations": ["#/dependentSchemas/aaa/required", "#/dependentSchemas/foo/required"] } ] }, @@ -158,54 +96,29 @@ "compatibility": "<=7", "schema": { "dependencies": { - "foo": { - "required": [ - "bar", - "baz" - ] - }, - "aaa": { - "required": [ - "bbb" - ] - } + "foo": { "required": ["bar", "baz"] }, + "aaa": { "required": ["bbb"] } } }, - "instance": { - "foo": 42, - "bar": true, - "aaa": true - }, + "instance": { "foo": 42, "bar": true, "aaa": true }, "errors": [ { "messageId": "required-message", "messageParams": { "count": 1, - "required": { - "and": [ - "baz" - ] - } + "required": { "and": ["baz"] } }, "instanceLocation": "#", - "schemaLocations": [ - "#/dependencies/foo/required" - ] + "schemaLocations": ["#/dependencies/foo/required"] }, { "messageId": "required-message", "messageParams": { "count": 1, - "required": { - "and": [ - "bbb" - ] - } + "required": { "and": ["bbb"] } }, "instanceLocation": "#", - "schemaLocations": [ - "#/dependencies/aaa/required" - ] + "schemaLocations": ["#/dependencies/aaa/required"] } ] }, @@ -214,11 +127,7 @@ "compatibility": "2019", "schema": { "dependentSchemas": { - "foo": { - "required": [ - "bar" - ] - } + "foo": { "required": ["bar"] } } }, "instance": 42, @@ -229,11 +138,7 @@ "compatibility": "<=7", "schema": { "dependentSchemas": { - "foo": { - "required": [ - "bar" - ] - } + "foo": { "required": ["bar"] } } }, "instance": 42, @@ -243,34 +148,20 @@ "description": "dependentSchemas pass", "schema": { "dependentSchemas": { - "foo": { - "required": [ - "bar" - ] - } + "foo": { "required": ["bar"] } } }, - "instance": { - "foo": 42, - "bar": true - }, + "instance": { "foo": 42, "bar": true }, "errors": [] }, { "description": "schem-form dependencies pass", "schema": { "dependencies": { - "foo": { - "required": [ - "bar" - ] - } + "foo": { "required": ["bar"] } } }, - "instance": { - "foo": 42, - "bar": true - }, + "instance": { "foo": 42, "bar": true }, "errors": [] }, { @@ -278,54 +169,31 @@ "compatibility": "<=7", "schema": { "dependencies": { - "foo": { - "required": [ - "bar", - "baz" - ] - }, - "aaa": [ - "bbb" - ] + "foo": { "required": ["bar", "baz"] }, + "aaa": ["bbb"] } }, - "instance": { - "foo": 42, - "bar": true, - "aaa": true - }, + "instance": { "foo": 42, "bar": true, "aaa": true }, "errors": [ { "messageId": "required-message", "messageParams": { "count": 1, - "required": { - "and": [ - "baz" - ] - } + "required": { "and": ["baz"] } }, "instanceLocation": "#", - "schemaLocations": [ - "#/dependencies/foo/required" - ] + "schemaLocations": ["#/dependencies/foo/required"] }, { "messageId": "required-message", "messageParams": { "count": 1, - "required": { - "and": [ - "bbb" - ] - } + "required": { "and": ["bbb"] } }, "instanceLocation": "#", - "schemaLocations": [ - "#/dependencies" - ] + "schemaLocations": ["#/dependencies"] } ] } ] -} \ No newline at end of file +} diff --git a/src/test-suite/tests/required.json b/src/test-suite/tests/required.json index 1aab783..1098a2c 100644 --- a/src/test-suite/tests/required.json +++ b/src/test-suite/tests/required.json @@ -23,7 +23,7 @@ }, { "description": "combined required and dependentRequired", - "compatibility": ">=2019", + "compatibility": "2019", "schema": { "required": ["foo", "bar"], "dependentRequired": { From 78699ad9a2e5d1550d863521a83508532396b4a4 Mon Sep 17 00:00:00 2001 From: Jason Desrosiers Date: Mon, 9 Feb 2026 14:18:37 -0800 Subject: [PATCH 4/8] Update code style --- src/error-handlers/required.js | 136 +++++++++--------- .../draft-04/dependencies.js | 2 + src/test-suite/tests/dependencies.json | 55 +++++-- src/test-suite/tests/required.json | 5 +- 4 files changed, 120 insertions(+), 78 deletions(-) diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index c2ccf02..e0b33b8 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -3,7 +3,7 @@ import * as Schema from "@hyperjump/browser"; import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** - * @import { ErrorHandler, ErrorObject } from "../index.d.ts" + * @import { ErrorHandler } from "../index.d.ts" */ /** @type ErrorHandler */ @@ -11,89 +11,91 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => const allMissingRequired = new Set(); const allSchemaLocations = []; - const requiredErrors = normalizedErrors["https://json-schema.org/keyword/required"]; - if (requiredErrors) { - for (const schemaLocation in requiredErrors) { - if (requiredErrors[schemaLocation] === false) { - allSchemaLocations.push(schemaLocation); - const keyword = await getSchema(schemaLocation); - const required = /** @type string[] */ (Schema.value(keyword)); - - for (const propertyName of required) { - if (!Instance.has(propertyName, instance)) { - allMissingRequired.add(propertyName); - } - } + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/required"]) { + if (normalizedErrors["https://json-schema.org/keyword/required"][schemaLocation]) { + continue; + } + + allSchemaLocations.push(schemaLocation); + const keyword = await getSchema(schemaLocation); + const required = /** @type string[] */ (Schema.value(keyword)); + + for (const propertyName of required) { + if (!Instance.has(propertyName, instance)) { + allMissingRequired.add(propertyName); } } } - const dependentRequiredErrors = normalizedErrors["https://json-schema.org/keyword/dependentRequired"]; - if (dependentRequiredErrors) { - for (const schemaLocation in dependentRequiredErrors) { - if (dependentRequiredErrors[schemaLocation] === false) { - allSchemaLocations.push(schemaLocation); - const keyword = await getSchema(schemaLocation); - const dependentRequired = /** @type Record */ (Schema.value(keyword)); - - for (const propertyName in dependentRequired) { - if (Instance.has(propertyName, instance)) { - for (const requiredPropertyName of dependentRequired[propertyName]) { - if (!Instance.has(requiredPropertyName, instance)) { - allMissingRequired.add(requiredPropertyName); - } - } - } + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) { + if (normalizedErrors["https://json-schema.org/keyword/dependentRequired"][schemaLocation]) { + continue; + } + + allSchemaLocations.push(schemaLocation); + const keyword = await getSchema(schemaLocation); + const dependentRequired = /** @type Record */ (Schema.value(keyword)); + + for (const propertyName in dependentRequired) { + if (!Instance.has(propertyName, instance)) { + continue; + } + + for (const requiredPropertyName of dependentRequired[propertyName]) { + if (!Instance.has(requiredPropertyName, instance)) { + allMissingRequired.add(requiredPropertyName); } } } } - const dependenciesErrors = normalizedErrors["https://json-schema.org/keyword/draft-04/dependencies"]; - if (dependenciesErrors) { - for (const schemaLocation in dependenciesErrors) { - const dependencyValue = dependenciesErrors[schemaLocation]; - if (dependencyValue === false || Array.isArray(dependencyValue)) { - const keyword = await getSchema(schemaLocation); - const dependencies = /** @type Record */ (Schema.value(keyword)); - - let hasArrayFormDependencies = false; - for (const propertyName in dependencies) { - if (Instance.has(propertyName, instance)) { - const dependency = dependencies[propertyName]; - if (Array.isArray(dependency)) { - hasArrayFormDependencies = true; - const dependencyArray = /** @type {string[]} */ (dependency); - for (const requiredPropertyName of dependencyArray) { - if (!Instance.has(requiredPropertyName, instance)) { - allMissingRequired.add(requiredPropertyName); - } - } - } - } - } + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/dependencies"]) { + if (typeof normalizedErrors["https://json-schema.org/keyword/draft-04/dependencies"][schemaLocation] === "boolean") { + continue; + } + + const keyword = await getSchema(schemaLocation); + const dependencies = /** @type Record */ (Schema.value(keyword)); - if (hasArrayFormDependencies) { - allSchemaLocations.push(schemaLocation); + let hasArrayFormDependencies = false; + for (const propertyName in dependencies) { + if (!Instance.has(propertyName, instance)) { + continue; + } + + const dependency = dependencies[propertyName]; + if (!Array.isArray(dependency)) { + continue; + } + + hasArrayFormDependencies = true; + const dependencyArray = /** @type {string[]} */ (dependency); + for (const requiredPropertyName of dependencyArray) { + if (!Instance.has(requiredPropertyName, instance)) { + allMissingRequired.add(requiredPropertyName); } } } + + if (hasArrayFormDependencies) { + allSchemaLocations.push(schemaLocation); + } } - if (allMissingRequired.size > 0) { - /** @type {string[]} */ - const missingProperties = [...allMissingRequired].sort(); - /** @type {string[]} */ - const locations = [...allSchemaLocations].sort(); - - return [{ - message: localization.getRequiredErrorMessage(missingProperties), - instanceLocation: Instance.uri(instance), - schemaLocations: locations - }]; + if (allMissingRequired.size === 0) { + return []; } - return []; + /** @type {string[]} */ + const missingProperties = [...allMissingRequired].sort(); + /** @type {string[]} */ + const locations = [...allSchemaLocations].sort(); + + return [{ + message: localization.getRequiredErrorMessage(missingProperties), + instanceLocation: Instance.uri(instance), + schemaLocations: locations + }]; }; export default requiredErrorHandler; diff --git a/src/normalization-handlers/draft-04/dependencies.js b/src/normalization-handlers/draft-04/dependencies.js index 21f6908..e2cf5c9 100644 --- a/src/normalization-handlers/draft-04/dependencies.js +++ b/src/normalization-handlers/draft-04/dependencies.js @@ -19,8 +19,10 @@ const dependenciesNormalizationHandler = { if (!Instance.has(propertyName, instance) || typeof dependency !== "string") { continue; } + outputs.push(evaluateSchema(dependency, instance, context)); } + return outputs; } }; diff --git a/src/test-suite/tests/dependencies.json b/src/test-suite/tests/dependencies.json index e4eba2f..9ec7552 100644 --- a/src/test-suite/tests/dependencies.json +++ b/src/test-suite/tests/dependencies.json @@ -12,7 +12,11 @@ "aaa": ["bbb"] } }, - "instance": { "foo": 42, "bar": true, "aaa": true }, + "instance": { + "foo": 42, + "bar": true, + "aaa": true + }, "errors": [ { "messageId": "required-message", @@ -34,7 +38,11 @@ "aaa": ["bbb"] } }, - "instance": { "foo": 42, "bar": true, "aaa": true }, + "instance": { + "foo": 42, + "bar": true, + "aaa": true + }, "errors": [ { "messageId": "required-message", @@ -55,7 +63,10 @@ "foo": ["bar"] } }, - "instance": { "foo": 42, "bar": true }, + "instance": { + "foo": 42, + "bar": true + }, "errors": [] }, { @@ -66,7 +77,10 @@ "foo": ["bar"] } }, - "instance": { "foo": 42, "bar": true }, + "instance": { + "foo": 42, + "bar": true + }, "errors": [] }, { @@ -78,7 +92,11 @@ "aaa": { "required": ["bbb"] } } }, - "instance": { "foo": 42, "bar": true, "aaa": true }, + "instance": { + "foo": 42, + "bar": true, + "aaa": true + }, "errors": [ { "messageId": "required-message", @@ -87,7 +105,10 @@ "required": { "and": ["baz", "bbb"] } }, "instanceLocation": "#", - "schemaLocations": ["#/dependentSchemas/aaa/required", "#/dependentSchemas/foo/required"] + "schemaLocations": [ + "#/dependentSchemas/aaa/required", + "#/dependentSchemas/foo/required" + ] } ] }, @@ -100,7 +121,11 @@ "aaa": { "required": ["bbb"] } } }, - "instance": { "foo": 42, "bar": true, "aaa": true }, + "instance": { + "foo": 42, + "bar": true, + "aaa": true + }, "errors": [ { "messageId": "required-message", @@ -151,7 +176,10 @@ "foo": { "required": ["bar"] } } }, - "instance": { "foo": 42, "bar": true }, + "instance": { + "foo": 42, + "bar": true + }, "errors": [] }, { @@ -161,7 +189,10 @@ "foo": { "required": ["bar"] } } }, - "instance": { "foo": 42, "bar": true }, + "instance": { + "foo": 42, + "bar": true + }, "errors": [] }, { @@ -173,7 +204,11 @@ "aaa": ["bbb"] } }, - "instance": { "foo": 42, "bar": true, "aaa": true }, + "instance": { + "foo": 42, + "bar": true, + "aaa": true + }, "errors": [ { "messageId": "required-message", diff --git a/src/test-suite/tests/required.json b/src/test-suite/tests/required.json index 1098a2c..18c8e9a 100644 --- a/src/test-suite/tests/required.json +++ b/src/test-suite/tests/required.json @@ -39,7 +39,10 @@ "required": { "and": ["b", "bar", "foo"] } }, "instanceLocation": "#", - "schemaLocations": ["#/dependentRequired", "#/required"] + "schemaLocations": [ + "#/dependentRequired", + "#/required" + ] } ] }, From f526987bd3d9bf00a27e9247cf2eb2aa03204bf0 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 11 Feb 2026 09:46:10 +0530 Subject: [PATCH 5/8] Schema AST function is now used in all interations --- src/error-handlers/required.js | 22 ++++++++++++---------- src/test-suite/tests/dependencies.json | 4 ++-- src/test-suite/tests/required.json | 6 +++--- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index e0b33b8..ed62cef 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -34,14 +34,18 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => allSchemaLocations.push(schemaLocation); const keyword = await getSchema(schemaLocation); - const dependentRequired = /** @type Record */ (Schema.value(keyword)); - for (const propertyName in dependentRequired) { + for await (const [propertyName, dependencyNode] of Schema.entries(keyword)) { if (!Instance.has(propertyName, instance)) { continue; } - for (const requiredPropertyName of dependentRequired[propertyName]) { + if (Schema.typeOf(dependencyNode) !== "array") { + continue; + } + + const requiredProperties = /** @type string[] */ (Schema.value(dependencyNode)); + for (const requiredPropertyName of requiredProperties) { if (!Instance.has(requiredPropertyName, instance)) { allMissingRequired.add(requiredPropertyName); } @@ -55,21 +59,19 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => } const keyword = await getSchema(schemaLocation); - const dependencies = /** @type Record */ (Schema.value(keyword)); let hasArrayFormDependencies = false; - for (const propertyName in dependencies) { + for await (const [propertyName, dependency] of Schema.entries(keyword)) { if (!Instance.has(propertyName, instance)) { continue; } - const dependency = dependencies[propertyName]; - if (!Array.isArray(dependency)) { + if (Schema.typeOf(dependency) !== "array") { continue; } hasArrayFormDependencies = true; - const dependencyArray = /** @type {string[]} */ (dependency); + const dependencyArray = /** @type {string[]} */ (Schema.value(dependency)); for (const requiredPropertyName of dependencyArray) { if (!Instance.has(requiredPropertyName, instance)) { allMissingRequired.add(requiredPropertyName); @@ -87,9 +89,9 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => } /** @type {string[]} */ - const missingProperties = [...allMissingRequired].sort(); + const missingProperties = [...allMissingRequired]; /** @type {string[]} */ - const locations = [...allSchemaLocations].sort(); + const locations = [...allSchemaLocations]; return [{ message: localization.getRequiredErrorMessage(missingProperties), diff --git a/src/test-suite/tests/dependencies.json b/src/test-suite/tests/dependencies.json index 9ec7552..643309a 100644 --- a/src/test-suite/tests/dependencies.json +++ b/src/test-suite/tests/dependencies.json @@ -106,8 +106,8 @@ }, "instanceLocation": "#", "schemaLocations": [ - "#/dependentSchemas/aaa/required", - "#/dependentSchemas/foo/required" + "#/dependentSchemas/foo/required", + "#/dependentSchemas/aaa/required" ] } ] diff --git a/src/test-suite/tests/required.json b/src/test-suite/tests/required.json index 18c8e9a..4a7ee6f 100644 --- a/src/test-suite/tests/required.json +++ b/src/test-suite/tests/required.json @@ -36,12 +36,12 @@ "messageId": "required-message", "messageParams": { "count": 3, - "required": { "and": ["b", "bar", "foo"] } + "required": { "and": ["foo", "bar", "b"] } }, "instanceLocation": "#", "schemaLocations": [ - "#/dependentRequired", - "#/required" + "#/required", + "#/dependentRequired" ] } ] From 705a19dc1a4ba08502240a711cc13d98a765bb84 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 12 Feb 2026 04:34:26 +0530 Subject: [PATCH 6/8] clean up is done --- src/error-handlers/required.js | 51 +++++++++++++--------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index ed62cef..1e30a88 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -8,9 +8,23 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** @type ErrorHandler */ const requiredErrorHandler = async (normalizedErrors, instance, localization) => { + /** @type {Set} */ const allMissingRequired = new Set(); const allSchemaLocations = []; + /** + * @param {string[]} requiredProperties + * @param {import("@hyperjump/json-schema/instance/experimental").JsonNode} instance + * @param {Set} missingSet + */ + const addMissingProperties = (requiredProperties, instance, missingSet) => { + for (const propertyName of requiredProperties) { + if (!Instance.has(propertyName, instance)) { + missingSet.add(propertyName); + } + } + }; + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/required"]) { if (normalizedErrors["https://json-schema.org/keyword/required"][schemaLocation]) { continue; @@ -20,11 +34,7 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => const keyword = await getSchema(schemaLocation); const required = /** @type string[] */ (Schema.value(keyword)); - for (const propertyName of required) { - if (!Instance.has(propertyName, instance)) { - allMissingRequired.add(propertyName); - } - } + addMissingProperties(required, instance, allMissingRequired); } for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/dependentRequired"]) { @@ -40,16 +50,8 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => continue; } - if (Schema.typeOf(dependencyNode) !== "array") { - continue; - } - const requiredProperties = /** @type string[] */ (Schema.value(dependencyNode)); - for (const requiredPropertyName of requiredProperties) { - if (!Instance.has(requiredPropertyName, instance)) { - allMissingRequired.add(requiredPropertyName); - } - } + addMissingProperties(requiredProperties, instance, allMissingRequired); } } @@ -62,21 +64,13 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => let hasArrayFormDependencies = false; for await (const [propertyName, dependency] of Schema.entries(keyword)) { - if (!Instance.has(propertyName, instance)) { - continue; - } - - if (Schema.typeOf(dependency) !== "array") { + if (!Instance.has(propertyName, instance) || Schema.typeOf(dependency) !== "array") { continue; } hasArrayFormDependencies = true; const dependencyArray = /** @type {string[]} */ (Schema.value(dependency)); - for (const requiredPropertyName of dependencyArray) { - if (!Instance.has(requiredPropertyName, instance)) { - allMissingRequired.add(requiredPropertyName); - } - } + addMissingProperties(dependencyArray, instance, allMissingRequired); } if (hasArrayFormDependencies) { @@ -88,15 +82,10 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => return []; } - /** @type {string[]} */ - const missingProperties = [...allMissingRequired]; - /** @type {string[]} */ - const locations = [...allSchemaLocations]; - return [{ - message: localization.getRequiredErrorMessage(missingProperties), + message: localization.getRequiredErrorMessage([...allMissingRequired]), instanceLocation: Instance.uri(instance), - schemaLocations: locations + schemaLocations: /** @type {string[]} */ ([...allSchemaLocations]) }]; }; From a45ef84bbb5970da61bbbe1df6a239b8e43d30dd Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 13 Feb 2026 04:28:47 +0530 Subject: [PATCH 7/8] helper function is positioned at bottom now --- src/error-handlers/required.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index 1e30a88..4740f71 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -12,19 +12,6 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => const allMissingRequired = new Set(); const allSchemaLocations = []; - /** - * @param {string[]} requiredProperties - * @param {import("@hyperjump/json-schema/instance/experimental").JsonNode} instance - * @param {Set} missingSet - */ - const addMissingProperties = (requiredProperties, instance, missingSet) => { - for (const propertyName of requiredProperties) { - if (!Instance.has(propertyName, instance)) { - missingSet.add(propertyName); - } - } - }; - for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/required"]) { if (normalizedErrors["https://json-schema.org/keyword/required"][schemaLocation]) { continue; @@ -89,4 +76,17 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => }]; }; +/** + * @param {string[]} requiredProperties + * @param {import("@hyperjump/json-schema/instance/experimental").JsonNode} instance + * @param {Set} missingSet + */ +const addMissingProperties = (requiredProperties, instance, missingSet) => { + for (const propertyName of requiredProperties) { + if (!Instance.has(propertyName, instance)) { + missingSet.add(propertyName); + } + } +}; + export default requiredErrorHandler; From 6f0d7a250273c527c6366919833635a01043a7e3 Mon Sep 17 00:00:00 2001 From: Jason Desrosiers Date: Fri, 13 Feb 2026 10:17:43 -0800 Subject: [PATCH 8/8] Some cleanup --- src/error-handlers/required.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/error-handlers/required.js b/src/error-handlers/required.js index 4740f71..c9174ae 100644 --- a/src/error-handlers/required.js +++ b/src/error-handlers/required.js @@ -4,6 +4,7 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** * @import { ErrorHandler } from "../index.d.ts" + * @import { JsonNode } from "@hyperjump/json-schema/instance/experimental" */ /** @type ErrorHandler */ @@ -76,11 +77,7 @@ const requiredErrorHandler = async (normalizedErrors, instance, localization) => }]; }; -/** - * @param {string[]} requiredProperties - * @param {import("@hyperjump/json-schema/instance/experimental").JsonNode} instance - * @param {Set} missingSet - */ +/** @type (requiredProperties: string[], instance: JsonNode, missingSet: Set) => void */ const addMissingProperties = (requiredProperties, instance, missingSet) => { for (const propertyName of requiredProperties) { if (!Instance.has(propertyName, instance)) {