123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- 'use strict';
- const Clone = require('@hapi/hoek/lib/clone');
- const Common = require('./common');
- const internals = {
- annotations: Symbol('annotations')
- };
- exports.error = function (stripColorCodes) {
- if (!this._original ||
- typeof this._original !== 'object') {
- return this.details[0].message;
- }
- const redFgEscape = stripColorCodes ? '' : '\u001b[31m';
- const redBgEscape = stripColorCodes ? '' : '\u001b[41m';
- const endColor = stripColorCodes ? '' : '\u001b[0m';
- const obj = Clone(this._original);
- for (let i = this.details.length - 1; i >= 0; --i) { // Reverse order to process deepest child first
- const pos = i + 1;
- const error = this.details[i];
- const path = error.path;
- let node = obj;
- for (let j = 0; ; ++j) {
- const seg = path[j];
- if (Common.isSchema(node)) {
- node = node.clone(); // joi schemas are not cloned by hoek, we have to take this extra step
- }
- if (j + 1 < path.length &&
- typeof node[seg] !== 'string') {
- node = node[seg];
- }
- else {
- const refAnnotations = node[internals.annotations] || { errors: {}, missing: {} };
- node[internals.annotations] = refAnnotations;
- const cacheKey = seg || error.context.key;
- if (node[seg] !== undefined) {
- refAnnotations.errors[cacheKey] = refAnnotations.errors[cacheKey] || [];
- refAnnotations.errors[cacheKey].push(pos);
- }
- else {
- refAnnotations.missing[cacheKey] = pos;
- }
- break;
- }
- }
- }
- const replacers = {
- key: /_\$key\$_([, \d]+)_\$end\$_"/g,
- missing: /"_\$miss\$_([^|]+)\|(\d+)_\$end\$_": "__missing__"/g,
- arrayIndex: /\s*"_\$idx\$_([, \d]+)_\$end\$_",?\n(.*)/g,
- specials: /"\[(NaN|Symbol.*|-?Infinity|function.*|\(.*)]"/g
- };
- let message = internals.safeStringify(obj, 2)
- .replace(replacers.key, ($0, $1) => `" ${redFgEscape}[${$1}]${endColor}`)
- .replace(replacers.missing, ($0, $1, $2) => `${redBgEscape}"${$1}"${endColor}${redFgEscape} [${$2}]: -- missing --${endColor}`)
- .replace(replacers.arrayIndex, ($0, $1, $2) => `\n${$2} ${redFgEscape}[${$1}]${endColor}`)
- .replace(replacers.specials, ($0, $1) => $1);
- message = `${message}\n${redFgEscape}`;
- for (let i = 0; i < this.details.length; ++i) {
- const pos = i + 1;
- message = `${message}\n[${pos}] ${this.details[i].message}`;
- }
- message = message + endColor;
- return message;
- };
- // Inspired by json-stringify-safe
- internals.safeStringify = function (obj, spaces) {
- return JSON.stringify(obj, internals.serializer(), spaces);
- };
- internals.serializer = function () {
- const keys = [];
- const stack = [];
- const cycleReplacer = (key, value) => {
- if (stack[0] === value) {
- return '[Circular ~]';
- }
- return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']';
- };
- return function (key, value) {
- if (stack.length > 0) {
- const thisPos = stack.indexOf(this);
- if (~thisPos) {
- stack.length = thisPos + 1;
- keys.length = thisPos + 1;
- keys[thisPos] = key;
- }
- else {
- stack.push(this);
- keys.push(key);
- }
- if (~stack.indexOf(value)) {
- value = cycleReplacer.call(this, key, value);
- }
- }
- else {
- stack.push(value);
- }
- if (value) {
- const annotations = value[internals.annotations];
- if (annotations) {
- if (Array.isArray(value)) {
- const annotated = [];
- for (let i = 0; i < value.length; ++i) {
- if (annotations.errors[i]) {
- annotated.push(`_$idx$_${annotations.errors[i].sort().join(', ')}_$end$_`);
- }
- annotated.push(value[i]);
- }
- value = annotated;
- }
- else {
- for (const errorKey in annotations.errors) {
- value[`${errorKey}_$key$_${annotations.errors[errorKey].sort().join(', ')}_$end$_`] = value[errorKey];
- value[errorKey] = undefined;
- }
- for (const missingKey in annotations.missing) {
- value[`_$miss$_${missingKey}|${annotations.missing[missingKey]}_$end$_`] = '__missing__';
- }
- }
- return value;
- }
- }
- if (value === Infinity ||
- value === -Infinity ||
- Number.isNaN(value) ||
- typeof value === 'function' ||
- typeof value === 'symbol') {
- return '[' + value.toString() + ']';
- }
- return value;
- };
- };
|