UNPKG

validate-fields

Version:
175 lines (159 loc) 4.9 kB
'use strict' let base64Regex = /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/][AQgw]==|[A-Za-z0-9+/]{2}[AEIMQUYcgkosw048]=)?$/ module.exports = function (context) { /** * A non-empty string (unless in a hash map, marked as optional) * Example: {name: {first: String, 'last?': String}} * The string will be HTML escaped if options.escape is true */ context.registerObjectType(String, 'string', (value, _, options) => options.escape ? escape(value) : value, '$String') /** * A date value in string format (like '2014-08-08T16:14:16.046Z') * It will convert the string into a date object * It accepts all formats defined by the Date(str) constructor, but the a ISO string is recommended */ context.registerObjectType(Date, 'string', value => { let date = new Date(value) if (isNaN(date.getTime())) { throw 'I was expecting a date string' } return date }, '$Date', () => ({ type: 'string', format: 'date-time' })) /** * A non-empty string with limited length * Examples: * 'string(12)': exactly 12 chars * 'string(,100)': at most 100 chars * 'string(8,)': at least 8 chars * 'string(8,100)': at most 100, at least 8 * The string will be HTML escaped if options.escape is true */ context.registerTaggedType({ tag: 'string', jsonType: 'string', minArgs: 1, maxArgs: 2, sparse: true, numeric: true }, (value, args, options) => { if (options.escape) { value = escape(value) } if (args.length === 1) { // Exact length if (value.length !== args[0]) { throw 'I was expecting exactly ' + args[0] + ' chars' } } else if (args[0] !== undefined && value.length < args[0]) { // Min length throw 'I was expecting at least ' + args[0] + ' chars' } else if (args[1] !== undefined && value.length > args[1]) { // Max length throw 'I was expecting at most ' + args[1] + ' chars' } return value }, extra => extra.original, args => { if (args.length === 1) { return buildSchema(undefined, args[0], args[0]) } return buildSchema(undefined, args[0], args[1]) }) /** * A non-empty hex-encoded string * Examples: * 'hex': a non-empty hex string (with even number of hex-chars) * 'hex(12)': a hex-string with exactly 12 hex-chars (that is, 6 bytes) */ context.registerTaggedType({ tag: 'hex', jsonType: 'string', maxArgs: 1, numeric: true }, (value, args) => { if (!value.match(/^[0-9a-fA-F]+$/)) { throw 'I was expecting a hex-encoded string' } else if (args.length && value.length !== args[0]) { throw 'I was expecting a string with ' + args[0] + ' hex-chars' } else if (!args.length && value.length % 2) { throw 'I was expecting an even number of hex-chars' } }, extra => extra.original, args => buildSchema('hex', args[0], args[0])) /** * A base64 string, with valid padding */ context.registerType('base64', 'string', value => { if (!base64Regex.test(value)) { throw 'I was expecting a base64-encoded string' } }, 'base64', () => buildSchema('byte')) /** * A mongo objectId as a 24-hex-char string */ context.registerType('id', 'string', value => { if (!value.match(/^[0-9a-fA-F]{24}$/)) { throw 'I was expecting a mongo objectId' } }, 'id', () => buildSchema('id')) /** * An email * Note: this regex isn't much strict on purpose, since: * 1) There is no complete regex that matches exactly the spec * 2) The right way to check if a email is valid is trying to send an message to it! * (even this is broken: it's easy to get a temporary trash mailbox) */ context.registerType('email', 'string', value => { // The regex forces one '@', followed by at least one '.' if (!value.match(/^[^@]+@.+\.[^.]+$/i)) { throw 'I was expecting an email address' } }, 'email', () => buildSchema('email')) /** * A string from the given set * Example: 'in(red, green, blue)' matches 'red', 'green' and 'blue', but no other */ context.registerTaggedType({ tag: 'in', jsonType: 'string' }, (value, args) => { if (args.indexOf(value) === -1) { throw 'I was expecting one of [' + args.join(', ') + ']' } }, extra => extra.original, args => ({ type: 'string', enum: args.slice() })) } /** * @param {string} str * @returns {string} */ function escape(str) { return str.replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/'/g, '&#39;') .replace(/"/g, '&quot;') } /** * @param {string} [pattern] * @param {number} [min] * @param {number} [max] */ function buildSchema(format, min, max) { let ret = { type: 'string' } if (format !== undefined) { ret.format = format } if (min !== undefined) { ret.minLength = min } if (max !== undefined) { ret.maxLength = max } return ret }