@kodepandai/node-input-validator
Version:
validation library for nodejs, inspired by laravel.
1,637 lines (1,596 loc) • 87.6 kB
JavaScript
import * as mimeTypes from 'mime-types';
import fileType from 'file-type';
import readChunk from 'read-chunk';
import fs from 'fs';
import util from 'util';
import { URL } from 'url';
var Langs;
(function (Langs) {
Langs["en_US"] = "en_US";
Langs["pb"] = "pb";
Langs["fa"] = "fa";
Langs["pt_BR"] = "pt_BR";
})(Langs || (Langs = {}));
const after$1 = 'The :attr must be a date after :arg0.';
const afterOrEqual = 'The :attr must be a date after or equal :arg0.';
const before$1 = 'The :attr must be a date before :arg0.';
const beforeOrEqual = 'The :attr must be a date before or equal to :date.';
const boolean$1 = 'The :attr field must be boolean.';
const dateISO$1 = 'The :attr must be a valid ISO-8601 date.';
const messages$1 = {
after: after$1,
afterOrEqual,
before: before$1,
beforeOrEqual,
accepted: 'The :attr must be accepted.',
acceptedIf: 'The :attr should be accepted if the parameter :arg0 is :arg1.',
acceptedNotIf: 'The :attr can\'t be accepted if the parameter :arg0 is :arg1.',
activeUrl: 'The :attr is not a valid url.',
alpha: 'The :attr can only contain alphabets.',
alphaDash: 'The :attr can only contain letters, numbers, and dashes.',
alphaNumeric: 'The :attr can only contain letters and numbers.',
alphaHyphen: 'The :attr can only contain letters and hyphen (-).',
alphaNumericDash: 'The :attr can only contain letters, numbers and dashes.',
arrayLen: 'The :attr must be an array of length :arg0.',
arrayLenRange: (params) => {
if (params.ruleArgs.length == 1) {
return 'The :attr must be an array max length :arg0.';
}
return 'The :attr must be an array of length between :arg1 - :arg0.';
},
arrayLenMin: 'The :attr must be an array with minimum length :arg0.',
arrayLenMax: 'The :attr must be an array with maximum length :arg0.',
array: 'The :attr must be an array.',
arrayUnique: 'The :attr must be an array of unique values.',
arrayUniqueObjects: 'The :attr must be an array of unique :args attributes of object.',
ascii: 'The :attr can only contains valid ascii characters.',
base64: 'The :attr must be a valid base64 string.',
between: 'The :attr must be between :arg0 and :arg1',
boolean: boolean$1,
booleanStrict: boolean$1,
booleanStr: boolean$1,
booleanInt: boolean$1,
confirmed: 'The :attr confirmation does not match.',
contains: 'The :attr must contains :arg0.',
creditCard: 'The :attr value must be a valid card number.',
date: 'The :attr must be a valid date.',
dateAfter: after$1,
dateAfterToday: 'The :attr must be a date after :arg0 :arg1.',
dateDaysAfterToday: 'The :attr must be a date after :arg0 days.',
dateYearsAfterToday: 'The :attr must be a date after :arg0 years.',
dateDaysBeforeToday: 'The :attr must be a date before :arg0 days.',
dateYearsBeforeToday: 'The :attr must be a date before :arg0 years.',
dateBefore: before$1,
dateBeforeToday: 'The :attr must be a date before :arg0 :arg1.',
dateFormat: 'The :attr does not match the date format :arg0.',
datetime: 'The :attr must be a valid datetime(YYYY-MM-DD HH:mm:ss).',
dateISO: dateISO$1,
dateiso: dateISO$1,
decimal: 'The :attr must be a valid decimal value.',
different: 'The :attr and :arg0 must be different.',
digits: 'The :attr must be of :arg0 digits.',
digitsBetween: 'The :attr must be between :arg0 and :arg1.',
dimensions: 'The :attr must meet dimension constraints as :args.',
domain: 'The :attr must be a valid domain.',
email: 'The :attr must be a valid email address.',
equals: 'The :attr must be a equals :arg0.',
gt: 'The :attr must be greater then :args.',
gte: 'The :attr must be greater then or equals to :args.',
length: 'The :attr length is not acceptable.',
lt: 'The :attr must be less then :args',
lte: 'The :attr must be less then or equals :args',
hash: 'The :attr must be a valid :arg0 hash.',
hex: 'The :attr must be a valid hex.',
hexColor: 'The :attr must be a valid hex color.',
in: 'The selected :attr is invalid.',
integer: 'The :attr must be an integer.',
ip: 'The :attr must be a valid IP address.',
ipv4: 'The :attr must be a valid IPv4 address.',
ipv6: 'The :attr must be a valid IPv6 address.',
iso8601: 'The :attr must be a valid ISO8601 string.',
json: 'The :attr must be a valid JSON string.',
latLong: 'The :attr must be a valid comma seperated lat and long without spaces.',
lengthBetween: 'The :attr length must be between :arg0 - :arg1.',
macAddress: 'The :attr must be a valid mac address.',
max: 'The :attr can not be greater than :arg0.',
maxLength: 'The :attr can not be greater than :arg0.',
mime: 'The :attr must be a file of type: :args.',
min: 'The :attr must be at least :arg0.',
minLength: 'The :attr can not be less than :arg0.',
mongoId: 'The :attr must be a valid mongo id.',
notContains: 'The :attr may not contains :arg0.',
notIn: 'The selected :attr is invalid.',
nullable: 'The :attr is required.',
numeric: 'The :attr must be a number.',
object: 'The :attr must be an object.',
phoneNumber: 'The :attr must be a valid phone number.',
regex: 'The :attr format is invalid.',
required: 'The :attr field is required.',
requiredIf: 'The :attr field is required.',
requiredNotIf: 'The :attr field is required.',
requiredWith: 'The :attr field is required.',
requiredWithout: 'The :attr field is required.',
requiredWithoutAll: 'The :attr field is required.',
same: 'The :attr and :arg0 must match.',
size: 'The :attr must be :arg0.',
sometimes: 'The :attr is required.',
string: 'The :attr must be a string.',
timezone: 'The :attr must be a valid zone.',
unique: 'The :attr has already been taken.',
url: 'The :attr format is invalid.',
// validator: (params: any) => {
// if (!params.ruleArgs || !params.ruleArgs[0]) {
// // throw params.ruleArgs;
// return messages.$default;
// }
// const rule = params.ruleArgs[0].replace('is', '').toLowerCase();
// // throw rule;
// // @ts-ignore
// return messages[rule] || messages.$default;
// },
any: 'At least one of :attr fields must be provided',
$niceNames: {},
$custom: {
// customAttributeName: 'Message goes here.',
// 'customAttributeName.ruleName': 'Message goes here.',
},
$default: "The :attr validation failed under rule :rule against value :value.",
};
var enUS_messages = /*#__PURE__*/Object.freeze({
__proto__: null,
messages: messages$1
});
let config = {
wildcardIterations: 1000,
wildcardSeperator: '.',
lang: Langs.en_US,
implicitRules: [
"required",
"requiredIf",
"requiredNotIf",
"requiredWith",
"requiredWithout",
"accepted",
"sometimes",
"nullable",
],
};
function set(customConfig) {
config = Object.assign(Object.assign({}, config), customConfig);
}
function get(key, defaultValue = null) {
if (key) {
return config[key] || defaultValue;
}
return config;
}
function modify(key, value) {
config[key] = value;
}
function addImplicitRule$1(ruleName) {
var _a;
(_a = config.implicitRules) === null || _a === void 0 ? void 0 : _a.push(ruleName);
}
function getKeyValue(key) {
return (obj) => obj[key];
}
function getValueByStringNotation(object, notation) {
const notationArr = notation.split(".");
let value;
notationArr.map((item) => {
if (value === undefined) {
value = object[item];
}
else {
value = value[item];
}
return value;
});
return value;
}
function isIterable(object) {
return object != null &&
(typeof object === "object" || Array.isArray(object));
}
function getValuesByWildCardStringNotation(iterable, rules = {}, options = {}) {
const currentConfig = get();
const { prefix, iterations, seperator } = Object.assign({ prefix: [], iterations: currentConfig.wildcardIterations, seperator: currentConfig.wildcardSeperator }, options);
const notationsVals = {};
const notationMap = {};
let iterationsCount = 1;
const parse = (data, prefix) => {
iterationsCount++;
if (iterationsCount > iterations) {
// eslint-disable-next-line no-console
throw new Error(`Max(${iterations}) repetation was reached.`);
}
Object.keys(data).forEach((key, index) => {
const v = data[key];
const notationKey = `${[...prefix, key].join(seperator)}`;
const notationMapKey = notationKey.replace(/\.[0-9+]\./g, ".*.").replace(/^[0-9+]\./g, "*.");
notationMap[notationMapKey] = notationMap[notationMapKey] || [];
notationMap[notationMapKey].push(notationKey);
if (isIterable(v)) {
parse(v, [...prefix, key]);
notationsVals[notationKey] = v;
}
else {
notationsVals[notationKey] = v;
}
});
};
parse(iterable, [...prefix]);
return { notationsVals, notationMap };
}
const DEFAULT_LANG = Langs.en_US;
const MessagesRef = {};
function messagesRefByLang(lang) {
let messages = getKeyValue(lang.toString())(MessagesRef);
if (typeof messages === "undefined") {
// @ts-ignore
MessagesRef[lang] = { messages: {} };
messages = getKeyValue(lang.toString())(MessagesRef);
}
return messages;
}
function extend$1(newMessages, lang = Langs.en_US) {
const messages = messagesRefByLang(lang);
Object.assign(messages, newMessages);
}
function addCustomMessages(customMessages, lang = Langs.en_US) {
const messages = messagesRefByLang(lang);
if (!messages.$custom) {
messages.$custom = {};
}
Object.assign(messages.$custom, customMessages);
}
function addNiceNames(attributesNiceNames, lang = Langs.en_US) {
const messages = messagesRefByLang(lang);
if (!messages.$niceNames) {
messages.$niceNames = {};
}
Object.assign(messages.$niceNames, attributesNiceNames);
}
extend$1(messages$1, Langs.en_US);
var messages = /*#__PURE__*/Object.freeze({
__proto__: null,
en_US: enUS_messages
});
var index = /*#__PURE__*/Object.freeze({
__proto__: null,
Messages: messages,
DEFAULT_LANG: DEFAULT_LANG,
messagesRefByLang: messagesRefByLang,
extend: extend$1,
addCustomMessages: addCustomMessages,
addNiceNames: addNiceNames
});
function reallyEmpty(value) {
const str = (value === undefined || value === null ? "" : value) + "";
return str.length === 0;
}
function fillMissingSpots(target, key) {
// convert key to an array or clone is already an array
const segments = Array.isArray(key) ? [...key] : key.split('.');
// pull the first key from array
const segment = segments.shift();
if (segment === '*') {
if (!Array.isArray(target)) {
target = [];
}
if (segments.length) {
if (!target.length) {
const targetVal = {};
targetVal[segments[0]] = null;
target.push(targetVal);
console.log('create new target', target);
}
target.forEach((childTarget) => {
fillMissingSpots(childTarget, segments);
});
}
}
else if (typeof target === 'object') {
if (segments.length) {
if (segment && !target[segment]) {
target[segment] = segments[0] === '*' ? [] : {};
}
if (target && segment && target[segment]) {
fillMissingSpots(target[segment], segments);
}
}
}
}
/**
* The field under validation must be yes, on, 1, or true.
* This is useful for validating "Terms of Service" acceptance.
* @param {Array<string>} args seeds
*/
/**
* Usage Example
*
* ```js
* async (req,res) => {
* const v = new niv.Validator(req.body, { tandc: 'accepted' })
* const passed = await v.validate();
* console.log(passed) // output: true/false, depends on input
* }
* ```
*/
function accepted(args = ["true", "1", "yes", "on"]) {
return {
name: "accepted",
handler: (value) => {
if (args.indexOf(value) >= 0) {
return true;
}
return false;
},
};
}
/**
* The field under validation must be yes, on, 1, or true if the attribute given in the seed present and has value given value.
* This is useful for validating "Terms of Service" acceptance of some service that is optional and user only have to agree, if user has enabled that service.
* @param args seeds
* @param acceptedValues
*/
/**
* Usage Example
*
* ```js
* async (req,res) => {
* const v = new niv.Validator(req.body, { tandc: 'acceptedIf:newsletter,yes' })
* const passed = await v.validate();
* console.log(passed) // output: true/false, depends on input
* }
* ```
*/
function acceptedIf(args, acceptedValues = ["true", "1", "yes", "on"]) {
const argsLen = args.length;
if (!args
|| argsLen < 2
|| argsLen % 2 !== 0) {
throw new Error('Invalid number of arguments.');
}
return {
name: "acceptedIf",
handler: (value, v) => {
let required = true;
let i = 0;
for (i; i < argsLen; i += 2) {
const attrName = args[i];
const requiredAttrVal = args[i + 1];
const actualValue = v.attributeValue(attrName);
if (reallyEmpty(actualValue)
|| String(requiredAttrVal) !== String(actualValue)) {
required = false;
break;
}
}
return (required && acceptedValues.indexOf(value) < 0) ? false : true;
},
};
}
/**
* The field under validation must no be yes, on, 1, or true if the attribute given in the seed present and has value given value.
* This is useful for validating "Terms of Service" acceptance of some service that user should not accept if user has disabled that service.
* @param args seeds
* @param acceptedValues
*/
/**
* Usage Example
*
* ```js
* async (req,res) => {
* const v = new niv.Validator(req.body, { tandc: 'acceptedNotIf:newsletter,no' })
* const passed = await v.validate();
* console.log(passed) // output: true/false, depends on input
* }
* ```
*/
function acceptedNotIf(args, acceptedValues = ["true", "1", "yes", "on"]) {
const argsLen = args.length;
if (!args
|| argsLen < 2
|| argsLen % 2 !== 0) {
throw new Error('Invalid number of arguments.');
}
return {
name: "acceptedNotIf",
handler: (value, v) => {
let required = true;
let i = 0;
for (i; i < argsLen; i += 2) {
const attrName = args[i];
const requiredAttrVal = args[i + 1];
const actualValue = v.attributeValue(attrName);
if (reallyEmpty(actualValue)
|| String(requiredAttrVal) === String(actualValue)) {
required = false;
break;
}
}
return (required === false && acceptedValues.indexOf(value) >= 0) ? false : true;
},
};
}
const REGEX_ALPHA = /^[A-Z]+$/i;
const REGEX_ALPHA_DASH = /^[A-Z_-]+$/i;
const REGEX_ALPHA_HYPHEN = /^[A-Z-]+$/i;
const REGEX_ALPHA_NUMERIC = /^[0-9A-Z]+$/i;
const REGEX_ALPHA_NUMERIC_DASH = /^[A-Z0-9_-]+$/i;
/**
* The field under validation must be entirely alphabetic characters.
*/
function alpha() {
return {
name: "alpha",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return REGEX_ALPHA.test(value);
},
};
}
/**
* The field under validation may have alpha characters, as well as hyphens and underscores.
*/
function alphaDash() {
return {
name: "alphaDash",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return REGEX_ALPHA_DASH.test(value);
},
};
}
/**
* The field under validation may have alpha characters, as well as hyphens.
*/
function alphaHyphen() {
return {
name: "alphaHyphen",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return REGEX_ALPHA_HYPHEN.test(value);
},
};
}
/**
* The field under validation must contains alpha-numeric characters.
*/
function alphaNumeric() {
return {
name: "alphaNumeric",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return REGEX_ALPHA_NUMERIC.test(value);
},
};
}
/**
* The field under validation may have alpha-numeric characters, as well as hyphens and underscores.
*/
function alphaNumericDash() {
return {
name: "alphaNumericDash",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return REGEX_ALPHA_NUMERIC_DASH.test(value);
},
};
}
// This file is the perfect example of when to place multiple rules in same file.
// All rules in this file have common check ie. isArray and on top of that
// 2 of them have unique check.
/**
* The field under validation must be array.
*/
function array() {
return {
name: "array",
handler: (value) => {
return Array.isArray(value);
},
};
}
/**
* The field under validation must be array of unique values.
* No need to use array rule. This rule will take care of that.
*/
function arrayUnique() {
return {
name: "arrayUnique",
handler: (value) => {
if (!Array.isArray(value)) {
return false;
}
return (new Set(value)).size === value.length;
},
};
}
/**
* The field under validation must be array and should have objects with unique attributes as per seed.
* No need to use array rule. This rule will take care of that.
* @param args seeds
*/
function arrayUniqueObjects(args) {
return {
name: "arrayUniqueObjects",
handler: (value) => {
if (!Array.isArray(value)) {
return false;
}
const result = new Set(value.map((o) => {
let output = "";
for (const attr of args) {
output += o[attr];
}
return output;
}));
return result.size === value.length;
},
};
}
/**
* @since: v5
* The field under validation must be array of length as per seed.
* @param args seeds
*/
function arrayLen(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const len = parseInt(args[0], 10);
return {
name: "arrayLen",
handler: (value) => {
return (Array.isArray(value) && value.length === len);
},
};
}
/**
* @since: v5
* The field under validation must be array and has length range as per seed.
* @param args seeds
*/
function arrayLenRange(args) {
if (args.length < 1 || args.length > 2) {
throw new Error('Invalid number of arguments.');
}
const max = parseInt(args[0], 10);
const min = parseInt(args[1] || '0', 10);
return {
name: "arrayLenRange",
handler: (value) => {
const len = value.length;
return (Array.isArray(value) && len <= max && (!min || len >= min));
},
};
}
/**
* @since: v5
* The field under validation must be array and has minumun length as per seed.
* @param args seeds
*/
function arrayLenMin(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const min = parseInt(args[0], 10);
return {
name: "arrayLenMin",
handler: (value) => {
const len = value.length;
return (Array.isArray(value) && len >= min);
},
};
}
/**
* @since: v5
* The field under validation must be array and has maximum length as per seed.
* @param args seeds
*/
function arrayLenMax(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const max = parseInt(args[0], 10);
return {
name: "arrayLenMax",
handler: (value) => {
const len = value.length;
return (Array.isArray(value) && len <= max);
},
};
}
function isInt(value, allowLeadingZero = false) {
if (allowLeadingZero) {
return /^[-+]?[0-9]+$/.test(value);
}
return /^(?:[-+]?(?:0|[1-9][0-9]*))$/.test(value);
}
function isNumeric(value) {
return /^[+-]?([0-9]*[.])?[0-9]+$/.test(value);
}
function isDecimal(value, strict = false, decimalDigits = 1, seperator = '.') {
const r = new RegExp(`^[-+]?([0-9]+)?(\\${seperator}[0-9]{${decimalDigits},})${strict ? '' : '?'}$`);
return r.test(value);
}
/**
* The field under validation must be between min and max seed.
* This will work with number as well as array.
* In case of array, array values must be numbers between min and max seed.
* @param args seeds
*/
function between(args) {
if (args.length !== 2) {
throw new Error('Invalid number of arguments.');
}
if (!isNumeric(args[0]) || !isNumeric(args[0])) {
throw new TypeError('Seeds must be number.');
}
const min = Number(args[0]);
const max = Number(args[1]);
if (min >= max) {
throw new RangeError('Seed min must be less then max.');
}
return {
name: 'between',
handler: (value) => {
if (Array.isArray(value)) {
let i = 0;
const len = value.length;
for (i; i < len; i++) {
const v = String(value[i]);
if (!isNumeric(v)) {
return false;
}
}
const minV = Math.min(...value);
const maxV = Math.max(...value);
if (minV < min || maxV > max) {
return false;
}
return true;
}
const v = String(value);
if (!isNumeric(v)) {
return false;
}
const valNum = Number(value);
if (valNum < min || valNum > max) {
return false;
}
return true;
},
};
}
function booleanStr() {
const args = ["true", "false"];
return {
name: "booleanStr",
handler: (value) => {
if (args.indexOf(value) >= 0) {
return true;
}
return false;
},
};
}
function booleanInt() {
const args = [0, 1];
return {
name: "booleanInt",
handler: (value) => {
if (args.indexOf(value) >= 0) {
return true;
}
return false;
},
};
}
function booleanStrict() {
const args = [true, false];
return {
name: "booleanStrict",
handler: (value) => {
if (args.indexOf(value) >= 0) {
return true;
}
return false;
},
};
}
// /**
// * @deprecated Since version 5.
// * Use booleanStrict,booleanStr,booleanInt instead.
// * @param args
// */
function boolean(args = [true, false, 0, 1, "true", "false", "0", "1"]) {
// console.warn("Rule boolean has be deprecated, please use booleanStrict,booleanStr,booleanInt instead.");
return {
name: "boolean",
handler: (value) => {
if (args.indexOf(value) >= 0) {
return true;
}
return false;
},
};
}
function confirmed(args = []) {
return {
name: "confirmed",
handler: (value, v, attrName) => {
if (value === v.attributeValue(`${attrName}Confirmation`)) {
return true;
}
return false;
},
};
}
function contains(args) {
if (args.length < 1 || args.length > 2) {
throw new Error('Invalid number of arguments.');
}
const [find, modifier] = args;
if (modifier && modifier !== 'i') {
throw new Error('Only support modifier is insensitive (i).');
}
return {
name: "contains",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
if (modifier === 'i') {
return value.toLowerCase().indexOf(find.toLowerCase()) >= 0;
}
return value.indexOf(find) >= 0;
},
};
}
function notContains(args) {
const containsHandler = contains(args).handler;
return {
name: "notContains",
handler: (value) => {
return !containsHandler(value);
},
};
}
// credits: https://github.com/validatorjs/validator.js/blob/master/src/lib/isFQDN.js
function isDomain(value) {
const parts = value.split('.');
const tld = parts[parts.length - 1];
// disallow fqdns without tld
if (parts.length < 2) {
return false;
}
if (!/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
return false;
}
// disallow spaces && special characers
if (/[\s\u2002-\u200B\u202F\u205F\u3000\uFEFF\uDB40\uDC20\u00A9\uFFFD]/.test(tld)) {
return false;
}
if (/^\d+$/.test(tld)) {
return false;
}
return parts.every((part) => {
if (part.length > 63) {
return false;
}
if (!/^[a-z_\u00a1-\uffff0-9-]+$/i.test(part)) {
return false;
}
// disallow full-width chars
if (/[\uff01-\uff5e]/.test(part)) {
return false;
}
// disallow parts starting or ending with hyphen
if (/^-|-$/.test(part)) {
return false;
}
if (/_/.test(part)) {
return false;
}
return true;
});
}
function isByteLength(str, options) {
let min = 0;
let max;
if (typeof (options) === 'object') {
min = options.min || 0;
max = options.max;
}
const len = encodeURI(str).split(/%..|./).length - 1;
return len >= min && (typeof max === 'undefined' || len <= max);
}
// credits: https://github.com/validatorjs/validator.js/blob/master/src/lib/isEmail.js
const emailUserUtf8Part = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i;
const quotedEmailUserUtf8 = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*$/i;
const defaultMaxEmailLength = 254;
function isEmail(str) {
if (str.length > defaultMaxEmailLength) {
return false;
}
const parts = str.split('@');
const domain = parts.pop() || '';
let user = parts.join('@');
domain.toLowerCase();
if (!isByteLength(user, { max: 64 }) ||
!isByteLength(domain, { max: 254 })) {
return false;
}
if (!isDomain(domain)) {
return false;
}
if (user[0] === '"') {
user = user.slice(1, user.length - 1);
return quotedEmailUserUtf8.test(user);
}
const pattern = emailUserUtf8Part;
const user_parts = user.split('.');
for (let i = 0; i < user_parts.length; i++) {
if (!pattern.test(user_parts[i])) {
return false;
}
}
return true;
}
const ipv4Maybe = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
const ipv6Block = /^[0-9A-F]{1,4}$/i;
function isIp(str, version = '') {
if (!version) {
return isIp(str, '4') || isIp(str, '6');
}
if (version === '4') {
if (!ipv4Maybe.test(str)) {
return false;
}
// @ts-ignore
const parts = str.split('.').sort((a, b) => a - b);
// @ts-ignore
return parts[3] <= 255;
}
if (version === '6') {
let addressAndZone = [str];
// ipv6 addresses could have scoped architecture
// according to https://tools.ietf.org/html/rfc4007#section-11
if (str.includes('%')) {
addressAndZone = str.split('%');
if (addressAndZone.length !== 2) {
// it must be just two parts
return false;
}
if (!addressAndZone[0].includes(':')) {
// the first part must be the address
return false;
}
if (addressAndZone[1] === '') {
// the second part must not be empty
return false;
}
}
const blocks = addressAndZone[0].split(':');
let foundOmissionBlock = false; // marker to indicate ::
// At least some OS accept the last 32 bits of an IPv6 address
// (i.e. 2 of the blocks) in IPv4 notation, and RFC 3493 says
// that '::ffff:a.b.c.d' is valid for IPv4-mapped IPv6 addresses,
// and '::a.b.c.d' is deprecated, but also valid.
const foundIPv4TransitionBlock = isIp(blocks[blocks.length - 1], '4');
const expectedNumberOfBlocks = foundIPv4TransitionBlock ? 7 : 8;
if (blocks.length > expectedNumberOfBlocks) {
return false;
}
// initial or final ::
if (str === '::') {
return true;
}
else if (str.substr(0, 2) === '::') {
blocks.shift();
blocks.shift();
foundOmissionBlock = true;
}
else if (str.substr(str.length - 2) === '::') {
blocks.pop();
blocks.pop();
foundOmissionBlock = true;
}
for (let i = 0; i < blocks.length; ++i) {
// test for a :: which can not be at the string start/end
// since those cases have been handled above
if (blocks[i] === '' && i > 0 && i < blocks.length - 1) {
if (foundOmissionBlock) {
return false; // multiple :: in address
}
foundOmissionBlock = true;
}
else if (foundIPv4TransitionBlock && i === blocks.length - 1) ;
else if (!ipv6Block.test(blocks[i])) {
return false;
}
}
if (foundOmissionBlock) {
return blocks.length >= 1;
}
return blocks.length === expectedNumberOfBlocks;
}
return false;
}
// credits: https://github.com/validatorjs/validator.js/blob/master/src/lib/isCreditCard.js
const creditCard$1 = /^(?:4[0-9]{12}(?:[0-9]{3,6})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12,15}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11}|6[27][0-9]{14})$/;
function isCreditCard(str) {
const sanitized = str.replace(/[- ]+/g, '');
if (!creditCard$1.test(sanitized)) {
return false;
}
let sum = 0;
let digit;
let tmpNum;
let shouldDouble;
for (let i = sanitized.length - 1; i >= 0; i--) {
digit = sanitized.substring(i, (i + 1));
tmpNum = parseInt(digit, 10);
if (shouldDouble) {
tmpNum *= 2;
if (tmpNum >= 10) {
sum += ((tmpNum % 10) + 1);
}
else {
sum += tmpNum;
}
}
else {
sum += tmpNum;
}
shouldDouble = !shouldDouble;
}
return !!((sum % 10) === 0 ? sanitized : false);
}
function creditCard() {
return {
name: "creditCard",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return isCreditCard(value);
},
};
}
class DateAdapter {
constructor(dateLib) {
this.dateLib = dateLib;
// https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
// readonly FORMAT_yyyy_MM_dd: string = 'yyyy-MM-dd';
this.FORMAT_DATE = 'yyyy-MM-dd';
this.FORMAT_DATETIME = 'yyyy-MM-dd HH:mm:ss';
}
}
class DateFnsAdapter extends DateAdapter {
constructor(dateLib) {
super(dateLib);
this.FORMAT_DATE = 'yyyy-MM-dd';
this.FORMAT_DATETIME = 'yyyy-MM-dd HH:mm:ss';
}
isAfter(format, date, dateToCompare) {
date = this.parseDate(date, format);
dateToCompare = this.parseDate(dateToCompare, format);
return this.dateLib.isAfter(date, dateToCompare);
}
isBefore(format, date, dateToCompare) {
date = this.parseDate(date, format);
dateToCompare = this.parseDate(dateToCompare, format);
return this.dateLib.isBefore(date, dateToCompare);
}
addDays(date, days) {
return this.dateLib.addDays(date, days);
}
subDays(date, days) {
return this.dateLib.subDays(date, days);
}
parseDate(date, format) {
if (!(date instanceof Date)) {
date = this.dateLib.parse(date, format, new Date);
}
// @ts-ignore
let d = date;
d.setHours(0);
d.setMinutes(0);
d.setSeconds(0);
d.setMilliseconds(0);
return d;
}
parse(date, format, referenceDate) {
return this.dateLib.parse(date, format, referenceDate);
}
isValidDateFormat(date, format) {
return this.dateLib.isValid(this.parse(date, format, new Date));
}
isValidIsoDateFormat(date) {
return this.dateLib.isValid(this.dateLib.parseISO(date));
}
format(date, format) {
return this.dateLib.format(date, format);
}
}
class MomentAdapter extends DateAdapter {
constructor(dateLib) {
super(dateLib);
this.FORMAT_DATE = 'YYYY-MM-DD';
this.FORMAT_DATETIME = 'YYYY-MM-DD HH:mm:ss';
}
isAfter(format, date, dateToCompare) {
return this.dateLib(date, format).isAfter(this.format(dateToCompare, format));
}
isBefore(format, date, dateToCompare) {
return this.dateLib(date, format).isBefore(this.format(dateToCompare, format));
}
addDays(date, days) {
return this.dateLib(date).add(days, 'days').toDate();
}
subDays(date, days) {
return this.dateLib(date).subtract(days, 'days').toDate();
}
parseDate(date, format) {
return this.dateLib(date, format).set({
h: 0,
m: 0,
s: 0,
ms: 0,
}).toDate();
}
parse(date, format, referenceDate) {
return this.dateLib(date, format).toDate();
}
isValidDateFormat(date, format) {
return this.dateLib(date, format, true).isValid();
}
isValidIsoDateFormat(date) {
return this.dateLib(date, this.dateLib.ISO_8601).isValid();
}
format(date, format) {
return this.dateLib(date).format(format);
}
}
function dateAdapter() {
const adapterInstance = get('dateAdapter');
if (!adapterInstance) {
throw new Error('Please set date adapter to use date rules.');
}
return adapterInstance;
}
function dateAfter(args) {
if (args.length !== 2) {
throw new Error('Rule accepts 2 argument (format,date).');
}
const adapter = dateAdapter();
const [format, dateToCompare] = args;
if (typeof format !== 'string') {
throw new TypeError('First element must be date format.');
}
return {
name: "dateAfter",
handler: (value) => {
return adapter.isAfter(format, value, dateToCompare || new Date);
},
};
}
function after(args) {
if (!args.length || args.length > 2) {
throw new Error('Rule accepts 2 argument (format,[date optional]).');
}
const adapter = dateAdapter();
const [format, dateToCompare] = args;
if (typeof format !== 'string') {
throw new TypeError('First element must be date format.');
}
return {
name: "after",
handler: (value) => {
return adapter.isAfter(format, value, dateToCompare || new Date());
},
};
}
function dateAfterToday(args) {
if (args.length !== 1) {
throw new Error('Rule accepts only 1 argument (format).');
}
const adapter = dateAdapter();
const [format] = args;
return {
name: "dateAfterToday",
handler: (value) => {
return adapter.isAfter(format, value, new Date);
},
};
}
function dateBefore(args) {
if (args.length !== 2) {
throw new Error('Rule accepts 2 argument (format,date).');
}
const adapter = dateAdapter();
const [format, dateToCompare] = args;
if (typeof format !== 'string') {
throw new TypeError('First element must be date format.');
}
return {
name: "dateBefore",
handler: (value) => {
return adapter.isBefore(format, value, dateToCompare);
},
};
}
function before(args) {
if (!args.length || args.length > 2) {
throw new Error('Rule accepts 2 argument (format,[date optional]).');
}
const adapter = dateAdapter();
const [format, dateToCompare] = args;
if (typeof format !== 'string') {
throw new TypeError('First element must be date format.');
}
return {
name: "before",
handler: (value) => {
return adapter.isBefore(format, value, dateToCompare || new Date());
},
};
}
function dateBeforeToday(args) {
if (args.length !== 1) {
throw new Error('Rule accepts only 1 argument (format).');
}
const adapter = dateAdapter();
const [format] = args;
return {
name: "dateBeforeToday",
handler: (value) => {
return adapter.isBefore(format, value, new Date);
},
};
}
function dateFormat(args) {
if (args.length !== 1) {
throw new Error('Rule accepts only 1 argument (format).');
}
const [format] = args;
const adapter = dateAdapter();
return {
name: "dateFormat",
handler: (value) => {
return adapter.isValidDateFormat(value, format);
},
};
}
function dateISO() {
const adapter = dateAdapter();
return {
name: "dateISO",
handler: (value) => {
return adapter.isValidIsoDateFormat(value);
},
};
}
// export { dateISO as dateiso, dateISO as iso8601 };
function datetime() {
const adapter = dateAdapter();
return {
name: 'datetime',
handler: (value) => {
return adapter.isValidDateFormat(value, adapter.FORMAT_DATETIME);
}
};
}
function date(args = []) {
const adapter = dateAdapter();
const [format = adapter.FORMAT_DATE] = args;
return {
name: 'date',
handler: (value) => {
return adapter.isValidDateFormat(value, format);
}
};
}
const iso8601Regex = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
function dateiso() {
return {
name: "dateiso",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return iso8601Regex.test(value);
},
};
}
function iso8601() {
return dateiso();
}
function different(args) {
if (!args.length) {
throw new Error("Invalid number of arguments.");
}
return {
name: "different",
handler: (value, v) => {
const [otherInput] = args;
const otherValue = v.attributeValue(otherInput);
if (otherValue === value) {
return false;
}
return true;
},
};
}
function digits(args = []) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const [lenStr] = args;
const len = parseInt(lenStr, 10);
if (!len) {
throw new TypeError('Seed must be number, greater then 0.');
}
return {
name: "digits",
handler: (value) => {
const v = String(value);
if (isInt(v, true)
&& v.length === len) {
return true;
}
return false;
},
};
}
function digitsBetween(args = []) {
if (args.length !== 2) {
throw new Error('Invalid number of arguments.');
}
const [min, max] = args;
const minInt = parseInt(min, 10);
const maxInt = parseInt(max, 10);
if (!minInt || !maxInt) {
throw new TypeError('Seeds must be number, greater then 0.');
}
if (min > max) {
throw new RangeError('Seed min must be less then max.');
}
return {
name: "digitsBetween",
handler: (value) => {
const v = String(value);
const len = v.length;
if (isInt(v, true)
&& len >= minInt
&& len <= maxInt) {
return true;
}
return false;
},
};
}
function domain() {
return {
name: "domain",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return isDomain(value);
},
};
}
function email() {
return {
name: "email",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return isEmail(value);
},
};
}
const asciiRegex = /^[\x00-\x7F]+$/;
function ascii() {
return {
name: "ascii",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return asciiRegex.test(value);
},
};
}
function json() {
return {
name: "json",
handler: (value) => {
try {
const obj = JSON.parse(value);
return !!obj && typeof obj === 'object';
}
catch (e) {
/* ignore */
}
return false;
},
};
}
const notBase64 = /[^A-Z0-9+\/=]/i;
const urlSafeBase64 = /^[A-Z0-9_\-]*$/i;
function base64(args = []) {
return {
name: "base64",
handler: (value) => {
const len = value.length;
if (args && args[0] === 'urlsafe') {
return urlSafeBase64.test(value);
}
if (len % 4 !== 0 || notBase64.test(value)) {
return false;
}
const firstPaddingChar = value.indexOf('=');
return firstPaddingChar === -1 ||
firstPaddingChar === len - 1 ||
(firstPaddingChar === len - 2 && value[len - 1] === '=');
},
};
}
function equals(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const [otherValue] = args;
return {
name: "equals",
handler: (value) => {
return value === otherValue;
},
};
}
function gt(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const [anotherAttr] = args;
return {
name: "gt",
handler: (value, v) => {
const anotherAttrVal = v.attributeValue(anotherAttr);
return Number(value) > Number(anotherAttrVal);
},
};
}
function gte(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
return {
name: "gte",
handler: (value, v) => {
const [anotherAttr] = args;
const anotherAttrVal = v.attributeValue(anotherAttr);
return Number(value) >= Number(anotherAttrVal);
},
};
}
const lengths = {
md5: 32,
md4: 32,
sha1: 40,
sha256: 64,
sha384: 96,
sha512: 128,
ripemd128: 32,
ripemd160: 40,
tiger128: 32,
tiger160: 40,
tiger192: 48,
crc32: 8,
crc32b: 8,
};
function hash(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const [algo] = args;
const len = lengths[algo];
if (!len) {
throw new Error(`Algo ${algo} not supported.`);
}
return {
name: "hash",
handler: (value) => {
const hash = new RegExp(`^[a-fA-F0-9]{${len}}$`);
return hash.test(value);
},
};
}
function camelCaseToSentance(str) {
return str.replace(/([A-Z]+)/g, " $1").trimLeft().toLowerCase();
}
function snakeCaseToSentance(str) {
return str.replace(/_/g, " ");
}
function sizeToBytes(inputSize) {
const size = inputSize.toLowerCase();
/* istanbul ignore next */
if (size.includes('gb') || size.includes('g')) {
return parseInt(size.replace('gb', '').replace('g', '')) * 1024 * 1024 * 1024;
}
/* istanbul ignore next */
if (size.includes('mb') || size.includes('m')) {
return parseInt(size.replace('mb', '').replace('m', '')) * 1024 * 1024;
}
/* istanbul ignore next */
if (size.includes('kb') || size.includes('k')) {
return parseInt(size.replace('kb', '').replace('k', '')) * 1024;
}
/* istanbul ignore next */
if (size.includes('b')) {
return parseInt(size.replace('b', ''));
}
/* istanbul ignore next */
return parseInt(size) * 1024;
}
const hexadecimal = /^(0x|0h)?[0-9A-F]+$/i;
function isHexadecimal(value) {
return hexadecimal.test(value);
}
const hexcolor = /^#?([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$/i;
function hex() {
return {
name: "hex",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return isHexadecimal(value);
},
};
}
function hexColor() {
return {
name: "hexColor",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return hexcolor.test(value);
},
};
}
function _in(args) {
if (!args || !args.length) {
throw new Error('Invalid number of arguments.');
}
return {
name: "in",
handler: (value) => {
return !(args.indexOf(value) < 0);
},
};
}
function notIn(args) {
if (!args.length) {
throw new Error('Invalid number of arguments.');
}
return {
name: "notIn",
handler: (value) => {
return !_in(args).handler(value);
},
};
}
function ip(args = []) {
return {
name: "ip",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
return isIp(value, args[0] || '');
},
};
}
const lat = /^\(?[+-]?(90(\.0+)?|[1-8]?\d(\.\d+)?)$/;
const long = /^\s?[+-]?(180(\.0+)?|1[0-7]\d(\.\d+)?|\d{1,2}(\.\d+)?)\)?$/;
const latDMS = /^(([1-8]?\d)\D+([1-5]?\d|60)\D+([1-5]?\d|60)(\.\d+)?|90\D+0\D+0)\D+[NSns]?$/i;
const longDMS = /^\s*([1-7]?\d{1,2}\D+([1-5]?\d|60)\D+([1-5]?\d|60)(\.\d+)?|180\D+0\D+0)\D+[EWew]?$/i;
function latLong(args = []) {
const checkDMS = args && args[0] === 'dms';
return {
name: "latLong",
handler: (value) => {
if (typeof value !== 'string') {
return false;
}
if (!value.includes(',')) {
return false;
}
const pair = value.split(',');
if ((pair[0].startsWith('(') && !pair[1].endsWith(')'))
|| (pair[1].endsWith(')') && !pair[0].startsWith('(')))
return false;
if (checkDMS) {
return latDMS.test(pair[0]) && longDMS.test(pair[1]);
}
return lat.test(pair[0]) && long.test(pair[1]);
},
};
}
function maxLength(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const maxNum = parseInt(args[0], 10);
return {
name: "maxLength",
handler: (value) => {
return value.toString().length <= maxNum;
},
};
}
function minLength(args) {
if (args.length !== 1) {
throw new Error('Invalid number of arguments.');
}
const maxNum = parseInt(args[0], 10);
return {
name: "minLength",
handler: (value) => {
return (value.toString().length >= maxNum);
},
};
}
function length(args) {
let min;
if (args.length < 1 || args.length > 2) {
throw new Error('Invalid number of arguments.');
}
const max = parseInt(args[0], 10);
if (args[1]) {
min = parseInt(args[1], 10);
}
return {
name: 'length',
handler: (value) => {
const len = value.length;
if (len <= max) {
if (min && len < min) {
return false;
}
return true;
}
return false;
},
};
}
function lengthBetween(args) {
if (args.length !== 2) {
throw new Error('Invalid number of arguments.');
}
const min = parseInt(args[0], 10);
const max = parseInt(args[1], 10);
if (min >= max) {
throw new RangeError('Seed min must be less then max.');
}
return {
name: 'lengthBetween',