Created
October 8, 2024 04:11
-
-
Save rafa-thayto/627ef8faf3dd7e8208c570667513b679 to your computer and use it in GitHub Desktop.
Typebox date, date-time, time, email, uuid, url, ipv4, ipv6 helper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { FormatRegistry } from "@sinclair/typebox"; | |
| export const setupFormatRegistry = () => { | |
| FormatRegistry.Set("date-time", (value) => IsDateTime(value, true)); | |
| FormatRegistry.Set("date", (value) => IsDate(value)); | |
| FormatRegistry.Set("time", (value) => IsTime(value)); | |
| FormatRegistry.Set("email", (value) => IsEmail(value)); | |
| FormatRegistry.Set("uuid", (value) => IsUuid(value)); | |
| FormatRegistry.Set("url", (value) => IsUrl(value)); | |
| FormatRegistry.Set("ipv6", (value) => IsIPv6(value)); | |
| FormatRegistry.Set("ipv4", (value) => IsIPv4(value)); | |
| }; | |
| const UUID = /^(?:urn:uuid:)?[\da-f]{8}-(?:[\da-f]{4}-){3}[\da-f]{12}$/i; | |
| const DATE_TIME_SEPARATOR = /t|\s/i; | |
| const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i; | |
| const DATE = /^(\d{4})-(\d{2})-(\d{2})$/; | |
| const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; | |
| const IPV4 = | |
| /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/; | |
| const IPV6 = | |
| /^((([\da-f]{1,4}:){7}([\da-f]{1,4}|:))|(([\da-f]{1,4}:){6}(:[\da-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([\da-f]{1,4}:){5}(((:[\da-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([\da-f]{1,4}:){4}(((:[\da-f]{1,4}){1,3})|((:[\da-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([\da-f]{1,4}:){3}(((:[\da-f]{1,4}){1,4})|((:[\da-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([\da-f]{1,4}:){2}(((:[\da-f]{1,4}){1,5})|((:[\da-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([\da-f]{1,4}:)(((:[\da-f]{1,4}){1,6})|((:[\da-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[\da-f]{1,4}){1,7})|((:[\da-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i; | |
| const URL = | |
| /^(?:https?|wss?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00A1}-\u{FFFF}]+-)*[a-z0-9\u{00A1}-\u{FFFF}]+)(?:\.(?:[a-z0-9\u{00A1}-\u{FFFF}]+-)*[a-z0-9\u{00A1}-\u{FFFF}]+)*(?:\.(?:[a-z\u{00A1}-\u{FFFF}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu; | |
| const EMAIL = | |
| // eslint-disable-next-line unicorn/better-regex | |
| /^[\w!#$%&'*+/=?^`{|}~-]+(?:\.[\w!#$%&'*+/=?^`{|}~-]+)*@(?:[a-z\d](?:[a-z\d-]*[a-z\d])?\.)+[a-z\d](?:[a-z\d-]*[a-z\d])?$/i; | |
| function IsLeapYear(year: number): boolean { | |
| return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); | |
| } | |
| function IsDate(str: string): boolean { | |
| const matches: string[] | undefined = DATE.exec(str) ?? undefined; | |
| if (!matches) { | |
| return false; | |
| } | |
| const year = Number(matches[1]); | |
| const month = Number(matches[2]); | |
| const day = Number(matches[3]); | |
| return ( | |
| month >= 1 && | |
| month <= 12 && | |
| day >= 1 && | |
| day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]!) | |
| ); | |
| } | |
| function IsTime(str: string, strictTimeZone?: boolean): boolean { | |
| const matches: string[] | undefined = TIME.exec(str) ?? undefined; | |
| if (!matches) { | |
| return false; | |
| } | |
| const hr = Number(matches[1]); | |
| const min = Number(matches[2]); | |
| const sec = Number(matches[3]); | |
| const tz: string | undefined = matches[4]; | |
| const tzSign: number = matches[5] === "-" ? -1 : 1; | |
| const tzH = Number(matches[6] || 0); | |
| const tzM = Number(matches[7] || 0); | |
| if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) { | |
| return false; | |
| } | |
| if (hr <= 23 && min <= 59 && sec < 60) { | |
| return true; | |
| } | |
| const utcMin = min - tzM * tzSign; | |
| const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0); | |
| return ( | |
| (utcHr === 23 || utcHr === -1) && | |
| (utcMin === 59 || utcMin === -1) && | |
| sec < 61 | |
| ); | |
| } | |
| function IsDateTime(value: string, strictTimeZone?: boolean): boolean { | |
| const dateTime: string[] = value.split(DATE_TIME_SEPARATOR); | |
| return ( | |
| dateTime.length === 2 && | |
| IsDate(dateTime[0]!) && | |
| IsTime(dateTime[1]!, strictTimeZone) | |
| ); | |
| } | |
| function IsEmail(value: string) { | |
| return EMAIL.test(value); | |
| } | |
| function IsUuid(value: string) { | |
| return UUID.test(value); | |
| } | |
| function IsUrl(value: string) { | |
| return URL.test(value); | |
| } | |
| function IsIPv6(value: string) { | |
| return IPV6.test(value); | |
| } | |
| function IsIPv4(value: string) { | |
| return IPV4.test(value); | |
| } | |
| // After this, implement in fastify | |
| setupFormatRegistry(); | |
| const server = fastify(serverOptions) | |
| .setValidatorCompiler(TypeBoxValidatorCompiler) | |
| .withTypeProvider<TypeBoxTypeProvider>(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment