arnaucube
arnaucube/thoughtsPublic Self-hosted repository
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};