UNPKG

vdck

Version:

Vdck is a small, fast, and robust class for type-checking your data in JavaScript/TypeScript, with built-in methods to validate IP and email addresses

224 lines (223 loc) 9.45 kB
export default class Vdck { printError; disabled; constructor(printError, disabled = false) { this.printError = printError; this.disabled = disabled; } eLog(message, type) { console.error(`[${new Date().toISOString()}] ${type ? `${type} error:` : "Error:"}`, message); } ; formatOptions(options) { const fnObj = { min: 0, max: null, trim: false, regex: null }; if (("min" in options) && typeof options.min == "number" && Number.isInteger(options.min) && options.min >= 0) fnObj.min = options.min; if (("max" in options) && typeof options.max == "number" && Number.isInteger(options.max) && options.max >= fnObj.min) fnObj.max = options.max; if (("trim" in options) && typeof options.trim == "boolean") fnObj.trim = options.trim; if (("regex" in options) && options.regex instanceof RegExp) fnObj.regex = options.regex; return fnObj; } isEmail(value, regex = null) { if (this.disabled) return true; const EMAIL_REGEX = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/; if (!(regex instanceof RegExp)) regex = EMAIL_REGEX; return this.type(value, "string", { min: 5, max: 254, regex: regex }); } isIP(value) { if (this.disabled) return true; if (!this.type(value, "string", { min: 3, max: 40 })) return false; if (value.startsWith("::ffff:")) { const ipv4Part = value.slice(7); return this.isIP(ipv4Part); } else if (["::1", "::"].includes(value)) { return true; } let splitType = ":"; if (/\./.test(value)) splitType = "."; const splittedIp = value.split(splitType); if (splittedIp.length == 4 && splitType == ".") { for (let i = 0; i < splittedIp.length; i++) { const triplet = splittedIp[i]; const castTriplet = Number(triplet); if (isNaN(castTriplet) || !(castTriplet >= 0 && castTriplet <= 255)) { if (this.printError) this.eLog(`the given ip was not a valid IPv4 address`, "General"); return false; } } } else if (splittedIp.length > 1 && splittedIp.length <= 8 && splitType == ":") { let emptySegments = 0; for (let i = 0; i < splittedIp.length; i++) { const part = splittedIp[i]; if (part === "") { emptySegments++; continue; } if (!/^[0-9a-fA-F]{1,4}$/.test(part)) { if (this.printError) this.eLog(`this "${part}" is an invalid IPv6 segment`, "General"); return false; } } if (emptySegments > 1) { if (this.printError) this.eLog(`invalid IPv6 address, too many "::"`, "General"); return false; } } else { if (this.printError) this.eLog(`the given ip was not a valid ip address`, "General"); return false; } return true; } type(value, type, options) { if (this.disabled) return true; let correctType = false; const valueCheck = {}.toString.call(value).slice(8, -1).toLowerCase(); switch (type) { case "class": if (valueCheck == "object") { try { if ((/^class/.test(value.constructor.toString()))) correctType = true; } catch (err) { if (this.printError) this.eLog(err, "Unknown"); return false; } } break; case "number": case "int": case "float": if (valueCheck == "string" || valueCheck == "number") { if (valueCheck == "string") { value = Number(value.trim()); if (isNaN(value)) break; } if (type == "float" && Number.isInteger(value)) break; if (type == "int" && !Number.isInteger(value)) break; correctType = true; } break; case "object": if (type == valueCheck && !Array.isArray(value)) { correctType = true; } break; default: if (type == valueCheck) correctType = true; break; } if (!correctType) { if (this.printError) this.eLog(`the given value is not a valid ${valueCheck}`, "Type"); return false; } if (options) { const fnOptions = this.formatOptions(options); if (valueCheck === "string") { if (fnOptions.trim) value = value.trim(); if (fnOptions.regex instanceof RegExp && !fnOptions.regex.test(value)) { if (this.printError) this.eLog("Params error", `the given string doesn't match the regex pattern`); return false; } } const size = valueCheck === "string" ? value.length : valueCheck.endsWith("array") ? value.length : valueCheck === "arraybuffer" ? value.byteLength : valueCheck === "object" ? Object.keys(value).length : valueCheck === "map" || valueCheck === "set" ? value.size : null; if (size !== null) { if (size < fnOptions.min) { if (this.printError) this.eLog("Params error", `the given value has fewer elements/characters than ${fnOptions.min}`); return false; } if (typeof fnOptions.max === "number" && size > fnOptions.max) { if (this.printError) this.eLog("Params error", `the given value has more elements/characters than ${fnOptions.max}`); return false; } } } return true; } sameObjects(main, struct) { if (this.disabled) return true; if (!this.type(main, "object") || !this.type(struct, "object")) return false; const mainObjectKeysLength = Object.keys(main).length; const structObjectKeysLength = Object.keys(struct).length; if ((mainObjectKeysLength > 0 && structObjectKeysLength == 0) || (mainObjectKeysLength == 0 && structObjectKeysLength > 0)) { return false; } try { for (const key in struct) { if (Object.prototype.hasOwnProperty.call(struct, key)) { const expectedType = struct[key]; const actualValue = main[key]; if (!(key in main)) { if (this.printError) this.eLog(`'${key}' key is not a main's object key`, "Invalid key"); return false; } if (typeof expectedType === "string") { if (!this.type(actualValue, expectedType.toLowerCase(), undefined)) { return false; } } else if (typeof expectedType === "object" && !Array.isArray(actualValue) && expectedType !== null) { if (!this.sameObjects(actualValue, expectedType)) { if (this.printError) this.eLog(`key '${key}' does not match the nested structure`, "Mismatched structure"); return false; } } else { if (this.printError) this.eLog(`invalid structure definition for key '${key}'`, "Unknown key's type"); return false; } } else { if (this.printError) this.eLog(`invalid property in object`, "Invalid property"); return false; } } } catch (err) { if (this.printError) this.eLog(`${String(err)}`, "General"); return false; } return true; } }