1 | 'use strict';
|
2 |
|
3 | const Annotate = require('./annotate');
|
4 | const Common = require('./common');
|
5 | const Template = require('./template');
|
6 |
|
7 |
|
8 | const internals = {};
|
9 |
|
10 |
|
11 | exports.Report = class {
|
12 |
|
13 | constructor(code, value, local, flags, messages, state, prefs) {
|
14 |
|
15 | this.code = code;
|
16 | this.flags = flags;
|
17 | this.messages = messages;
|
18 | this.path = state.path;
|
19 | this.prefs = prefs;
|
20 | this.state = state;
|
21 | this.value = value;
|
22 |
|
23 | this.message = null;
|
24 | this.template = null;
|
25 |
|
26 | this.local = local || {};
|
27 | this.local.label = exports.label(this.flags, this.state, this.prefs, this.messages);
|
28 |
|
29 | if (this.value !== undefined &&
|
30 | !this.local.hasOwnProperty('value')) {
|
31 |
|
32 | this.local.value = this.value;
|
33 | }
|
34 |
|
35 | if (this.path.length) {
|
36 | const key = this.path[this.path.length - 1];
|
37 | if (typeof key !== 'object') {
|
38 | this.local.key = key;
|
39 | }
|
40 | }
|
41 | }
|
42 |
|
43 | _setTemplate(template) {
|
44 |
|
45 | this.template = template;
|
46 |
|
47 | if (!this.flags.label &&
|
48 | this.path.length === 0) {
|
49 |
|
50 | const localized = this._template(this.template, 'root');
|
51 | if (localized) {
|
52 | this.local.label = localized;
|
53 | }
|
54 | }
|
55 | }
|
56 |
|
57 | toString() {
|
58 |
|
59 | if (this.message) {
|
60 | return this.message;
|
61 | }
|
62 |
|
63 | const code = this.code;
|
64 |
|
65 | if (!this.prefs.errors.render) {
|
66 | return this.code;
|
67 | }
|
68 |
|
69 | const template = this._template(this.template) ||
|
70 | this._template(this.prefs.messages) ||
|
71 | this._template(this.messages);
|
72 |
|
73 | if (template === undefined) {
|
74 | return `Error code "${code}" is not defined, your custom type is missing the correct messages definition`;
|
75 | }
|
76 |
|
77 |
|
78 |
|
79 | this.message = template.render(this.value, this.state, this.prefs, this.local, { errors: this.prefs.errors, messages: [this.prefs.messages, this.messages] });
|
80 | if (!this.prefs.errors.label) {
|
81 | this.message = this.message.replace(/^"" /, '').trim();
|
82 | }
|
83 |
|
84 | return this.message;
|
85 | }
|
86 |
|
87 | _template(messages, code) {
|
88 |
|
89 | return exports.template(this.value, messages, code || this.code, this.state, this.prefs);
|
90 | }
|
91 | };
|
92 |
|
93 |
|
94 | exports.path = function (path) {
|
95 |
|
96 | let label = '';
|
97 | for (const segment of path) {
|
98 | if (typeof segment === 'object') {
|
99 | continue;
|
100 | }
|
101 |
|
102 | if (typeof segment === 'string') {
|
103 | if (label) {
|
104 | label += '.';
|
105 | }
|
106 |
|
107 | label += segment;
|
108 | }
|
109 | else {
|
110 | label += `[${segment}]`;
|
111 | }
|
112 | }
|
113 |
|
114 | return label;
|
115 | };
|
116 |
|
117 |
|
118 | exports.template = function (value, messages, code, state, prefs) {
|
119 |
|
120 | if (!messages) {
|
121 | return;
|
122 | }
|
123 |
|
124 | if (Template.isTemplate(messages)) {
|
125 | return code !== 'root' ? messages : null;
|
126 | }
|
127 |
|
128 | let lang = prefs.errors.language;
|
129 | if (Common.isResolvable(lang)) {
|
130 | lang = lang.resolve(value, state, prefs);
|
131 | }
|
132 |
|
133 | if (lang &&
|
134 | messages[lang] &&
|
135 | messages[lang][code] !== undefined) {
|
136 |
|
137 | return messages[lang][code];
|
138 | }
|
139 |
|
140 | return messages[code];
|
141 | };
|
142 |
|
143 |
|
144 | exports.label = function (flags, state, prefs, messages) {
|
145 |
|
146 | if (flags.label) {
|
147 | return flags.label;
|
148 | }
|
149 |
|
150 | if (!prefs.errors.label) {
|
151 | return '';
|
152 | }
|
153 |
|
154 | let path = state.path;
|
155 | if (prefs.errors.label === 'key' &&
|
156 | state.path.length > 1) {
|
157 |
|
158 | path = state.path.slice(-1);
|
159 | }
|
160 |
|
161 | const normalized = exports.path(path);
|
162 | if (normalized) {
|
163 | return normalized;
|
164 | }
|
165 |
|
166 | return exports.template(null, prefs.messages, 'root', state, prefs) ||
|
167 | messages && exports.template(null, messages, 'root', state, prefs) ||
|
168 | 'value';
|
169 | };
|
170 |
|
171 |
|
172 | exports.process = function (errors, original, prefs) {
|
173 |
|
174 | if (!errors) {
|
175 | return null;
|
176 | }
|
177 |
|
178 | const { override, message, details } = exports.details(errors);
|
179 | if (override) {
|
180 | return override;
|
181 | }
|
182 |
|
183 | if (prefs.errors.stack) {
|
184 | return new exports.ValidationError(message, details, original);
|
185 | }
|
186 |
|
187 | const limit = Error.stackTraceLimit;
|
188 | Error.stackTraceLimit = 0;
|
189 | const validationError = new exports.ValidationError(message, details, original);
|
190 | Error.stackTraceLimit = limit;
|
191 | return validationError;
|
192 | };
|
193 |
|
194 |
|
195 | exports.details = function (errors, options = {}) {
|
196 |
|
197 | let messages = [];
|
198 | const details = [];
|
199 |
|
200 | for (const item of errors) {
|
201 |
|
202 |
|
203 |
|
204 | if (item instanceof Error) {
|
205 | if (options.override !== false) {
|
206 | return { override: item };
|
207 | }
|
208 |
|
209 | const message = item.toString();
|
210 | messages.push(message);
|
211 |
|
212 | details.push({
|
213 | message,
|
214 | type: 'override',
|
215 | context: { error: item }
|
216 | });
|
217 |
|
218 | continue;
|
219 | }
|
220 |
|
221 |
|
222 |
|
223 | const message = item.toString();
|
224 | messages.push(message);
|
225 |
|
226 | details.push({
|
227 | message,
|
228 | path: item.path.filter((v) => typeof v !== 'object'),
|
229 | type: item.code,
|
230 | context: item.local
|
231 | });
|
232 | }
|
233 |
|
234 | if (messages.length > 1) {
|
235 | messages = [...new Set(messages)];
|
236 | }
|
237 |
|
238 | return { message: messages.join('. '), details };
|
239 | };
|
240 |
|
241 |
|
242 | exports.ValidationError = class extends Error {
|
243 |
|
244 | constructor(message, details, original) {
|
245 |
|
246 | super(message);
|
247 | this._original = original;
|
248 | this.details = details;
|
249 | }
|
250 |
|
251 | static isError(err) {
|
252 |
|
253 | return err instanceof exports.ValidationError;
|
254 | }
|
255 | };
|
256 |
|
257 |
|
258 | exports.ValidationError.prototype.isJoi = true;
|
259 |
|
260 | exports.ValidationError.prototype.name = 'ValidationError';
|
261 |
|
262 | exports.ValidationError.prototype.annotate = Annotate.error;
|