Skip to content

Commit bbfaabc

Browse files
committed
fix(string): implement
1 parent d128dc7 commit bbfaabc

File tree

3 files changed

+240
-9
lines changed

3 files changed

+240
-9
lines changed

src/internals/schemas/checks/string-format.ts

Lines changed: 138 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as core from 'zod/v4/core'
2+
import { HashFormat } from 'zod/v4/core/util'
23
import { rootFake as internalFake } from '../../fake'
34
import { getFaker, randexp } from '../../random'
45

@@ -7,7 +8,7 @@ export function fakeStringFormat<T extends core.$ZodStringFormat>(
78
rootFake: typeof internalFake,
89
): undefined | string {
910
let data = undefined
10-
const format = schema._zod.def.format as core.$ZodStringFormats
11+
const format = schema._zod.def.format as core.$ZodStringFormats | 'hex' | 'hostname' | 'httpUrl' | HashFormat
1112
switch (format) {
1213
case 'base64': {
1314
data = randexp(core.regexes.base64)
@@ -41,7 +42,14 @@ export function fakeStringFormat<T extends core.$ZodStringFormat>(
4142
break
4243
}
4344
case 'datetime': {
44-
data = randexp(core.regexes.datetime({}))
45+
const def = schema._zod.def as core.$ZodISODateTimeDef
46+
data = randexp(
47+
core.regexes.datetime({
48+
local: def.local,
49+
offset: def.offset,
50+
precision: def.precision,
51+
}),
52+
)
4553
break
4654
}
4755
case 'duration': {
@@ -67,13 +75,25 @@ export function fakeStringFormat<T extends core.$ZodStringFormat>(
6775
break
6876
}
6977
case 'email': {
70-
data = getFaker().internet.email()
78+
data = randexp(new RegExp(schema._zod.def.pattern ?? core.regexes.email.source))
7179
break
7280
}
7381
case 'emoji': {
7482
data = '☘'
7583
break
7684
}
85+
case 'hex': {
86+
data = randexp(core.regexes.hex)
87+
break
88+
}
89+
case 'hostname': {
90+
data = getFaker().internet.domainName()
91+
break
92+
}
93+
case 'httpUrl': {
94+
data = getFaker().internet.url()
95+
break
96+
}
7797
case 'guid': {
7898
data = randexp(core.regexes.guid)
7999
break
@@ -87,13 +107,31 @@ export function fakeStringFormat<T extends core.$ZodStringFormat>(
87107
break
88108
}
89109
case 'jwt': {
90-
data = getFaker().internet.jwt()
110+
const def = schema._zod.def as core.$ZodJWTDef
111+
data = getFaker().internet.jwt({
112+
header: {
113+
alg: def.alg || getFaker().internet.jwtAlgorithm(),
114+
typ: 'JWT',
115+
},
116+
})
91117
break
92118
}
93119
case 'ksuid': {
94120
data = randexp(core.regexes.ksuid)
95121
break
96122
}
123+
case 'md5_base64': {
124+
data = randexp(core.regexes.md5_base64)
125+
break
126+
}
127+
case 'md5_base64url': {
128+
data = randexp(core.regexes.md5_base64url)
129+
break
130+
}
131+
case 'md5_hex': {
132+
data = randexp(core.regexes.md5_hex)
133+
break
134+
}
97135
case 'nanoid': {
98136
data = randexp(core.regexes.nanoid)
99137
break
@@ -102,20 +140,113 @@ export function fakeStringFormat<T extends core.$ZodStringFormat>(
102140
data = randexp(schema._zod.def.pattern!)
103141
break
104142
}
143+
case 'sha1_base64': {
144+
data = randexp(core.regexes.sha1_base64)
145+
break
146+
}
147+
case 'sha1_base64url': {
148+
data = randexp(core.regexes.sha1_base64url)
149+
break
150+
}
151+
case 'sha1_hex': {
152+
data = randexp(core.regexes.sha1_hex)
153+
break
154+
}
155+
case 'sha256_base64': {
156+
data = randexp(core.regexes.sha256_base64)
157+
break
158+
}
159+
case 'sha256_base64url': {
160+
data = randexp(core.regexes.sha256_base64url)
161+
break
162+
}
163+
case 'sha256_hex': {
164+
data = randexp(core.regexes.sha256_hex)
165+
break
166+
}
167+
case 'sha384_base64': {
168+
data = randexp(core.regexes.sha384_base64)
169+
break
170+
}
171+
case 'sha384_base64url': {
172+
data = randexp(core.regexes.sha384_base64url)
173+
break
174+
}
175+
case 'sha384_hex': {
176+
data = randexp(core.regexes.sha384_hex)
177+
break
178+
}
179+
case 'sha512_base64': {
180+
data = randexp(core.regexes.sha512_base64)
181+
break
182+
}
183+
case 'sha512_base64url': {
184+
data = randexp(core.regexes.sha512_base64url)
185+
break
186+
}
187+
case 'sha512_hex': {
188+
data = randexp(core.regexes.sha512_hex)
189+
break
190+
}
105191
case 'time': {
106-
data = randexp(core.regexes.time({}))
192+
const def = schema._zod.def as core.$ZodISOTimeDef
193+
data = randexp(
194+
core.regexes.time({
195+
precision: def.precision,
196+
}),
197+
)
107198
break
108199
}
109200
case 'ulid': {
110201
data = randexp(core.regexes.ulid)
111202
break
112203
}
113204
case 'url': {
114-
data = getFaker().internet.url()
205+
const protocol = (schema as core.$ZodURL)._zod.def.protocol
206+
const hostname = (schema as core.$ZodURL)._zod.def.hostname
207+
if (protocol && hostname) {
208+
data = randexp(protocol) + '://' + randexp(hostname)
209+
} else if (protocol) {
210+
data = randexp(protocol) + '://' + getFaker().internet.domainName()
211+
} else if (hostname) {
212+
data = 'https://' + randexp(hostname)
213+
} else {
214+
data = getFaker().internet.url()
215+
}
115216
break
116217
}
117218
case 'uuid': {
118-
data = randexp(core.regexes.uuid())
219+
const version = (schema as core.$ZodUUID)._zod.def.version
220+
switch (version) {
221+
case 'v1':
222+
data = randexp(core.regexes.uuid(1))
223+
break
224+
case 'v2':
225+
data = randexp(core.regexes.uuid(2))
226+
break
227+
case 'v3':
228+
data = randexp(core.regexes.uuid(3))
229+
break
230+
case 'v4':
231+
data = randexp(core.regexes.uuid(4))
232+
break
233+
case 'v5':
234+
data = randexp(core.regexes.uuid(5))
235+
break
236+
case 'v6':
237+
data = randexp(core.regexes.uuid(6))
238+
break
239+
case 'v7':
240+
data = randexp(core.regexes.uuid(7))
241+
break
242+
case 'v8':
243+
data = randexp(core.regexes.uuid(8))
244+
break
245+
default: {
246+
const _: undefined = version
247+
data = randexp(core.regexes.uuid())
248+
}
249+
}
119250
break
120251
}
121252
case 'xid': {

tests/mini.test.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,24 +529,74 @@ const validSuits: { schema: z.ZodMiniType; description?: string; only?: boolean;
529529
{ schema: z.cuid2(), description: 'cuid2' },
530530
{ schema: z.e164(), description: 'e164' },
531531
{ schema: z.email(), description: 'email' },
532-
{ schema: z.email({ pattern: z.regexes.html5Email }), description: 'email html5' },
533532
{ schema: z.email({ pattern: z.regexes.browserEmail }), description: 'email browser' },
533+
{ schema: z.email({ pattern: z.regexes.email }), description: 'email email' },
534+
{ schema: z.email({ pattern: z.regexes.idnEmail }), description: 'email idnEmail' },
535+
{ schema: z.email({ pattern: z.regexes.html5Email }), description: 'email html5' },
534536
{ schema: z.email({ pattern: z.regexes.rfc5322Email }), description: 'email rfc5322' },
535537
{ schema: z.email({ pattern: z.regexes.unicodeEmail }), description: 'email unicode' },
536538
{ schema: z.emoji(), description: 'emoji' },
537539
{ schema: z.guid(), description: 'guid' },
540+
{ schema: z.hash('md5'), description: 'hash md5' },
541+
{ schema: z.hash('md5', { enc: 'hex' }), description: 'hash md5 hex' },
542+
{ schema: z.hash('md5', { enc: 'base64' }), description: 'hash md5 base64' },
543+
{ schema: z.hash('md5', { enc: 'base64url' }), description: 'hash md5 base64url' },
544+
{ schema: z.hash('sha1'), description: 'hash sha1' },
545+
{ schema: z.hash('sha1', { enc: 'hex' }), description: 'hash sha1 hex' },
546+
{ schema: z.hash('sha1', { enc: 'base64' }), description: 'hash sha1 base64' },
547+
{ schema: z.hash('sha1', { enc: 'base64url' }), description: 'hash sha1 base64url' },
548+
{ schema: z.hash('sha256'), description: 'hash sha256' },
549+
{ schema: z.hash('sha256', { enc: 'hex' }), description: 'hash sha256 hex' },
550+
{ schema: z.hash('sha256', { enc: 'base64' }), description: 'hash sha256 base64' },
551+
{ schema: z.hash('sha256', { enc: 'base64url' }), description: 'hash sha256 base64url' },
552+
{ schema: z.hash('sha384'), description: 'hash sha384' },
553+
{ schema: z.hash('sha384', { enc: 'hex' }), description: 'hash sha384 hex' },
554+
{ schema: z.hash('sha384', { enc: 'base64' }), description: 'hash sha384 base64' },
555+
{ schema: z.hash('sha384', { enc: 'base64url' }), description: 'hash sha384 base64url' },
556+
{ schema: z.hash('sha512'), description: 'hash sha512' },
557+
{ schema: z.hash('sha512', { enc: 'hex' }), description: 'hash sha512 hex' },
558+
{ schema: z.hash('sha512', { enc: 'base64' }), description: 'hash sha512 base64' },
559+
{ schema: z.hash('sha512', { enc: 'base64url' }), description: 'hash sha512 base64url' },
560+
{ schema: z.hex(), description: 'hex' },
561+
{ schema: z.hostname(), description: 'hostname' },
562+
{ schema: z.httpUrl(), description: 'httpUrl' },
538563
{ schema: z.ipv4(), description: 'ipv4' },
539564
{ schema: z.ipv6(), description: 'ipv6' },
540565
{ schema: z.iso.date(), description: 'date' },
541566
{ schema: z.iso.datetime(), description: 'datetime' },
567+
{ schema: z.iso.datetime({ local: true }), description: 'datetime local' },
568+
{ schema: z.iso.datetime({ offset: true }), description: 'datetime offset' },
569+
{ schema: z.iso.datetime({ precision: 3 }), description: 'datetime precision' },
542570
{ schema: z.iso.duration(), description: 'duration' },
543571
{ schema: z.iso.time(), description: 'time' },
572+
{ schema: z.iso.time({ precision: 9 }), description: 'time precision' },
544573
{ schema: z.jwt(), description: 'jwt' },
574+
{ schema: z.jwt({ alg: 'HS256' }), description: 'jwt alg' },
545575
{ schema: z.ksuid(), description: 'ksuid' },
546576
{ schema: z.nanoid(), description: 'naoid' },
547577
{ schema: z.ulid(), description: 'ulid' },
548578
{ schema: z.url(), description: 'url' },
579+
{ schema: z.url({ hostname: /^example\.com$/ }), description: 'url hostname' },
580+
{ schema: z.url({ protocol: /^https$/ }), description: 'url protocol' },
581+
{
582+
schema: z.url({
583+
protocol: /^https?$/,
584+
hostname: z.regexes.domain,
585+
}),
586+
description: 'url protocol hostname',
587+
},
549588
{ schema: z.uuid(), description: 'uuid' },
589+
{ schema: z.uuid({ version: 'v1' }), description: 'uuid v1' },
590+
{ schema: z.uuid({ version: 'v2' }), description: 'uuid v2' },
591+
{ schema: z.uuid({ version: 'v3' }), description: 'uuid v3' },
592+
{ schema: z.uuid({ version: 'v4' }), description: 'uuid v4' },
593+
{ schema: z.uuid({ version: 'v5' }), description: 'uuid v5' },
594+
{ schema: z.uuid({ version: 'v6' }), description: 'uuid v6' },
595+
{ schema: z.uuid({ version: 'v7' }), description: 'uuid v7' },
596+
{ schema: z.uuid({ version: 'v8' }), description: 'uuid v8' },
597+
{ schema: z.uuidv4(), description: 'uuidv4' },
598+
{ schema: z.uuidv6(), description: 'uuidv6' },
599+
{ schema: z.uuidv7(), description: 'uuidv7' },
550600
{ schema: z.xid(), description: 'xid' },
551601
{
552602
schema: z.string().check(z.minLength(5), z.minLength(3), z.minLength(4), z.maxLength(5)),

tests/zod.test.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,24 +536,74 @@ const validSuits: { schema: z.ZodType; description?: string; only?: boolean; asy
536536
{ schema: z.cuid2(), description: 'cuid2' },
537537
{ schema: z.e164(), description: 'e164' },
538538
{ schema: z.email(), description: 'email' },
539-
{ schema: z.email({ pattern: z.regexes.html5Email }), description: 'email html5' },
540539
{ schema: z.email({ pattern: z.regexes.browserEmail }), description: 'email browser' },
540+
{ schema: z.email({ pattern: z.regexes.email }), description: 'email email' },
541+
{ schema: z.email({ pattern: z.regexes.idnEmail }), description: 'email idnEmail' },
542+
{ schema: z.email({ pattern: z.regexes.html5Email }), description: 'email html5' },
541543
{ schema: z.email({ pattern: z.regexes.rfc5322Email }), description: 'email rfc5322' },
542544
{ schema: z.email({ pattern: z.regexes.unicodeEmail }), description: 'email unicode' },
543545
{ schema: z.emoji(), description: 'emoji' },
544546
{ schema: z.guid(), description: 'guid' },
547+
{ schema: z.hash('md5'), description: 'hash md5' },
548+
{ schema: z.hash('md5', { enc: 'hex' }), description: 'hash md5 hex' },
549+
{ schema: z.hash('md5', { enc: 'base64' }), description: 'hash md5 base64' },
550+
{ schema: z.hash('md5', { enc: 'base64url' }), description: 'hash md5 base64url' },
551+
{ schema: z.hash('sha1'), description: 'hash sha1' },
552+
{ schema: z.hash('sha1', { enc: 'hex' }), description: 'hash sha1 hex' },
553+
{ schema: z.hash('sha1', { enc: 'base64' }), description: 'hash sha1 base64' },
554+
{ schema: z.hash('sha1', { enc: 'base64url' }), description: 'hash sha1 base64url' },
555+
{ schema: z.hash('sha256'), description: 'hash sha256' },
556+
{ schema: z.hash('sha256', { enc: 'hex' }), description: 'hash sha256 hex' },
557+
{ schema: z.hash('sha256', { enc: 'base64' }), description: 'hash sha256 base64' },
558+
{ schema: z.hash('sha256', { enc: 'base64url' }), description: 'hash sha256 base64url' },
559+
{ schema: z.hash('sha384'), description: 'hash sha384' },
560+
{ schema: z.hash('sha384', { enc: 'hex' }), description: 'hash sha384 hex' },
561+
{ schema: z.hash('sha384', { enc: 'base64' }), description: 'hash sha384 base64' },
562+
{ schema: z.hash('sha384', { enc: 'base64url' }), description: 'hash sha384 base64url' },
563+
{ schema: z.hash('sha512'), description: 'hash sha512' },
564+
{ schema: z.hash('sha512', { enc: 'hex' }), description: 'hash sha512 hex' },
565+
{ schema: z.hash('sha512', { enc: 'base64' }), description: 'hash sha512 base64' },
566+
{ schema: z.hash('sha512', { enc: 'base64url' }), description: 'hash sha512 base64url' },
567+
{ schema: z.hex(), description: 'hex' },
568+
{ schema: z.hostname(), description: 'hostname' },
569+
{ schema: z.httpUrl(), description: 'httpUrl' },
545570
{ schema: z.ipv4(), description: 'ipv4' },
546571
{ schema: z.ipv6(), description: 'ipv6' },
547572
{ schema: z.iso.date(), description: 'date' },
548573
{ schema: z.iso.datetime(), description: 'datetime' },
574+
{ schema: z.iso.datetime({ local: true }), description: 'datetime local' },
575+
{ schema: z.iso.datetime({ offset: true }), description: 'datetime offset' },
576+
{ schema: z.iso.datetime({ precision: 3 }), description: 'datetime precision' },
549577
{ schema: z.iso.duration(), description: 'duration' },
550578
{ schema: z.iso.time(), description: 'time' },
579+
{ schema: z.iso.time({ precision: 9 }), description: 'time precision' },
551580
{ schema: z.jwt(), description: 'jwt' },
581+
{ schema: z.jwt({ alg: 'HS256' }), description: 'jwt alg' },
552582
{ schema: z.ksuid(), description: 'ksuid' },
553583
{ schema: z.nanoid(), description: 'naoid' },
554584
{ schema: z.ulid(), description: 'ulid' },
555585
{ schema: z.url(), description: 'url' },
586+
{ schema: z.url({ hostname: /^example\.com$/ }), description: 'url hostname' },
587+
{ schema: z.url({ protocol: /^https$/ }), description: 'url protocol' },
588+
{
589+
schema: z.url({
590+
protocol: /^https?$/,
591+
hostname: z.regexes.domain,
592+
}),
593+
description: 'url protocol hostname',
594+
},
556595
{ schema: z.uuid(), description: 'uuid' },
596+
{ schema: z.uuid({ version: 'v1' }), description: 'uuid v1' },
597+
{ schema: z.uuid({ version: 'v2' }), description: 'uuid v2' },
598+
{ schema: z.uuid({ version: 'v3' }), description: 'uuid v3' },
599+
{ schema: z.uuid({ version: 'v4' }), description: 'uuid v4' },
600+
{ schema: z.uuid({ version: 'v5' }), description: 'uuid v5' },
601+
{ schema: z.uuid({ version: 'v6' }), description: 'uuid v6' },
602+
{ schema: z.uuid({ version: 'v7' }), description: 'uuid v7' },
603+
{ schema: z.uuid({ version: 'v8' }), description: 'uuid v8' },
604+
{ schema: z.uuidv4(), description: 'uuidv4' },
605+
{ schema: z.uuidv6(), description: 'uuidv6' },
606+
{ schema: z.uuidv7(), description: 'uuidv7' },
557607
{ schema: z.xid(), description: 'xid' },
558608
{ schema: z.string().min(5).min(3).min(4).max(5), description: 'min (multiple)' },
559609
{ schema: z.string().max(3).max(5).max(4).min(3), description: 'max (multiple)' },

0 commit comments

Comments
 (0)