@@ -3,6 +3,7 @@ import { getSchema } from "@hyperjump/json-schema/experimental";
33import * as Instance from "@hyperjump/json-schema/instance/experimental" ;
44import leven from "leven" ;
55import { normalizedErrorOuput } from "./normalizeOutputFormat/normalizeOutput.js" ;
6+ import { Localization } from "./localization.js" ;
67
78/**
89 * @import { Browser } from "@hyperjump/browser";
@@ -13,6 +14,8 @@ import { normalizedErrorOuput } from "./normalizeOutputFormat/normalizeOutput.js
1314 * @import { NormalizedOutput, InstanceOutput } from "./normalizeOutputFormat/normalizeOutput.js"
1415 */
1516
17+ const localization = await Localization . forLocale ( "en-US" ) ;
18+
1619/** @type betterJsonSchemaErrors */
1720export async function betterJsonSchemaErrors ( instance , errorOutput , schemaUri ) {
1821 const normalizedErrors = await normalizedErrorOuput ( instance , errorOutput , schemaUri ) ;
@@ -76,7 +79,7 @@ const errorHandlers = [
7679 }
7780 }
7881 errors . push ( {
79- message : `The instance must be a ${ [ ...expectedTypes ] . join ( ", " ) } . Found ' ${ Instance . typeOf ( instance ) } '.` ,
82+ message : localization . getTypeErrorMessage ( [ ...expectedTypes ] , Instance . typeOf ( instance ) ) ,
8083 instanceLocation : Instance . uri ( instance ) ,
8184 schemaLocation : schemaLocation
8285 } ) ;
@@ -148,7 +151,7 @@ const errorHandlers = [
148151 if ( ! normalizedErrors [ "https://json-schema.org/keyword/type" ] [ schemaLocation ] ) {
149152 const keyword = await getSchema ( schemaLocation ) ;
150153 errors . push ( {
151- message : `The instance should be of type " ${ Schema . value ( keyword ) } " but found " ${ Instance . typeOf ( instance ) } ".` ,
154+ message : localization . getTypeErrorMessage ( Schema . value ( keyword ) , Instance . typeOf ( instance ) ) ,
152155 instanceLocation : Instance . uri ( instance ) ,
153156 schemaLocation : schemaLocation
154157 } ) ;
@@ -490,48 +493,91 @@ const errorHandlers = [
490493 return errors ;
491494 } ,
492495
493- // eslint-disable-next-line @typescript-eslint/require-await
494496 async ( normalizedErrors , instance ) => {
495497 /** @type ErrorObject[] */
496498 const errors = [ ] ;
497-
498499 if ( normalizedErrors [ "https://json-schema.org/keyword/contains" ] ) {
499500 for ( const schemaLocation in normalizedErrors [ "https://json-schema.org/keyword/contains" ] ) {
500- // const keyword = await getSchema(schemaLocation);
501501 errors . push ( {
502- message : `TODO - contains ` ,
502+ message : `A required value is missing from the list ` ,
503503 instanceLocation : Instance . uri ( instance ) ,
504504 schemaLocation : schemaLocation
505505 } ) ;
506+ const containsNodes = /** @type NormalizedOutput[] */ ( normalizedErrors [ "https://json-schema.org/keyword/contains" ] [ schemaLocation ] ) ;
507+ for ( const errorOutput of containsNodes ) {
508+ const containsSubErrors = await getErrors ( errorOutput , instance ) ;
509+ errors . push ( ...containsSubErrors ) ;
510+ }
511+ }
512+ }
513+
514+ return errors ;
515+ } ,
516+
517+ // eslint-disable-next-line @typescript-eslint/require-await
518+ async ( normalizedErrors , instance ) => {
519+ /** @type ErrorObject[] */
520+ const errors = [ ] ;
521+
522+ if ( normalizedErrors [ "https://json-schema.org/keyword/not" ] ) {
523+ for ( const schemaLocation in normalizedErrors [ "https://json-schema.org/keyword/not" ] ) {
524+ errors . push ( {
525+ message : `The instance is not allowed to be used in this schema.` ,
526+ instanceLocation : Instance . uri ( instance ) ,
527+ schemaLocation : schemaLocation
528+ } ) ;
529+ }
530+ }
531+
532+ return errors ;
533+ } ,
534+
535+ // eslint-disable-next-line @typescript-eslint/require-await
536+ async ( normalizedErrors , instance ) => {
537+ /** @type ErrorObject[] */
538+ const errors = [ ] ;
539+ if ( normalizedErrors [ "https://json-schema.org/validation" ] ) {
540+ for ( const schemaLocation in normalizedErrors [ "https://json-schema.org/validation" ] ) {
541+ if ( ! normalizedErrors [ "https://json-schema.org/validation" ] [ schemaLocation ] && schemaLocation . endsWith ( "/additionalProperties" ) ) {
542+ const notAllowedValue = Instance . uri ( instance ) . split ( "/" ) . pop ( ) ;
543+ errors . push ( {
544+ message : `The property "${ notAllowedValue } " is not allowed.` ,
545+ instanceLocation : Instance . uri ( instance ) ,
546+ schemaLocation : schemaLocation
547+ } ) ;
548+ }
549+ }
550+ }
551+ return errors ;
552+ } ,
553+
554+ async ( normalizedErrors , instance ) => {
555+ /** @type ErrorObject[] */
556+ const errors = [ ] ;
557+
558+ if ( normalizedErrors [ "https://json-schema.org/keyword/dependentRequired" ] ) {
559+ for ( const schemaLocation in normalizedErrors [ "https://json-schema.org/keyword/dependentRequired" ] ) {
560+ if ( ! normalizedErrors [ "https://json-schema.org/keyword/dependentRequired" ] [ schemaLocation ] ) {
561+ const keyword = await getSchema ( schemaLocation ) ;
562+ const dependentRequired = /** @type {Record<string, string[]> } */ ( Schema . value ( keyword ) ) ;
563+ for ( const propertyName in dependentRequired ) {
564+ if ( Instance . has ( propertyName , instance ) ) {
565+ const required = dependentRequired [ propertyName ] ;
566+ const missing = required . filter ( ( prop ) => ! Instance . has ( prop , instance ) ) ;
567+
568+ if ( missing . length > 0 ) {
569+ errors . push ( {
570+ message : `Property "${ propertyName } " requires property(s): ${ missing . join ( ", " ) } .` ,
571+ instanceLocation : Instance . uri ( instance ) ,
572+ schemaLocation : schemaLocation
573+ } ) ;
574+ }
575+ }
576+ }
577+ }
506578 }
507579 }
508580
509581 return errors ;
510582 }
511583] ;
512-
513- // /** @type (value: Json) => "null" | "boolean" | "number" | "string" | "array" | "object" | "undefined" */
514- // const jsonTypeOf = (value) => {
515- // const jsType = typeof value;
516-
517- // switch (jsType) {
518- // case "number":
519- // case "string":
520- // case "boolean":
521- // case "undefined":
522- // return jsType;
523- // case "object":
524- // if (Array.isArray(value)) {
525- // return "array";
526- // } else if (value === null) {
527- // return "null";
528- // } else if (Object.getPrototypeOf(value) === Object.prototype) {
529- // return "object";
530- // }
531- // default: {
532- // // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
533- // const type = jsType === "object" ? Object.getPrototypeOf(value).constructor.name ?? "anonymous" : jsType;
534- // throw Error(`Not a JSON compatible type: ${type}`);
535- // }
536- // }
537- // };
0 commit comments