1aeb6a8d031b6791715e288c468efc33aec9217839d92c233b6e9569fa34d46936107517c39975da2661165ded74f0b9cbd5458ea6ac6ce2a1eeeeaab16146-exec 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. 'use strict';
  2. const Hoek = require('@hapi/hoek');
  3. const Cast = require('../../cast');
  4. const Settings = require('./settings');
  5. const Ref = require('../../ref');
  6. const Errors = require('../../errors');
  7. const State = require('../state');
  8. const Symbols = require('../symbols');
  9. const Pkg = require('../../../package.json');
  10. let Alternatives = null; // Delay-loaded to prevent circular dependencies
  11. let Schemas = null;
  12. const internals = {
  13. Set: require('../../set'),
  14. symbol: Symbol.for('@hapi/joi/schema')
  15. };
  16. internals.defaults = {
  17. abortEarly: true,
  18. convert: true,
  19. allowUnknown: false,
  20. skipFunctions: false,
  21. stripUnknown: false,
  22. language: {},
  23. presence: 'optional',
  24. noDefaults: false,
  25. escapeHtml: false
  26. // context: null
  27. };
  28. module.exports = internals.Any = class {
  29. constructor() {
  30. this.isJoi = true;
  31. this._type = 'any';
  32. this._settings = null;
  33. this._valids = new internals.Set();
  34. this._invalids = new internals.Set();
  35. this._tests = [];
  36. this._refs = [];
  37. this._flags = {
  38. /*
  39. presence: 'optional', // optional, required, forbidden, ignore
  40. allowOnly: false,
  41. allowUnknown: undefined,
  42. default: undefined,
  43. forbidden: false,
  44. encoding: undefined,
  45. insensitive: false,
  46. trim: false,
  47. normalize: undefined, // NFC, NFD, NFKC, NFKD
  48. case: undefined, // upper, lower
  49. empty: undefined,
  50. func: false,
  51. raw: false
  52. */
  53. };
  54. this._description = null;
  55. this._unit = null;
  56. this._notes = [];
  57. this._tags = [];
  58. this._examples = [];
  59. this._meta = [];
  60. this._inner = {}; // Hash of arrays of immutable objects
  61. }
  62. _init() {
  63. return this;
  64. }
  65. get schemaType() {
  66. return this._type;
  67. }
  68. createError(type, context, state, options, flags = this._flags) {
  69. return Errors.create(type, context, state, options, flags);
  70. }
  71. createOverrideError(type, context, state, options, message, template) {
  72. return Errors.create(type, context, state, options, this._flags, message, template);
  73. }
  74. checkOptions(options) {
  75. Schemas = Schemas || require('../../schemas');
  76. const result = Schemas.options.validate(options);
  77. if (result.error) {
  78. throw new Error(result.error.details[0].message);
  79. }
  80. }
  81. clone() {
  82. const obj = Object.create(Object.getPrototypeOf(this));
  83. obj.isJoi = true;
  84. obj._currentJoi = this._currentJoi;
  85. obj._type = this._type;
  86. obj._settings = this._settings;
  87. obj._baseType = this._baseType;
  88. obj._valids = this._valids.slice();
  89. obj._invalids = this._invalids.slice();
  90. obj._tests = this._tests.slice();
  91. obj._refs = this._refs.slice();
  92. obj._flags = Hoek.clone(this._flags);
  93. obj._description = this._description;
  94. obj._unit = this._unit;
  95. obj._notes = this._notes.slice();
  96. obj._tags = this._tags.slice();
  97. obj._examples = this._examples.slice();
  98. obj._meta = this._meta.slice();
  99. obj._inner = {};
  100. const inners = Object.keys(this._inner);
  101. for (let i = 0; i < inners.length; ++i) {
  102. const key = inners[i];
  103. obj._inner[key] = this._inner[key] ? this._inner[key].slice() : null;
  104. }
  105. return obj;
  106. }
  107. concat(schema) {
  108. Hoek.assert(schema instanceof internals.Any, 'Invalid schema object');
  109. Hoek.assert(this._type === 'any' || schema._type === 'any' || schema._type === this._type, 'Cannot merge type', this._type, 'with another type:', schema._type);
  110. let obj = this.clone();
  111. if (this._type === 'any' && schema._type !== 'any') {
  112. // Reset values as if we were "this"
  113. const tmpObj = schema.clone();
  114. const keysToRestore = ['_settings', '_valids', '_invalids', '_tests', '_refs', '_flags', '_description', '_unit',
  115. '_notes', '_tags', '_examples', '_meta', '_inner'];
  116. for (let i = 0; i < keysToRestore.length; ++i) {
  117. tmpObj[keysToRestore[i]] = obj[keysToRestore[i]];
  118. }
  119. obj = tmpObj;
  120. }
  121. obj._settings = obj._settings ? Settings.concat(obj._settings, schema._settings) : schema._settings;
  122. obj._valids.merge(schema._valids, schema._invalids);
  123. obj._invalids.merge(schema._invalids, schema._valids);
  124. obj._tests.push(...schema._tests);
  125. obj._refs.push(...schema._refs);
  126. if (obj._flags.empty && schema._flags.empty) {
  127. obj._flags.empty = obj._flags.empty.concat(schema._flags.empty);
  128. const flags = Object.assign({}, schema._flags);
  129. delete flags.empty;
  130. Hoek.merge(obj._flags, flags);
  131. }
  132. else if (schema._flags.empty) {
  133. obj._flags.empty = schema._flags.empty;
  134. const flags = Object.assign({}, schema._flags);
  135. delete flags.empty;
  136. Hoek.merge(obj._flags, flags);
  137. }
  138. else {
  139. Hoek.merge(obj._flags, schema._flags);
  140. }
  141. obj._description = schema._description || obj._description;
  142. obj._unit = schema._unit || obj._unit;
  143. obj._notes.push(...schema._notes);
  144. obj._tags.push(...schema._tags);
  145. obj._examples.push(...schema._examples);
  146. obj._meta.push(...schema._meta);
  147. const inners = Object.keys(schema._inner);
  148. const isObject = obj._type === 'object';
  149. for (let i = 0; i < inners.length; ++i) {
  150. const key = inners[i];
  151. const source = schema._inner[key];
  152. if (source) {
  153. const target = obj._inner[key];
  154. if (target) {
  155. if (isObject && key === 'children') {
  156. const keys = {};
  157. for (let j = 0; j < target.length; ++j) {
  158. keys[target[j].key] = j;
  159. }
  160. for (let j = 0; j < source.length; ++j) {
  161. const sourceKey = source[j].key;
  162. if (keys[sourceKey] >= 0) {
  163. target[keys[sourceKey]] = {
  164. key: sourceKey,
  165. schema: target[keys[sourceKey]].schema.concat(source[j].schema)
  166. };
  167. }
  168. else {
  169. target.push(source[j]);
  170. }
  171. }
  172. }
  173. else {
  174. obj._inner[key] = obj._inner[key].concat(source);
  175. }
  176. }
  177. else {
  178. obj._inner[key] = source.slice();
  179. }
  180. }
  181. }
  182. return obj;
  183. }
  184. _test(name, arg, func, options) {
  185. const obj = this.clone();
  186. obj._tests.push({ func, name, arg, options });
  187. return obj;
  188. }
  189. _testUnique(name, arg, func, options) {
  190. const obj = this.clone();
  191. obj._tests = obj._tests.filter((test) => test.name !== name);
  192. obj._tests.push({ func, name, arg, options });
  193. return obj;
  194. }
  195. options(options) {
  196. Hoek.assert(!options.context, 'Cannot override context');
  197. this.checkOptions(options);
  198. const obj = this.clone();
  199. obj._settings = Settings.concat(obj._settings, options);
  200. return obj;
  201. }
  202. strict(isStrict) {
  203. const obj = this.clone();
  204. const convert = isStrict === undefined ? false : !isStrict;
  205. obj._settings = Settings.concat(obj._settings, { convert });
  206. return obj;
  207. }
  208. raw(isRaw) {
  209. const value = isRaw === undefined ? true : isRaw;
  210. if (this._flags.raw === value) {
  211. return this;
  212. }
  213. const obj = this.clone();
  214. obj._flags.raw = value;
  215. return obj;
  216. }
  217. error(err, options = { self: false }) {
  218. Hoek.assert(err && (err instanceof Error || typeof err === 'function'), 'Must provide a valid Error object or a function');
  219. const unknownKeys = Object.keys(options).filter((k) => !['self'].includes(k));
  220. Hoek.assert(unknownKeys.length === 0, `Options ${unknownKeys} are unknown`);
  221. const obj = this.clone();
  222. obj._flags.error = err;
  223. if (options.self) {
  224. obj._flags.selfError = true;
  225. }
  226. return obj;
  227. }
  228. allow(...values) {
  229. const obj = this.clone();
  230. values = Hoek.flatten(values);
  231. for (let i = 0; i < values.length; ++i) {
  232. const value = values[i];
  233. Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');
  234. obj._invalids.remove(value);
  235. obj._valids.add(value, obj._refs);
  236. }
  237. return obj;
  238. }
  239. valid(...values) {
  240. const obj = this.allow(...values);
  241. obj._flags.allowOnly = true;
  242. return obj;
  243. }
  244. invalid(...values) {
  245. const obj = this.clone();
  246. values = Hoek.flatten(values);
  247. for (let i = 0; i < values.length; ++i) {
  248. const value = values[i];
  249. Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');
  250. obj._valids.remove(value);
  251. obj._invalids.add(value, obj._refs);
  252. }
  253. return obj;
  254. }
  255. required() {
  256. if (this._flags.presence === 'required') {
  257. return this;
  258. }
  259. const obj = this.clone();
  260. obj._flags.presence = 'required';
  261. return obj;
  262. }
  263. optional() {
  264. if (this._flags.presence === 'optional') {
  265. return this;
  266. }
  267. const obj = this.clone();
  268. obj._flags.presence = 'optional';
  269. return obj;
  270. }
  271. forbidden() {
  272. if (this._flags.presence === 'forbidden') {
  273. return this;
  274. }
  275. const obj = this.clone();
  276. obj._flags.presence = 'forbidden';
  277. return obj;
  278. }
  279. strip() {
  280. if (this._flags.strip) {
  281. return this;
  282. }
  283. const obj = this.clone();
  284. obj._flags.strip = true;
  285. return obj;
  286. }
  287. applyFunctionToChildren(children, fn, args = [], root) {
  288. children = [].concat(children);
  289. if (children.length !== 1 || children[0] !== '') {
  290. root = root ? (root + '.') : '';
  291. const extraChildren = (children[0] === '' ? children.slice(1) : children).map((child) => {
  292. return root + child;
  293. });
  294. throw new Error('unknown key(s) ' + extraChildren.join(', '));
  295. }
  296. return this[fn](...args);
  297. }
  298. default(value, description) {
  299. if (typeof value === 'function' &&
  300. !Ref.isRef(value)) {
  301. if (!value.description &&
  302. description) {
  303. value.description = description;
  304. }
  305. if (!this._flags.func) {
  306. Hoek.assert(typeof value.description === 'string' && value.description.length > 0, 'description must be provided when default value is a function');
  307. }
  308. }
  309. const obj = this.clone();
  310. obj._flags.default = value;
  311. Ref.push(obj._refs, value);
  312. return obj;
  313. }
  314. empty(schema) {
  315. const obj = this.clone();
  316. if (schema === undefined) {
  317. delete obj._flags.empty;
  318. }
  319. else {
  320. obj._flags.empty = Cast.schema(this._currentJoi, schema);
  321. }
  322. return obj;
  323. }
  324. when(condition, options) {
  325. Hoek.assert(options && typeof options === 'object', 'Invalid options');
  326. Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"');
  327. const then = options.hasOwnProperty('then') ? this.concat(Cast.schema(this._currentJoi, options.then)) : undefined;
  328. const otherwise = options.hasOwnProperty('otherwise') ? this.concat(Cast.schema(this._currentJoi, options.otherwise)) : undefined;
  329. Alternatives = Alternatives || require('../alternatives');
  330. const alternativeOptions = { then, otherwise };
  331. if (Object.prototype.hasOwnProperty.call(options, 'is')) {
  332. alternativeOptions.is = options.is;
  333. }
  334. const obj = Alternatives.when(condition, alternativeOptions);
  335. obj._flags.presence = 'ignore';
  336. obj._baseType = this;
  337. return obj;
  338. }
  339. description(desc) {
  340. Hoek.assert(desc && typeof desc === 'string', 'Description must be a non-empty string');
  341. const obj = this.clone();
  342. obj._description = desc;
  343. return obj;
  344. }
  345. notes(notes) {
  346. Hoek.assert(notes && (typeof notes === 'string' || Array.isArray(notes)), 'Notes must be a non-empty string or array');
  347. const obj = this.clone();
  348. obj._notes = obj._notes.concat(notes);
  349. return obj;
  350. }
  351. tags(tags) {
  352. Hoek.assert(tags && (typeof tags === 'string' || Array.isArray(tags)), 'Tags must be a non-empty string or array');
  353. const obj = this.clone();
  354. obj._tags = obj._tags.concat(tags);
  355. return obj;
  356. }
  357. meta(meta) {
  358. Hoek.assert(meta !== undefined, 'Meta cannot be undefined');
  359. const obj = this.clone();
  360. obj._meta = obj._meta.concat(meta);
  361. return obj;
  362. }
  363. example(...examples) {
  364. Hoek.assert(examples.length > 0, 'Missing examples');
  365. const processed = [];
  366. for (let i = 0; i < examples.length; ++i) {
  367. const example = [].concat(examples[i]);
  368. Hoek.assert(example.length <= 2, `Bad example format at index ${i}`);
  369. const value = example[0];
  370. let options = example[1];
  371. if (options !== undefined) {
  372. Hoek.assert(options && typeof options === 'object', `Options for example at index ${i} must be an object`);
  373. const unknownOptions = Object.keys(options).filter((option) => !['parent', 'context'].includes(option));
  374. Hoek.assert(unknownOptions.length === 0, `Unknown example options ${unknownOptions} at index ${i}`);
  375. }
  376. else {
  377. options = {};
  378. }
  379. const localState = new State('', [], options.parent || null);
  380. const result = this._validate(value, localState, Settings.concat(internals.defaults, options.context ? { context: options.context } : null));
  381. Hoek.assert(!result.errors, `Bad example at index ${i}:`, result.errors && Errors.process(result.errors, value));
  382. const ex = { value };
  383. if (Object.keys(options).length) {
  384. ex.options = options;
  385. }
  386. processed.push(ex);
  387. }
  388. const obj = this.clone();
  389. obj._examples = processed;
  390. return obj;
  391. }
  392. unit(name) {
  393. Hoek.assert(name && typeof name === 'string', 'Unit name must be a non-empty string');
  394. const obj = this.clone();
  395. obj._unit = name;
  396. return obj;
  397. }
  398. _prepareEmptyValue(value) {
  399. if (typeof value === 'string' && this._flags.trim) {
  400. return value.trim();
  401. }
  402. return value;
  403. }
  404. _validate(value, state, options, reference) {
  405. const originalValue = value;
  406. // Setup state and settings
  407. state = state || new State('', [], null, reference);
  408. if (this._settings) {
  409. const isDefaultOptions = options === internals.defaults;
  410. if (isDefaultOptions && this._settings[Symbols.settingsCache]) {
  411. options = this._settings[Symbols.settingsCache];
  412. }
  413. else {
  414. options = Settings.concat(this._language ? Settings.concat({ language: this._language }, options) : options, this._settings);
  415. if (isDefaultOptions) {
  416. this._settings[Symbols.settingsCache] = options;
  417. }
  418. }
  419. }
  420. else if (this._language) {
  421. options = Settings.concat({ language: this._language }, options);
  422. }
  423. let errors = [];
  424. if (this._coerce) {
  425. const coerced = this._coerce(value, state, options);
  426. if (coerced.errors) {
  427. value = coerced.value;
  428. errors = errors.concat(coerced.errors);
  429. return this._finalizeValue(value, originalValue, errors, state, options); // Coerced error always aborts early
  430. }
  431. value = coerced.value;
  432. }
  433. if (this._flags.empty && !this._flags.empty._validate(this._prepareEmptyValue(value), null, internals.defaults).errors) {
  434. value = undefined;
  435. }
  436. // Check presence requirements
  437. const presence = this._flags.presence || options.presence;
  438. if (presence === 'optional') {
  439. if (value === undefined) {
  440. const isDeepDefault = this._flags.hasOwnProperty('default') && this._flags.default === undefined;
  441. if (isDeepDefault && this._type === 'object') {
  442. value = {};
  443. }
  444. else {
  445. return this._finalizeValue(value, originalValue, errors, state, options);
  446. }
  447. }
  448. }
  449. else if (presence === 'required' &&
  450. value === undefined) {
  451. errors.push(this.createError('any.required', null, state, options));
  452. return this._finalizeValue(value, originalValue, errors, state, options);
  453. }
  454. else if (presence === 'forbidden') {
  455. if (value === undefined) {
  456. return this._finalizeValue(value, originalValue, errors, state, options);
  457. }
  458. errors.push(this.createError('any.unknown', null, state, options));
  459. return this._finalizeValue(value, originalValue, errors, state, options);
  460. }
  461. // Check allowed and denied values using the original value
  462. let match = this._valids.get(value, state, options, this._flags.insensitive);
  463. if (match) {
  464. if (options.convert) {
  465. value = match.value;
  466. }
  467. return this._finalizeValue(value, originalValue, errors, state, options);
  468. }
  469. if (this._invalids.has(value, state, options, this._flags.insensitive)) {
  470. errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));
  471. if (options.abortEarly) {
  472. return this._finalizeValue(value, originalValue, errors, state, options);
  473. }
  474. }
  475. // Convert value and validate type
  476. if (this._base) {
  477. const base = this._base(value, state, options);
  478. if (base.errors) {
  479. value = base.value;
  480. errors = errors.concat(base.errors);
  481. return this._finalizeValue(value, originalValue, errors, state, options); // Base error always aborts early
  482. }
  483. if (base.value !== value) {
  484. value = base.value;
  485. // Check allowed and denied values using the converted value
  486. match = this._valids.get(value, state, options, this._flags.insensitive);
  487. if (match) {
  488. value = match.value;
  489. return this._finalizeValue(value, originalValue, errors, state, options);
  490. }
  491. if (this._invalids.has(value, state, options, this._flags.insensitive)) {
  492. errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));
  493. if (options.abortEarly) {
  494. return this._finalizeValue(value, originalValue, errors, state, options);
  495. }
  496. }
  497. }
  498. }
  499. // Required values did not match
  500. if (this._flags.allowOnly) {
  501. errors.push(this.createError('any.allowOnly', { value, valids: this._valids.values({ stripUndefined: true }) }, state, options));
  502. if (options.abortEarly) {
  503. return this._finalizeValue(value, originalValue, errors, state, options);
  504. }
  505. }
  506. // Validate tests
  507. for (let i = 0; i < this._tests.length; ++i) {
  508. const test = this._tests[i];
  509. const ret = test.func.call(this, value, state, options);
  510. if (ret instanceof Errors.Err) {
  511. errors.push(ret);
  512. if (options.abortEarly) {
  513. return this._finalizeValue(value, originalValue, errors, state, options);
  514. }
  515. }
  516. else {
  517. value = ret;
  518. }
  519. }
  520. return this._finalizeValue(value, originalValue, errors, state, options);
  521. }
  522. _finalizeValue(value, originalValue, errors, state, options) {
  523. let finalValue;
  524. if (value !== undefined) {
  525. finalValue = this._flags.raw ? originalValue : value;
  526. }
  527. else if (options.noDefaults) {
  528. finalValue = value;
  529. }
  530. else if (Ref.isRef(this._flags.default)) {
  531. finalValue = this._flags.default(state.parent, options);
  532. }
  533. else if (typeof this._flags.default === 'function' &&
  534. !(this._flags.func && !this._flags.default.description)) {
  535. let args;
  536. if (state.parent !== null &&
  537. this._flags.default.length > 0) {
  538. args = [Hoek.clone(state.parent), options];
  539. }
  540. const defaultValue = internals._try(this._flags.default, args);
  541. finalValue = defaultValue.value;
  542. if (defaultValue.error) {
  543. errors.push(this.createError('any.default', { error: defaultValue.error }, state, options));
  544. }
  545. }
  546. else {
  547. finalValue = Hoek.clone(this._flags.default);
  548. }
  549. if (errors.length &&
  550. typeof this._flags.error === 'function' &&
  551. (
  552. !this._flags.selfError ||
  553. errors.some((e) => state.path.length === e.path.length)
  554. )
  555. ) {
  556. const change = this._flags.error.call(this, errors);
  557. if (typeof change === 'string') {
  558. errors = [this.createOverrideError('override', { reason: errors }, state, options, change)];
  559. }
  560. else {
  561. errors = [].concat(change)
  562. .map((err) => {
  563. return err instanceof Error ?
  564. err :
  565. this.createOverrideError(err.type || 'override', err.context, state, options, err.message, err.template);
  566. });
  567. }
  568. }
  569. return {
  570. value: this._flags.strip ? undefined : finalValue,
  571. finalValue,
  572. errors: errors.length ? errors : null
  573. };
  574. }
  575. _validateWithOptions(value, options, callback) {
  576. if (options) {
  577. this.checkOptions(options);
  578. }
  579. const settings = Settings.concat(internals.defaults, options);
  580. const result = this._validate(value, null, settings);
  581. const errors = Errors.process(result.errors, value);
  582. if (callback) {
  583. return callback(errors, result.value);
  584. }
  585. return {
  586. error: errors,
  587. value: result.value,
  588. then(resolve, reject) {
  589. if (errors) {
  590. return Promise.reject(errors).catch(reject);
  591. }
  592. return Promise.resolve(result.value).then(resolve);
  593. },
  594. catch(reject) {
  595. if (errors) {
  596. return Promise.reject(errors).catch(reject);
  597. }
  598. return Promise.resolve(result.value);
  599. }
  600. };
  601. }
  602. validate(value, options, callback) {
  603. if (typeof options === 'function') {
  604. return this._validateWithOptions(value, null, options);
  605. }
  606. return this._validateWithOptions(value, options, callback);
  607. }
  608. describe() {
  609. const description = {
  610. type: this._type
  611. };
  612. const flags = Object.keys(this._flags);
  613. if (flags.length) {
  614. if (['empty', 'default', 'lazy', 'label'].some((flag) => this._flags.hasOwnProperty(flag))) {
  615. description.flags = {};
  616. for (let i = 0; i < flags.length; ++i) {
  617. const flag = flags[i];
  618. if (flag === 'empty') {
  619. description.flags[flag] = this._flags[flag].describe();
  620. }
  621. else if (flag === 'default') {
  622. if (Ref.isRef(this._flags[flag])) {
  623. description.flags[flag] = this._flags[flag].toString();
  624. }
  625. else if (typeof this._flags[flag] === 'function') {
  626. description.flags[flag] = {
  627. description: this._flags[flag].description,
  628. function : this._flags[flag]
  629. };
  630. }
  631. else {
  632. description.flags[flag] = this._flags[flag];
  633. }
  634. }
  635. else if (flag === 'lazy' || flag === 'label') {
  636. // We don't want it in the description
  637. }
  638. else {
  639. description.flags[flag] = this._flags[flag];
  640. }
  641. }
  642. }
  643. else {
  644. description.flags = this._flags;
  645. }
  646. }
  647. if (this._settings) {
  648. description.options = Hoek.clone(this._settings);
  649. }
  650. if (this._baseType) {
  651. description.base = this._baseType.describe();
  652. }
  653. if (this._description) {
  654. description.description = this._description;
  655. }
  656. if (this._notes.length) {
  657. description.notes = this._notes;
  658. }
  659. if (this._tags.length) {
  660. description.tags = this._tags;
  661. }
  662. if (this._meta.length) {
  663. description.meta = this._meta;
  664. }
  665. if (this._examples.length) {
  666. description.examples = this._examples;
  667. }
  668. if (this._unit) {
  669. description.unit = this._unit;
  670. }
  671. const valids = this._valids.values();
  672. if (valids.length) {
  673. description.valids = valids.map((v) => {
  674. return Ref.isRef(v) ? v.toString() : v;
  675. });
  676. }
  677. const invalids = this._invalids.values();
  678. if (invalids.length) {
  679. description.invalids = invalids.map((v) => {
  680. return Ref.isRef(v) ? v.toString() : v;
  681. });
  682. }
  683. description.rules = [];
  684. for (let i = 0; i < this._tests.length; ++i) {
  685. const validator = this._tests[i];
  686. const item = { name: validator.name };
  687. if (validator.arg !== void 0) {
  688. item.arg = Ref.isRef(validator.arg) ? validator.arg.toString() : validator.arg;
  689. }
  690. const options = validator.options;
  691. if (options) {
  692. if (options.hasRef) {
  693. item.arg = {};
  694. const keys = Object.keys(validator.arg);
  695. for (let j = 0; j < keys.length; ++j) {
  696. const key = keys[j];
  697. const value = validator.arg[key];
  698. item.arg[key] = Ref.isRef(value) ? value.toString() : value;
  699. }
  700. }
  701. if (typeof options.description === 'string') {
  702. item.description = options.description;
  703. }
  704. else if (typeof options.description === 'function') {
  705. item.description = options.description(item.arg);
  706. }
  707. }
  708. description.rules.push(item);
  709. }
  710. if (!description.rules.length) {
  711. delete description.rules;
  712. }
  713. const label = this._getLabel();
  714. if (label) {
  715. description.label = label;
  716. }
  717. return description;
  718. }
  719. label(name) {
  720. Hoek.assert(name && typeof name === 'string', 'Label name must be a non-empty string');
  721. const obj = this.clone();
  722. obj._flags.label = name;
  723. return obj;
  724. }
  725. _getLabel(def) {
  726. return this._flags.label || def;
  727. }
  728. };
  729. internals.Any.prototype.isImmutable = true; // Prevents Hoek from deep cloning schema objects
  730. // Aliases
  731. internals.Any.prototype.only = internals.Any.prototype.equal = internals.Any.prototype.valid;
  732. internals.Any.prototype.disallow = internals.Any.prototype.not = internals.Any.prototype.invalid;
  733. internals.Any.prototype.exist = internals.Any.prototype.required;
  734. internals.Any.prototype[internals.symbol] = {
  735. version: Pkg.version,
  736. compile: Cast.schema,
  737. root: '_currentJoi'
  738. };
  739. internals._try = function (fn, args = []) {
  740. let err;
  741. let result;
  742. try {
  743. result = fn(...args);
  744. }
  745. catch (e) {
  746. err = e;
  747. }
  748. return {
  749. value: result,
  750. error: err
  751. };
  752. };