Skip to content
47 changes: 47 additions & 0 deletions src/error-handlers/draft-04/dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { getSchema } from "@hyperjump/json-schema/experimental";
import { getErrors } from "../../json-schema-errors.js";
import * as Schema from "@hyperjump/browser";
import * as Instance from "@hyperjump/json-schema/instance/experimental";

/**
* @import { ErrorHandler, ErrorObject, NormalizedOutput } from "../../index.d.ts"
*/

/** @type ErrorHandler */
const dependenciesErrorHandler = async (normalizedErrors, instance, localization) => {
/** @type ErrorObject[] */
const errors = [];

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 outputs = normalizedErrors["https://json-schema.org/keyword/draft-04/dependencies"][schemaLocation];
for (const output of outputs) {
const result = await getErrors(output, instance, localization);
errors.push(...result);
}

const dependencies = await getSchema(schemaLocation);
for await (const [property, dependency] of Schema.entries(dependencies)) {
if (!Instance.has(property, instance)) {
continue;
}

if (Schema.typeOf(dependency) === "array") {
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;
};

export default dependenciesErrorHandler;
52 changes: 52 additions & 0 deletions src/error-handlers/draft-04/maximum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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 maximumErrorHandler = async (normalizedErrors, instance, localization) => {
/** @type ErrorObject[] */
const errors = [];

for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/maximum"]) {
if (normalizedErrors["https://json-schema.org/keyword/draft-04/maximum"][schemaLocation]) {
continue;
}

const compiled = await getSchema(schemaLocation);
const maximum = /** @type number */ (Schema.value(compiled));

const parentLocation = schemaLocation.replace(/\/[^/]+$/, "");

let exclusive = false;
for (const exclusiveLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/exclusiveMaximum"]) {
const exclusiveParentLocation = exclusiveLocation.replace(/\/[^/]+$/, "");
if (exclusiveParentLocation === parentLocation) {
const exclusiveCompiled = await getSchema(exclusiveLocation);
exclusive = /** @type boolean */ (Schema.value(exclusiveCompiled));
break;
}
}

if (exclusive) {
errors.push({
message: localization.getExclusiveMaximumErrorMessage(maximum),
instanceLocation: Instance.uri(instance),
schemaLocations: [schemaLocation]
});
} else {
errors.push({
message: localization.getMaximumErrorMessage(maximum),
instanceLocation: Instance.uri(instance),
schemaLocations: [schemaLocation]
});
}
}

return errors;
};

export default maximumErrorHandler;
52 changes: 52 additions & 0 deletions src/error-handlers/draft-04/minimum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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 minimumErrorHandler = async (normalizedErrors, instance, localization) => {
/** @type ErrorObject[] */
const errors = [];

for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/minimum"]) {
if (normalizedErrors["https://json-schema.org/keyword/draft-04/minimum"][schemaLocation]) {
continue;
}

const compiled = await getSchema(schemaLocation);
const minimum = /** @type number */ (Schema.value(compiled));

const parentLocation = schemaLocation.replace(/\/[^/]+$/, "");

let exclusive = false;
for (const exclusiveLocation in normalizedErrors["https://json-schema.org/keyword/draft-04/exclusiveMinimum"]) {
const exclusiveParentLocation = exclusiveLocation.replace(/\/[^/]+$/, "");
if (exclusiveParentLocation === parentLocation) {
const exclusiveCompiled = await getSchema(exclusiveLocation);
exclusive = /** @type boolean */ (Schema.value(exclusiveCompiled));
break;
}
}

if (exclusive) {
errors.push({
message: localization.getExclusiveMinimumErrorMessage(minimum),
instanceLocation: Instance.uri(instance),
schemaLocations: [schemaLocation]
});
} else {
errors.push({
message: localization.getMinimumErrorMessage(minimum),
instanceLocation: Instance.uri(instance),
schemaLocations: [schemaLocation]
});
}
}

return errors;
};

export default minimumErrorHandler;
51 changes: 39 additions & 12 deletions src/error-handlers/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,53 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental";
* @import { ErrorHandler, ErrorObject } from "../index.d.ts"
*/

const ALL_TYPES = new Set(["null", "boolean", "number", "string", "array", "object", "integer"]);

/** @type ErrorHandler */
const typeErrorHandler = async (normalizedErrors, instance, localization) => {
/** @type ErrorObject[] */
const errors = [];

for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/type"]) {
if (normalizedErrors["https://json-schema.org/keyword/type"][schemaLocation]) {
continue;
if (normalizedErrors["https://json-schema.org/keyword/type"]) {
/** @type {Set<string>} */
let allowedTypes = ALL_TYPES;
const failedTypeLocations = [];

for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/type"]) {
const isValid = normalizedErrors["https://json-schema.org/keyword/type"][schemaLocation];
if (!isValid) {
failedTypeLocations.push(schemaLocation);

const keyword = await getSchema(schemaLocation);
/** @type {string|string[]} */
const value = Schema.value(keyword);
const types = Array.isArray(value) ? value : [value];
/** @type {Set<string>} */
const keywordTypes = new Set(types);
if (keywordTypes.has("number")) {
keywordTypes.add("integer");
}
allowedTypes = allowedTypes.intersection(keywordTypes);
}
}

const keyword = await getSchema(schemaLocation);
const expectedTypes = /** @type string[] */ (Schema.typeOf(keyword) === "array"
? Schema.value(keyword)
: [Schema.value(keyword)]);
if (allowedTypes.has("number")) {
allowedTypes.delete("integer");
}

errors.push({
message: localization.getTypeErrorMessage(expectedTypes),
instanceLocation: Instance.uri(instance),
schemaLocations: [schemaLocation]
});
if (allowedTypes.size === 0) {
errors.push({
message: localization.getUnknownErrorMessage("type"),
instanceLocation: Instance.uri(instance),
schemaLocations: failedTypeLocations
});
} else if (failedTypeLocations.length > 0) {
errors.push({
message: localization.getTypeErrorMessage([...allowedTypes]),
instanceLocation: Instance.uri(instance),
schemaLocations: failedTypeLocations
});
}
}

return errors;
Expand Down
3 changes: 2 additions & 1 deletion src/hyperjump-json-schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "@hyperjump/json-schema/formats";
import { BASIC } from "@hyperjump/json-schema/experimental";
import { jsonSchemaErrors } from "../src/index.js";
import { FluentBundle, FluentResource } from "@fluent/bundle";
import "@hyperjump/json-schema/draft-04";

/**
* @import { SchemaObject } from "@hyperjump/json-schema"
Expand Down Expand Up @@ -183,4 +184,4 @@ runTests("https://json-schema.org/draft/2020-12/schema", 2020);
// 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);
runTests("http://json-schema.org/draft-04/schema", 4);
22 changes: 22 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ import unevaluatedItemsNormalizationHandler from "./normalization-handlers/uneva
import unevaluatedPropertiesNormalizationHandler from "./normalization-handlers/unevaluatedProperties.js";
import uniqueItemsNormalizationHandler from "./normalization-handlers/uniqueItems.js";
import unknownNormalizationHandler from "./normalization-handlers/unknown.js";
// Draft 04 Normalization Handlers
import draft04AdditionalItemsNormalizationHandler from "./normalization-handlers/draft-04/additionalItems.js";
import draft04DependenciesNormalizationHandler from "./normalization-handlers/draft-04/dependencies.js";
import draft04ExclusiveMaximumNormalizationHandler from "./normalization-handlers/draft-04/exclusiveMaximum.js";
import draft04ExclusiveMinimumNormalizationHandler from "./normalization-handlers/draft-04/exclusiveMinimum.js";
import draft04ItemsNormalizationHandler from "./normalization-handlers/draft-04/items.js";
import draft04MaximumNormalizationHandler from "./normalization-handlers/draft-04/maximum.js";
import draft04MinimumNormalizationHandler from "./normalization-handlers/draft-04/minimum.js";

// Error Handlers
import anyOfErrorHandler from "./error-handlers/anyOf.js";
Expand Down Expand Up @@ -70,6 +78,10 @@ import requiredErrorHandler from "./error-handlers/required.js";
import typeErrorHandler from "./error-handlers/type.js";
import uniqueItemsErrorHandler from "./error-handlers/uniqueItems.js";
import unknownErrorHandler from "./error-handlers/unknown.js";
// Draft 04 Error Handlers
import draft04DependenciesErrorHandler from "./error-handlers/draft-04/dependencies.js";
import draft04MaximumErrorHandler from "./error-handlers/draft-04/maximum.js";
import draft04MinimumErrorHandler from "./error-handlers/draft-04/minimum.js";

setNormalizationHandler("https://json-schema.org/keyword/additionalProperties", additionalPropertiesNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/allOf", allOfNormalizationHandler);
Expand Down Expand Up @@ -119,6 +131,13 @@ setNormalizationHandler("https://json-schema.org/keyword/unevaluatedItems", unev
setNormalizationHandler("https://json-schema.org/keyword/unevaluatedProperties", unevaluatedPropertiesNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/uniqueItems", uniqueItemsNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/unknown", unknownNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/draft-04/additionalItems", draft04AdditionalItemsNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/draft-04/dependencies", draft04DependenciesNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/draft-04/exclusiveMaximum", draft04ExclusiveMaximumNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/draft-04/exclusiveMinimum", draft04ExclusiveMinimumNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/draft-04/items", draft04ItemsNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/draft-04/maximum", draft04MaximumNormalizationHandler);
setNormalizationHandler("https://json-schema.org/keyword/draft-04/minimum", draft04MinimumNormalizationHandler);

addErrorHandler(anyOfErrorHandler);
addErrorHandler(booleanSchemaErrorHandler);
Expand All @@ -145,6 +164,9 @@ addErrorHandler(requiredErrorHandler);
addErrorHandler(typeErrorHandler);
addErrorHandler(uniqueItemsErrorHandler);
addErrorHandler(unknownErrorHandler);
addErrorHandler(draft04DependenciesErrorHandler);
addErrorHandler(draft04MaximumErrorHandler);
addErrorHandler(draft04MinimumErrorHandler);

export {
addErrorHandler,
Expand Down
28 changes: 28 additions & 0 deletions src/normalization-handlers/draft-04/additionalItems.js
Original file line number Diff line number Diff line change
@@ -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 item of Pact.drop(numberOfItems, Instance.iter(instance))) {
outputs.push(evaluateSchema(additionalItems, item, context));
}

return outputs;
},
simpleApplicator: true
};

export default additionalItemsNormalizationHandler;
32 changes: 32 additions & 0 deletions src/normalization-handlers/draft-04/dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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<Array<[string, string | string[]]>> */
const dependenciesNormalizationHandler = {
evaluate(dependencies, instance, context) {
/** @type NormalizedOutput[] */
const outputs = [];

if (Instance.typeOf(instance) !== "object") {
return outputs;
}

for (const [property, dependency] of dependencies) {
if (!Instance.has(property, instance)) {
continue;
}

if (typeof dependency === "string") {
outputs.push(evaluateSchema(dependency, instance, context));
}
}

return outputs;
}
};

export default dependenciesNormalizationHandler;
11 changes: 11 additions & 0 deletions src/normalization-handlers/draft-04/exclusiveMaximum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @import { NormalizationHandler } from "../../index.d.ts"
*/

/** @type NormalizationHandler */
const exclusiveMaximumNormalizationHandler = {
evaluate() {
}
};

export default exclusiveMaximumNormalizationHandler;
11 changes: 11 additions & 0 deletions src/normalization-handlers/draft-04/exclusiveMinimum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @import { NormalizationHandler } from "../../index.d.ts"
*/

/** @type NormalizationHandler */
const exclusiveMinimumNormalizationHandler = {
evaluate() {
}
};

export default exclusiveMinimumNormalizationHandler;
Loading