thoughts/node_modules/jsonwebtoken/node_modules/joi/lib/errors.js
node_modules/jsonwebtoken/node_modules/joi/lib/errors.jsRaw
1// Load modules
2
3var Hoek = require('hoek');
4var Language = require('./language');
5
6
7// Declare internals
8
9var internals = {};
10
11internals.stringify = function (value, wrapArrays) {
12
13 var type = typeof value;
14
15 if (value === null) {
16 return 'null';
17 }
18
19 if (type === 'string') {
20 return value;
21 }
22
23 if (value instanceof internals.Err || type === 'function') {
24 return value.toString();
25 }
26
27 if (type === 'object') {
28 if (Array.isArray(value)) {
29 var partial = '';
30
31 for (var i = 0, il = value.length; i < il; ++i) {
32 partial += (partial.length ? ', ' : '') + internals.stringify(value[i], wrapArrays);
33 }
34
35 return wrapArrays ? '[' + partial + ']' : partial;
36 }
37
38 return value.toString();
39 }
40
41 return JSON.stringify(value);
42};
43
44internals.Err = function (type, context, state, options) {
45
46 this.type = type;
47 this.context = context || {};
48 this.context.key = state.key;
49 this.path = state.path;
50 this.options = options;
51};
52
53
54internals.Err.prototype.toString = function () {
55
56 var self = this;
57
58 var localized = this.options.language;
59
60 if (localized.label) {
61 this.context.key = localized.label;
62 }
63 else if (this.context.key === '' || this.context.key === null) {
64 this.context.key = localized.root || Language.errors.root;
65 }
66
67 var format = Hoek.reach(localized, this.type) || Hoek.reach(Language.errors, this.type);
68 var hasKey = /\{\{\!?key\}\}/.test(format);
69 var skipKey = format.length > 2 && format[0] === '!' && format[1] === '!';
70
71 if (skipKey) {
72 format = format.slice(2);
73 }
74
75 if (!hasKey && !skipKey) {
76 format = (Hoek.reach(localized, 'key') || Hoek.reach(Language.errors, 'key')) + format;
77 }
78
79 var wrapArrays = Hoek.reach(localized, 'messages.wrapArrays');
80 if (typeof wrapArrays !== 'boolean') {
81 wrapArrays = Language.errors.messages.wrapArrays;
82 }
83
84 var message = format.replace(/\{\{(\!?)([^}]+)\}\}/g, function ($0, isSecure, name) {
85
86 var value = Hoek.reach(self.context, name);
87 var normalized = internals.stringify(value, wrapArrays);
88 return (isSecure ? Hoek.escapeHtml(normalized) : normalized);
89 });
90
91 return message;
92};
93
94
95exports.create = function (type, context, state, options) {
96
97 return new internals.Err(type, context, state, options);
98};
99
100
101exports.process = function (errors, object) {
102
103 if (!errors || !errors.length) {
104 return null;
105 }
106
107 // Construct error
108
109 var message = '';
110 var details = [];
111
112 var processErrors = function (localErrors, parent) {
113
114 for (var i = 0, il = localErrors.length; i < il; ++i) {
115 var item = localErrors[i];
116
117 var detail = {
118 message: item.toString(),
119 path: internals.getPath(item),
120 type: item.type,
121 context: item.context
122 };
123
124 if (!parent) {
125 message += (message ? '. ' : '') + detail.message;
126 }
127
128 // Do not push intermediate errors, we're only interested in leafs
129 if (item.context.reason && item.context.reason.length) {
130 processErrors(item.context.reason, item.path);
131 }
132 else {
133 details.push(detail);
134 }
135 }
136 };
137
138 processErrors(errors);
139
140 var error = new Error(message);
141 error.name = 'ValidationError';
142 error.details = details;
143 error._object = object;
144 error.annotate = internals.annotate;
145 return error;
146};
147
148
149internals.getPath = function (item) {
150
151 var recursePath = function (it) {
152
153 var reachedItem = Hoek.reach(it, 'context.reason.0');
154 if (reachedItem && reachedItem.context) {
155 return recursePath(reachedItem);
156 }
157
158 return it.path;
159 };
160
161 return recursePath(item) || item.context.key;
162};
163
164
165// Inspired by json-stringify-safe
166internals.safeStringify = function (obj, spaces) {
167
168 return JSON.stringify(obj, internals.serializer(), spaces);
169};
170
171internals.serializer = function () {
172
173 var cycleReplacer = function (key, value) {
174
175 if (stack[0] === value) {
176 return '[Circular ~]';
177 }
178
179 return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']';
180 };
181
182 var keys = [], stack = [];
183
184 return function (key, value) {
185
186 if (stack.length > 0) {
187 var thisPos = stack.indexOf(this);
188 if (~thisPos) {
189 stack.length = thisPos + 1;
190 keys.length = thisPos + 1;
191 keys[thisPos] = key;
192 }
193 else {
194 stack.push(this);
195 keys.push(key);
196 }
197
198 if (~stack.indexOf(value)) {
199 value = cycleReplacer.call(this, key, value);
200 }
201 }
202 else {
203 stack.push(value);
204 }
205
206 if (Array.isArray(value) && value.placeholders) {
207 var placeholders = value.placeholders;
208 var arrWithPlaceholders = [];
209 for (var i = 0, il = value.length; i < il; ++i) {
210 if (placeholders[i]) {
211 arrWithPlaceholders.push(placeholders[i]);
212 }
213 arrWithPlaceholders.push(value[i]);
214 }
215
216 value = arrWithPlaceholders;
217 }
218
219 return value;
220 };
221};
222
223
224internals.annotate = function () {
225
226 var obj = Hoek.clone(this._object || {});
227
228 var lookup = {};
229 var el = this.details.length;
230 for (var e = el - 1; e >= 0; --e) { // Reverse order to process deepest child first
231 var pos = el - e;
232 var error = this.details[e];
233 var path = error.path.split('.');
234 var ref = obj;
235 for (var i = 0, il = path.length; i < il && ref; ++i) {
236 var seg = path[i];
237 if (i + 1 < il) {
238 ref = ref[seg];
239 }
240 else {
241 var value = ref[seg];
242 if (Array.isArray(ref)) {
243 var arrayLabel = '_$idx$_' + (e + 1) + '_$end$_';
244 if (!ref.placeholders) {
245 ref.placeholders = {};
246 }
247
248 if (ref.placeholders[seg]) {
249 ref.placeholders[seg] = ref.placeholders[seg].replace('_$end$_', ', ' + (e + 1) + '_$end$_');
250 }
251 else {
252 ref.placeholders[seg] = arrayLabel;
253 }
254 } else {
255 if (value !== undefined) {
256 delete ref[seg];
257 var objectLabel = seg + '_$key$_' + pos + '_$end$_';
258 ref[objectLabel] = value;
259 lookup[error.path] = objectLabel;
260 }
261 else if (lookup[error.path]) {
262 var replacement = lookup[error.path];
263 var appended = replacement.replace('_$end$_', ', ' + pos + '_$end$_');
264 ref[appended] = ref[replacement];
265 lookup[error.path] = appended;
266 delete ref[replacement];
267 }
268 else {
269 ref['_$miss$_' + seg + '|' + pos + '_$end$_'] = '__missing__';
270 }
271 }
272 }
273 }
274 }
275
276 var message = internals.safeStringify(obj, 2)
277 .replace(/_\$key\$_([, \d]+)_\$end\$_\"/g, function ($0, $1) {
278
279 return '" \u001b[31m[' + $1 + ']\u001b[0m';
280 }).replace(/\"_\$miss\$_([^\|]+)\|(\d+)_\$end\$_\"\: \"__missing__\"/g, function ($0, $1, $2) {
281
282 return '\u001b[41m"' + $1 + '"\u001b[0m\u001b[31m [' + $2 + ']: -- missing --\u001b[0m';
283 }).replace(/\s*\"_\$idx\$_([, \d]+)_\$end\$_\",?\n(.*)/g, function ($0, $1, $2) {
284
285 return '\n' + $2 + ' \u001b[31m[' + $1 + ']\u001b[0m';
286 });
287
288 message += '\n\u001b[31m';
289
290 for (e = 0; e < el; ++e) {
291 message += '\n[' + (e + 1) + '] ' + this.details[e].message;
292 }
293
294 message += '\u001b[0m';
295
296 return message;
297};