| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 |
- import { z } from 'zod'
- const HTTP_URL_PROTOCOL_REGEX = /^https?:\/\//
- const IPV4_SEGMENT = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)'
- const IPV4_REGEX = new RegExp(`^(?:${IPV4_SEGMENT}\\.){3}${IPV4_SEGMENT}$`)
- const BRACKETED_IPV6_REGEX = /^\[[0-9a-f:.]+\]$/i
- export const hasHttpUrlProtocol = (value: string) => HTTP_URL_PROTOCOL_REGEX.test(value)
- export const isValidHttpEndpointUrl = (value: string) => {
- try {
- const url = new URL(value)
- if (url.protocol !== 'http:' && url.protocol !== 'https:') return false
- const { hostname } = url
- return (
- hostname === 'localhost' ||
- hostname.includes('.') ||
- IPV4_REGEX.test(hostname) ||
- BRACKETED_IPV6_REGEX.test(hostname)
- )
- } catch {
- return false
- }
- }
- type HttpEndpointUrlSchemaOptions = {
- requiredMessage: string
- invalidMessage: string
- prefixMessage: string
- }
- export const httpEndpointUrlSchema = ({
- requiredMessage,
- invalidMessage,
- prefixMessage,
- }: HttpEndpointUrlSchemaOptions) =>
- z
- .string()
- .trim()
- .min(1, requiredMessage)
- .superRefine((value, ctx) => {
- if (!value) return
- if (!hasHttpUrlProtocol(value)) {
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: prefixMessage,
- })
- return
- }
- if (!isValidHttpEndpointUrl(value)) {
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: invalidMessage,
- })
- }
- })
|