diff --git a/lib/util/schema-utils.spec.ts b/lib/util/schema-utils.spec.ts index f62fcc4bbffe08a3898a5ae1f455f93d5f8d1f21..9be7ac651c4489a43f87f2c15c90aca5c43e294a 100644 --- a/lib/util/schema-utils.spec.ts +++ b/lib/util/schema-utils.spec.ts @@ -1,5 +1,12 @@ import { z } from 'zod'; -import { Json, Json5, LooseArray, LooseRecord, UtcDate } from './schema-utils'; +import { + Json, + Json5, + LooseArray, + LooseRecord, + Url, + UtcDate, +} from './schema-utils'; describe('util/schema-utils', () => { describe('LooseArray', () => { @@ -270,4 +277,22 @@ describe('util/schema-utils', () => { expect(() => UtcDate.parse('foobar')).toThrow(); }); }); + + describe('Url', () => { + it('parses valid URLs', () => { + const urlStr = 'https://www.example.com/foo/bar?baz=qux'; + const parsedUrl = Url.parse(urlStr); + expect(parsedUrl).toMatchObject({ + protocol: 'https:', + hostname: 'www.example.com', + pathname: '/foo/bar', + search: '?baz=qux', + }); + }); + + it('throws an error for invalid URLs', () => { + const urlStr = 'invalid-url-string'; + expect(() => Url.parse(urlStr)).toThrow('Invalid URL'); + }); + }); }); diff --git a/lib/util/schema-utils.ts b/lib/util/schema-utils.ts index f3dfe108ce7a508901156887e21ceaae6d61d11f..0caa342e39164329121da32abf098b993f9a4a1c 100644 --- a/lib/util/schema-utils.ts +++ b/lib/util/schema-utils.ts @@ -223,3 +223,12 @@ export const UtcDate = z } return date; }); + +export const Url = z.string().transform((str, ctx): URL => { + try { + return new URL(str); + } catch (e) { + ctx.addIssue({ code: 'custom', message: 'Invalid URL' }); + return z.NEVER; + } +});