From 07bda938c0fda5743d9d4a056d6580bfcad02205 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 28 Jan 2026 12:18:04 +0530 Subject: [PATCH 1/3] combined multipleOf messages --- src/error-handlers/multipleOf.js | 38 ++++++++++- src/test-suite/tests/multipleOf.json | 99 ++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) diff --git a/src/error-handlers/multipleOf.js b/src/error-handlers/multipleOf.js index 22a4418..5c3583d 100644 --- a/src/error-handlers/multipleOf.js +++ b/src/error-handlers/multipleOf.js @@ -6,11 +6,39 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental"; * @import { ErrorHandler, ErrorObject } from "../index.d.ts" */ +/** + * @param {number} a + * @param {number} b + * @returns {number} + */ +const gcd = (a, b) => { + while (b !== 0) { + const temp = b; + b = a % b; + a = temp; + } + return Math.abs(a); +}; + +/** + * @param {number} a + * @param {number} b + * @returns {number} + */ +const lcm = (a, b) => { + return Math.abs(a * b) / gcd(a, b); +}; /** @type ErrorHandler */ const multipleOfErrorHandler = async (normalizedErrors, instance, localization) => { /** @type ErrorObject[] */ const errors = []; + /** + * @type number[] */ + const multipleOfValues = []; + /** @type string[] */ + const schemaLocations = []; + for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/multipleOf"]) { if (normalizedErrors["https://json-schema.org/keyword/multipleOf"][schemaLocation]) { continue; @@ -19,10 +47,16 @@ const multipleOfErrorHandler = async (normalizedErrors, instance, localization) const keyword = await getSchema(schemaLocation); const multipleOf = /** @type number */ (Schema.value(keyword)); + multipleOfValues.push(multipleOf); + schemaLocations.push(schemaLocation); + } + + if (multipleOfValues.length > 0) { + const combineMultipleOf = multipleOfValues.reduce((acc, val) => lcm(acc, val), multipleOfValues[0]); errors.push({ - message: localization.getMultipleOfErrorMessage(multipleOf), + message: localization.getMultipleOfErrorMessage(combineMultipleOf), instanceLocation: Instance.uri(instance), - schemaLocations: [schemaLocation] + schemaLocations: schemaLocations }); } diff --git a/src/test-suite/tests/multipleOf.json b/src/test-suite/tests/multipleOf.json index 53f0dfe..e305b41 100644 --- a/src/test-suite/tests/multipleOf.json +++ b/src/test-suite/tests/multipleOf.json @@ -25,6 +25,105 @@ }, "instance": 6, "errors": [] + }, + { + "description": "multiple multipleOf constraints (combined)", + "schema": { + "allOf": [ + { "multipleOf": 3 }, + { "multipleOf": 2 } + ] + }, + "instance": 5, + "errors": [ + { + "messageId": "multipleOf-message", + "messageParams": { "multipleOf": "6" }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf"] + } + ] + }, + { + "description": "multiple multipleOf constraints pass", + "schema": { + "allOf": [ + { "multipleOf": 3 }, + { "multipleOf": 2 } + ] + }, + "instance": 6, + "errors": [] + }, + { + "description": "multiple multipleOf constraints (combined)", + "schema": { + "allOf": [ + { "multipleOf": 4 }, + { "multipleOf": 6 }, + { "multipleOf": 8 } + ] + }, + "instance": 10, + "errors": [ + { + "messageId": "multipleOf-message", + "messageParams": { "multipleOf": "24" }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf", "#/allOf/2/multipleOf"] + } + ] + }, + { + "description": "single multipleOf still works", + "schema": { + "multipleOf": 5 + }, + "instance": 7, + "errors": [ + { + "messageId": "multipleOf-message", + "messageParams": { "multipleOf": "5" }, + "instanceLocation": "#", + "schemaLocations": ["#/multipleOf"] + } + ] + }, + { + "description": "two identical multipleOf constraints", + "schema": { + "allOf": [ + { "multipleOf": 4 }, + { "multipleOf": 4 } + ] + }, + "instance": 6, + "errors": [ + { + "messageId": "multipleOf-message", + "messageParams": { "multipleOf": "4" }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf"] + } + ] + }, + { + "description": "prime multipleOf constraints (LCM of primes)", + "schema": { + "allOf": [ + { "multipleOf": 3 }, + { "multipleOf": 5 } + ] + }, + "instance": 7, + "errors": [ + { + "messageId": "multipleOf-message", + "messageParams": { "multipleOf": "15" }, + "instanceLocation": "#", + "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf"] + } + ] } ] } From c862a4c6ce1849d26ae31f85a133a8868177f551 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 29 Jan 2026 04:14:43 +0530 Subject: [PATCH 2/3] all redefinement are resolved --- src/error-handlers/multipleOf.js | 59 ++++++++++---------- src/test-suite/tests/multipleOf.json | 83 +--------------------------- 2 files changed, 30 insertions(+), 112 deletions(-) diff --git a/src/error-handlers/multipleOf.js b/src/error-handlers/multipleOf.js index 5c3583d..5589929 100644 --- a/src/error-handlers/multipleOf.js +++ b/src/error-handlers/multipleOf.js @@ -6,36 +6,13 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental"; * @import { ErrorHandler, ErrorObject } from "../index.d.ts" */ -/** - * @param {number} a - * @param {number} b - * @returns {number} - */ -const gcd = (a, b) => { - while (b !== 0) { - const temp = b; - b = a % b; - a = temp; - } - return Math.abs(a); -}; - -/** - * @param {number} a - * @param {number} b - * @returns {number} - */ -const lcm = (a, b) => { - return Math.abs(a * b) / gcd(a, b); -}; /** @type ErrorHandler */ const multipleOfErrorHandler = async (normalizedErrors, instance, localization) => { /** @type ErrorObject[] */ const errors = []; - /** - * @type number[] */ - const multipleOfValues = []; + /** @type (number | null) */ + let combinedMultipleOf = null; /** @type string[] */ const schemaLocations = []; @@ -47,20 +24,42 @@ const multipleOfErrorHandler = async (normalizedErrors, instance, localization) const keyword = await getSchema(schemaLocation); const multipleOf = /** @type number */ (Schema.value(keyword)); - multipleOfValues.push(multipleOf); + combinedMultipleOf = combinedMultipleOf === null ? multipleOf : lcm(combinedMultipleOf, multipleOf); schemaLocations.push(schemaLocation); } - if (multipleOfValues.length > 0) { - const combineMultipleOf = multipleOfValues.reduce((acc, val) => lcm(acc, val), multipleOfValues[0]); + if (combinedMultipleOf !== null) { errors.push({ - message: localization.getMultipleOfErrorMessage(combineMultipleOf), + message: localization.getMultipleOfErrorMessage(combinedMultipleOf), instanceLocation: Instance.uri(instance), - schemaLocations: schemaLocations + schemaLocations }); } return errors; }; +/** + * @param {number} a + * @param {number} b + * @returns {number} + */ +const gcd = (a, b) => { + while (b !== 0) { + const temp = b; + b = a % b; + a = temp; + } + return Math.abs(a); +}; + +/** + * @param {number} a + * @param {number} b + * @returns {number} + */ +const lcm = (a, b) => { + return Math.abs(a * b) / gcd(a, b); +}; + export default multipleOfErrorHandler; diff --git a/src/test-suite/tests/multipleOf.json b/src/test-suite/tests/multipleOf.json index e305b41..3503333 100644 --- a/src/test-suite/tests/multipleOf.json +++ b/src/test-suite/tests/multipleOf.json @@ -27,7 +27,7 @@ "errors": [] }, { - "description": "multiple multipleOf constraints (combined)", + "description": "multiple multipleOf constraints (LCM of 3 and 2)", "schema": { "allOf": [ { "multipleOf": 3 }, @@ -43,87 +43,6 @@ "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf"] } ] - }, - { - "description": "multiple multipleOf constraints pass", - "schema": { - "allOf": [ - { "multipleOf": 3 }, - { "multipleOf": 2 } - ] - }, - "instance": 6, - "errors": [] - }, - { - "description": "multiple multipleOf constraints (combined)", - "schema": { - "allOf": [ - { "multipleOf": 4 }, - { "multipleOf": 6 }, - { "multipleOf": 8 } - ] - }, - "instance": 10, - "errors": [ - { - "messageId": "multipleOf-message", - "messageParams": { "multipleOf": "24" }, - "instanceLocation": "#", - "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf", "#/allOf/2/multipleOf"] - } - ] - }, - { - "description": "single multipleOf still works", - "schema": { - "multipleOf": 5 - }, - "instance": 7, - "errors": [ - { - "messageId": "multipleOf-message", - "messageParams": { "multipleOf": "5" }, - "instanceLocation": "#", - "schemaLocations": ["#/multipleOf"] - } - ] - }, - { - "description": "two identical multipleOf constraints", - "schema": { - "allOf": [ - { "multipleOf": 4 }, - { "multipleOf": 4 } - ] - }, - "instance": 6, - "errors": [ - { - "messageId": "multipleOf-message", - "messageParams": { "multipleOf": "4" }, - "instanceLocation": "#", - "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf"] - } - ] - }, - { - "description": "prime multipleOf constraints (LCM of primes)", - "schema": { - "allOf": [ - { "multipleOf": 3 }, - { "multipleOf": 5 } - ] - }, - "instance": 7, - "errors": [ - { - "messageId": "multipleOf-message", - "messageParams": { "multipleOf": "15" }, - "instanceLocation": "#", - "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf"] - } - ] } ] } From 117e5a41868d99053a32506c1359716854b93f2d Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 30 Jan 2026 03:50:02 +0530 Subject: [PATCH 3/3] added 3-value test case --- src/test-suite/tests/multipleOf.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/test-suite/tests/multipleOf.json b/src/test-suite/tests/multipleOf.json index 3503333..766f089 100644 --- a/src/test-suite/tests/multipleOf.json +++ b/src/test-suite/tests/multipleOf.json @@ -27,20 +27,21 @@ "errors": [] }, { - "description": "multiple multipleOf constraints (LCM of 3 and 2)", + "description": "multiple multipleOf constraints (LCM of 4, 6, and 8)", "schema": { "allOf": [ - { "multipleOf": 3 }, - { "multipleOf": 2 } + { "multipleOf": 4 }, + { "multipleOf": 6 }, + { "multipleOf": 8 } ] }, - "instance": 5, + "instance": 10, "errors": [ { "messageId": "multipleOf-message", - "messageParams": { "multipleOf": "6" }, + "messageParams": { "multipleOf": "24" }, "instanceLocation": "#", - "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf"] + "schemaLocations": ["#/allOf/0/multipleOf", "#/allOf/1/multipleOf", "#/allOf/2/multipleOf"] } ] }