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

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,128 @@
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
+ $ # Or
19
+ $ yarn add better-ajv-errors
20
+ ```
21
+
22
+ 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.
23
+
24
+ ## Usage
25
+
26
+ First, you need to validate your payload with `ajv`. If it's invalid then you can pass `validate.errors` object into `better-ajv-errors`.
27
+
28
+ ```js
29
+ import Ajv from 'ajv';
30
+ import betterAjvErrors from 'better-ajv-errors';
31
+ // const Ajv = require('ajv');
32
+ // const betterAjvErrors = require('better-ajv-errors').default;
33
+ // Or
34
+ // const { default: betterAjvErrors } = require('better-ajv-errors');
35
+
36
+ // `allErrors` makes ajv report every error instead of stopping at the first one.
37
+ // ajv 6 and older also need `jsonPointers: true`.
38
+ const ajv = new Ajv({ allErrors: true });
39
+
40
+ // Load schema and data
41
+ const schema = ...;
42
+ const data = ...;
43
+
44
+ const validate = ajv.compile(schema);
45
+ const valid = validate(data);
46
+
47
+ if (!valid) {
48
+ const output = betterAjvErrors(schema, data, validate.errors);
49
+ console.log(output);
50
+ }
51
+ ```
52
+
53
+ `better-ajv-errors` leaves out errors that another error usually explains:
54
+
55
+ - A missing `required` property hides the other errors reported for that object, such as `additionalProperties`.
56
+ - An `enum` error is hidden when another error is reported at the same level.
57
+
58
+ ## API
59
+
60
+ ### betterAjvErrors(schema, data, errors, [options])
61
+
62
+ Returns formatted validation error to **print** in `console`. See [`options.format`](#format) for further details.
63
+
64
+ #### schema
65
+
66
+ Type: `Object`
67
+
68
+ The JSON Schema you used for validation with `ajv`
69
+
70
+ #### data
71
+
72
+ Type: `Object`
73
+
74
+ The JSON payload you validate against using `ajv`
75
+
76
+ #### errors
77
+
78
+ Type: `Array`
79
+
80
+ Array of [ajv validation errors](https://ajv.js.org/api.html#validation-errors)
81
+
82
+ #### options
83
+
84
+ Type: `Object`
85
+
86
+ ##### format
87
+
88
+ Type: `string`
89
+ Default: `cli`
90
+ Values: `cli` `js`
91
+
92
+ Use default `cli` output format if you want to **print** beautiful validation errors like following:
93
+
94
+ <img width="620" src="media/screenshot.svg">
95
+
96
+ Or, use `js` if you are planning to use this with some API. Your output will look like following:
97
+
98
+ ```javascript
99
+ [
100
+ {
101
+ start: { line: 6, column: 15, offset: 70 },
102
+ end: { line: 6, column: 26, offset: 81 },
103
+ error:
104
+ '/content/0/type must be equal to one of the allowed values: paragraph, codeBlock, blockquote',
105
+ path: '/content/0/type',
106
+ suggestion: 'Did you mean paragraph?',
107
+ },
108
+ ];
109
+ ```
110
+
111
+ `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.
112
+
113
+ ##### indent
114
+
115
+ Type: `number` `null`
116
+ Default: `null`
117
+
118
+ If you have an unindented JSON payload and you want the error output indented.
119
+
120
+ This option has no effect when using the `json` option.
121
+
122
+ ##### json
123
+
124
+ Type: `string` `null`
125
+ Default: `null`
126
+
127
+ Raw JSON payload used when formatting codeframe.
128
+ Gives accurate line and column listings.
package/dist/index.cjs ADDED
@@ -0,0 +1,347 @@
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 _babel_code_frame = require("@babel/code-frame");
30
+ let leven = require("leven");
31
+ leven = __toESM(leven, 1);
32
+ let jsonpointer = require("jsonpointer");
33
+ jsonpointer = __toESM(jsonpointer, 1);
34
+ //#region src/utils.js
35
+ const eq = (x) => (y) => x === y;
36
+ const not = (fn) => (x) => !fn(x);
37
+ const getValues = (o) => Object.values(o);
38
+ const notUndefined = (x) => x !== void 0;
39
+ const isXError = (x) => (error) => error.keyword === x;
40
+ const isRequiredError = isXError("required");
41
+ const isAnyOfError = isXError("anyOf");
42
+ const isEnumError = isXError("enum");
43
+ const getErrors = (node) => node && node.errors ? node.errors.map((e) => e.keyword === "errorMessage" ? {
44
+ ...e.params.errors[0],
45
+ message: e.message
46
+ } : e) : [];
47
+ const getChildren = (node) => node && getValues(node.children) || [];
48
+ const getSiblings = (parent) => (node) => getChildren(parent).filter(not(eq(node)));
49
+ const concatAll = (xs) => (ys) => ys.reduce((zs, z) => zs.concat(z), xs);
50
+ //#endregion
51
+ //#region src/json/utils.js
52
+ const getPointers = (dataPath) => {
53
+ return dataPath.split("/").slice(1).map((pointer) => pointer.split("~1").join("/").split("~0").join("~"));
54
+ };
55
+ //#endregion
56
+ //#region src/json/get-meta-from-path.js
57
+ function getMetaFromPath(jsonAst, dataPath, includeIdentifierLocation) {
58
+ const pointers = getPointers(dataPath);
59
+ const lastPointerIndex = pointers.length - 1;
60
+ return pointers.reduce((obj, pointer, idx) => {
61
+ switch (obj.type) {
62
+ case "Object": {
63
+ const member = obj.members.findLast((child) => child.name.value === pointer);
64
+ if (!member) throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);
65
+ const { name, value } = member;
66
+ return includeIdentifierLocation && idx === lastPointerIndex ? name : value;
67
+ }
68
+ case "Array": {
69
+ const element = obj.elements[pointer];
70
+ return element && element.value;
71
+ }
72
+ default: console.log(obj);
73
+ }
74
+ }, jsonAst.body);
75
+ }
76
+ //#endregion
77
+ //#region src/json/get-decorated-data-path.js
78
+ function getDecoratedDataPath(jsonAst, dataPath) {
79
+ let decoratedPath = "";
80
+ getPointers(dataPath).reduce((obj, pointer) => {
81
+ switch (obj.type) {
82
+ case "Object": {
83
+ decoratedPath += `/${pointer}`;
84
+ const member = obj.members.findLast((child) => child.name.value === pointer);
85
+ if (!member) throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);
86
+ return member.value;
87
+ }
88
+ case "Array": {
89
+ const element = obj.elements[pointer];
90
+ const value = element && element.value;
91
+ decoratedPath += `/${pointer}${getTypeName(value)}`;
92
+ return value;
93
+ }
94
+ default: console.log(obj);
95
+ }
96
+ }, jsonAst.body);
97
+ return decoratedPath;
98
+ }
99
+ function getTypeName(obj) {
100
+ if (!obj || !obj.elements) return "";
101
+ const type = obj.elements.filter((child) => child && child.name && child.name.value === "type");
102
+ if (!type.length) return "";
103
+ return type[0].value && `:${type[0].value.value}` || "";
104
+ }
105
+ //#endregion
106
+ //#region src/validation-errors/base.js
107
+ var BaseValidationError = class {
108
+ constructor(options = { isIdentifierLocation: false }, { data, schema, jsonAst, jsonRaw }) {
109
+ this.options = options;
110
+ this.data = data;
111
+ this.schema = schema;
112
+ this.jsonAst = jsonAst;
113
+ this.jsonRaw = jsonRaw;
114
+ }
115
+ getLocation(dataPath = this.instancePath) {
116
+ const { isIdentifierLocation, isSkipEndLocation } = this.options;
117
+ const { loc } = getMetaFromPath(this.jsonAst, dataPath, isIdentifierLocation);
118
+ return {
119
+ start: loc.start,
120
+ end: isSkipEndLocation ? void 0 : loc.end
121
+ };
122
+ }
123
+ getDecoratedPath(dataPath = this.instancePath) {
124
+ return getDecoratedDataPath(this.jsonAst, dataPath);
125
+ }
126
+ withDecoratedPath(message, separator = " ") {
127
+ const decoratedPath = this.getDecoratedPath();
128
+ return decoratedPath ? `${decoratedPath}${separator}${message}` : message;
129
+ }
130
+ getCodeFrame(message, dataPath = this.instancePath) {
131
+ return (0, _babel_code_frame.codeFrameColumns)(this.jsonRaw, this.getLocation(dataPath), {
132
+ highlightCode: true,
133
+ message
134
+ });
135
+ }
136
+ /**
137
+ * @return {string}
138
+ */
139
+ get instancePath() {
140
+ return typeof this.options.instancePath !== "undefined" ? this.options.instancePath : this.options.dataPath;
141
+ }
142
+ print() {
143
+ throw new Error(`Implement the 'print' method inside ${this.constructor.name}!`);
144
+ }
145
+ getError() {
146
+ throw new Error(`Implement the 'getError' method inside ${this.constructor.name}!`);
147
+ }
148
+ };
149
+ //#endregion
150
+ //#region src/validation-errors/required.js
151
+ var RequiredValidationError = class extends BaseValidationError {
152
+ getLocation(dataPath = this.instancePath) {
153
+ const { start } = super.getLocation(dataPath);
154
+ return { start };
155
+ }
156
+ print() {
157
+ const { message, params } = this.options;
158
+ 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!"));
159
+ }
160
+ getError() {
161
+ const { message } = this.options;
162
+ return {
163
+ ...this.getLocation(),
164
+ error: this.withDecoratedPath(message),
165
+ path: this.instancePath
166
+ };
167
+ }
168
+ };
169
+ //#endregion
170
+ //#region src/validation-errors/additional-prop.js
171
+ var AdditionalPropValidationError = class extends BaseValidationError {
172
+ constructor(...args) {
173
+ super(...args);
174
+ this.options.isIdentifierLocation = true;
175
+ }
176
+ print() {
177
+ const { message, params } = this.options;
178
+ 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}`));
179
+ }
180
+ getError() {
181
+ const { params } = this.options;
182
+ return {
183
+ ...this.getLocation(`${this.instancePath}/${params.additionalProperty}`),
184
+ error: this.withDecoratedPath(`Property ${params.additionalProperty} is not expected to be here`),
185
+ path: this.instancePath
186
+ };
187
+ }
188
+ };
189
+ //#endregion
190
+ //#region src/validation-errors/enum.js
191
+ var EnumValidationError = class extends BaseValidationError {
192
+ print() {
193
+ const { message, params: { allowedValues } } = this.options;
194
+ const bestMatch = this.findBestMatch();
195
+ const [firstValue, ...rest] = allowedValues;
196
+ const allowedValuesMessage = rest.reduce((acc, value) => `${acc}, ${value}`, firstValue || "");
197
+ 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"));
198
+ }
199
+ getError() {
200
+ const { message, params } = this.options;
201
+ const bestMatch = this.findBestMatch();
202
+ const allowedValues = params.allowedValues.join(", ");
203
+ const output = {
204
+ ...this.getLocation(),
205
+ error: this.withDecoratedPath(`${message}: ${allowedValues}`),
206
+ path: this.instancePath
207
+ };
208
+ if (bestMatch !== null) output.suggestion = `Did you mean ${bestMatch}?`;
209
+ return output;
210
+ }
211
+ findBestMatch() {
212
+ const { params: { allowedValues } } = this.options;
213
+ const currentValue = this.instancePath === "" ? this.data : jsonpointer.default.get(this.data, this.instancePath);
214
+ if (!currentValue) return null;
215
+ const bestMatch = allowedValues.map((value) => ({
216
+ value,
217
+ weight: typeof value === "string" ? (0, leven.default)(value, currentValue.toString()) : Infinity
218
+ })).sort((x, y) => x.weight > y.weight ? 1 : x.weight < y.weight ? -1 : 0)[0];
219
+ return allowedValues.length === 1 || bestMatch.weight < bestMatch.value.length ? bestMatch.value : null;
220
+ }
221
+ };
222
+ //#endregion
223
+ //#region src/validation-errors/default.js
224
+ var DefaultValidationError = class extends BaseValidationError {
225
+ print() {
226
+ const { keyword, message } = this.options;
227
+ 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));
228
+ }
229
+ getError() {
230
+ const { keyword, message } = this.options;
231
+ return {
232
+ ...this.getLocation(),
233
+ error: this.withDecoratedPath(`${keyword} ${message}`, ": "),
234
+ path: this.instancePath
235
+ };
236
+ }
237
+ };
238
+ //#endregion
239
+ //#region src/helpers.js
240
+ const JSON_POINTERS_REGEX = /\/[\w_-]+(\/\d+)?/g;
241
+ function makeTree(ajvErrors = []) {
242
+ const root = { children: {} };
243
+ ajvErrors.forEach((ajvError) => {
244
+ const instancePath = typeof ajvError.instancePath !== "undefined" ? ajvError.instancePath : ajvError.dataPath;
245
+ const paths = instancePath === "" ? [""] : instancePath.match(JSON_POINTERS_REGEX);
246
+ if (paths) paths.reduce((obj, path, i) => {
247
+ obj.children[path] = obj.children[path] || {
248
+ children: {},
249
+ errors: []
250
+ };
251
+ if (i === paths.length - 1) obj.children[path].errors.push(ajvError);
252
+ return obj.children[path];
253
+ }, root);
254
+ });
255
+ return root;
256
+ }
257
+ function filterRedundantErrors(root, parent, key) {
258
+ /**
259
+ * Partition out the errors by kind for ease of proceessing
260
+ */
261
+ const { anyOfErrors, enumErrors, requiredErrors } = getErrors(root).reduce((acc, error) => {
262
+ if (isRequiredError(error)) {
263
+ acc.requiredErrors.push(error);
264
+ return acc;
265
+ }
266
+ if (isAnyOfError(error)) acc.anyOfErrors.push(error);
267
+ if (isEnumError(error)) acc.enumErrors.push(error);
268
+ return acc;
269
+ }, {
270
+ anyOfErrors: [],
271
+ enumErrors: [],
272
+ requiredErrors: []
273
+ });
274
+ /**
275
+ * If there is are `required` errors then we can just drop every non-required error.
276
+ * And, also `required` should have more priority than `anyOf`. @see #8
277
+ */
278
+ if (requiredErrors.length > 0) {
279
+ root.errors = requiredErrors;
280
+ root.children = {};
281
+ return;
282
+ }
283
+ /**
284
+ * If there is an `anyOf` error that means we have more meaningful errors
285
+ * inside children. So we will just remove all errors from this level.
286
+ *
287
+ * If there are no children, then we don't delete the errors since we should
288
+ * have at least one error to report.
289
+ */
290
+ if (anyOfErrors.length > 0 && Object.keys(root.children).length > 0) delete root.errors;
291
+ /**
292
+ * If all errors are `enum` and siblings have any error then we can safely
293
+ * ignore the node. As we return early if there's required errors, we only
294
+ * need to check anyofErrors length
295
+ *
296
+ * **CAUTION**
297
+ * Need explicit `root.errors` check because `[].every(fn) === true`
298
+ * https://en.wikipedia.org/wiki/Vacuous_truth#Vacuous_truths_in_mathematics
299
+ */
300
+ if (enumErrors.length > 0 && anyOfErrors.length === 0) {
301
+ if (getSiblings(parent)(root).filter(notUndefined).some(getErrors)) delete parent.children[key];
302
+ }
303
+ Object.entries(root.children).forEach(([key, child]) => filterRedundantErrors(child, root, key));
304
+ }
305
+ function createErrorInstances(root, options) {
306
+ const errors = getErrors(root);
307
+ if (errors.length && errors.every(isEnumError)) {
308
+ const allowedValues = [...new Set(concatAll([])(errors.map((e) => e.params.allowedValues)))];
309
+ const error = errors[0];
310
+ return [new EnumValidationError({
311
+ ...error,
312
+ params: { allowedValues }
313
+ }, options)];
314
+ } else return concatAll(errors.reduce((ret, error) => {
315
+ switch (error.keyword) {
316
+ case "additionalProperties": return ret.concat(new AdditionalPropValidationError(error, options));
317
+ case "required": return ret.concat(new RequiredValidationError(error, options));
318
+ default: return ret.concat(new DefaultValidationError(error, options));
319
+ }
320
+ }, []))(getChildren(root).map((child) => createErrorInstances(child, options)));
321
+ }
322
+ var helpers_default = (ajvErrors, options) => {
323
+ const tree = makeTree(ajvErrors || []);
324
+ filterRedundantErrors(tree);
325
+ return createErrorInstances(tree, options);
326
+ };
327
+ //#endregion
328
+ //#region src/index.js
329
+ var src_default = (schema, data, errors, options = {}) => {
330
+ const { format = "cli", indent = null, json = null } = options;
331
+ const jsonRaw = json || JSON.stringify(data, null, indent);
332
+ const jsonAst = (0, _humanwhocodes_momoa.parse)(jsonRaw);
333
+ const customErrorToText = (error) => error.print().join("\n");
334
+ const customErrorToStructure = (error) => error.getError();
335
+ const customErrors = helpers_default(errors, {
336
+ data,
337
+ schema,
338
+ jsonAst,
339
+ jsonRaw
340
+ });
341
+ if (format === "cli") return customErrors.map(customErrorToText).join("\n\n");
342
+ else return customErrors.map(customErrorToStructure);
343
+ };
344
+ //#endregion
345
+ exports.default = src_default;
346
+
347
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["codeFrameColumns","styleText","styleText","styleText","pointer","styleText","parse","prettify"],"sources":["../src/utils.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","// 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 { codeFrameColumns } from '@babel/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 codeFrameColumns(this.jsonRaw, this.getLocation(dataPath), {\n highlightCode: true,\n message,\n });\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,319 @@
1
+ import { parse } from "@humanwhocodes/momoa";
2
+ import { styleText } from "node:util";
3
+ import { codeFrameColumns } from "@babel/code-frame";
4
+ import leven from "leven";
5
+ import pointer from "jsonpointer";
6
+ //#region src/utils.js
7
+ const eq = (x) => (y) => x === y;
8
+ const not = (fn) => (x) => !fn(x);
9
+ const getValues = (o) => Object.values(o);
10
+ const notUndefined = (x) => x !== void 0;
11
+ const isXError = (x) => (error) => error.keyword === x;
12
+ const isRequiredError = isXError("required");
13
+ const isAnyOfError = isXError("anyOf");
14
+ const isEnumError = isXError("enum");
15
+ const getErrors = (node) => node && node.errors ? node.errors.map((e) => e.keyword === "errorMessage" ? {
16
+ ...e.params.errors[0],
17
+ message: e.message
18
+ } : e) : [];
19
+ const getChildren = (node) => node && getValues(node.children) || [];
20
+ const getSiblings = (parent) => (node) => getChildren(parent).filter(not(eq(node)));
21
+ const concatAll = (xs) => (ys) => ys.reduce((zs, z) => zs.concat(z), xs);
22
+ //#endregion
23
+ //#region src/json/utils.js
24
+ const getPointers = (dataPath) => {
25
+ return dataPath.split("/").slice(1).map((pointer) => pointer.split("~1").join("/").split("~0").join("~"));
26
+ };
27
+ //#endregion
28
+ //#region src/json/get-meta-from-path.js
29
+ function getMetaFromPath(jsonAst, dataPath, includeIdentifierLocation) {
30
+ const pointers = getPointers(dataPath);
31
+ const lastPointerIndex = pointers.length - 1;
32
+ return pointers.reduce((obj, pointer, idx) => {
33
+ switch (obj.type) {
34
+ case "Object": {
35
+ const member = obj.members.findLast((child) => child.name.value === pointer);
36
+ if (!member) throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);
37
+ const { name, value } = member;
38
+ return includeIdentifierLocation && idx === lastPointerIndex ? name : value;
39
+ }
40
+ case "Array": {
41
+ const element = obj.elements[pointer];
42
+ return element && element.value;
43
+ }
44
+ default: console.log(obj);
45
+ }
46
+ }, jsonAst.body);
47
+ }
48
+ //#endregion
49
+ //#region src/json/get-decorated-data-path.js
50
+ function getDecoratedDataPath(jsonAst, dataPath) {
51
+ let decoratedPath = "";
52
+ getPointers(dataPath).reduce((obj, pointer) => {
53
+ switch (obj.type) {
54
+ case "Object": {
55
+ decoratedPath += `/${pointer}`;
56
+ const member = obj.members.findLast((child) => child.name.value === pointer);
57
+ if (!member) throw new Error(`Couldn't find property ${pointer} of ${dataPath}`);
58
+ return member.value;
59
+ }
60
+ case "Array": {
61
+ const element = obj.elements[pointer];
62
+ const value = element && element.value;
63
+ decoratedPath += `/${pointer}${getTypeName(value)}`;
64
+ return value;
65
+ }
66
+ default: console.log(obj);
67
+ }
68
+ }, jsonAst.body);
69
+ return decoratedPath;
70
+ }
71
+ function getTypeName(obj) {
72
+ if (!obj || !obj.elements) return "";
73
+ const type = obj.elements.filter((child) => child && child.name && child.name.value === "type");
74
+ if (!type.length) return "";
75
+ return type[0].value && `:${type[0].value.value}` || "";
76
+ }
77
+ //#endregion
78
+ //#region src/validation-errors/base.js
79
+ var BaseValidationError = class {
80
+ constructor(options = { isIdentifierLocation: false }, { data, schema, jsonAst, jsonRaw }) {
81
+ this.options = options;
82
+ this.data = data;
83
+ this.schema = schema;
84
+ this.jsonAst = jsonAst;
85
+ this.jsonRaw = jsonRaw;
86
+ }
87
+ getLocation(dataPath = this.instancePath) {
88
+ const { isIdentifierLocation, isSkipEndLocation } = this.options;
89
+ const { loc } = getMetaFromPath(this.jsonAst, dataPath, isIdentifierLocation);
90
+ return {
91
+ start: loc.start,
92
+ end: isSkipEndLocation ? void 0 : loc.end
93
+ };
94
+ }
95
+ getDecoratedPath(dataPath = this.instancePath) {
96
+ return getDecoratedDataPath(this.jsonAst, dataPath);
97
+ }
98
+ withDecoratedPath(message, separator = " ") {
99
+ const decoratedPath = this.getDecoratedPath();
100
+ return decoratedPath ? `${decoratedPath}${separator}${message}` : message;
101
+ }
102
+ getCodeFrame(message, dataPath = this.instancePath) {
103
+ return codeFrameColumns(this.jsonRaw, this.getLocation(dataPath), {
104
+ highlightCode: true,
105
+ message
106
+ });
107
+ }
108
+ /**
109
+ * @return {string}
110
+ */
111
+ get instancePath() {
112
+ return typeof this.options.instancePath !== "undefined" ? this.options.instancePath : this.options.dataPath;
113
+ }
114
+ print() {
115
+ throw new Error(`Implement the 'print' method inside ${this.constructor.name}!`);
116
+ }
117
+ getError() {
118
+ throw new Error(`Implement the 'getError' method inside ${this.constructor.name}!`);
119
+ }
120
+ };
121
+ //#endregion
122
+ //#region src/validation-errors/required.js
123
+ var RequiredValidationError = class extends BaseValidationError {
124
+ getLocation(dataPath = this.instancePath) {
125
+ const { start } = super.getLocation(dataPath);
126
+ return { start };
127
+ }
128
+ print() {
129
+ const { message, params } = this.options;
130
+ return [styleText("red", styleText("bold", "REQUIRED") + " " + message) + "\n"].concat(this.getCodeFrame("☹️ " + styleText("magentaBright", params.missingProperty) + " is missing here!"));
131
+ }
132
+ getError() {
133
+ const { message } = this.options;
134
+ return {
135
+ ...this.getLocation(),
136
+ error: this.withDecoratedPath(message),
137
+ path: this.instancePath
138
+ };
139
+ }
140
+ };
141
+ //#endregion
142
+ //#region src/validation-errors/additional-prop.js
143
+ var AdditionalPropValidationError = class extends BaseValidationError {
144
+ constructor(...args) {
145
+ super(...args);
146
+ this.options.isIdentifierLocation = true;
147
+ }
148
+ print() {
149
+ const { message, params } = this.options;
150
+ 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}`));
151
+ }
152
+ getError() {
153
+ const { params } = this.options;
154
+ return {
155
+ ...this.getLocation(`${this.instancePath}/${params.additionalProperty}`),
156
+ error: this.withDecoratedPath(`Property ${params.additionalProperty} is not expected to be here`),
157
+ path: this.instancePath
158
+ };
159
+ }
160
+ };
161
+ //#endregion
162
+ //#region src/validation-errors/enum.js
163
+ var EnumValidationError = class extends BaseValidationError {
164
+ print() {
165
+ const { message, params: { allowedValues } } = this.options;
166
+ const bestMatch = this.findBestMatch();
167
+ const [firstValue, ...rest] = allowedValues;
168
+ const allowedValuesMessage = rest.reduce((acc, value) => `${acc}, ${value}`, firstValue || "");
169
+ 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"));
170
+ }
171
+ getError() {
172
+ const { message, params } = this.options;
173
+ const bestMatch = this.findBestMatch();
174
+ const allowedValues = params.allowedValues.join(", ");
175
+ const output = {
176
+ ...this.getLocation(),
177
+ error: this.withDecoratedPath(`${message}: ${allowedValues}`),
178
+ path: this.instancePath
179
+ };
180
+ if (bestMatch !== null) output.suggestion = `Did you mean ${bestMatch}?`;
181
+ return output;
182
+ }
183
+ findBestMatch() {
184
+ const { params: { allowedValues } } = this.options;
185
+ const currentValue = this.instancePath === "" ? this.data : pointer.get(this.data, this.instancePath);
186
+ if (!currentValue) return null;
187
+ const bestMatch = allowedValues.map((value) => ({
188
+ value,
189
+ weight: typeof value === "string" ? leven(value, currentValue.toString()) : Infinity
190
+ })).sort((x, y) => x.weight > y.weight ? 1 : x.weight < y.weight ? -1 : 0)[0];
191
+ return allowedValues.length === 1 || bestMatch.weight < bestMatch.value.length ? bestMatch.value : null;
192
+ }
193
+ };
194
+ //#endregion
195
+ //#region src/validation-errors/default.js
196
+ var DefaultValidationError = class extends BaseValidationError {
197
+ print() {
198
+ const { keyword, message } = this.options;
199
+ return [styleText("red", styleText("bold", keyword.toUpperCase()) + " " + message) + "\n"].concat(this.getCodeFrame("👈🏽 " + styleText("magentaBright", keyword) + " " + message));
200
+ }
201
+ getError() {
202
+ const { keyword, message } = this.options;
203
+ return {
204
+ ...this.getLocation(),
205
+ error: this.withDecoratedPath(`${keyword} ${message}`, ": "),
206
+ path: this.instancePath
207
+ };
208
+ }
209
+ };
210
+ //#endregion
211
+ //#region src/helpers.js
212
+ const JSON_POINTERS_REGEX = /\/[\w_-]+(\/\d+)?/g;
213
+ function makeTree(ajvErrors = []) {
214
+ const root = { children: {} };
215
+ ajvErrors.forEach((ajvError) => {
216
+ const instancePath = typeof ajvError.instancePath !== "undefined" ? ajvError.instancePath : ajvError.dataPath;
217
+ const paths = instancePath === "" ? [""] : instancePath.match(JSON_POINTERS_REGEX);
218
+ if (paths) paths.reduce((obj, path, i) => {
219
+ obj.children[path] = obj.children[path] || {
220
+ children: {},
221
+ errors: []
222
+ };
223
+ if (i === paths.length - 1) obj.children[path].errors.push(ajvError);
224
+ return obj.children[path];
225
+ }, root);
226
+ });
227
+ return root;
228
+ }
229
+ function filterRedundantErrors(root, parent, key) {
230
+ /**
231
+ * Partition out the errors by kind for ease of proceessing
232
+ */
233
+ const { anyOfErrors, enumErrors, requiredErrors } = getErrors(root).reduce((acc, error) => {
234
+ if (isRequiredError(error)) {
235
+ acc.requiredErrors.push(error);
236
+ return acc;
237
+ }
238
+ if (isAnyOfError(error)) acc.anyOfErrors.push(error);
239
+ if (isEnumError(error)) acc.enumErrors.push(error);
240
+ return acc;
241
+ }, {
242
+ anyOfErrors: [],
243
+ enumErrors: [],
244
+ requiredErrors: []
245
+ });
246
+ /**
247
+ * If there is are `required` errors then we can just drop every non-required error.
248
+ * And, also `required` should have more priority than `anyOf`. @see #8
249
+ */
250
+ if (requiredErrors.length > 0) {
251
+ root.errors = requiredErrors;
252
+ root.children = {};
253
+ return;
254
+ }
255
+ /**
256
+ * If there is an `anyOf` error that means we have more meaningful errors
257
+ * inside children. So we will just remove all errors from this level.
258
+ *
259
+ * If there are no children, then we don't delete the errors since we should
260
+ * have at least one error to report.
261
+ */
262
+ if (anyOfErrors.length > 0 && Object.keys(root.children).length > 0) delete root.errors;
263
+ /**
264
+ * If all errors are `enum` and siblings have any error then we can safely
265
+ * ignore the node. As we return early if there's required errors, we only
266
+ * need to check anyofErrors length
267
+ *
268
+ * **CAUTION**
269
+ * Need explicit `root.errors` check because `[].every(fn) === true`
270
+ * https://en.wikipedia.org/wiki/Vacuous_truth#Vacuous_truths_in_mathematics
271
+ */
272
+ if (enumErrors.length > 0 && anyOfErrors.length === 0) {
273
+ if (getSiblings(parent)(root).filter(notUndefined).some(getErrors)) delete parent.children[key];
274
+ }
275
+ Object.entries(root.children).forEach(([key, child]) => filterRedundantErrors(child, root, key));
276
+ }
277
+ function createErrorInstances(root, options) {
278
+ const errors = getErrors(root);
279
+ if (errors.length && errors.every(isEnumError)) {
280
+ const allowedValues = [...new Set(concatAll([])(errors.map((e) => e.params.allowedValues)))];
281
+ const error = errors[0];
282
+ return [new EnumValidationError({
283
+ ...error,
284
+ params: { allowedValues }
285
+ }, options)];
286
+ } else return concatAll(errors.reduce((ret, error) => {
287
+ switch (error.keyword) {
288
+ case "additionalProperties": return ret.concat(new AdditionalPropValidationError(error, options));
289
+ case "required": return ret.concat(new RequiredValidationError(error, options));
290
+ default: return ret.concat(new DefaultValidationError(error, options));
291
+ }
292
+ }, []))(getChildren(root).map((child) => createErrorInstances(child, options)));
293
+ }
294
+ var helpers_default = (ajvErrors, options) => {
295
+ const tree = makeTree(ajvErrors || []);
296
+ filterRedundantErrors(tree);
297
+ return createErrorInstances(tree, options);
298
+ };
299
+ //#endregion
300
+ //#region src/index.js
301
+ var src_default = (schema, data, errors, options = {}) => {
302
+ const { format = "cli", indent = null, json = null } = options;
303
+ const jsonRaw = json || JSON.stringify(data, null, indent);
304
+ const jsonAst = parse(jsonRaw);
305
+ const customErrorToText = (error) => error.print().join("\n");
306
+ const customErrorToStructure = (error) => error.getError();
307
+ const customErrors = helpers_default(errors, {
308
+ data,
309
+ schema,
310
+ jsonAst,
311
+ jsonRaw
312
+ });
313
+ if (format === "cli") return customErrors.map(customErrorToText).join("\n\n");
314
+ else return customErrors.map(customErrorToStructure);
315
+ };
316
+ //#endregion
317
+ export { src_default as default };
318
+
319
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["prettify"],"sources":["../src/utils.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","// 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 { codeFrameColumns } from '@babel/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 codeFrameColumns(this.jsonRaw, this.getLocation(dataPath), {\n highlightCode: true,\n message,\n });\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,83 @@
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.0",
4
+ "description": "JSON Schema validation for Human",
5
+ "repository": "atlassian/better-ajv-errors",
6
+ "main": "./dist/index.cjs",
7
+ "types": "./typings.d.cts",
8
+ "type": "module",
9
+ "exports": {
10
+ "require": {
11
+ "types": "./typings.d.cts",
12
+ "default": "./dist/index.cjs"
13
+ },
14
+ "import": {
15
+ "types": "./typings.d.mts",
16
+ "default": "./dist/index.mjs"
17
+ }
18
+ },
19
+ "module": "./dist/index.mjs",
20
+ "sideEffects": false,
21
+ "engines": {
22
+ "node": ">= 22"
23
+ },
24
+ "packageManager": "pnpm@10.34.6",
25
+ "keywords": [
26
+ "json-schema",
27
+ "ajv",
28
+ "ajv-errors"
29
+ ],
30
+ "author": "Rifat Nabi <to.rifat@gmail.com>",
31
+ "maintainers": [
32
+ "Rifat Nabi <to.rifat@gmail.com>",
33
+ "Dmitrii Sorin <info@staypositive.ru>",
34
+ "Tong Li"
35
+ ],
36
+ "license": "Apache-2.0",
37
+ "files": [
38
+ "dist",
39
+ "typings.d.mts",
40
+ "typings.d.cts"
41
+ ],
42
+ "scripts": {
43
+ "build": "vp pack",
44
+ "prerelease": "npm run build",
45
+ "release": "changeset publish",
46
+ "release:atlassian": "node publish-atlassian.js",
47
+ "format": "vp fmt './src/**/*.js' './.changeset/*.json'",
48
+ "format:check": "vp fmt --check './src/**/*.js' './.changeset/*.json'",
49
+ "prelint": "npm run build",
50
+ "lint": "vp lint && publint",
51
+ "lint:actions": "actionlint && zizmor .github/workflows",
52
+ "test": "vp test",
53
+ "test-ci": "vp test --coverage",
54
+ "prescreenshot": "npm run build",
55
+ "screenshot": "svg-term --command='node screenshot' --out=./media/screenshot.svg --padding=5 --width=80 --height=13 --at=1000 --no-cursor --term iterm2 --profile='deep' --window",
56
+ "prepare": "is-ci || vp config"
57
+ },
58
+ "dependencies": {
59
+ "@babel/code-frame": "^7.29.7",
60
+ "@humanwhocodes/momoa": "^3.3.13",
61
+ "jsonpointer": "^5.0.1",
62
+ "leven": "^3.1.0 < 4"
63
+ },
64
+ "devDependencies": {
65
+ "@changesets/cli": "^3.0.3",
66
+ "@vitest/coverage-v8": "catalog:",
67
+ "@vitest/ui": "catalog:",
68
+ "ajv": "^8.20.0",
69
+ "is-ci": "^4.1.0",
70
+ "jest-fixtures": "^0.6.0",
71
+ "publint": "^0.3.25",
72
+ "svg-term-cli": "^2.1.1",
73
+ "vite": "catalog:",
74
+ "vite-plus": "catalog:",
75
+ "vitest": "catalog:"
76
+ },
77
+ "peerDependencies": {
78
+ "ajv": "4.11.8 - 8"
79
+ },
80
+ "publishConfig": {
81
+ "registry": "https://packages.atlassian.com/api/npm/npm-public/"
82
+ }
83
+ }
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;