From 2493dd552e63b44dceec565d0c008ec56c67135b Mon Sep 17 00:00:00 2001 From: Jason Desrosiers Date: Mon, 23 Mar 2026 14:01:22 -0700 Subject: [PATCH] Fixes and documentation for types --- package.json | 2 +- src/error-handlers/contains.js | 3 +- src/index.d.ts | 94 +++++++++++++++++++++++++++++++--- src/json-schema-errors.js | 6 +-- src/localization.js | 9 +--- 5 files changed, 94 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 3c93663..d9bdead 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "lint": "oxlint", "test": "vitest run", "type-check": "tsc --noEmit", - "docs": "typedoc --excludeExternals" + "docs": "typedoc --excludeExternals src/index.d.ts" }, "dependencies": { "@fluent/bundle": "^0.19.1", diff --git a/src/error-handlers/contains.js b/src/error-handlers/contains.js index 2e631c8..400e09c 100644 --- a/src/error-handlers/contains.js +++ b/src/error-handlers/contains.js @@ -3,8 +3,7 @@ import * as Schema from "@hyperjump/browser"; import * as Instance from "@hyperjump/json-schema/instance/experimental"; /** - * @import { ContainsRange } from "../localization.js" - * @import { ErrorHandler, ErrorObject } from "../index.d.ts" + * @import { ContainsRange, ErrorHandler, ErrorObject } from "../index.d.ts" */ /** @type ErrorHandler */ diff --git a/src/index.d.ts b/src/index.d.ts index 90e768c..367f26d 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -2,19 +2,40 @@ import { AST, EvaluationPlugin } from "@hyperjump/json-schema/experimental"; import { JsonNode } from "@hyperjump/json-schema/instance/experimental"; import { Localization } from "./localization.js"; +/** + * Converts standard JSON Schema validation output into human-oriented, localized + * messages. Schemas need to be registered with `@hyperjump/json-schema`'s + * `registerSchema` function. The default locale is `en-US`. + * + * @param errorOutput - The validation output in standard JSON Schema output format. + * @param schemaUri - The URI of the JSON Schema the data was validated against + * @param instance - The JSON data that was validated + * @param options - Options to configure the error handler (default locale is "en-US") + */ export const jsonSchemaErrors: ( errorOutput: OutputFormat, schemaUri: string, instance: Json, - options?: Options + options?: JsonSchemaErrorsOptions ) => Promise; -export const setNormalizationHandler: (uri: string, handler: NormalizationHandler) => void; +/** + * Sets a normalization handler for a specific keyword URI. Normalization handlers + * process keyword values during schema validation to produce normalized output. + */ +export const setNormalizationHandler: (keywordUri: string, handler: NormalizationHandler) => void; +/** + * The standard JSON Schema output format. Supports the "basic", "detailed", and + * "verbose" formats. + */ export type OutputFormat = OutputUnit & { valid: boolean; }; +/** + * A single node of the JSON Schema output format. + */ export type OutputUnit = { valid?: boolean; absoluteKeywordLocation?: string; @@ -28,12 +49,24 @@ export type JsonObject = { [property: string]: Json; }; -export type Options = { - language?: string; +export type JsonSchemaErrorsOptions = { + /** + * A locale identifier in the form of "{language}-{region}". + * + * @example "en-US" + */ + locale?: string; }; +/** + * An array of error objects representing validation failures. + */ export type JsonSchemaErrors = ErrorObject[]; +/** + * Represents a single validation error with message and schema location + * information. + */ export type ErrorObject = { message: string; alternatives?: ErrorObject[][]; @@ -41,8 +74,26 @@ export type ErrorObject = { schemaLocations: string[]; }; +/** + * Used to convert a specific keyword to the normalized format used by the error + * handlers. + */ export type NormalizationHandler = { + /** + * For non-applicator keywords, this doesn't need to do anything. Just return void. + * + * For applicator keywords, it should call `evaluateSchema` on each subschema and + * return an array with each result. + */ evaluate(value: KeywordValue, instance: JsonNode, context: Context): NormalizedOutput[] | void; + + /** + * Simple applicators just apply subschemas and don't have any validation behavior + * of their own. For example, `allOf` and `properties` are simple applicators. They + * never fail. Only their subschema can fail. `anyOf` and `oneOf` are not simple + * applicators because they can fail independently of the validation result of + * their subschemas. + */ simpleApplicator?: true; }; @@ -58,20 +109,51 @@ export type ErrorIndex = { }; }; +/** + * The normalized keyword result keyed by keyword URI and keyword location. If the + * keyword is an applicator the values can be `false` or `NormalizedOutput[]`. If + * the value is not an applicator, the value is just a boolean. + */ export type InstanceOutput = { [keywordUri: string]: { [keywordLocation: string]: boolean | NormalizedOutput[]; }; }; +/** + * A map of an instance location to the normalized keyword result for that location. + */ export type NormalizedOutput = { [instanceLocation: string]: InstanceOutput; }; +/** + * Builds the normalized output format for a schema. It's used in normalization + * handlers to evaluate an applicator's subschemas. + * + * @param schemaLocation - A URI with a JSON Pointer fragment + * @param instance + * @param context + */ +export const evaluateSchema: (schemaLocation: string, instance: JsonNode, context: EvaluationContext) => NormalizedOutput; + export const addErrorHandler: (handler: ErrorHandler) => void; +/** + * A function that transforms normalized errors for one or more keywords into human + * readable messages. + */ export type ErrorHandler = (normalizedErrors: InstanceOutput, instance: JsonNode, localization: Localization) => Promise; -export const evaluateSchema: (schemaLocation: string, instance: JsonNode, context: EvaluationContext) => NormalizedOutput; +/** + * Converts the normalized error format to human readable errors. It's used to + * build errors in applicator error handlers. + */ +export const getErrors: (normalizedErrors: NormalizedOutput, instance: JsonNode, localization: Localization) => Promise; + +export type { Localization }; -export const getErrors: (normalizedErrors: NormalizedOutput, rootInstance: JsonNode, language: Localization) => Promise; +export type ContainsRange = { + minContains?: number; + maxContains?: number; +}; diff --git a/src/json-schema-errors.js b/src/json-schema-errors.js index 6040616..ad8f3fb 100644 --- a/src/json-schema-errors.js +++ b/src/json-schema-errors.js @@ -16,7 +16,7 @@ import { Localization } from "./localization.js"; export const jsonSchemaErrors = async (errorOutput, schemaUri, instance, options = {}) => { const normalizedErrors = await normalizedOutput(instance, errorOutput, schemaUri); const rootInstance = Instance.fromJs(instance); - const localization = await Localization.forLocale(options.language ?? "en-US"); + const localization = await Localization.forLocale(options.locale ?? "en-US"); return await getErrors(normalizedErrors, rootInstance, localization); }; @@ -24,8 +24,8 @@ export const jsonSchemaErrors = async (errorOutput, schemaUri, instance, options const normalizationHandlers = {}; /** @type API.setNormalizationHandler */ -export const setNormalizationHandler = (uri, handler) => { - normalizationHandlers[uri] = handler; +export const setNormalizationHandler = (schemaUri, handler) => { + normalizationHandlers[schemaUri] = handler; }; /** @type (instance: API.Json, errorOutput: API.OutputUnit, subjectUri: string) => Promise */ diff --git a/src/localization.js b/src/localization.js index ddd16d5..eb7fe16 100644 --- a/src/localization.js +++ b/src/localization.js @@ -3,14 +3,7 @@ import { FluentBundle, FluentResource } from "@fluent/bundle"; /** * @import { FluentVariable} from "@fluent/bundle" - * @import { Json } from "./index.js" - */ - -/** - * @typedef {{ - * minContains?: number; - * maxContains?: number; - * }} ContainsRange + * @import { ContainsRange, Json } from "./index.d.ts" */ export class Localization {