1+ import * as Browser from "@hyperjump/browser" ;
2+ import { registerSchema , unregisterSchema } from "@hyperjump/json-schema/draft-2020-12" ;
3+ import { getSchema } from "@hyperjump/json-schema/experimental" ;
4+ import { pointerSegments } from "@hyperjump/json-pointer" ;
5+ import { randomUUID } from "crypto" ;
6+
17/**
2- * @import {OutputFormat, OutputUnit, NormalizedError } from "../index.d.ts"
8+ * @import { OutputFormat, OutputUnit, NormalizedError, SchemaObject} from "../index.d.ts";
9+ * @import { SchemaDocument } from "@hyperjump/json-schema/experimental";
10+ * @import { Browser as BrowserType } from "@hyperjump/browser";
311 */
412
5- /** @type {(errorOutput: OutputFormat) => NormalizedError[] } */
6- export function normalizeOutputFormat ( errorOutput ) {
7- /** @type NormalizedError[] */
13+ /**
14+ * @param {OutputFormat } errorOutput
15+ * @param {SchemaObject } schema
16+ * @returns {Promise<NormalizedError[]> }
17+ */
18+ export async function normalizeOutputFormat ( errorOutput , schema ) {
19+ /** @type {NormalizedError[] } */
820 const output = [ ] ;
21+
922 if ( ! errorOutput || errorOutput . valid !== false ) {
1023 throw new Error ( "error Output must follow Draft 2019-09" ) ;
1124 }
1225
13- /** @type {(errorOutput: OutputUnit) => void } */
14- function collectErrors ( error ) {
26+ const keywords = new Set ( [
27+ "type" , "minLength" , "maxLength" , "minimum" , "maximum" , "format" , "pattern" ,
28+ "enum" , "const" , "required" , "items" , "properties" , "allOf" , "anyOf" , "oneOf" ,
29+ "not" , "contains" , "uniqueItems" , "additionalProperties" , "minItems" , "maxItems" ,
30+ "minProperties" , "maxProperties" , "dependentRequired" , "dependencies"
31+ ] ) ;
32+
33+ /** @type {(errorOutput: OutputUnit) => Promise<void> } */
34+ async function collectErrors ( error ) {
1535 if ( error . valid ) return ;
1636
1737 if ( ! ( "instanceLocation" in error ) || ! ( "absoluteKeywordLocation" in error || "keywordLocation" in error ) ) {
1838 throw new Error ( "error Output must follow Draft 2019-09" ) ;
1939 }
2040
21- // TODO: Convert keywordLocation to absoluteKeywordLocation
22- error . absoluteKeywordLocation ??= "https://example.com/main#/minLength" ;
41+ const absoluteKeywordLocation = error . absoluteKeywordLocation
42+ ?? await toAbsoluteKeywordLocation ( schema , /** @type string */ ( error . keywordLocation ) ) ;
43+
44+ const fragment = absoluteKeywordLocation . split ( "#" ) [ 1 ] ;
45+ const lastSegment = fragment . split ( "/" ) . filter ( Boolean ) . pop ( ) ;
2346
24- output . push ( {
25- valid : false ,
26- absoluteKeywordLocation : error . absoluteKeywordLocation ,
27- instanceLocation : normalizeInstanceLocation ( error . instanceLocation )
28- } ) ;
47+ // make a check here to remove the schemaLocation.
48+ if ( lastSegment && keywords . has ( lastSegment ) ) {
49+ output . push ( {
50+ valid : false ,
51+ absoluteKeywordLocation,
52+ instanceLocation : normalizeInstanceLocation ( error . instanceLocation )
53+ } ) ;
54+ }
2955
3056 if ( error . errors ) {
3157 for ( const nestedError of error . errors ) {
32- collectErrors ( nestedError ) ;
58+ await collectErrors ( nestedError ) ; // Recursive
3359 }
3460 }
3561 }
@@ -39,14 +65,35 @@ export function normalizeOutputFormat(errorOutput) {
3965 }
4066
4167 for ( const err of errorOutput . errors ) {
42- collectErrors ( err ) ;
68+ await collectErrors ( err ) ;
4369 }
4470
4571 return output ;
4672}
4773
48- /** @type (location: string) => string */
74+ /** @type { (location: string) => string } */
4975function normalizeInstanceLocation ( location ) {
50- if ( location . startsWith ( "/" ) || location === "" ) return "#" + location ;
51- return location ;
76+ return location . startsWith ( "/" ) || location === "" ? `#${ location } ` : location ;
77+ }
78+
79+ /**
80+ * Convert keywordLocation to absoluteKeywordLocation
81+ * @param {SchemaObject } schema
82+ * @param {string } keywordLocation
83+ * @returns {Promise<string> }
84+ */
85+ export async function toAbsoluteKeywordLocation ( schema , keywordLocation ) {
86+ const uri = `urn:uuid:${ randomUUID ( ) } ` ;
87+ try {
88+ registerSchema ( schema , uri ) ;
89+
90+ let browser = await getSchema ( uri ) ;
91+ for ( const segment of pointerSegments ( keywordLocation ) ) {
92+ browser = /** @type BrowserType<SchemaDocument> */ ( await Browser . step ( segment , browser ) ) ;
93+ }
94+
95+ return `${ browser . document . baseUri } #${ browser . cursor } ` ;
96+ } finally {
97+ unregisterSchema ( uri ) ;
98+ }
5299}
0 commit comments