'use strict'; const Annotate = require('./annotate'); const Common = require('./common'); const Template = require('./template'); const internals = {}; exports.Report = class { constructor(code, value, local, flags, messages, state, prefs) { this.code = code; this.flags = flags; this.messages = messages; this.path = state.path; this.prefs = prefs; this.state = state; this.value = value; this.message = null; this.template = null; this.local = local || {}; this.local.label = exports.label(this.flags, this.state, this.prefs, this.messages); if (this.value !== undefined && !this.local.hasOwnProperty('value')) { this.local.value = this.value; } if (this.path.length) { const key = this.path[this.path.length - 1]; if (typeof key !== 'object') { this.local.key = key; } } } _setTemplate(template) { this.template = template; if (!this.flags.label && this.path.length === 0) { const localized = this._template(this.template, 'root'); if (localized) { this.local.label = localized; } } } toString() { if (this.message) { return this.message; } const code = this.code; if (!this.prefs.errors.render) { return this.code; } const template = this._template(this.template) || this._template(this.prefs.messages) || this._template(this.messages); if (template === undefined) { return `Error code "${code}" is not defined, your custom type is missing the correct messages definition`; } // Render and cache result this.message = template.render(this.value, this.state, this.prefs, this.local, { errors: this.prefs.errors, messages: [this.prefs.messages, this.messages] }); if (!this.prefs.errors.label) { this.message = this.message.replace(/^"" /, '').trim(); } return this.message; } _template(messages, code) { return exports.template(this.value, messages, code || this.code, this.state, this.prefs); } }; exports.path = function (path) { let label = ''; for (const segment of path) { if (typeof segment === 'object') { // Exclude array single path segment continue; } if (typeof segment === 'string') { if (label) { label += '.'; } label += segment; } else { label += `[${segment}]`; } } return label; }; exports.template = function (value, messages, code, state, prefs) { if (!messages) { return; } if (Template.isTemplate(messages)) { return code !== 'root' ? messages : null; } let lang = prefs.errors.language; if (Common.isResolvable(lang)) { lang = lang.resolve(value, state, prefs); } if (lang && messages[lang]) { if (messages[lang][code] !== undefined) { return messages[lang][code]; } if (messages[lang]['*'] !== undefined) { return messages[lang]['*']; } } if (!messages[code]) { return messages['*']; } return messages[code]; }; exports.label = function (flags, state, prefs, messages) { if (!prefs.errors.label) { return ''; } if (flags.label) { return flags.label; } let path = state.path; if (prefs.errors.label === 'key' && state.path.length > 1) { path = state.path.slice(-1); } const normalized = exports.path(path); if (normalized) { return normalized; } return exports.template(null, prefs.messages, 'root', state, prefs) || messages && exports.template(null, messages, 'root', state, prefs) || 'value'; }; exports.process = function (errors, original, prefs) { if (!errors) { return null; } const { override, message, details } = exports.details(errors); if (override) { return override; } if (prefs.errors.stack) { return new exports.ValidationError(message, details, original); } const limit = Error.stackTraceLimit; Error.stackTraceLimit = 0; const validationError = new exports.ValidationError(message, details, original); Error.stackTraceLimit = limit; return validationError; }; exports.details = function (errors, options = {}) { let messages = []; const details = []; for (const item of errors) { // Override if (item instanceof Error) { if (options.override !== false) { return { override: item }; } const message = item.toString(); messages.push(message); details.push({ message, type: 'override', context: { error: item } }); continue; } // Report const message = item.toString(); messages.push(message); details.push({ message, path: item.path.filter((v) => typeof v !== 'object'), type: item.code, context: item.local }); } if (messages.length > 1) { messages = [...new Set(messages)]; } return { message: messages.join('. '), details }; }; exports.ValidationError = class extends Error { constructor(message, details, original) { super(message); this._original = original; this.details = details; } static isError(err) { return err instanceof exports.ValidationError; } }; exports.ValidationError.prototype.isJoi = true; exports.ValidationError.prototype.name = 'ValidationError'; exports.ValidationError.prototype.annotate = Annotate.error;