Skip to content

Instantly share code, notes, and snippets.

@rafa-thayto
Created October 8, 2024 04:11
Show Gist options
  • Select an option

  • Save rafa-thayto/627ef8faf3dd7e8208c570667513b679 to your computer and use it in GitHub Desktop.

Select an option

Save rafa-thayto/627ef8faf3dd7e8208c570667513b679 to your computer and use it in GitHub Desktop.
Typebox date, date-time, time, email, uuid, url, ipv4, ipv6 helper
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