Skip to content

Commit 3b3f546

Browse files
committed
added options param in the main function
1 parent 6612f06 commit 3b3f546

File tree

5 files changed

+147
-88
lines changed

5 files changed

+147
-88
lines changed

src/index.d.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
export const betterJsonSchemaErrors: (
22
instance: Json,
3-
schema: SchemaObject,
4-
errorOutput: OutputFormat
3+
errorOutput: OutputFormat,
4+
options?: BetterJsonSchemaErrorsOptions
55
) => Promise<BetterJsonSchemaErrors>;
66

77
export type BetterJsonSchemaErrors = {
88
errors: ErrorObject[];
99
};
1010

11+
export type BetterJsonSchemaErrorsOptions = {
12+
schemaUri?: string;
13+
};
14+
1115
export type ErrorObject = {
1216
schemaLocation: string;
1317
instanceLocation: string;
@@ -31,6 +35,7 @@ export type OutputFormat = {
3135

3236
export type OutputUnit = {
3337
valid?: boolean;
38+
keyword?: string;
3439
absoluteKeywordLocation?: string;
3540
keywordLocation?: string;
3641
instanceLocation: string;
@@ -40,6 +45,7 @@ export type OutputUnit = {
4045

4146
export type NormalizedError = {
4247
valid: false;
48+
keyword: string;
4349
absoluteKeywordLocation: string;
4450
instanceLocation: string;
4551
};

src/index.js

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,66 @@
11
import { normalizeOutputFormat } from "./normalizeOutputFormat/normalizeOutput.js";
2+
import * as Schema from "@hyperjump/browser";
3+
import { getSchema } from "@hyperjump/json-schema/experimental";
24

35
/**
4-
* @import {betterJsonSchemaErrors} from "./index.d.ts"
6+
* @import { Browser } from "@hyperjump/browser";
7+
* @import { SchemaDocument } from "@hyperjump/json-schema/experimental";
8+
* @import { Json } from "@hyperjump/json-pointer";
9+
* @import {betterJsonSchemaErrors, OutputUnit } from "./index.d.ts"
510
*/
611

712
/** @type betterJsonSchemaErrors */
8-
export async function betterJsonSchemaErrors(instance, schema, errorOutput) {
9-
const normalizedErrors = await normalizeOutputFormat(errorOutput, schema);
10-
13+
export async function betterJsonSchemaErrors(instance, errorOutput, options = {}) {
14+
const normalizedErrors = await normalizeOutputFormat(errorOutput, options.schemaUri);
1115
const errors = [];
1216
for (const error of normalizedErrors) {
17+
if (skip.has(error.keyword)) {
18+
continue;
19+
}
20+
21+
/** @type Browser<SchemaDocument> */
22+
const schema = await getSchema(error.absoluteKeywordLocation);
1323
errors.push({
14-
message: "The instance should be at least 3 characters",
24+
message: getErrorMessage(error, schema, instance),
1525
instanceLocation: error.instanceLocation,
1626
schemaLocation: error.absoluteKeywordLocation
1727
});
1828
}
1929

2030
return { errors };
2131
}
32+
33+
/** @type (outputUnit: OutputUnit, schema: Browser<SchemaDocument>, instance: Json) => string */
34+
const getErrorMessage = (outputUnit, schema) => {
35+
if (outputUnit.keyword === "https://json-schema.org/keyword/minLength") {
36+
return `The instance should be at least ${Schema.value(schema)} characters`;
37+
}
38+
39+
throw Error("TODO: Error message not implemented");
40+
// if (outputUnit.keyword === "https://json-schema.org/keyword/required") {
41+
// const schemaDocument = await Schema.get(outputUnit.absoluteKeywordLocation);
42+
// const required = new Set(Schema.value(schemaDocument));
43+
// const object = Instance.get(outputUnit.instanceLocation, instance);
44+
// for (const propertyName of Instance.keys(object)) {
45+
// required.delete(propertyName);
46+
// }
47+
48+
// return `"${outputUnit.instanceLocation}" is missing required property(s): ${[...required]}. Schema location: ${outputUnit.absoluteKeywordLocation}`;
49+
// } else {
50+
// // Default message
51+
// return `"${outputUnit.instanceLocation}" fails schema constraint ${outputUnit.absoluteKeywordLocation}`;
52+
// }
53+
};
54+
55+
// These are probably not very useful for human readable messaging, so we'll skip them.
56+
const skip = new Set([
57+
"https://json-schema.org/evaluation/validate",
58+
"https://json-schema.org/keyword/ref",
59+
"https://json-schema.org/keyword/properties",
60+
"https://json-schema.org/keyword/patternProperties",
61+
"https://json-schema.org/keyword/items",
62+
"https://json-schema.org/keyword/prefixItems",
63+
"https://json-schema.org/keyword/if",
64+
"https://json-schema.org/keyword/then",
65+
"https://json-schema.org/keyword/else"
66+
]);

src/keywordErrorMessage.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { describe, test, expect } from "vitest";
2+
import { betterJsonSchemaErrors } from "./index.js";
3+
import { registerSchema } from "@hyperjump/json-schema/draft-2020-12";
4+
5+
describe("Error messages", () => {
6+
test("minLength", async () => {
7+
registerSchema({
8+
$id: "https://example.com/main",
9+
$schema: "https://json-schema.org/draft/2020-12/schema",
10+
minLength: 3
11+
});
12+
13+
const instance = "aa";
14+
15+
/** @type OutputFormat */
16+
const output = {
17+
valid: false,
18+
errors: [
19+
{
20+
absoluteKeywordLocation: "https://example.com/main#/minLength",
21+
instanceLocation: "#"
22+
}
23+
]
24+
};
25+
26+
const result = await betterJsonSchemaErrors(instance, output);
27+
expect(result.errors).to.eql([{
28+
schemaLocation: "https://example.com/main#/minLength",
29+
instanceLocation: "#",
30+
message: "The instance should be at least 3 characters"
31+
}
32+
]);
33+
});
34+
});

src/normalizeOutputFormat/normalizeOutput.js

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
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";
2+
import { getSchema, getKeywordId } from "@hyperjump/json-schema/experimental";
43
import { pointerSegments } from "@hyperjump/json-pointer";
5-
import { randomUUID } from "crypto";
64

75
/**
86
* @import { OutputFormat, OutputUnit, NormalizedError, SchemaObject} from "../index.d.ts";
@@ -12,10 +10,10 @@ import { randomUUID } from "crypto";
1210

1311
/**
1412
* @param {OutputFormat} errorOutput
15-
* @param {SchemaObject} schema
13+
* @param {string} [schemaUri]
1614
* @returns {Promise<NormalizedError[]>}
1715
*/
18-
export async function normalizeOutputFormat(errorOutput, schema) {
16+
export async function normalizeOutputFormat(errorOutput, schemaUri) {
1917
/** @type {NormalizedError[]} */
2018
const output = [];
2119

@@ -39,7 +37,7 @@ export async function normalizeOutputFormat(errorOutput, schema) {
3937
}
4038

4139
const absoluteKeywordLocation = error.absoluteKeywordLocation
42-
?? await toAbsoluteKeywordLocation(schema, /** @type string */ (error.keywordLocation));
40+
?? await toAbsoluteKeywordLocation(/** @type string */ (schemaUri), /** @type string */ (error.keywordLocation));
4341

4442
const fragment = absoluteKeywordLocation.split("#")[1];
4543
const lastSegment = fragment.split("/").filter(Boolean).pop();
@@ -48,6 +46,7 @@ export async function normalizeOutputFormat(errorOutput, schema) {
4846
if (lastSegment && keywords.has(lastSegment)) {
4947
output.push({
5048
valid: false,
49+
keyword: error.keyword ?? getKeywordId(lastSegment, "https://json-schema.org/draft/2020-12/schema"),
5150
absoluteKeywordLocation,
5251
instanceLocation: normalizeInstanceLocation(error.instanceLocation)
5352
});
@@ -78,22 +77,15 @@ function normalizeInstanceLocation(location) {
7877

7978
/**
8079
* Convert keywordLocation to absoluteKeywordLocation
81-
* @param {SchemaObject} schema
80+
* @param {string} uri
8281
* @param {string} keywordLocation
8382
* @returns {Promise<string>}
8483
*/
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);
84+
export async function toAbsoluteKeywordLocation(uri, keywordLocation) {
85+
let browser = await getSchema(uri);
86+
for (const segment of pointerSegments(keywordLocation)) {
87+
browser = /** @type BrowserType<SchemaDocument> */ (await Browser.step(segment, browser));
9888
}
89+
90+
return `${browser.document.baseUri}#${browser.cursor}`;
9991
}

0 commit comments

Comments
 (0)