@atlassian/better-ajv-errors 0.0.0-stage → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2018 Atlassian Pty Ltd
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/README.md CHANGED
@@ -1,3 +1,126 @@
1
- # Temporary Holding Version
1
+ <h1 align="center">
2
+ <img width="570" src="media/logo.png" alt="better-ajv-errors">
3
+ <br>
4
+ </h1>
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ > JSON Schema validation for Human 👨‍🎤
7
+
8
+ Main goal of this library is to provide relevant error messages like the following:
9
+
10
+ <p align="center">
11
+ <img src="media/screenshot.svg">
12
+ </p>
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm i better-ajv-errors
18
+ ```
19
+
20
+ Also install [ajv](https://www.npmjs.com/package/ajv), version 4.11.8 to 8, to validate data against JSON schemas. `better-ajv-errors` requires Node.js 22 or later.
21
+
22
+ ## Usage
23
+
24
+ First, you need to validate your payload with `ajv`. If it's invalid then you can pass `validate.errors` object into `better-ajv-errors`.
25
+
26
+ ```js
27
+ import Ajv from 'ajv';
28
+ import betterAjvErrors from 'better-ajv-errors';
29
+ // const Ajv = require('ajv');
30
+ // const betterAjvErrors = require('better-ajv-errors').default;
31
+ // Or
32
+ // const { default: betterAjvErrors } = require('better-ajv-errors');
33
+
34
+ // `allErrors` makes ajv report every error instead of stopping at the first one.
35
+ // ajv 6 and older also need `jsonPointers: true`.
36
+ const ajv = new Ajv({ allErrors: true });
37
+
38
+ // Load schema and data
39
+ const schema = ...;
40
+ const data = ...;
41
+
42
+ const validate = ajv.compile(schema);
43
+ const valid = validate(data);
44
+
45
+ if (!valid) {
46
+ const output = betterAjvErrors(schema, data, validate.errors);
47
+ console.log(output);
48
+ }
49
+ ```
50
+
51
+ `better-ajv-errors` leaves out errors that another error usually explains:
52
+
53
+ - A missing `required` property hides the other errors reported for that object, such as `additionalProperties`.
54
+ - An `enum` error is hidden when another error is reported at the same level.
55
+
56
+ ## API
57
+
58
+ ### betterAjvErrors(schema, data, errors, [options])
59
+
60
+ Returns formatted validation error to **print** in `console`. See [`options.format`](#format) for further details.
61
+
62
+ #### schema
63
+
64
+ Type: `Object`
65
+
66
+ The JSON Schema you used for validation with `ajv`
67
+
68
+ #### data
69
+
70
+ Type: `Object`
71
+
72
+ The JSON payload you validate against using `ajv`
73
+
74
+ #### errors
75
+
76
+ Type: `Array`
77
+
78
+ Array of [ajv validation errors](https://ajv.js.org/api.html#validation-errors)
79
+
80
+ #### options
81
+
82
+ Type: `Object`
83
+
84
+ ##### format
85
+
86
+ Type: `string`
87
+ Default: `cli`
88
+ Values: `cli` `js`
89
+
90
+ Use default `cli` output format if you want to **print** beautiful validation errors like following:
91
+
92
+ <img width="620" src="media/screenshot.svg">
93
+
94
+ Or, use `js` if you are planning to use this with some API. Your output will look like following:
95
+
96
+ ```javascript
97
+ [
98
+ {
99
+ start: { line: 6, column: 15, offset: 70 },
100
+ end: { line: 6, column: 26, offset: 81 },
101
+ error:
102
+ '/content/0/type must be equal to one of the allowed values: paragraph, codeBlock, blockquote',
103
+ path: '/content/0/type',
104
+ suggestion: 'Did you mean paragraph?',
105
+ },
106
+ ];
107
+ ```
108
+
109
+ `path` is the `instancePath` of the ajv error. `end` is left out for `required` errors, and `suggestion` appears on some `enum` errors, naming the allowed value closest to the actual one.
110
+
111
+ ##### indent
112
+
113
+ Type: `number` `null`
114
+ Default: `null`
115
+
116
+ If you have an unindented JSON payload and you want the error output indented.
117
+
118
+ This option has no effect when using the `json` option.
119
+
120
+ ##### json
121
+
122
+ Type: `string` `null`
123
+ Default: `null`
124
+
125
+ Raw JSON payload used when formatting codeframe.
126
+ Gives accurate line and column listings.
package/dist/index.cjs ADDED
@@ -0,0 +1,418 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ //#region \0rolldown/runtime.js
6
+ var __create = Object.create;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
14
+ key = keys[i];
15
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: ((k) => from[k]).bind(null, key),
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+ //#endregion
27
+ let _humanwhocodes_momoa = require("@humanwhocodes/momoa");
28
+ let node_util = require("node:util");
29
+ let leven = require("leven");
30
+ leven = __toESM(leven, 1);
31
+ let jsonpointer = require("jsonpointer");
32
+ jsonpointer = __toESM(jsonpointer, 1);
33
+ //#region src/utils.js
34
+ const eq = (x) => (y) => x === y;
35
+ const not = (fn) => (x) => !fn(x);
36
+ const getValues = (o) => Object.values(o);
37
+ const notUndefined = (x) => x !== void 0;
38
+ const isXError = (x) => (error) => error.keyword === x;
39
+ const isRequiredError = isXError("required");
40
+ const isAnyOfError = isXError("anyOf");
41
+ const isEnumError = isXError("enum");
42
+ const getErrors = (node) => node && node.errors ? node.errors.map((e) => e.keyword === "errorMessage" ? {
43
+ ...e.params.errors[0],
44
+ message: e.message
45
+ } : e) : [];
46
+ const getChildren = (node) => node && getValues(node.children) || [];
47
+ const getSiblings = (parent) => (node) => getChildren(parent).filter(not(eq(node)));
48
+ const concatAll = (xs) => (ys) => ys.reduce((zs, z) => zs.concat(z), xs);
49
+ //#endregion
50
+ //#region src/code-frame.js
51
+ const LINES_ABOVE = 2;
52
+ const LINES_BELOW = 3;
53
+ const NEWLINE = /\r\n|[\r\n]/;
54
+ const TOKEN_STYLES = {
55
+ String: "green",
56
+ Number: "magenta",
57
+ Boolean: "cyan",
58
+ Null: "cyan",
59
+ Colon: "yellow",
60
+ Comma: "yellow"
61
+ };
62
+ const MARKER_STYLE = ["red", "bold"];
63
+ const supportsColor = () => (0, node_util.styleText)("red", "-") !== "-";
64
+ /**
65
+ * Renders the lines of `source` around `location`, with carets under the located text and
66
+ * `message` after the last caret.
67
+ *
68
+ * Lines and columns are 1-based and `end` is exclusive, as in momoa's locations. Without
69
+ * `end`, a single caret marks `start`.
70
+ *
71
+ * @param {string} source
72
+ * @param {{ start: { line: number, column: number }, end?: { line: number, column: number } }} location
73
+ * @param {{ message?: string, color?: boolean }} [options]
74
+ * @return {string}
75
+ */
76
+ function codeFrame(source, { start, end = start }, { message, color = supportsColor() } = {}) {
77
+ const paint = color ? (format, text) => (0, node_util.styleText)(format, text, { validateStream: false }) : (_format, text) => text;
78
+ const lines = source.split(NEWLINE);
79
+ const markers = getMarkers(lines, start, end);
80
+ const first = Math.max(start.line - LINES_ABOVE, 1);
81
+ const last = Math.min(end.line + LINES_BELOW, lines.length);
82
+ const width = String(last).length;
83
+ const rows = [];
84
+ for (let number = first; number <= last; number++) {
85
+ const line = lines[number - 1];
86
+ const gutter = ` ${String(number).padStart(width)} |`;
87
+ const code = line.length > 0 ? ` ${color ? highlight(line, paint) : line}` : "";
88
+ const marker = markers.get(number);
89
+ if (!marker) {
90
+ rows.push(` ${paint("gray", gutter)}${code}`);
91
+ continue;
92
+ }
93
+ const [column, count] = marker;
94
+ const spacing = line.slice(0, column - 1).replace(/[^\t]/g, " ");
95
+ let caretRow = ` ${paint("gray", gutter.replace(/\d/g, " "))} ${spacing}${paint(MARKER_STYLE, "^".repeat(count))}`;
96
+ if (message && number === end.line) caretRow += ` ${paint(MARKER_STYLE, message)}`;
97
+ rows.push(`${paint(MARKER_STYLE, ">")}${paint("gray", gutter)}${code}`, caretRow);
98
+ }
99
+ return rows.join("\n");
100
+ }
101
+ function getMarkers(lines, start, end) {
102
+ const markers = /* @__PURE__ */ new Map();
103
+ if (start.line === end.line) {
104
+ markers.set(start.line, [start.column, Math.max(end.column - start.column, 1)]);
105
+ return markers;
106
+ }
107
+ markers.set(start.line, [start.column, lines[start.line - 1].length - start.column + 1]);
108
+ for (let number = start.line + 1; number < end.line; number++) markers.set(number, [1, Math.max(lines[number - 1].length, 1)]);
109
+ markers.set(end.line, [1, end.column - 1]);
110
+ return markers;
111
+ }
112
+ function highlight(line, paint) {
113
+ let output = "";
114
+ let offset = 0;
115
+ for (const { type, loc } of (0, _humanwhocodes_momoa.tokenize)(line)) {
116
+ const style = TOKEN_STYLES[type];
117
+ if (style) {
118
+ output += line.slice(offset, loc.start.offset) + paint(style, line.slice(loc.start.offset, loc.end.offset));
119
+ offset = loc.end.offset;
120
+ }
121
+ }
122
+ return output + line.slice(offset);
123
+ }
124
+ //#endregion
125
+ //#region src/json/utils.js
126
+ const getPointers = (dataPath) => {
127
+ return dataPath.split("/").slice(1).map((pointer) => pointer.split("~1").join("/").split("~0").join("~"));
128
+ };
129
+ //#endregion
130
+ //#region src/json/get-meta-from-path.js
131
+ function getMetaFromPath(jsonAst, dataPath, includeIdentifierLocation) {
132
+ const pointers = getPointers(dataPath);
133
+ const lastPointerIndex = pointers.length - 1;
134
+ return pointers.reduce((obj, pointer, idx) => {
135
+ switch (obj.type) {
136
+ case "Object": {
137
+ const member = obj.members.findLast((child) => child.name.value === pointer);
138
+ if (!member) throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);
139
+ const { name, value } = member;
140
+ return includeIdentifierLocation && idx === lastPointerIndex ? name : value;
141
+ }
142
+ case "Array": {
143
+ const element = obj.elements[pointer];
144
+ return element && element.value;
145
+ }
146
+ default: console.log(obj);
147
+ }
148
+ }, jsonAst.body);
149
+ }
150
+ //#endregion
151
+ //#region src/json/get-decorated-data-path.js
152
+ function getDecoratedDataPath(jsonAst, dataPath) {
153
+ let decoratedPath = "";
154
+ getPointers(dataPath).reduce((obj, pointer) => {
155
+ switch (obj.type) {
156
+ case "Object": {
157
+ decoratedPath += `/${pointer}`;
158
+ const member = obj.members.findLast((child) => child.name.value === pointer);
159
+ if (!member) throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);
160
+ return member.value;
161
+ }
162
+ case "Array": {
163
+ const element = obj.elements[pointer];
164
+ const value = element && element.value;
165
+ decoratedPath += `/${pointer}${getTypeName(value)}`;
166
+ return value;
167
+ }
168
+ default: console.log(obj);
169
+ }
170
+ }, jsonAst.body);
171
+ return decoratedPath;
172
+ }
173
+ function getTypeName(obj) {
174
+ if (!obj || !obj.elements) return "";
175
+ const type = obj.elements.filter((child) => child && child.name && child.name.value === "type");
176
+ if (!type.length) return "";
177
+ return type[0].value && `:${type[0].value.value}` || "";
178
+ }
179
+ //#endregion
180
+ //#region src/validation-errors/base.js
181
+ var BaseValidationError = class {
182
+ constructor(options = { isIdentifierLocation: false }, { data, schema, jsonAst, jsonRaw }) {
183
+ this.options = options;
184
+ this.data = data;
185
+ this.schema = schema;
186
+ this.jsonAst = jsonAst;
187
+ this.jsonRaw = jsonRaw;
188
+ }
189
+ getLocation(dataPath = this.instancePath) {
190
+ const { isIdentifierLocation, isSkipEndLocation } = this.options;
191
+ const { loc } = getMetaFromPath(this.jsonAst, dataPath, isIdentifierLocation);
192
+ return {
193
+ start: loc.start,
194
+ end: isSkipEndLocation ? void 0 : loc.end
195
+ };
196
+ }
197
+ getDecoratedPath(dataPath = this.instancePath) {
198
+ return getDecoratedDataPath(this.jsonAst, dataPath);
199
+ }
200
+ withDecoratedPath(message, separator = " ") {
201
+ const decoratedPath = this.getDecoratedPath();
202
+ return decoratedPath ? `${decoratedPath}${separator}${message}` : message;
203
+ }
204
+ getCodeFrame(message, dataPath = this.instancePath) {
205
+ return codeFrame(this.jsonRaw, this.getLocation(dataPath), { message });
206
+ }
207
+ /**
208
+ * @return {string}
209
+ */
210
+ get instancePath() {
211
+ return typeof this.options.instancePath !== "undefined" ? this.options.instancePath : this.options.dataPath;
212
+ }
213
+ print() {
214
+ throw new Error(`Implement the 'print' method inside ${this.constructor.name}!`);
215
+ }
216
+ getError() {
217
+ throw new Error(`Implement the 'getError' method inside ${this.constructor.name}!`);
218
+ }
219
+ };
220
+ //#endregion
221
+ //#region src/validation-errors/required.js
222
+ var RequiredValidationError = class extends BaseValidationError {
223
+ getLocation(dataPath = this.instancePath) {
224
+ const { start } = super.getLocation(dataPath);
225
+ return { start };
226
+ }
227
+ print() {
228
+ const { message, params } = this.options;
229
+ return [(0, node_util.styleText)("red", (0, node_util.styleText)("bold", "REQUIRED") + " " + message) + "\n"].concat(this.getCodeFrame("☹️ " + (0, node_util.styleText)("magentaBright", params.missingProperty) + " is missing here!"));
230
+ }
231
+ getError() {
232
+ const { message } = this.options;
233
+ return {
234
+ ...this.getLocation(),
235
+ error: this.withDecoratedPath(message),
236
+ path: this.instancePath
237
+ };
238
+ }
239
+ };
240
+ //#endregion
241
+ //#region src/validation-errors/additional-prop.js
242
+ var AdditionalPropValidationError = class extends BaseValidationError {
243
+ constructor(...args) {
244
+ super(...args);
245
+ this.options.isIdentifierLocation = true;
246
+ }
247
+ print() {
248
+ const { message, params } = this.options;
249
+ return [(0, node_util.styleText)("red", (0, node_util.styleText)("bold", "ADDITIONAL PROPERTY") + " " + message) + "\n"].concat(this.getCodeFrame("😲 " + (0, node_util.styleText)("magentaBright", params.additionalProperty) + " is not expected to be here!", `${this.instancePath}/${params.additionalProperty}`));
250
+ }
251
+ getError() {
252
+ const { params } = this.options;
253
+ return {
254
+ ...this.getLocation(`${this.instancePath}/${params.additionalProperty}`),
255
+ error: this.withDecoratedPath(`Property ${params.additionalProperty} is not expected to be here`),
256
+ path: this.instancePath
257
+ };
258
+ }
259
+ };
260
+ //#endregion
261
+ //#region src/validation-errors/enum.js
262
+ var EnumValidationError = class extends BaseValidationError {
263
+ print() {
264
+ const { message, params: { allowedValues } } = this.options;
265
+ const bestMatch = this.findBestMatch();
266
+ const [firstValue, ...rest] = allowedValues;
267
+ const allowedValuesMessage = rest.reduce((acc, value) => `${acc}, ${value}`, firstValue || "");
268
+ return [(0, node_util.styleText)("red", (0, node_util.styleText)("bold", "ENUM") + " " + message), (0, node_util.styleText)("red", `(${allowedValuesMessage})\n`)].concat(this.getCodeFrame(bestMatch !== null ? "👈🏽 Did you mean " + (0, node_util.styleText)("magentaBright", String(bestMatch)) + " here?" : "👈🏽 Unexpected value, should be equal to one of the allowed values"));
269
+ }
270
+ getError() {
271
+ const { message, params } = this.options;
272
+ const bestMatch = this.findBestMatch();
273
+ const allowedValues = params.allowedValues.join(", ");
274
+ const output = {
275
+ ...this.getLocation(),
276
+ error: this.withDecoratedPath(`${message}: ${allowedValues}`),
277
+ path: this.instancePath
278
+ };
279
+ if (bestMatch !== null) output.suggestion = `Did you mean ${bestMatch}?`;
280
+ return output;
281
+ }
282
+ findBestMatch() {
283
+ const { params: { allowedValues } } = this.options;
284
+ const currentValue = this.instancePath === "" ? this.data : jsonpointer.default.get(this.data, this.instancePath);
285
+ if (!currentValue) return null;
286
+ const bestMatch = allowedValues.map((value) => ({
287
+ value,
288
+ weight: typeof value === "string" ? (0, leven.default)(value, currentValue.toString()) : Infinity
289
+ })).sort((x, y) => x.weight > y.weight ? 1 : x.weight < y.weight ? -1 : 0)[0];
290
+ return allowedValues.length === 1 || bestMatch.weight < bestMatch.value.length ? bestMatch.value : null;
291
+ }
292
+ };
293
+ //#endregion
294
+ //#region src/validation-errors/default.js
295
+ var DefaultValidationError = class extends BaseValidationError {
296
+ print() {
297
+ const { keyword, message } = this.options;
298
+ return [(0, node_util.styleText)("red", (0, node_util.styleText)("bold", keyword.toUpperCase()) + " " + message) + "\n"].concat(this.getCodeFrame("👈🏽 " + (0, node_util.styleText)("magentaBright", keyword) + " " + message));
299
+ }
300
+ getError() {
301
+ const { keyword, message } = this.options;
302
+ return {
303
+ ...this.getLocation(),
304
+ error: this.withDecoratedPath(`${keyword} ${message}`, ": "),
305
+ path: this.instancePath
306
+ };
307
+ }
308
+ };
309
+ //#endregion
310
+ //#region src/helpers.js
311
+ const JSON_POINTERS_REGEX = /\/[\w_-]+(\/\d+)?/g;
312
+ function makeTree(ajvErrors = []) {
313
+ const root = { children: {} };
314
+ ajvErrors.forEach((ajvError) => {
315
+ const instancePath = typeof ajvError.instancePath !== "undefined" ? ajvError.instancePath : ajvError.dataPath;
316
+ const paths = instancePath === "" ? [""] : instancePath.match(JSON_POINTERS_REGEX);
317
+ if (paths) paths.reduce((obj, path, i) => {
318
+ obj.children[path] = obj.children[path] || {
319
+ children: {},
320
+ errors: []
321
+ };
322
+ if (i === paths.length - 1) obj.children[path].errors.push(ajvError);
323
+ return obj.children[path];
324
+ }, root);
325
+ });
326
+ return root;
327
+ }
328
+ function filterRedundantErrors(root, parent, key) {
329
+ /**
330
+ * Partition out the errors by kind for ease of proceessing
331
+ */
332
+ const { anyOfErrors, enumErrors, requiredErrors } = getErrors(root).reduce((acc, error) => {
333
+ if (isRequiredError(error)) {
334
+ acc.requiredErrors.push(error);
335
+ return acc;
336
+ }
337
+ if (isAnyOfError(error)) acc.anyOfErrors.push(error);
338
+ if (isEnumError(error)) acc.enumErrors.push(error);
339
+ return acc;
340
+ }, {
341
+ anyOfErrors: [],
342
+ enumErrors: [],
343
+ requiredErrors: []
344
+ });
345
+ /**
346
+ * If there is are `required` errors then we can just drop every non-required error.
347
+ * And, also `required` should have more priority than `anyOf`. @see #8
348
+ */
349
+ if (requiredErrors.length > 0) {
350
+ root.errors = requiredErrors;
351
+ root.children = {};
352
+ return;
353
+ }
354
+ /**
355
+ * If there is an `anyOf` error that means we have more meaningful errors
356
+ * inside children. So we will just remove all errors from this level.
357
+ *
358
+ * If there are no children, then we don't delete the errors since we should
359
+ * have at least one error to report.
360
+ */
361
+ if (anyOfErrors.length > 0 && Object.keys(root.children).length > 0) delete root.errors;
362
+ /**
363
+ * If all errors are `enum` and siblings have any error then we can safely
364
+ * ignore the node. As we return early if there's required errors, we only
365
+ * need to check anyofErrors length
366
+ *
367
+ * **CAUTION**
368
+ * Need explicit `root.errors` check because `[].every(fn) === true`
369
+ * https://en.wikipedia.org/wiki/Vacuous_truth#Vacuous_truths_in_mathematics
370
+ */
371
+ if (enumErrors.length > 0 && anyOfErrors.length === 0) {
372
+ if (getSiblings(parent)(root).filter(notUndefined).some(getErrors)) delete parent.children[key];
373
+ }
374
+ Object.entries(root.children).forEach(([key, child]) => filterRedundantErrors(child, root, key));
375
+ }
376
+ function createErrorInstances(root, options) {
377
+ const errors = getErrors(root);
378
+ if (errors.length && errors.every(isEnumError)) {
379
+ const allowedValues = [...new Set(concatAll([])(errors.map((e) => e.params.allowedValues)))];
380
+ const error = errors[0];
381
+ return [new EnumValidationError({
382
+ ...error,
383
+ params: { allowedValues }
384
+ }, options)];
385
+ } else return concatAll(errors.reduce((ret, error) => {
386
+ switch (error.keyword) {
387
+ case "additionalProperties": return ret.concat(new AdditionalPropValidationError(error, options));
388
+ case "required": return ret.concat(new RequiredValidationError(error, options));
389
+ default: return ret.concat(new DefaultValidationError(error, options));
390
+ }
391
+ }, []))(getChildren(root).map((child) => createErrorInstances(child, options)));
392
+ }
393
+ var helpers_default = (ajvErrors, options) => {
394
+ const tree = makeTree(ajvErrors || []);
395
+ filterRedundantErrors(tree);
396
+ return createErrorInstances(tree, options);
397
+ };
398
+ //#endregion
399
+ //#region src/index.js
400
+ var src_default = (schema, data, errors, options = {}) => {
401
+ const { format = "cli", indent = null, json = null } = options;
402
+ const jsonRaw = json || JSON.stringify(data, null, indent);
403
+ const jsonAst = (0, _humanwhocodes_momoa.parse)(jsonRaw);
404
+ const customErrorToText = (error) => error.print().join("\n");
405
+ const customErrorToStructure = (error) => error.getError();
406
+ const customErrors = helpers_default(errors, {
407
+ data,
408
+ schema,
409
+ jsonAst,
410
+ jsonRaw
411
+ });
412
+ if (format === "cli") return customErrors.map(customErrorToText).join("\n\n");
413
+ else return customErrors.map(customErrorToStructure);
414
+ };
415
+ //#endregion
416
+ exports.default = src_default;
417
+
418
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["styleText","tokenize","styleText","styleText","styleText","pointer","styleText","parse","prettify"],"sources":["../src/utils.js","../src/code-frame.js","../src/json/utils.js","../src/json/get-meta-from-path.js","../src/json/get-decorated-data-path.js","../src/validation-errors/base.js","../src/validation-errors/required.js","../src/validation-errors/additional-prop.js","../src/validation-errors/enum.js","../src/validation-errors/default.js","../src/helpers.js","../src/index.js"],"sourcesContent":["// Basic\nconst eq = x => y => x === y;\nconst not = fn => x => !fn(x);\n\nconst getValues = o => Object.values(o);\n\nexport const notUndefined = x => x !== undefined;\n\n// Error\nconst isXError = x => error => error.keyword === x;\nexport const isRequiredError = isXError('required');\nexport const isAnyOfError = isXError('anyOf');\nexport const isEnumError = isXError('enum');\nexport const getErrors = node =>\n node && node.errors\n ? node.errors.map(e =>\n e.keyword === 'errorMessage' ? { ...e.params.errors[0], message: e.message } : e\n )\n : [];\n\n// Node\nexport const getChildren = node => (node && getValues(node.children)) || [];\n\nexport const getSiblings = parent => node => getChildren(parent).filter(not(eq(node)));\n\nexport const concatAll = xs => ys => ys.reduce((zs, z) => zs.concat(z), xs);\n","import { styleText } from 'node:util';\nimport { tokenize } from '@humanwhocodes/momoa';\n\nconst LINES_ABOVE = 2;\nconst LINES_BELOW = 3;\n\n// The line breaks momoa counts, so that its line numbers index into the split source.\nconst NEWLINE = /\\r\\n|[\\r\\n]/;\n\nconst TOKEN_STYLES = {\n String: 'green',\n Number: 'magenta',\n Boolean: 'cyan',\n Null: 'cyan',\n Colon: 'yellow',\n Comma: 'yellow',\n};\n\nconst MARKER_STYLE = ['red', 'bold'];\n\n// The check `styleText` makes for the headings, so that the frame is colored along with them.\nconst supportsColor = () => styleText('red', '-') !== '-';\n\n/**\n * Renders the lines of `source` around `location`, with carets under the located text and\n * `message` after the last caret.\n *\n * Lines and columns are 1-based and `end` is exclusive, as in momoa's locations. Without\n * `end`, a single caret marks `start`.\n *\n * @param {string} source\n * @param {{ start: { line: number, column: number }, end?: { line: number, column: number } }} location\n * @param {{ message?: string, color?: boolean }} [options]\n * @return {string}\n */\nexport default function codeFrame(\n source,\n { start, end = start },\n { message, color = supportsColor() } = {}\n) {\n const paint = color\n ? (format, text) => styleText(format, text, { validateStream: false })\n : (_format, text) => text;\n\n const lines = source.split(NEWLINE);\n const markers = getMarkers(lines, start, end);\n const first = Math.max(start.line - LINES_ABOVE, 1);\n const last = Math.min(end.line + LINES_BELOW, lines.length);\n const width = String(last).length;\n\n const rows = [];\n for (let number = first; number <= last; number++) {\n const line = lines[number - 1];\n const gutter = ` ${String(number).padStart(width)} |`;\n const code = line.length > 0 ? ` ${color ? highlight(line, paint) : line}` : '';\n const marker = markers.get(number);\n if (!marker) {\n rows.push(` ${paint('gray', gutter)}${code}`);\n continue;\n }\n\n const [column, count] = marker;\n const spacing = line.slice(0, column - 1).replace(/[^\\t]/g, ' ');\n let caretRow = ` ${paint('gray', gutter.replace(/\\d/g, ' '))} ${spacing}${paint(MARKER_STYLE, '^'.repeat(count))}`;\n if (message && number === end.line) {\n caretRow += ` ${paint(MARKER_STYLE, message)}`;\n }\n rows.push(`${paint(MARKER_STYLE, '>')}${paint('gray', gutter)}${code}`, caretRow);\n }\n\n return rows.join('\\n');\n}\n\n// Maps each marked line number to the column of its first caret and the number of carets.\nfunction getMarkers(lines, start, end) {\n const markers = new Map();\n if (start.line === end.line) {\n markers.set(start.line, [start.column, Math.max(end.column - start.column, 1)]);\n return markers;\n }\n\n markers.set(start.line, [start.column, lines[start.line - 1].length - start.column + 1]);\n for (let number = start.line + 1; number < end.line; number++) {\n markers.set(number, [1, Math.max(lines[number - 1].length, 1)]);\n }\n markers.set(end.line, [1, end.column - 1]);\n return markers;\n}\n\n// JSON strings can't contain raw line breaks, so each line holds whole tokens.\nfunction highlight(line, paint) {\n let output = '';\n let offset = 0;\n for (const { type, loc } of tokenize(line)) {\n const style = TOKEN_STYLES[type];\n if (style) {\n output +=\n line.slice(offset, loc.start.offset) +\n paint(style, line.slice(loc.start.offset, loc.end.offset));\n offset = loc.end.offset;\n }\n }\n return output + line.slice(offset);\n}\n","// TODO: Better error handling\nexport const getPointers = dataPath => {\n return dataPath\n .split('/')\n .slice(1)\n .map(pointer => pointer.split('~1').join('/').split('~0').join('~'));\n};\n","import { getPointers } from './utils';\n\nexport default function getMetaFromPath(jsonAst, dataPath, includeIdentifierLocation) {\n const pointers = getPointers(dataPath);\n const lastPointerIndex = pointers.length - 1;\n return pointers.reduce((obj, pointer, idx) => {\n switch (obj.type) {\n case 'Object': {\n // JSON.parse keeps the last of duplicate keys, so that is the one ajv validated.\n const member = obj.members.findLast(child => child.name.value === pointer);\n if (!member) {\n throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);\n }\n\n const { name, value } = member;\n return includeIdentifierLocation && idx === lastPointerIndex ? name : value;\n }\n case 'Array': {\n const element = obj.elements[pointer];\n return element && element.value;\n }\n default:\n console.log(obj);\n }\n }, jsonAst.body);\n}\n","import { getPointers } from './utils';\n\nexport default function getDecoratedDataPath(jsonAst, dataPath) {\n let decoratedPath = '';\n getPointers(dataPath).reduce((obj, pointer) => {\n switch (obj.type) {\n case 'Object': {\n decoratedPath += `/${pointer}`;\n // JSON.parse keeps the last of duplicate keys, so that is the one ajv validated.\n const member = obj.members.findLast(child => child.name.value === pointer);\n if (!member) {\n throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);\n }\n return member.value;\n }\n case 'Array': {\n const element = obj.elements[pointer];\n const value = element && element.value;\n decoratedPath += `/${pointer}${getTypeName(value)}`;\n return value;\n }\n default:\n console.log(obj);\n }\n }, jsonAst.body);\n return decoratedPath;\n}\n\nfunction getTypeName(obj) {\n if (!obj || !obj.elements) {\n return '';\n }\n const type = obj.elements.filter(child => child && child.name && child.name.value === 'type');\n\n if (!type.length) {\n return '';\n }\n\n return (type[0].value && `:${type[0].value.value}`) || '';\n}\n","import codeFrame from '../code-frame';\nimport { getMetaFromPath, getDecoratedDataPath } from '../json/index';\n\nexport default class BaseValidationError {\n constructor(options = { isIdentifierLocation: false }, { data, schema, jsonAst, jsonRaw }) {\n this.options = options;\n this.data = data;\n this.schema = schema;\n this.jsonAst = jsonAst;\n this.jsonRaw = jsonRaw;\n }\n\n getLocation(dataPath = this.instancePath) {\n const { isIdentifierLocation, isSkipEndLocation } = this.options;\n const { loc } = getMetaFromPath(this.jsonAst, dataPath, isIdentifierLocation);\n return {\n start: loc.start,\n end: isSkipEndLocation ? undefined : loc.end,\n };\n }\n\n getDecoratedPath(dataPath = this.instancePath) {\n const decoratedPath = getDecoratedDataPath(this.jsonAst, dataPath);\n return decoratedPath;\n }\n\n // The decorated path is empty at the root, where the message stands alone.\n withDecoratedPath(message, separator = ' ') {\n const decoratedPath = this.getDecoratedPath();\n return decoratedPath ? `${decoratedPath}${separator}${message}` : message;\n }\n\n getCodeFrame(message, dataPath = this.instancePath) {\n return codeFrame(this.jsonRaw, this.getLocation(dataPath), { message });\n }\n\n /**\n * @return {string}\n */\n get instancePath() {\n return typeof this.options.instancePath !== 'undefined'\n ? this.options.instancePath\n : this.options.dataPath;\n }\n\n print() {\n throw new Error(`Implement the 'print' method inside ${this.constructor.name}!`);\n }\n\n getError() {\n throw new Error(`Implement the 'getError' method inside ${this.constructor.name}!`);\n }\n}\n","import { styleText } from 'node:util';\nimport BaseValidationError from './base';\n\nexport default class RequiredValidationError extends BaseValidationError {\n getLocation(dataPath = this.instancePath) {\n const { start } = super.getLocation(dataPath);\n return { start };\n }\n\n print() {\n const { message, params } = this.options;\n const output = [styleText('red', styleText('bold', 'REQUIRED') + ' ' + message) + '\\n'];\n\n return output.concat(\n this.getCodeFrame(\n '☹️ ' + styleText('magentaBright', params.missingProperty) + ' is missing here!'\n )\n );\n }\n\n getError() {\n const { message } = this.options;\n\n return {\n ...this.getLocation(),\n error: this.withDecoratedPath(message),\n path: this.instancePath,\n };\n }\n}\n","import { styleText } from 'node:util';\nimport BaseValidationError from './base';\n\nexport default class AdditionalPropValidationError extends BaseValidationError {\n constructor(...args) {\n super(...args);\n this.options.isIdentifierLocation = true;\n }\n\n print() {\n const { message, params } = this.options;\n const output = [\n styleText('red', styleText('bold', 'ADDITIONAL PROPERTY') + ' ' + message) + '\\n',\n ];\n\n return output.concat(\n this.getCodeFrame(\n '😲 ' +\n styleText('magentaBright', params.additionalProperty) +\n ' is not expected to be here!',\n `${this.instancePath}/${params.additionalProperty}`\n )\n );\n }\n\n getError() {\n const { params } = this.options;\n\n return {\n ...this.getLocation(`${this.instancePath}/${params.additionalProperty}`),\n error: this.withDecoratedPath(\n `Property ${params.additionalProperty} is not expected to be here`\n ),\n path: this.instancePath,\n };\n }\n}\n","import { styleText } from 'node:util';\nimport leven from 'leven';\nimport pointer from 'jsonpointer';\nimport BaseValidationError from './base';\n\nexport default class EnumValidationError extends BaseValidationError {\n print() {\n const {\n message,\n params: { allowedValues },\n } = this.options;\n const bestMatch = this.findBestMatch();\n\n // Needed to handle nullable enums, as joining on null just prints \", \"\n const [firstValue, ...rest] = allowedValues;\n const allowedValuesMessage = rest.reduce((acc, value) => `${acc}, ${value}`, firstValue || '');\n\n const output = [\n styleText('red', styleText('bold', 'ENUM') + ' ' + message),\n styleText('red', `(${allowedValuesMessage})\\n`),\n ];\n\n return output.concat(\n this.getCodeFrame(\n bestMatch !== null\n ? '👈🏽 Did you mean ' + styleText('magentaBright', String(bestMatch)) + ' here?'\n : '👈🏽 Unexpected value, should be equal to one of the allowed values'\n )\n );\n }\n\n getError() {\n const { message, params } = this.options;\n const bestMatch = this.findBestMatch();\n const allowedValues = params.allowedValues.join(', ');\n\n const output = {\n ...this.getLocation(),\n error: this.withDecoratedPath(`${message}: ${allowedValues}`),\n path: this.instancePath,\n };\n\n if (bestMatch !== null) {\n output.suggestion = `Did you mean ${bestMatch}?`;\n }\n\n return output;\n }\n\n findBestMatch() {\n const {\n params: { allowedValues },\n } = this.options;\n\n const currentValue =\n this.instancePath === '' ? this.data : pointer.get(this.data, this.instancePath);\n\n if (!currentValue) {\n return null;\n }\n\n const bestMatch = allowedValues\n .map(value => ({\n value,\n // leven compares strings, so other allowed values cannot be a close match.\n weight: typeof value === 'string' ? leven(value, currentValue.toString()) : Infinity,\n }))\n .sort((x, y) => (x.weight > y.weight ? 1 : x.weight < y.weight ? -1 : 0))[0];\n\n return allowedValues.length === 1 || bestMatch.weight < bestMatch.value.length\n ? bestMatch.value\n : null;\n }\n}\n","import { styleText } from 'node:util';\nimport BaseValidationError from './base';\n\nexport default class DefaultValidationError extends BaseValidationError {\n print() {\n const { keyword, message } = this.options;\n const output = [\n styleText('red', styleText('bold', keyword.toUpperCase()) + ' ' + message) + '\\n',\n ];\n\n return output.concat(\n this.getCodeFrame('👈🏽 ' + styleText('magentaBright', keyword) + ' ' + message)\n );\n }\n\n getError() {\n const { keyword, message } = this.options;\n\n return {\n ...this.getLocation(),\n error: this.withDecoratedPath(`${keyword} ${message}`, ': '),\n path: this.instancePath,\n };\n }\n}\n","import {\n getChildren,\n getErrors,\n getSiblings,\n isAnyOfError,\n isEnumError,\n isRequiredError,\n concatAll,\n notUndefined,\n} from './utils';\nimport {\n AdditionalPropValidationError,\n RequiredValidationError,\n EnumValidationError,\n DefaultValidationError,\n} from './validation-errors/index';\n\nconst JSON_POINTERS_REGEX = /\\/[\\w_-]+(\\/\\d+)?/g;\n\n// Make a tree of errors from ajv errors array\nexport function makeTree(ajvErrors = []) {\n const root = { children: {} };\n ajvErrors.forEach(ajvError => {\n const instancePath =\n typeof ajvError.instancePath !== 'undefined' ? ajvError.instancePath : ajvError.dataPath;\n\n // `dataPath === ''` is root\n const paths = instancePath === '' ? [''] : instancePath.match(JSON_POINTERS_REGEX);\n if (paths) {\n paths.reduce((obj, path, i) => {\n obj.children[path] = obj.children[path] || { children: {}, errors: [] };\n if (i === paths.length - 1) {\n obj.children[path].errors.push(ajvError);\n }\n return obj.children[path];\n }, root);\n }\n });\n return root;\n}\n\nexport function filterRedundantErrors(root, parent, key) {\n /**\n * Partition out the errors by kind for ease of proceessing\n */\n const { anyOfErrors, enumErrors, requiredErrors } = getErrors(root).reduce(\n (acc, error) => {\n if (isRequiredError(error)) {\n acc.requiredErrors.push(error);\n\n return acc;\n }\n\n if (isAnyOfError(error)) {\n acc.anyOfErrors.push(error);\n }\n\n if (isEnumError(error)) {\n acc.enumErrors.push(error);\n }\n\n return acc;\n },\n { anyOfErrors: [], enumErrors: [], requiredErrors: [] }\n );\n\n /**\n * If there is are `required` errors then we can just drop every non-required error.\n * And, also `required` should have more priority than `anyOf`. @see #8\n */\n\n if (requiredErrors.length > 0) {\n root.errors = requiredErrors;\n root.children = {};\n\n // No need for further processing, as we've emptied out children\n return;\n }\n\n /**\n * If there is an `anyOf` error that means we have more meaningful errors\n * inside children. So we will just remove all errors from this level.\n *\n * If there are no children, then we don't delete the errors since we should\n * have at least one error to report.\n */\n if (anyOfErrors.length > 0 && Object.keys(root.children).length > 0) {\n delete root.errors;\n }\n\n /**\n * If all errors are `enum` and siblings have any error then we can safely\n * ignore the node. As we return early if there's required errors, we only\n * need to check anyofErrors length\n *\n * **CAUTION**\n * Need explicit `root.errors` check because `[].every(fn) === true`\n * https://en.wikipedia.org/wiki/Vacuous_truth#Vacuous_truths_in_mathematics\n */\n if (enumErrors.length > 0 && anyOfErrors.length === 0) {\n if (\n getSiblings(parent)(root)\n // Remove any reference which becomes `undefined` later\n .filter(notUndefined)\n .some(getErrors)\n ) {\n delete parent.children[key];\n }\n }\n\n Object.entries(root.children).forEach(([key, child]) => filterRedundantErrors(child, root, key));\n}\n\nexport function createErrorInstances(root, options) {\n const errors = getErrors(root);\n if (errors.length && errors.every(isEnumError)) {\n const uniqueValues = new Set(concatAll([])(errors.map(e => e.params.allowedValues)));\n const allowedValues = [...uniqueValues];\n const error = errors[0];\n return [\n new EnumValidationError(\n {\n ...error,\n params: { allowedValues },\n },\n options\n ),\n ];\n } else {\n return concatAll(\n errors.reduce((ret, error) => {\n switch (error.keyword) {\n case 'additionalProperties':\n return ret.concat(new AdditionalPropValidationError(error, options));\n case 'required':\n return ret.concat(new RequiredValidationError(error, options));\n default:\n return ret.concat(new DefaultValidationError(error, options));\n }\n }, [])\n )(getChildren(root).map(child => createErrorInstances(child, options)));\n }\n}\n\nexport default (ajvErrors, options) => {\n const tree = makeTree(ajvErrors || []);\n filterRedundantErrors(tree);\n return createErrorInstances(tree, options);\n};\n","import { parse } from '@humanwhocodes/momoa';\nimport prettify from './helpers';\n\nexport default (schema, data, errors, options = {}) => {\n const { format = 'cli', indent = null, json = null } = options;\n\n const jsonRaw = json || JSON.stringify(data, null, indent);\n const jsonAst = parse(jsonRaw);\n\n const customErrorToText = error => error.print().join('\\n');\n const customErrorToStructure = error => error.getError();\n const customErrors = prettify(errors, {\n data,\n schema,\n jsonAst,\n jsonRaw,\n });\n\n if (format === 'cli') {\n return customErrors.map(customErrorToText).join('\\n\\n');\n } else {\n return customErrors.map(customErrorToStructure);\n }\n};\n"],"mappings":"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"}
package/dist/index.mjs ADDED
@@ -0,0 +1,390 @@
1
+ import { parse, tokenize } from "@humanwhocodes/momoa";
2
+ import { styleText } from "node:util";
3
+ import leven from "leven";
4
+ import pointer from "jsonpointer";
5
+ //#region src/utils.js
6
+ const eq = (x) => (y) => x === y;
7
+ const not = (fn) => (x) => !fn(x);
8
+ const getValues = (o) => Object.values(o);
9
+ const notUndefined = (x) => x !== void 0;
10
+ const isXError = (x) => (error) => error.keyword === x;
11
+ const isRequiredError = isXError("required");
12
+ const isAnyOfError = isXError("anyOf");
13
+ const isEnumError = isXError("enum");
14
+ const getErrors = (node) => node && node.errors ? node.errors.map((e) => e.keyword === "errorMessage" ? {
15
+ ...e.params.errors[0],
16
+ message: e.message
17
+ } : e) : [];
18
+ const getChildren = (node) => node && getValues(node.children) || [];
19
+ const getSiblings = (parent) => (node) => getChildren(parent).filter(not(eq(node)));
20
+ const concatAll = (xs) => (ys) => ys.reduce((zs, z) => zs.concat(z), xs);
21
+ //#endregion
22
+ //#region src/code-frame.js
23
+ const LINES_ABOVE = 2;
24
+ const LINES_BELOW = 3;
25
+ const NEWLINE = /\r\n|[\r\n]/;
26
+ const TOKEN_STYLES = {
27
+ String: "green",
28
+ Number: "magenta",
29
+ Boolean: "cyan",
30
+ Null: "cyan",
31
+ Colon: "yellow",
32
+ Comma: "yellow"
33
+ };
34
+ const MARKER_STYLE = ["red", "bold"];
35
+ const supportsColor = () => styleText("red", "-") !== "-";
36
+ /**
37
+ * Renders the lines of `source` around `location`, with carets under the located text and
38
+ * `message` after the last caret.
39
+ *
40
+ * Lines and columns are 1-based and `end` is exclusive, as in momoa's locations. Without
41
+ * `end`, a single caret marks `start`.
42
+ *
43
+ * @param {string} source
44
+ * @param {{ start: { line: number, column: number }, end?: { line: number, column: number } }} location
45
+ * @param {{ message?: string, color?: boolean }} [options]
46
+ * @return {string}
47
+ */
48
+ function codeFrame(source, { start, end = start }, { message, color = supportsColor() } = {}) {
49
+ const paint = color ? (format, text) => styleText(format, text, { validateStream: false }) : (_format, text) => text;
50
+ const lines = source.split(NEWLINE);
51
+ const markers = getMarkers(lines, start, end);
52
+ const first = Math.max(start.line - LINES_ABOVE, 1);
53
+ const last = Math.min(end.line + LINES_BELOW, lines.length);
54
+ const width = String(last).length;
55
+ const rows = [];
56
+ for (let number = first; number <= last; number++) {
57
+ const line = lines[number - 1];
58
+ const gutter = ` ${String(number).padStart(width)} |`;
59
+ const code = line.length > 0 ? ` ${color ? highlight(line, paint) : line}` : "";
60
+ const marker = markers.get(number);
61
+ if (!marker) {
62
+ rows.push(` ${paint("gray", gutter)}${code}`);
63
+ continue;
64
+ }
65
+ const [column, count] = marker;
66
+ const spacing = line.slice(0, column - 1).replace(/[^\t]/g, " ");
67
+ let caretRow = ` ${paint("gray", gutter.replace(/\d/g, " "))} ${spacing}${paint(MARKER_STYLE, "^".repeat(count))}`;
68
+ if (message && number === end.line) caretRow += ` ${paint(MARKER_STYLE, message)}`;
69
+ rows.push(`${paint(MARKER_STYLE, ">")}${paint("gray", gutter)}${code}`, caretRow);
70
+ }
71
+ return rows.join("\n");
72
+ }
73
+ function getMarkers(lines, start, end) {
74
+ const markers = /* @__PURE__ */ new Map();
75
+ if (start.line === end.line) {
76
+ markers.set(start.line, [start.column, Math.max(end.column - start.column, 1)]);
77
+ return markers;
78
+ }
79
+ markers.set(start.line, [start.column, lines[start.line - 1].length - start.column + 1]);
80
+ for (let number = start.line + 1; number < end.line; number++) markers.set(number, [1, Math.max(lines[number - 1].length, 1)]);
81
+ markers.set(end.line, [1, end.column - 1]);
82
+ return markers;
83
+ }
84
+ function highlight(line, paint) {
85
+ let output = "";
86
+ let offset = 0;
87
+ for (const { type, loc } of tokenize(line)) {
88
+ const style = TOKEN_STYLES[type];
89
+ if (style) {
90
+ output += line.slice(offset, loc.start.offset) + paint(style, line.slice(loc.start.offset, loc.end.offset));
91
+ offset = loc.end.offset;
92
+ }
93
+ }
94
+ return output + line.slice(offset);
95
+ }
96
+ //#endregion
97
+ //#region src/json/utils.js
98
+ const getPointers = (dataPath) => {
99
+ return dataPath.split("/").slice(1).map((pointer) => pointer.split("~1").join("/").split("~0").join("~"));
100
+ };
101
+ //#endregion
102
+ //#region src/json/get-meta-from-path.js
103
+ function getMetaFromPath(jsonAst, dataPath, includeIdentifierLocation) {
104
+ const pointers = getPointers(dataPath);
105
+ const lastPointerIndex = pointers.length - 1;
106
+ return pointers.reduce((obj, pointer, idx) => {
107
+ switch (obj.type) {
108
+ case "Object": {
109
+ const member = obj.members.findLast((child) => child.name.value === pointer);
110
+ if (!member) throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);
111
+ const { name, value } = member;
112
+ return includeIdentifierLocation && idx === lastPointerIndex ? name : value;
113
+ }
114
+ case "Array": {
115
+ const element = obj.elements[pointer];
116
+ return element && element.value;
117
+ }
118
+ default: console.log(obj);
119
+ }
120
+ }, jsonAst.body);
121
+ }
122
+ //#endregion
123
+ //#region src/json/get-decorated-data-path.js
124
+ function getDecoratedDataPath(jsonAst, dataPath) {
125
+ let decoratedPath = "";
126
+ getPointers(dataPath).reduce((obj, pointer) => {
127
+ switch (obj.type) {
128
+ case "Object": {
129
+ decoratedPath += `/${pointer}`;
130
+ const member = obj.members.findLast((child) => child.name.value === pointer);
131
+ if (!member) throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);
132
+ return member.value;
133
+ }
134
+ case "Array": {
135
+ const element = obj.elements[pointer];
136
+ const value = element && element.value;
137
+ decoratedPath += `/${pointer}${getTypeName(value)}`;
138
+ return value;
139
+ }
140
+ default: console.log(obj);
141
+ }
142
+ }, jsonAst.body);
143
+ return decoratedPath;
144
+ }
145
+ function getTypeName(obj) {
146
+ if (!obj || !obj.elements) return "";
147
+ const type = obj.elements.filter((child) => child && child.name && child.name.value === "type");
148
+ if (!type.length) return "";
149
+ return type[0].value && `:${type[0].value.value}` || "";
150
+ }
151
+ //#endregion
152
+ //#region src/validation-errors/base.js
153
+ var BaseValidationError = class {
154
+ constructor(options = { isIdentifierLocation: false }, { data, schema, jsonAst, jsonRaw }) {
155
+ this.options = options;
156
+ this.data = data;
157
+ this.schema = schema;
158
+ this.jsonAst = jsonAst;
159
+ this.jsonRaw = jsonRaw;
160
+ }
161
+ getLocation(dataPath = this.instancePath) {
162
+ const { isIdentifierLocation, isSkipEndLocation } = this.options;
163
+ const { loc } = getMetaFromPath(this.jsonAst, dataPath, isIdentifierLocation);
164
+ return {
165
+ start: loc.start,
166
+ end: isSkipEndLocation ? void 0 : loc.end
167
+ };
168
+ }
169
+ getDecoratedPath(dataPath = this.instancePath) {
170
+ return getDecoratedDataPath(this.jsonAst, dataPath);
171
+ }
172
+ withDecoratedPath(message, separator = " ") {
173
+ const decoratedPath = this.getDecoratedPath();
174
+ return decoratedPath ? `${decoratedPath}${separator}${message}` : message;
175
+ }
176
+ getCodeFrame(message, dataPath = this.instancePath) {
177
+ return codeFrame(this.jsonRaw, this.getLocation(dataPath), { message });
178
+ }
179
+ /**
180
+ * @return {string}
181
+ */
182
+ get instancePath() {
183
+ return typeof this.options.instancePath !== "undefined" ? this.options.instancePath : this.options.dataPath;
184
+ }
185
+ print() {
186
+ throw new Error(`Implement the 'print' method inside ${this.constructor.name}!`);
187
+ }
188
+ getError() {
189
+ throw new Error(`Implement the 'getError' method inside ${this.constructor.name}!`);
190
+ }
191
+ };
192
+ //#endregion
193
+ //#region src/validation-errors/required.js
194
+ var RequiredValidationError = class extends BaseValidationError {
195
+ getLocation(dataPath = this.instancePath) {
196
+ const { start } = super.getLocation(dataPath);
197
+ return { start };
198
+ }
199
+ print() {
200
+ const { message, params } = this.options;
201
+ return [styleText("red", styleText("bold", "REQUIRED") + " " + message) + "\n"].concat(this.getCodeFrame("☹️ " + styleText("magentaBright", params.missingProperty) + " is missing here!"));
202
+ }
203
+ getError() {
204
+ const { message } = this.options;
205
+ return {
206
+ ...this.getLocation(),
207
+ error: this.withDecoratedPath(message),
208
+ path: this.instancePath
209
+ };
210
+ }
211
+ };
212
+ //#endregion
213
+ //#region src/validation-errors/additional-prop.js
214
+ var AdditionalPropValidationError = class extends BaseValidationError {
215
+ constructor(...args) {
216
+ super(...args);
217
+ this.options.isIdentifierLocation = true;
218
+ }
219
+ print() {
220
+ const { message, params } = this.options;
221
+ return [styleText("red", styleText("bold", "ADDITIONAL PROPERTY") + " " + message) + "\n"].concat(this.getCodeFrame("😲 " + styleText("magentaBright", params.additionalProperty) + " is not expected to be here!", `${this.instancePath}/${params.additionalProperty}`));
222
+ }
223
+ getError() {
224
+ const { params } = this.options;
225
+ return {
226
+ ...this.getLocation(`${this.instancePath}/${params.additionalProperty}`),
227
+ error: this.withDecoratedPath(`Property ${params.additionalProperty} is not expected to be here`),
228
+ path: this.instancePath
229
+ };
230
+ }
231
+ };
232
+ //#endregion
233
+ //#region src/validation-errors/enum.js
234
+ var EnumValidationError = class extends BaseValidationError {
235
+ print() {
236
+ const { message, params: { allowedValues } } = this.options;
237
+ const bestMatch = this.findBestMatch();
238
+ const [firstValue, ...rest] = allowedValues;
239
+ const allowedValuesMessage = rest.reduce((acc, value) => `${acc}, ${value}`, firstValue || "");
240
+ return [styleText("red", styleText("bold", "ENUM") + " " + message), styleText("red", `(${allowedValuesMessage})\n`)].concat(this.getCodeFrame(bestMatch !== null ? "👈🏽 Did you mean " + styleText("magentaBright", String(bestMatch)) + " here?" : "👈🏽 Unexpected value, should be equal to one of the allowed values"));
241
+ }
242
+ getError() {
243
+ const { message, params } = this.options;
244
+ const bestMatch = this.findBestMatch();
245
+ const allowedValues = params.allowedValues.join(", ");
246
+ const output = {
247
+ ...this.getLocation(),
248
+ error: this.withDecoratedPath(`${message}: ${allowedValues}`),
249
+ path: this.instancePath
250
+ };
251
+ if (bestMatch !== null) output.suggestion = `Did you mean ${bestMatch}?`;
252
+ return output;
253
+ }
254
+ findBestMatch() {
255
+ const { params: { allowedValues } } = this.options;
256
+ const currentValue = this.instancePath === "" ? this.data : pointer.get(this.data, this.instancePath);
257
+ if (!currentValue) return null;
258
+ const bestMatch = allowedValues.map((value) => ({
259
+ value,
260
+ weight: typeof value === "string" ? leven(value, currentValue.toString()) : Infinity
261
+ })).sort((x, y) => x.weight > y.weight ? 1 : x.weight < y.weight ? -1 : 0)[0];
262
+ return allowedValues.length === 1 || bestMatch.weight < bestMatch.value.length ? bestMatch.value : null;
263
+ }
264
+ };
265
+ //#endregion
266
+ //#region src/validation-errors/default.js
267
+ var DefaultValidationError = class extends BaseValidationError {
268
+ print() {
269
+ const { keyword, message } = this.options;
270
+ return [styleText("red", styleText("bold", keyword.toUpperCase()) + " " + message) + "\n"].concat(this.getCodeFrame("👈🏽 " + styleText("magentaBright", keyword) + " " + message));
271
+ }
272
+ getError() {
273
+ const { keyword, message } = this.options;
274
+ return {
275
+ ...this.getLocation(),
276
+ error: this.withDecoratedPath(`${keyword} ${message}`, ": "),
277
+ path: this.instancePath
278
+ };
279
+ }
280
+ };
281
+ //#endregion
282
+ //#region src/helpers.js
283
+ const JSON_POINTERS_REGEX = /\/[\w_-]+(\/\d+)?/g;
284
+ function makeTree(ajvErrors = []) {
285
+ const root = { children: {} };
286
+ ajvErrors.forEach((ajvError) => {
287
+ const instancePath = typeof ajvError.instancePath !== "undefined" ? ajvError.instancePath : ajvError.dataPath;
288
+ const paths = instancePath === "" ? [""] : instancePath.match(JSON_POINTERS_REGEX);
289
+ if (paths) paths.reduce((obj, path, i) => {
290
+ obj.children[path] = obj.children[path] || {
291
+ children: {},
292
+ errors: []
293
+ };
294
+ if (i === paths.length - 1) obj.children[path].errors.push(ajvError);
295
+ return obj.children[path];
296
+ }, root);
297
+ });
298
+ return root;
299
+ }
300
+ function filterRedundantErrors(root, parent, key) {
301
+ /**
302
+ * Partition out the errors by kind for ease of proceessing
303
+ */
304
+ const { anyOfErrors, enumErrors, requiredErrors } = getErrors(root).reduce((acc, error) => {
305
+ if (isRequiredError(error)) {
306
+ acc.requiredErrors.push(error);
307
+ return acc;
308
+ }
309
+ if (isAnyOfError(error)) acc.anyOfErrors.push(error);
310
+ if (isEnumError(error)) acc.enumErrors.push(error);
311
+ return acc;
312
+ }, {
313
+ anyOfErrors: [],
314
+ enumErrors: [],
315
+ requiredErrors: []
316
+ });
317
+ /**
318
+ * If there is are `required` errors then we can just drop every non-required error.
319
+ * And, also `required` should have more priority than `anyOf`. @see #8
320
+ */
321
+ if (requiredErrors.length > 0) {
322
+ root.errors = requiredErrors;
323
+ root.children = {};
324
+ return;
325
+ }
326
+ /**
327
+ * If there is an `anyOf` error that means we have more meaningful errors
328
+ * inside children. So we will just remove all errors from this level.
329
+ *
330
+ * If there are no children, then we don't delete the errors since we should
331
+ * have at least one error to report.
332
+ */
333
+ if (anyOfErrors.length > 0 && Object.keys(root.children).length > 0) delete root.errors;
334
+ /**
335
+ * If all errors are `enum` and siblings have any error then we can safely
336
+ * ignore the node. As we return early if there's required errors, we only
337
+ * need to check anyofErrors length
338
+ *
339
+ * **CAUTION**
340
+ * Need explicit `root.errors` check because `[].every(fn) === true`
341
+ * https://en.wikipedia.org/wiki/Vacuous_truth#Vacuous_truths_in_mathematics
342
+ */
343
+ if (enumErrors.length > 0 && anyOfErrors.length === 0) {
344
+ if (getSiblings(parent)(root).filter(notUndefined).some(getErrors)) delete parent.children[key];
345
+ }
346
+ Object.entries(root.children).forEach(([key, child]) => filterRedundantErrors(child, root, key));
347
+ }
348
+ function createErrorInstances(root, options) {
349
+ const errors = getErrors(root);
350
+ if (errors.length && errors.every(isEnumError)) {
351
+ const allowedValues = [...new Set(concatAll([])(errors.map((e) => e.params.allowedValues)))];
352
+ const error = errors[0];
353
+ return [new EnumValidationError({
354
+ ...error,
355
+ params: { allowedValues }
356
+ }, options)];
357
+ } else return concatAll(errors.reduce((ret, error) => {
358
+ switch (error.keyword) {
359
+ case "additionalProperties": return ret.concat(new AdditionalPropValidationError(error, options));
360
+ case "required": return ret.concat(new RequiredValidationError(error, options));
361
+ default: return ret.concat(new DefaultValidationError(error, options));
362
+ }
363
+ }, []))(getChildren(root).map((child) => createErrorInstances(child, options)));
364
+ }
365
+ var helpers_default = (ajvErrors, options) => {
366
+ const tree = makeTree(ajvErrors || []);
367
+ filterRedundantErrors(tree);
368
+ return createErrorInstances(tree, options);
369
+ };
370
+ //#endregion
371
+ //#region src/index.js
372
+ var src_default = (schema, data, errors, options = {}) => {
373
+ const { format = "cli", indent = null, json = null } = options;
374
+ const jsonRaw = json || JSON.stringify(data, null, indent);
375
+ const jsonAst = parse(jsonRaw);
376
+ const customErrorToText = (error) => error.print().join("\n");
377
+ const customErrorToStructure = (error) => error.getError();
378
+ const customErrors = helpers_default(errors, {
379
+ data,
380
+ schema,
381
+ jsonAst,
382
+ jsonRaw
383
+ });
384
+ if (format === "cli") return customErrors.map(customErrorToText).join("\n\n");
385
+ else return customErrors.map(customErrorToStructure);
386
+ };
387
+ //#endregion
388
+ export { src_default as default };
389
+
390
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["prettify"],"sources":["../src/utils.js","../src/code-frame.js","../src/json/utils.js","../src/json/get-meta-from-path.js","../src/json/get-decorated-data-path.js","../src/validation-errors/base.js","../src/validation-errors/required.js","../src/validation-errors/additional-prop.js","../src/validation-errors/enum.js","../src/validation-errors/default.js","../src/helpers.js","../src/index.js"],"sourcesContent":["// Basic\nconst eq = x => y => x === y;\nconst not = fn => x => !fn(x);\n\nconst getValues = o => Object.values(o);\n\nexport const notUndefined = x => x !== undefined;\n\n// Error\nconst isXError = x => error => error.keyword === x;\nexport const isRequiredError = isXError('required');\nexport const isAnyOfError = isXError('anyOf');\nexport const isEnumError = isXError('enum');\nexport const getErrors = node =>\n node && node.errors\n ? node.errors.map(e =>\n e.keyword === 'errorMessage' ? { ...e.params.errors[0], message: e.message } : e\n )\n : [];\n\n// Node\nexport const getChildren = node => (node && getValues(node.children)) || [];\n\nexport const getSiblings = parent => node => getChildren(parent).filter(not(eq(node)));\n\nexport const concatAll = xs => ys => ys.reduce((zs, z) => zs.concat(z), xs);\n","import { styleText } from 'node:util';\nimport { tokenize } from '@humanwhocodes/momoa';\n\nconst LINES_ABOVE = 2;\nconst LINES_BELOW = 3;\n\n// The line breaks momoa counts, so that its line numbers index into the split source.\nconst NEWLINE = /\\r\\n|[\\r\\n]/;\n\nconst TOKEN_STYLES = {\n String: 'green',\n Number: 'magenta',\n Boolean: 'cyan',\n Null: 'cyan',\n Colon: 'yellow',\n Comma: 'yellow',\n};\n\nconst MARKER_STYLE = ['red', 'bold'];\n\n// The check `styleText` makes for the headings, so that the frame is colored along with them.\nconst supportsColor = () => styleText('red', '-') !== '-';\n\n/**\n * Renders the lines of `source` around `location`, with carets under the located text and\n * `message` after the last caret.\n *\n * Lines and columns are 1-based and `end` is exclusive, as in momoa's locations. Without\n * `end`, a single caret marks `start`.\n *\n * @param {string} source\n * @param {{ start: { line: number, column: number }, end?: { line: number, column: number } }} location\n * @param {{ message?: string, color?: boolean }} [options]\n * @return {string}\n */\nexport default function codeFrame(\n source,\n { start, end = start },\n { message, color = supportsColor() } = {}\n) {\n const paint = color\n ? (format, text) => styleText(format, text, { validateStream: false })\n : (_format, text) => text;\n\n const lines = source.split(NEWLINE);\n const markers = getMarkers(lines, start, end);\n const first = Math.max(start.line - LINES_ABOVE, 1);\n const last = Math.min(end.line + LINES_BELOW, lines.length);\n const width = String(last).length;\n\n const rows = [];\n for (let number = first; number <= last; number++) {\n const line = lines[number - 1];\n const gutter = ` ${String(number).padStart(width)} |`;\n const code = line.length > 0 ? ` ${color ? highlight(line, paint) : line}` : '';\n const marker = markers.get(number);\n if (!marker) {\n rows.push(` ${paint('gray', gutter)}${code}`);\n continue;\n }\n\n const [column, count] = marker;\n const spacing = line.slice(0, column - 1).replace(/[^\\t]/g, ' ');\n let caretRow = ` ${paint('gray', gutter.replace(/\\d/g, ' '))} ${spacing}${paint(MARKER_STYLE, '^'.repeat(count))}`;\n if (message && number === end.line) {\n caretRow += ` ${paint(MARKER_STYLE, message)}`;\n }\n rows.push(`${paint(MARKER_STYLE, '>')}${paint('gray', gutter)}${code}`, caretRow);\n }\n\n return rows.join('\\n');\n}\n\n// Maps each marked line number to the column of its first caret and the number of carets.\nfunction getMarkers(lines, start, end) {\n const markers = new Map();\n if (start.line === end.line) {\n markers.set(start.line, [start.column, Math.max(end.column - start.column, 1)]);\n return markers;\n }\n\n markers.set(start.line, [start.column, lines[start.line - 1].length - start.column + 1]);\n for (let number = start.line + 1; number < end.line; number++) {\n markers.set(number, [1, Math.max(lines[number - 1].length, 1)]);\n }\n markers.set(end.line, [1, end.column - 1]);\n return markers;\n}\n\n// JSON strings can't contain raw line breaks, so each line holds whole tokens.\nfunction highlight(line, paint) {\n let output = '';\n let offset = 0;\n for (const { type, loc } of tokenize(line)) {\n const style = TOKEN_STYLES[type];\n if (style) {\n output +=\n line.slice(offset, loc.start.offset) +\n paint(style, line.slice(loc.start.offset, loc.end.offset));\n offset = loc.end.offset;\n }\n }\n return output + line.slice(offset);\n}\n","// TODO: Better error handling\nexport const getPointers = dataPath => {\n return dataPath\n .split('/')\n .slice(1)\n .map(pointer => pointer.split('~1').join('/').split('~0').join('~'));\n};\n","import { getPointers } from './utils';\n\nexport default function getMetaFromPath(jsonAst, dataPath, includeIdentifierLocation) {\n const pointers = getPointers(dataPath);\n const lastPointerIndex = pointers.length - 1;\n return pointers.reduce((obj, pointer, idx) => {\n switch (obj.type) {\n case 'Object': {\n // JSON.parse keeps the last of duplicate keys, so that is the one ajv validated.\n const member = obj.members.findLast(child => child.name.value === pointer);\n if (!member) {\n throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);\n }\n\n const { name, value } = member;\n return includeIdentifierLocation && idx === lastPointerIndex ? name : value;\n }\n case 'Array': {\n const element = obj.elements[pointer];\n return element && element.value;\n }\n default:\n console.log(obj);\n }\n }, jsonAst.body);\n}\n","import { getPointers } from './utils';\n\nexport default function getDecoratedDataPath(jsonAst, dataPath) {\n let decoratedPath = '';\n getPointers(dataPath).reduce((obj, pointer) => {\n switch (obj.type) {\n case 'Object': {\n decoratedPath += `/${pointer}`;\n // JSON.parse keeps the last of duplicate keys, so that is the one ajv validated.\n const member = obj.members.findLast(child => child.name.value === pointer);\n if (!member) {\n throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);\n }\n return member.value;\n }\n case 'Array': {\n const element = obj.elements[pointer];\n const value = element && element.value;\n decoratedPath += `/${pointer}${getTypeName(value)}`;\n return value;\n }\n default:\n console.log(obj);\n }\n }, jsonAst.body);\n return decoratedPath;\n}\n\nfunction getTypeName(obj) {\n if (!obj || !obj.elements) {\n return '';\n }\n const type = obj.elements.filter(child => child && child.name && child.name.value === 'type');\n\n if (!type.length) {\n return '';\n }\n\n return (type[0].value && `:${type[0].value.value}`) || '';\n}\n","import codeFrame from '../code-frame';\nimport { getMetaFromPath, getDecoratedDataPath } from '../json/index';\n\nexport default class BaseValidationError {\n constructor(options = { isIdentifierLocation: false }, { data, schema, jsonAst, jsonRaw }) {\n this.options = options;\n this.data = data;\n this.schema = schema;\n this.jsonAst = jsonAst;\n this.jsonRaw = jsonRaw;\n }\n\n getLocation(dataPath = this.instancePath) {\n const { isIdentifierLocation, isSkipEndLocation } = this.options;\n const { loc } = getMetaFromPath(this.jsonAst, dataPath, isIdentifierLocation);\n return {\n start: loc.start,\n end: isSkipEndLocation ? undefined : loc.end,\n };\n }\n\n getDecoratedPath(dataPath = this.instancePath) {\n const decoratedPath = getDecoratedDataPath(this.jsonAst, dataPath);\n return decoratedPath;\n }\n\n // The decorated path is empty at the root, where the message stands alone.\n withDecoratedPath(message, separator = ' ') {\n const decoratedPath = this.getDecoratedPath();\n return decoratedPath ? `${decoratedPath}${separator}${message}` : message;\n }\n\n getCodeFrame(message, dataPath = this.instancePath) {\n return codeFrame(this.jsonRaw, this.getLocation(dataPath), { message });\n }\n\n /**\n * @return {string}\n */\n get instancePath() {\n return typeof this.options.instancePath !== 'undefined'\n ? this.options.instancePath\n : this.options.dataPath;\n }\n\n print() {\n throw new Error(`Implement the 'print' method inside ${this.constructor.name}!`);\n }\n\n getError() {\n throw new Error(`Implement the 'getError' method inside ${this.constructor.name}!`);\n }\n}\n","import { styleText } from 'node:util';\nimport BaseValidationError from './base';\n\nexport default class RequiredValidationError extends BaseValidationError {\n getLocation(dataPath = this.instancePath) {\n const { start } = super.getLocation(dataPath);\n return { start };\n }\n\n print() {\n const { message, params } = this.options;\n const output = [styleText('red', styleText('bold', 'REQUIRED') + ' ' + message) + '\\n'];\n\n return output.concat(\n this.getCodeFrame(\n '☹️ ' + styleText('magentaBright', params.missingProperty) + ' is missing here!'\n )\n );\n }\n\n getError() {\n const { message } = this.options;\n\n return {\n ...this.getLocation(),\n error: this.withDecoratedPath(message),\n path: this.instancePath,\n };\n }\n}\n","import { styleText } from 'node:util';\nimport BaseValidationError from './base';\n\nexport default class AdditionalPropValidationError extends BaseValidationError {\n constructor(...args) {\n super(...args);\n this.options.isIdentifierLocation = true;\n }\n\n print() {\n const { message, params } = this.options;\n const output = [\n styleText('red', styleText('bold', 'ADDITIONAL PROPERTY') + ' ' + message) + '\\n',\n ];\n\n return output.concat(\n this.getCodeFrame(\n '😲 ' +\n styleText('magentaBright', params.additionalProperty) +\n ' is not expected to be here!',\n `${this.instancePath}/${params.additionalProperty}`\n )\n );\n }\n\n getError() {\n const { params } = this.options;\n\n return {\n ...this.getLocation(`${this.instancePath}/${params.additionalProperty}`),\n error: this.withDecoratedPath(\n `Property ${params.additionalProperty} is not expected to be here`\n ),\n path: this.instancePath,\n };\n }\n}\n","import { styleText } from 'node:util';\nimport leven from 'leven';\nimport pointer from 'jsonpointer';\nimport BaseValidationError from './base';\n\nexport default class EnumValidationError extends BaseValidationError {\n print() {\n const {\n message,\n params: { allowedValues },\n } = this.options;\n const bestMatch = this.findBestMatch();\n\n // Needed to handle nullable enums, as joining on null just prints \", \"\n const [firstValue, ...rest] = allowedValues;\n const allowedValuesMessage = rest.reduce((acc, value) => `${acc}, ${value}`, firstValue || '');\n\n const output = [\n styleText('red', styleText('bold', 'ENUM') + ' ' + message),\n styleText('red', `(${allowedValuesMessage})\\n`),\n ];\n\n return output.concat(\n this.getCodeFrame(\n bestMatch !== null\n ? '👈🏽 Did you mean ' + styleText('magentaBright', String(bestMatch)) + ' here?'\n : '👈🏽 Unexpected value, should be equal to one of the allowed values'\n )\n );\n }\n\n getError() {\n const { message, params } = this.options;\n const bestMatch = this.findBestMatch();\n const allowedValues = params.allowedValues.join(', ');\n\n const output = {\n ...this.getLocation(),\n error: this.withDecoratedPath(`${message}: ${allowedValues}`),\n path: this.instancePath,\n };\n\n if (bestMatch !== null) {\n output.suggestion = `Did you mean ${bestMatch}?`;\n }\n\n return output;\n }\n\n findBestMatch() {\n const {\n params: { allowedValues },\n } = this.options;\n\n const currentValue =\n this.instancePath === '' ? this.data : pointer.get(this.data, this.instancePath);\n\n if (!currentValue) {\n return null;\n }\n\n const bestMatch = allowedValues\n .map(value => ({\n value,\n // leven compares strings, so other allowed values cannot be a close match.\n weight: typeof value === 'string' ? leven(value, currentValue.toString()) : Infinity,\n }))\n .sort((x, y) => (x.weight > y.weight ? 1 : x.weight < y.weight ? -1 : 0))[0];\n\n return allowedValues.length === 1 || bestMatch.weight < bestMatch.value.length\n ? bestMatch.value\n : null;\n }\n}\n","import { styleText } from 'node:util';\nimport BaseValidationError from './base';\n\nexport default class DefaultValidationError extends BaseValidationError {\n print() {\n const { keyword, message } = this.options;\n const output = [\n styleText('red', styleText('bold', keyword.toUpperCase()) + ' ' + message) + '\\n',\n ];\n\n return output.concat(\n this.getCodeFrame('👈🏽 ' + styleText('magentaBright', keyword) + ' ' + message)\n );\n }\n\n getError() {\n const { keyword, message } = this.options;\n\n return {\n ...this.getLocation(),\n error: this.withDecoratedPath(`${keyword} ${message}`, ': '),\n path: this.instancePath,\n };\n }\n}\n","import {\n getChildren,\n getErrors,\n getSiblings,\n isAnyOfError,\n isEnumError,\n isRequiredError,\n concatAll,\n notUndefined,\n} from './utils';\nimport {\n AdditionalPropValidationError,\n RequiredValidationError,\n EnumValidationError,\n DefaultValidationError,\n} from './validation-errors/index';\n\nconst JSON_POINTERS_REGEX = /\\/[\\w_-]+(\\/\\d+)?/g;\n\n// Make a tree of errors from ajv errors array\nexport function makeTree(ajvErrors = []) {\n const root = { children: {} };\n ajvErrors.forEach(ajvError => {\n const instancePath =\n typeof ajvError.instancePath !== 'undefined' ? ajvError.instancePath : ajvError.dataPath;\n\n // `dataPath === ''` is root\n const paths = instancePath === '' ? [''] : instancePath.match(JSON_POINTERS_REGEX);\n if (paths) {\n paths.reduce((obj, path, i) => {\n obj.children[path] = obj.children[path] || { children: {}, errors: [] };\n if (i === paths.length - 1) {\n obj.children[path].errors.push(ajvError);\n }\n return obj.children[path];\n }, root);\n }\n });\n return root;\n}\n\nexport function filterRedundantErrors(root, parent, key) {\n /**\n * Partition out the errors by kind for ease of proceessing\n */\n const { anyOfErrors, enumErrors, requiredErrors } = getErrors(root).reduce(\n (acc, error) => {\n if (isRequiredError(error)) {\n acc.requiredErrors.push(error);\n\n return acc;\n }\n\n if (isAnyOfError(error)) {\n acc.anyOfErrors.push(error);\n }\n\n if (isEnumError(error)) {\n acc.enumErrors.push(error);\n }\n\n return acc;\n },\n { anyOfErrors: [], enumErrors: [], requiredErrors: [] }\n );\n\n /**\n * If there is are `required` errors then we can just drop every non-required error.\n * And, also `required` should have more priority than `anyOf`. @see #8\n */\n\n if (requiredErrors.length > 0) {\n root.errors = requiredErrors;\n root.children = {};\n\n // No need for further processing, as we've emptied out children\n return;\n }\n\n /**\n * If there is an `anyOf` error that means we have more meaningful errors\n * inside children. So we will just remove all errors from this level.\n *\n * If there are no children, then we don't delete the errors since we should\n * have at least one error to report.\n */\n if (anyOfErrors.length > 0 && Object.keys(root.children).length > 0) {\n delete root.errors;\n }\n\n /**\n * If all errors are `enum` and siblings have any error then we can safely\n * ignore the node. As we return early if there's required errors, we only\n * need to check anyofErrors length\n *\n * **CAUTION**\n * Need explicit `root.errors` check because `[].every(fn) === true`\n * https://en.wikipedia.org/wiki/Vacuous_truth#Vacuous_truths_in_mathematics\n */\n if (enumErrors.length > 0 && anyOfErrors.length === 0) {\n if (\n getSiblings(parent)(root)\n // Remove any reference which becomes `undefined` later\n .filter(notUndefined)\n .some(getErrors)\n ) {\n delete parent.children[key];\n }\n }\n\n Object.entries(root.children).forEach(([key, child]) => filterRedundantErrors(child, root, key));\n}\n\nexport function createErrorInstances(root, options) {\n const errors = getErrors(root);\n if (errors.length && errors.every(isEnumError)) {\n const uniqueValues = new Set(concatAll([])(errors.map(e => e.params.allowedValues)));\n const allowedValues = [...uniqueValues];\n const error = errors[0];\n return [\n new EnumValidationError(\n {\n ...error,\n params: { allowedValues },\n },\n options\n ),\n ];\n } else {\n return concatAll(\n errors.reduce((ret, error) => {\n switch (error.keyword) {\n case 'additionalProperties':\n return ret.concat(new AdditionalPropValidationError(error, options));\n case 'required':\n return ret.concat(new RequiredValidationError(error, options));\n default:\n return ret.concat(new DefaultValidationError(error, options));\n }\n }, [])\n )(getChildren(root).map(child => createErrorInstances(child, options)));\n }\n}\n\nexport default (ajvErrors, options) => {\n const tree = makeTree(ajvErrors || []);\n filterRedundantErrors(tree);\n return createErrorInstances(tree, options);\n};\n","import { parse } from '@humanwhocodes/momoa';\nimport prettify from './helpers';\n\nexport default (schema, data, errors, options = {}) => {\n const { format = 'cli', indent = null, json = null } = options;\n\n const jsonRaw = json || JSON.stringify(data, null, indent);\n const jsonAst = parse(jsonRaw);\n\n const customErrorToText = error => error.print().join('\\n');\n const customErrorToStructure = error => error.getError();\n const customErrors = prettify(errors, {\n data,\n schema,\n jsonAst,\n jsonRaw,\n });\n\n if (format === 'cli') {\n return customErrors.map(customErrorToText).join('\\n\\n');\n } else {\n return customErrors.map(customErrorToStructure);\n }\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,84 @@
1
1
  {
2
2
  "name": "@atlassian/better-ajv-errors",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "3.0.1",
4
+ "description": "JSON Schema validation for Human",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/atlassian/better-ajv-errors.git"
8
+ },
9
+ "main": "./dist/index.cjs",
10
+ "types": "./typings.d.cts",
11
+ "type": "module",
12
+ "exports": {
13
+ "require": {
14
+ "types": "./typings.d.cts",
15
+ "default": "./dist/index.cjs"
16
+ },
17
+ "import": {
18
+ "types": "./typings.d.mts",
19
+ "default": "./dist/index.mjs"
20
+ }
21
+ },
22
+ "module": "./dist/index.mjs",
23
+ "sideEffects": false,
24
+ "engines": {
25
+ "node": ">= 22"
26
+ },
27
+ "packageManager": "pnpm@12.11.2",
28
+ "keywords": [
29
+ "json-schema",
30
+ "ajv",
31
+ "ajv-errors"
32
+ ],
33
+ "author": "Rifat Nabi <to.rifat@gmail.com>",
34
+ "maintainers": [
35
+ "Rifat Nabi <to.rifat@gmail.com>",
36
+ "Dmitrii Sorin <info@staypositive.ru>",
37
+ "Tong Li"
38
+ ],
39
+ "license": "Apache-2.0",
40
+ "files": [
41
+ "dist",
42
+ "typings.d.mts",
43
+ "typings.d.cts"
44
+ ],
45
+ "scripts": {
46
+ "build": "vp pack",
47
+ "prerelease": "npm run build",
48
+ "release": "pnpm publish",
49
+ "release:atlassian": "node publish-atlassian.js",
50
+ "format": "vp fmt './src/**/*.js'",
51
+ "format:check": "vp fmt --check './src/**/*.js'",
52
+ "prelint": "npm run build",
53
+ "lint": "vp lint && publint",
54
+ "lint:actions": "actionlint && zizmor .github/workflows",
55
+ "test": "vp test",
56
+ "test-ci": "vp test --coverage",
57
+ "prescreenshot": "npm run build",
58
+ "screenshot": "FORCE_COLOR=1 node screenshot | svg-term --out=./media/screenshot.svg --padding=5 --width=80 --height=13 --at=1000 --no-cursor --term xresources --profile=./media/deep.Xresources --window",
59
+ "prepare": "is-ci || vp config"
60
+ },
61
+ "dependencies": {
62
+ "@humanwhocodes/momoa": "^3.3.13",
63
+ "jsonpointer": "^5.0.1",
64
+ "leven": "^3.1.0 < 4"
65
+ },
66
+ "devDependencies": {
67
+ "@vitest/coverage-v8": "catalog:",
68
+ "@vitest/ui": "catalog:",
69
+ "ajv": "^8.20.0",
70
+ "is-ci": "^4.1.0",
71
+ "jest-fixtures": "^0.6.0",
72
+ "publint": "^0.3.25",
73
+ "svg-term-cli": "^2.1.1",
74
+ "vite": "catalog:",
75
+ "vite-plus": "catalog:",
76
+ "vitest": "catalog:"
77
+ },
78
+ "peerDependencies": {
79
+ "ajv": "4.11.8 - 8"
80
+ },
81
+ "publishConfig": {
82
+ "registry": "https://packages.atlassian.com/api/npm/npm-public/"
83
+ }
84
+ }
package/typings.d.cts ADDED
@@ -0,0 +1,24 @@
1
+ import type { ErrorObject } from 'ajv';
2
+
3
+ export interface IOutputError {
4
+ start: { line: number; column: number; offset: number };
5
+ // Optional for required
6
+ end?: { line: number; column: number; offset: number };
7
+ error: string;
8
+ suggestion?: string;
9
+ }
10
+
11
+ export interface IInputOptions {
12
+ format?: 'cli' | 'js';
13
+ indent?: number | null;
14
+
15
+ /** Raw JSON used when highlighting error location */
16
+ json?: string | null;
17
+ }
18
+
19
+ export default function <S, T, Options extends IInputOptions>(
20
+ schema: S,
21
+ data: T,
22
+ errors: Array<ErrorObject>,
23
+ options?: Options
24
+ ): Options extends { format: 'js' } ? Array<IOutputError> : string;
package/typings.d.mts ADDED
@@ -0,0 +1,24 @@
1
+ import type { ErrorObject } from 'ajv';
2
+
3
+ export interface IOutputError {
4
+ start: { line: number; column: number; offset: number };
5
+ // Optional for required
6
+ end?: { line: number; column: number; offset: number };
7
+ error: string;
8
+ suggestion?: string;
9
+ }
10
+
11
+ export interface IInputOptions {
12
+ format?: 'cli' | 'js';
13
+ indent?: number | null;
14
+
15
+ /** Raw JSON used when highlighting error location */
16
+ json?: string | null;
17
+ }
18
+
19
+ export default function <S, T, Options extends IInputOptions>(
20
+ schema: S,
21
+ data: T,
22
+ errors: Array<ErrorObject>,
23
+ options?: Options
24
+ ): Options extends { format: 'js' } ? Array<IOutputError> : string;