async-validator.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
  1. function _extends() {
  2. _extends = Object.assign || function (target) {
  3. for (let i = 1; i < arguments.length; i++) {
  4. const source = arguments[i]
  5. for (const key in source) {
  6. if (Object.prototype.hasOwnProperty.call(source, key)) {
  7. target[key] = source[key]
  8. }
  9. }
  10. }
  11. return target
  12. }
  13. return _extends.apply(this, arguments)
  14. }
  15. /* eslint no-console:0 */
  16. const formatRegExp = /%[sdj%]/g
  17. let warning = function warning() {} // don't print warning message when in production env or node runtime
  18. if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window
  19. !== 'undefined' && typeof document !== 'undefined') {
  20. warning = function warning(type, errors) {
  21. if (typeof console !== 'undefined' && console.warn) {
  22. if (errors.every((e) => typeof e === 'string')) {
  23. console.warn(type, errors)
  24. }
  25. }
  26. }
  27. }
  28. function convertFieldsError(errors) {
  29. if (!errors || !errors.length) return null
  30. const fields = {}
  31. errors.forEach((error) => {
  32. const { field } = error
  33. fields[field] = fields[field] || []
  34. fields[field].push(error)
  35. })
  36. return fields
  37. }
  38. function format() {
  39. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  40. args[_key] = arguments[_key]
  41. }
  42. let i = 1
  43. const f = args[0]
  44. const len = args.length
  45. if (typeof f === 'function') {
  46. return f.apply(null, args.slice(1))
  47. }
  48. if (typeof f === 'string') {
  49. let str = String(f).replace(formatRegExp, (x) => {
  50. if (x === '%%') {
  51. return '%'
  52. }
  53. if (i >= len) {
  54. return x
  55. }
  56. switch (x) {
  57. case '%s':
  58. return String(args[i++])
  59. case '%d':
  60. return Number(args[i++])
  61. case '%j':
  62. try {
  63. return JSON.stringify(args[i++])
  64. } catch (_) {
  65. return '[Circular]'
  66. }
  67. break
  68. default:
  69. return x
  70. }
  71. })
  72. for (let arg = args[i]; i < len; arg = args[++i]) {
  73. str += ` ${arg}`
  74. }
  75. return str
  76. }
  77. return f
  78. }
  79. function isNativeStringType(type) {
  80. return type === 'string' || type === 'url' || type === 'hex' || type === 'email' || type === 'pattern'
  81. }
  82. function isEmptyValue(value, type) {
  83. if (value === undefined || value === null) {
  84. return true
  85. }
  86. if (type === 'array' && Array.isArray(value) && !value.length) {
  87. return true
  88. }
  89. if (isNativeStringType(type) && typeof value === 'string' && !value) {
  90. return true
  91. }
  92. return false
  93. }
  94. function asyncParallelArray(arr, func, callback) {
  95. const results = []
  96. let total = 0
  97. const arrLength = arr.length
  98. function count(errors) {
  99. results.push.apply(results, errors)
  100. total++
  101. if (total === arrLength) {
  102. callback(results)
  103. }
  104. }
  105. arr.forEach((a) => {
  106. func(a, count)
  107. })
  108. }
  109. function asyncSerialArray(arr, func, callback) {
  110. let index = 0
  111. const arrLength = arr.length
  112. function next(errors) {
  113. if (errors && errors.length) {
  114. callback(errors)
  115. return
  116. }
  117. const original = index
  118. index += 1
  119. if (original < arrLength) {
  120. func(arr[original], next)
  121. } else {
  122. callback([])
  123. }
  124. }
  125. next([])
  126. }
  127. function flattenObjArr(objArr) {
  128. const ret = []
  129. Object.keys(objArr).forEach((k) => {
  130. ret.push.apply(ret, objArr[k])
  131. })
  132. return ret
  133. }
  134. function asyncMap(objArr, option, func, callback) {
  135. if (option.first) {
  136. const _pending = new Promise((resolve, reject) => {
  137. const next = function next(errors) {
  138. callback(errors)
  139. return errors.length ? reject({
  140. errors,
  141. fields: convertFieldsError(errors)
  142. }) : resolve()
  143. }
  144. const flattenArr = flattenObjArr(objArr)
  145. asyncSerialArray(flattenArr, func, next)
  146. })
  147. _pending.catch((e) => e)
  148. return _pending
  149. }
  150. let firstFields = option.firstFields || []
  151. if (firstFields === true) {
  152. firstFields = Object.keys(objArr)
  153. }
  154. const objArrKeys = Object.keys(objArr)
  155. const objArrLength = objArrKeys.length
  156. let total = 0
  157. const results = []
  158. const pending = new Promise((resolve, reject) => {
  159. const next = function next(errors) {
  160. results.push.apply(results, errors)
  161. total++
  162. if (total === objArrLength) {
  163. callback(results)
  164. return results.length ? reject({
  165. errors: results,
  166. fields: convertFieldsError(results)
  167. }) : resolve()
  168. }
  169. }
  170. if (!objArrKeys.length) {
  171. callback(results)
  172. resolve()
  173. }
  174. objArrKeys.forEach((key) => {
  175. const arr = objArr[key]
  176. if (firstFields.indexOf(key) !== -1) {
  177. asyncSerialArray(arr, func, next)
  178. } else {
  179. asyncParallelArray(arr, func, next)
  180. }
  181. })
  182. })
  183. pending.catch((e) => e)
  184. return pending
  185. }
  186. function complementError(rule) {
  187. return function (oe) {
  188. if (oe && oe.message) {
  189. oe.field = oe.field || rule.fullField
  190. return oe
  191. }
  192. return {
  193. message: typeof oe === 'function' ? oe() : oe,
  194. field: oe.field || rule.fullField
  195. }
  196. }
  197. }
  198. function deepMerge(target, source) {
  199. if (source) {
  200. for (const s in source) {
  201. if (source.hasOwnProperty(s)) {
  202. const value = source[s]
  203. if (typeof value === 'object' && typeof target[s] === 'object') {
  204. target[s] = { ...target[s], ...value }
  205. } else {
  206. target[s] = value
  207. }
  208. }
  209. }
  210. }
  211. return target
  212. }
  213. /**
  214. * Rule for validating required fields.
  215. *
  216. * @param rule The validation rule.
  217. * @param value The value of the field on the source object.
  218. * @param source The source object being validated.
  219. * @param errors An array of errors that this rule may add
  220. * validation errors to.
  221. * @param options The validation options.
  222. * @param options.messages The validation messages.
  223. */
  224. function required(rule, value, source, errors, options, type) {
  225. if (rule.required && (!source.hasOwnProperty(rule.field) || isEmptyValue(value, type || rule.type))) {
  226. errors.push(format(options.messages.required, rule.fullField))
  227. }
  228. }
  229. /**
  230. * Rule for validating whitespace.
  231. *
  232. * @param rule The validation rule.
  233. * @param value The value of the field on the source object.
  234. * @param source The source object being validated.
  235. * @param errors An array of errors that this rule may add
  236. * validation errors to.
  237. * @param options The validation options.
  238. * @param options.messages The validation messages.
  239. */
  240. function whitespace(rule, value, source, errors, options) {
  241. if (/^\s+$/.test(value) || value === '') {
  242. errors.push(format(options.messages.whitespace, rule.fullField))
  243. }
  244. }
  245. /* eslint max-len:0 */
  246. const pattern = {
  247. // http://emailregex.com/
  248. email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
  249. url: new RegExp(
  250. '^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$',
  251. 'i'
  252. ),
  253. hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i
  254. }
  255. var types = {
  256. integer: function integer(value) {
  257. return /^(-)?\d+$/.test(value);
  258. },
  259. float: function float(value) {
  260. return /^(-)?\d+(\.\d+)?$/.test(value);
  261. },
  262. array: function array(value) {
  263. return Array.isArray(value)
  264. },
  265. regexp: function regexp(value) {
  266. if (value instanceof RegExp) {
  267. return true
  268. }
  269. try {
  270. return !!new RegExp(value)
  271. } catch (e) {
  272. return false
  273. }
  274. },
  275. date: function date(value) {
  276. return typeof value.getTime === 'function' && typeof value.getMonth === 'function' && typeof value.getYear
  277. === 'function'
  278. },
  279. number: function number(value) {
  280. if (isNaN(value)) {
  281. return false
  282. }
  283. // 修改源码,将字符串数值先转为数值
  284. return typeof +value === 'number'
  285. },
  286. object: function object(value) {
  287. return typeof value === 'object' && !types.array(value)
  288. },
  289. method: function method(value) {
  290. return typeof value === 'function'
  291. },
  292. email: function email(value) {
  293. return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255
  294. },
  295. url: function url(value) {
  296. return typeof value === 'string' && !!value.match(pattern.url)
  297. },
  298. hex: function hex(value) {
  299. return typeof value === 'string' && !!value.match(pattern.hex)
  300. }
  301. }
  302. /**
  303. * Rule for validating the type of a value.
  304. *
  305. * @param rule The validation rule.
  306. * @param value The value of the field on the source object.
  307. * @param source The source object being validated.
  308. * @param errors An array of errors that this rule may add
  309. * validation errors to.
  310. * @param options The validation options.
  311. * @param options.messages The validation messages.
  312. */
  313. function type(rule, value, source, errors, options) {
  314. if (rule.required && value === undefined) {
  315. required(rule, value, source, errors, options)
  316. return
  317. }
  318. const custom = ['integer', 'float', 'array', 'regexp', 'object', 'method', 'email', 'number', 'date', 'url', 'hex']
  319. const ruleType = rule.type
  320. if (custom.indexOf(ruleType) > -1) {
  321. if (!types[ruleType](value)) {
  322. errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type))
  323. } // straight typeof check
  324. } else if (ruleType && typeof value !== rule.type) {
  325. errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type))
  326. }
  327. }
  328. /**
  329. * Rule for validating minimum and maximum allowed values.
  330. *
  331. * @param rule The validation rule.
  332. * @param value The value of the field on the source object.
  333. * @param source The source object being validated.
  334. * @param errors An array of errors that this rule may add
  335. * validation errors to.
  336. * @param options The validation options.
  337. * @param options.messages The validation messages.
  338. */
  339. function range(rule, value, source, errors, options) {
  340. const len = typeof rule.len === 'number'
  341. const min = typeof rule.min === 'number'
  342. const max = typeof rule.max === 'number' // 正则匹配码点范围从U+010000一直到U+10FFFF的文字(补充平面Supplementary Plane)
  343. const spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g
  344. let val = value
  345. let key = null
  346. const num = typeof value === 'number'
  347. const str = typeof value === 'string'
  348. const arr = Array.isArray(value)
  349. if (num) {
  350. key = 'number'
  351. } else if (str) {
  352. key = 'string'
  353. } else if (arr) {
  354. key = 'array'
  355. } // if the value is not of a supported type for range validation
  356. // the validation rule rule should use the
  357. // type property to also test for a particular type
  358. if (!key) {
  359. return false
  360. }
  361. if (arr) {
  362. val = value.length
  363. }
  364. if (str) {
  365. // 处理码点大于U+010000的文字length属性不准确的bug,如"𠮷𠮷𠮷".lenght !== 3
  366. val = value.replace(spRegexp, '_').length
  367. }
  368. if (len) {
  369. if (val !== rule.len) {
  370. errors.push(format(options.messages[key].len, rule.fullField, rule.len))
  371. }
  372. } else if (min && !max && val < rule.min) {
  373. errors.push(format(options.messages[key].min, rule.fullField, rule.min))
  374. } else if (max && !min && val > rule.max) {
  375. errors.push(format(options.messages[key].max, rule.fullField, rule.max))
  376. } else if (min && max && (val < rule.min || val > rule.max)) {
  377. errors.push(format(options.messages[key].range, rule.fullField, rule.min, rule.max))
  378. }
  379. }
  380. const ENUM = 'enum'
  381. /**
  382. * Rule for validating a value exists in an enumerable list.
  383. *
  384. * @param rule The validation rule.
  385. * @param value The value of the field on the source object.
  386. * @param source The source object being validated.
  387. * @param errors An array of errors that this rule may add
  388. * validation errors to.
  389. * @param options The validation options.
  390. * @param options.messages The validation messages.
  391. */
  392. function enumerable(rule, value, source, errors, options) {
  393. rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : []
  394. if (rule[ENUM].indexOf(value) === -1) {
  395. errors.push(format(options.messages[ENUM], rule.fullField, rule[ENUM].join(', ')))
  396. }
  397. }
  398. /**
  399. * Rule for validating a regular expression pattern.
  400. *
  401. * @param rule The validation rule.
  402. * @param value The value of the field on the source object.
  403. * @param source The source object being validated.
  404. * @param errors An array of errors that this rule may add
  405. * validation errors to.
  406. * @param options The validation options.
  407. * @param options.messages The validation messages.
  408. */
  409. function pattern$1(rule, value, source, errors, options) {
  410. if (rule.pattern) {
  411. if (rule.pattern instanceof RegExp) {
  412. // if a RegExp instance is passed, reset `lastIndex` in case its `global`
  413. // flag is accidentally set to `true`, which in a validation scenario
  414. // is not necessary and the result might be misleading
  415. rule.pattern.lastIndex = 0
  416. if (!rule.pattern.test(value)) {
  417. errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern))
  418. }
  419. } else if (typeof rule.pattern === 'string') {
  420. const _pattern = new RegExp(rule.pattern)
  421. if (!_pattern.test(value)) {
  422. errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern))
  423. }
  424. }
  425. }
  426. }
  427. const rules = {
  428. required,
  429. whitespace,
  430. type,
  431. range,
  432. enum: enumerable,
  433. pattern: pattern$1
  434. }
  435. /**
  436. * Performs validation for string types.
  437. *
  438. * @param rule The validation rule.
  439. * @param value The value of the field on the source object.
  440. * @param callback The callback function.
  441. * @param source The source object being validated.
  442. * @param options The validation options.
  443. * @param options.messages The validation messages.
  444. */
  445. function string(rule, value, callback, source, options) {
  446. const errors = []
  447. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  448. if (validate) {
  449. if (isEmptyValue(value, 'string') && !rule.required) {
  450. return callback()
  451. }
  452. rules.required(rule, value, source, errors, options, 'string')
  453. if (!isEmptyValue(value, 'string')) {
  454. rules.type(rule, value, source, errors, options)
  455. rules.range(rule, value, source, errors, options)
  456. rules.pattern(rule, value, source, errors, options)
  457. if (rule.whitespace === true) {
  458. rules.whitespace(rule, value, source, errors, options)
  459. }
  460. }
  461. }
  462. callback(errors)
  463. }
  464. /**
  465. * Validates a function.
  466. *
  467. * @param rule The validation rule.
  468. * @param value The value of the field on the source object.
  469. * @param callback The callback function.
  470. * @param source The source object being validated.
  471. * @param options The validation options.
  472. * @param options.messages The validation messages.
  473. */
  474. function method(rule, value, callback, source, options) {
  475. const errors = []
  476. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  477. if (validate) {
  478. if (isEmptyValue(value) && !rule.required) {
  479. return callback()
  480. }
  481. rules.required(rule, value, source, errors, options)
  482. if (value !== undefined) {
  483. rules.type(rule, value, source, errors, options)
  484. }
  485. }
  486. callback(errors)
  487. }
  488. /**
  489. * Validates a number.
  490. *
  491. * @param rule The validation rule.
  492. * @param value The value of the field on the source object.
  493. * @param callback The callback function.
  494. * @param source The source object being validated.
  495. * @param options The validation options.
  496. * @param options.messages The validation messages.
  497. */
  498. function number(rule, value, callback, source, options) {
  499. const errors = []
  500. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  501. if (validate) {
  502. if (value === '') {
  503. value = undefined
  504. }
  505. if (isEmptyValue(value) && !rule.required) {
  506. return callback()
  507. }
  508. rules.required(rule, value, source, errors, options)
  509. if (value !== undefined) {
  510. rules.type(rule, value, source, errors, options)
  511. rules.range(rule, value, source, errors, options)
  512. }
  513. }
  514. callback(errors)
  515. }
  516. /**
  517. * Validates a boolean.
  518. *
  519. * @param rule The validation rule.
  520. * @param value The value of the field on the source object.
  521. * @param callback The callback function.
  522. * @param source The source object being validated.
  523. * @param options The validation options.
  524. * @param options.messages The validation messages.
  525. */
  526. function _boolean(rule, value, callback, source, options) {
  527. const errors = []
  528. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  529. if (validate) {
  530. if (isEmptyValue(value) && !rule.required) {
  531. return callback()
  532. }
  533. rules.required(rule, value, source, errors, options)
  534. if (value !== undefined) {
  535. rules.type(rule, value, source, errors, options)
  536. }
  537. }
  538. callback(errors)
  539. }
  540. /**
  541. * Validates the regular expression type.
  542. *
  543. * @param rule The validation rule.
  544. * @param value The value of the field on the source object.
  545. * @param callback The callback function.
  546. * @param source The source object being validated.
  547. * @param options The validation options.
  548. * @param options.messages The validation messages.
  549. */
  550. function regexp(rule, value, callback, source, options) {
  551. const errors = []
  552. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  553. if (validate) {
  554. if (isEmptyValue(value) && !rule.required) {
  555. return callback()
  556. }
  557. rules.required(rule, value, source, errors, options)
  558. if (!isEmptyValue(value)) {
  559. rules.type(rule, value, source, errors, options)
  560. }
  561. }
  562. callback(errors)
  563. }
  564. /**
  565. * Validates a number is an integer.
  566. *
  567. * @param rule The validation rule.
  568. * @param value The value of the field on the source object.
  569. * @param callback The callback function.
  570. * @param source The source object being validated.
  571. * @param options The validation options.
  572. * @param options.messages The validation messages.
  573. */
  574. function integer(rule, value, callback, source, options) {
  575. const errors = []
  576. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  577. if (validate) {
  578. if (isEmptyValue(value) && !rule.required) {
  579. return callback()
  580. }
  581. rules.required(rule, value, source, errors, options)
  582. if (value !== undefined) {
  583. rules.type(rule, value, source, errors, options)
  584. rules.range(rule, value, source, errors, options)
  585. }
  586. }
  587. callback(errors)
  588. }
  589. /**
  590. * Validates a number is a floating point number.
  591. *
  592. * @param rule The validation rule.
  593. * @param value The value of the field on the source object.
  594. * @param callback The callback function.
  595. * @param source The source object being validated.
  596. * @param options The validation options.
  597. * @param options.messages The validation messages.
  598. */
  599. function floatFn(rule, value, callback, source, options) {
  600. const errors = []
  601. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  602. if (validate) {
  603. if (isEmptyValue(value) && !rule.required) {
  604. return callback()
  605. }
  606. rules.required(rule, value, source, errors, options)
  607. if (value !== undefined) {
  608. rules.type(rule, value, source, errors, options)
  609. rules.range(rule, value, source, errors, options)
  610. }
  611. }
  612. callback(errors)
  613. }
  614. /**
  615. * Validates an array.
  616. *
  617. * @param rule The validation rule.
  618. * @param value The value of the field on the source object.
  619. * @param callback The callback function.
  620. * @param source The source object being validated.
  621. * @param options The validation options.
  622. * @param options.messages The validation messages.
  623. */
  624. function array(rule, value, callback, source, options) {
  625. const errors = []
  626. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  627. if (validate) {
  628. if (isEmptyValue(value, 'array') && !rule.required) {
  629. return callback()
  630. }
  631. rules.required(rule, value, source, errors, options, 'array')
  632. if (!isEmptyValue(value, 'array')) {
  633. rules.type(rule, value, source, errors, options)
  634. rules.range(rule, value, source, errors, options)
  635. }
  636. }
  637. callback(errors)
  638. }
  639. /**
  640. * Validates an object.
  641. *
  642. * @param rule The validation rule.
  643. * @param value The value of the field on the source object.
  644. * @param callback The callback function.
  645. * @param source The source object being validated.
  646. * @param options The validation options.
  647. * @param options.messages The validation messages.
  648. */
  649. function object(rule, value, callback, source, options) {
  650. const errors = []
  651. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  652. if (validate) {
  653. if (isEmptyValue(value) && !rule.required) {
  654. return callback()
  655. }
  656. rules.required(rule, value, source, errors, options)
  657. if (value !== undefined) {
  658. rules.type(rule, value, source, errors, options)
  659. }
  660. }
  661. callback(errors)
  662. }
  663. const ENUM$1 = 'enum'
  664. /**
  665. * Validates an enumerable list.
  666. *
  667. * @param rule The validation rule.
  668. * @param value The value of the field on the source object.
  669. * @param callback The callback function.
  670. * @param source The source object being validated.
  671. * @param options The validation options.
  672. * @param options.messages The validation messages.
  673. */
  674. function enumerable$1(rule, value, callback, source, options) {
  675. const errors = []
  676. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  677. if (validate) {
  678. if (isEmptyValue(value) && !rule.required) {
  679. return callback()
  680. }
  681. rules.required(rule, value, source, errors, options)
  682. if (value !== undefined) {
  683. rules[ENUM$1](rule, value, source, errors, options)
  684. }
  685. }
  686. callback(errors)
  687. }
  688. /**
  689. * Validates a regular expression pattern.
  690. *
  691. * Performs validation when a rule only contains
  692. * a pattern property but is not declared as a string type.
  693. *
  694. * @param rule The validation rule.
  695. * @param value The value of the field on the source object.
  696. * @param callback The callback function.
  697. * @param source The source object being validated.
  698. * @param options The validation options.
  699. * @param options.messages The validation messages.
  700. */
  701. function pattern$2(rule, value, callback, source, options) {
  702. const errors = []
  703. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  704. if (validate) {
  705. if (isEmptyValue(value, 'string') && !rule.required) {
  706. return callback()
  707. }
  708. rules.required(rule, value, source, errors, options)
  709. if (!isEmptyValue(value, 'string')) {
  710. rules.pattern(rule, value, source, errors, options)
  711. }
  712. }
  713. callback(errors)
  714. }
  715. function date(rule, value, callback, source, options) {
  716. const errors = []
  717. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  718. if (validate) {
  719. if (isEmptyValue(value) && !rule.required) {
  720. return callback()
  721. }
  722. rules.required(rule, value, source, errors, options)
  723. if (!isEmptyValue(value)) {
  724. let dateObject
  725. if (typeof value === 'number') {
  726. dateObject = new Date(value)
  727. } else {
  728. dateObject = value
  729. }
  730. rules.type(rule, dateObject, source, errors, options)
  731. if (dateObject) {
  732. rules.range(rule, dateObject.getTime(), source, errors, options)
  733. }
  734. }
  735. }
  736. callback(errors)
  737. }
  738. function required$1(rule, value, callback, source, options) {
  739. const errors = []
  740. const type = Array.isArray(value) ? 'array' : typeof value
  741. rules.required(rule, value, source, errors, options, type)
  742. callback(errors)
  743. }
  744. function type$1(rule, value, callback, source, options) {
  745. const ruleType = rule.type
  746. const errors = []
  747. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  748. if (validate) {
  749. if (isEmptyValue(value, ruleType) && !rule.required) {
  750. return callback()
  751. }
  752. rules.required(rule, value, source, errors, options, ruleType)
  753. if (!isEmptyValue(value, ruleType)) {
  754. rules.type(rule, value, source, errors, options)
  755. }
  756. }
  757. callback(errors)
  758. }
  759. /**
  760. * Performs validation for any type.
  761. *
  762. * @param rule The validation rule.
  763. * @param value The value of the field on the source object.
  764. * @param callback The callback function.
  765. * @param source The source object being validated.
  766. * @param options The validation options.
  767. * @param options.messages The validation messages.
  768. */
  769. function any(rule, value, callback, source, options) {
  770. const errors = []
  771. const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
  772. if (validate) {
  773. if (isEmptyValue(value) && !rule.required) {
  774. return callback()
  775. }
  776. rules.required(rule, value, source, errors, options)
  777. }
  778. callback(errors)
  779. }
  780. const validators = {
  781. string,
  782. method,
  783. number,
  784. boolean: _boolean,
  785. regexp,
  786. integer,
  787. float: floatFn,
  788. array,
  789. object,
  790. enum: enumerable$1,
  791. pattern: pattern$2,
  792. date,
  793. url: type$1,
  794. hex: type$1,
  795. email: type$1,
  796. required: required$1,
  797. any
  798. }
  799. function newMessages() {
  800. return {
  801. default: 'Validation error on field %s',
  802. required: '%s is required',
  803. enum: '%s must be one of %s',
  804. whitespace: '%s cannot be empty',
  805. date: {
  806. format: '%s date %s is invalid for format %s',
  807. parse: '%s date could not be parsed, %s is invalid ',
  808. invalid: '%s date %s is invalid'
  809. },
  810. types: {
  811. string: '%s is not a %s',
  812. method: '%s is not a %s (function)',
  813. array: '%s is not an %s',
  814. object: '%s is not an %s',
  815. number: '%s is not a %s',
  816. date: '%s is not a %s',
  817. boolean: '%s is not a %s',
  818. integer: '%s is not an %s',
  819. float: '%s is not a %s',
  820. regexp: '%s is not a valid %s',
  821. email: '%s is not a valid %s',
  822. url: '%s is not a valid %s',
  823. hex: '%s is not a valid %s'
  824. },
  825. string: {
  826. len: '%s must be exactly %s characters',
  827. min: '%s must be at least %s characters',
  828. max: '%s cannot be longer than %s characters',
  829. range: '%s must be between %s and %s characters'
  830. },
  831. number: {
  832. len: '%s must equal %s',
  833. min: '%s cannot be less than %s',
  834. max: '%s cannot be greater than %s',
  835. range: '%s must be between %s and %s'
  836. },
  837. array: {
  838. len: '%s must be exactly %s in length',
  839. min: '%s cannot be less than %s in length',
  840. max: '%s cannot be greater than %s in length',
  841. range: '%s must be between %s and %s in length'
  842. },
  843. pattern: {
  844. mismatch: '%s value %s does not match pattern %s'
  845. },
  846. clone: function clone() {
  847. const cloned = JSON.parse(JSON.stringify(this))
  848. cloned.clone = this.clone
  849. return cloned
  850. }
  851. }
  852. }
  853. const messages = newMessages()
  854. /**
  855. * Encapsulates a validation schema.
  856. *
  857. * @param descriptor An object declaring validation rules
  858. * for this schema.
  859. */
  860. function Schema(descriptor) {
  861. this.rules = null
  862. this._messages = messages
  863. this.define(descriptor)
  864. }
  865. Schema.prototype = {
  866. messages: function messages(_messages) {
  867. if (_messages) {
  868. this._messages = deepMerge(newMessages(), _messages)
  869. }
  870. return this._messages
  871. },
  872. define: function define(rules) {
  873. if (!rules) {
  874. throw new Error('Cannot configure a schema with no rules')
  875. }
  876. if (typeof rules !== 'object' || Array.isArray(rules)) {
  877. throw new Error('Rules must be an object')
  878. }
  879. this.rules = {}
  880. let z
  881. let item
  882. for (z in rules) {
  883. if (rules.hasOwnProperty(z)) {
  884. item = rules[z]
  885. this.rules[z] = Array.isArray(item) ? item : [item]
  886. }
  887. }
  888. },
  889. validate: function validate(source_, o, oc) {
  890. const _this = this
  891. if (o === void 0) {
  892. o = {}
  893. }
  894. if (oc === void 0) {
  895. oc = function oc() {}
  896. }
  897. let source = source_
  898. let options = o
  899. let callback = oc
  900. if (typeof options === 'function') {
  901. callback = options
  902. options = {}
  903. }
  904. if (!this.rules || Object.keys(this.rules).length === 0) {
  905. if (callback) {
  906. callback()
  907. }
  908. return Promise.resolve()
  909. }
  910. function complete(results) {
  911. let i
  912. let errors = []
  913. let fields = {}
  914. function add(e) {
  915. if (Array.isArray(e)) {
  916. let _errors
  917. errors = (_errors = errors).concat.apply(_errors, e)
  918. } else {
  919. errors.push(e)
  920. }
  921. }
  922. for (i = 0; i < results.length; i++) {
  923. add(results[i])
  924. }
  925. if (!errors.length) {
  926. errors = null
  927. fields = null
  928. } else {
  929. fields = convertFieldsError(errors)
  930. }
  931. callback(errors, fields)
  932. }
  933. if (options.messages) {
  934. let messages$1 = this.messages()
  935. if (messages$1 === messages) {
  936. messages$1 = newMessages()
  937. }
  938. deepMerge(messages$1, options.messages)
  939. options.messages = messages$1
  940. } else {
  941. options.messages = this.messages()
  942. }
  943. let arr
  944. let value
  945. const series = {}
  946. const keys = options.keys || Object.keys(this.rules)
  947. keys.forEach((z) => {
  948. arr = _this.rules[z]
  949. value = source[z]
  950. arr.forEach((r) => {
  951. let rule = r
  952. if (typeof rule.transform === 'function') {
  953. if (source === source_) {
  954. source = { ...source }
  955. }
  956. value = source[z] = rule.transform(value)
  957. }
  958. if (typeof rule === 'function') {
  959. rule = {
  960. validator: rule
  961. }
  962. } else {
  963. rule = { ...rule }
  964. }
  965. rule.validator = _this.getValidationMethod(rule)
  966. rule.field = z
  967. rule.fullField = rule.fullField || z
  968. rule.type = _this.getType(rule)
  969. if (!rule.validator) {
  970. return
  971. }
  972. series[z] = series[z] || []
  973. series[z].push({
  974. rule,
  975. value,
  976. source,
  977. field: z
  978. })
  979. })
  980. })
  981. const errorFields = {}
  982. return asyncMap(series, options, (data, doIt) => {
  983. const { rule } = data
  984. let deep = (rule.type === 'object' || rule.type === 'array') && (typeof rule.fields === 'object' || typeof rule.defaultField
  985. === 'object')
  986. deep = deep && (rule.required || !rule.required && data.value)
  987. rule.field = data.field
  988. function addFullfield(key, schema) {
  989. return { ...schema, fullField: `${rule.fullField}.${key}` }
  990. }
  991. function cb(e) {
  992. if (e === void 0) {
  993. e = []
  994. }
  995. let errors = e
  996. if (!Array.isArray(errors)) {
  997. errors = [errors]
  998. }
  999. if (!options.suppressWarning && errors.length) {
  1000. Schema.warning('async-validator:', errors)
  1001. }
  1002. if (errors.length && rule.message) {
  1003. errors = [].concat(rule.message)
  1004. }
  1005. errors = errors.map(complementError(rule))
  1006. if (options.first && errors.length) {
  1007. errorFields[rule.field] = 1
  1008. return doIt(errors)
  1009. }
  1010. if (!deep) {
  1011. doIt(errors)
  1012. } else {
  1013. // if rule is required but the target object
  1014. // does not exist fail at the rule level and don't
  1015. // go deeper
  1016. if (rule.required && !data.value) {
  1017. if (rule.message) {
  1018. errors = [].concat(rule.message).map(complementError(rule))
  1019. } else if (options.error) {
  1020. errors = [options.error(rule, format(options.messages.required, rule.field))]
  1021. } else {
  1022. errors = []
  1023. }
  1024. return doIt(errors)
  1025. }
  1026. let fieldsSchema = {}
  1027. if (rule.defaultField) {
  1028. for (const k in data.value) {
  1029. if (data.value.hasOwnProperty(k)) {
  1030. fieldsSchema[k] = rule.defaultField
  1031. }
  1032. }
  1033. }
  1034. fieldsSchema = { ...fieldsSchema, ...data.rule.fields }
  1035. for (const f in fieldsSchema) {
  1036. if (fieldsSchema.hasOwnProperty(f)) {
  1037. const fieldSchema = Array.isArray(fieldsSchema[f]) ? fieldsSchema[f] : [fieldsSchema[f]]
  1038. fieldsSchema[f] = fieldSchema.map(addFullfield.bind(null, f))
  1039. }
  1040. }
  1041. const schema = new Schema(fieldsSchema)
  1042. schema.messages(options.messages)
  1043. if (data.rule.options) {
  1044. data.rule.options.messages = options.messages
  1045. data.rule.options.error = options.error
  1046. }
  1047. schema.validate(data.value, data.rule.options || options, (errs) => {
  1048. const finalErrors = []
  1049. if (errors && errors.length) {
  1050. finalErrors.push.apply(finalErrors, errors)
  1051. }
  1052. if (errs && errs.length) {
  1053. finalErrors.push.apply(finalErrors, errs)
  1054. }
  1055. doIt(finalErrors.length ? finalErrors : null)
  1056. })
  1057. }
  1058. }
  1059. let res
  1060. if (rule.asyncValidator) {
  1061. res = rule.asyncValidator(rule, data.value, cb, data.source, options)
  1062. } else if (rule.validator) {
  1063. res = rule.validator(rule, data.value, cb, data.source, options)
  1064. if (res === true) {
  1065. cb()
  1066. } else if (res === false) {
  1067. cb(rule.message || `${rule.field} fails`)
  1068. } else if (res instanceof Array) {
  1069. cb(res)
  1070. } else if (res instanceof Error) {
  1071. cb(res.message)
  1072. }
  1073. }
  1074. if (res && res.then) {
  1075. res.then(() => cb(), (e) => cb(e))
  1076. }
  1077. }, (results) => {
  1078. complete(results)
  1079. })
  1080. },
  1081. getType: function getType(rule) {
  1082. if (rule.type === undefined && rule.pattern instanceof RegExp) {
  1083. rule.type = 'pattern'
  1084. }
  1085. if (typeof rule.validator !== 'function' && rule.type && !validators.hasOwnProperty(rule.type)) {
  1086. throw new Error(format('Unknown rule type %s', rule.type))
  1087. }
  1088. return rule.type || 'string'
  1089. },
  1090. getValidationMethod: function getValidationMethod(rule) {
  1091. if (typeof rule.validator === 'function') {
  1092. return rule.validator
  1093. }
  1094. const keys = Object.keys(rule)
  1095. const messageIndex = keys.indexOf('message')
  1096. if (messageIndex !== -1) {
  1097. keys.splice(messageIndex, 1)
  1098. }
  1099. if (keys.length === 1 && keys[0] === 'required') {
  1100. return validators.required
  1101. }
  1102. return validators[this.getType(rule)] || false
  1103. }
  1104. }
  1105. Schema.register = function register(type, validator) {
  1106. if (typeof validator !== 'function') {
  1107. throw new Error('Cannot register a validator by type, validator is not a function')
  1108. }
  1109. validators[type] = validator
  1110. }
  1111. Schema.warning = warning
  1112. Schema.messages = messages
  1113. export default Schema
  1114. // # sourceMappingURL=index.js.map