@hy_ong/zod-kit 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +40 -21
- package/dist/index.d.cts +24 -11
- package/dist/index.d.ts +24 -11
- package/dist/index.js +40 -21
- package/package.json +1 -1
- package/src/validators/common/date.ts +26 -16
- package/src/validators/common/datetime.ts +46 -28
- package/src/validators/common/email.ts +57 -15
- package/src/validators/common/id.ts +139 -58
- package/src/validators/common/number.ts +80 -43
- package/src/validators/common/password.ts +30 -18
- package/src/validators/common/text.ts +48 -14
- package/src/validators/common/time.ts +34 -22
- package/src/validators/common/url.ts +40 -23
- package/src/validators/taiwan/business-id.ts +19 -19
- package/src/validators/taiwan/fax.ts +29 -28
- package/src/validators/taiwan/mobile.ts +29 -28
- package/src/validators/taiwan/national-id.ts +27 -27
- package/src/validators/taiwan/postal-code.ts +51 -45
- package/src/validators/taiwan/tel.ts +29 -28
- package/tests/common/id.test.ts +62 -4
- package/tests/taiwan/business-id.test.ts +23 -23
- package/tests/taiwan/fax.test.ts +34 -34
- package/tests/taiwan/mobile.test.ts +33 -33
- package/tests/taiwan/national-id.test.ts +32 -32
- package/tests/taiwan/postal-code.test.ts +62 -62
- package/tests/taiwan/tel.test.ts +34 -34
|
@@ -515,25 +515,27 @@ export function datetime<IsRequired extends boolean = false>(required?: IsRequir
|
|
|
515
515
|
|
|
516
516
|
const baseSchema = isRequired ? z.preprocess(preprocessFn, z.string()) : z.preprocess(preprocessFn, z.string().nullable())
|
|
517
517
|
|
|
518
|
-
const schema = baseSchema.
|
|
519
|
-
if (val === null) return
|
|
518
|
+
const schema = baseSchema.superRefine((val, ctx) => {
|
|
519
|
+
if (val === null) return
|
|
520
520
|
|
|
521
521
|
// Required check
|
|
522
522
|
if (isRequired && (val === "" || val === "null" || val === "undefined")) {
|
|
523
|
-
|
|
523
|
+
ctx.addIssue({ code: "custom", message: getMessage("required") })
|
|
524
|
+
return
|
|
524
525
|
}
|
|
525
526
|
|
|
526
|
-
if (val === null) return
|
|
527
|
-
if (!isRequired && val === "") return
|
|
527
|
+
if (val === null) return
|
|
528
|
+
if (!isRequired && val === "") return
|
|
528
529
|
|
|
529
530
|
// Whitelist check
|
|
530
531
|
if (whitelist && whitelist.length > 0) {
|
|
531
532
|
if (whitelist.includes(val)) {
|
|
532
|
-
return
|
|
533
|
+
return
|
|
533
534
|
}
|
|
534
535
|
// If whitelistOnly is true, reject values not in whitelist
|
|
535
536
|
if (whitelistOnly) {
|
|
536
|
-
|
|
537
|
+
ctx.addIssue({ code: "custom", message: getMessage("notInWhitelist") })
|
|
538
|
+
return
|
|
537
539
|
}
|
|
538
540
|
// Otherwise, continue with normal validation
|
|
539
541
|
}
|
|
@@ -541,32 +543,36 @@ export function datetime<IsRequired extends boolean = false>(required?: IsRequir
|
|
|
541
543
|
// Custom regex validation (overrides format validation)
|
|
542
544
|
if (regex) {
|
|
543
545
|
if (!regex.test(val)) {
|
|
544
|
-
|
|
546
|
+
ctx.addIssue({ code: "custom", message: getMessage("customRegex") })
|
|
547
|
+
return
|
|
545
548
|
}
|
|
546
549
|
} else {
|
|
547
550
|
// DateTime format validation (only if no regex is provided)
|
|
548
551
|
if (!validateDateTimeFormat(val, format)) {
|
|
549
|
-
|
|
552
|
+
ctx.addIssue({ code: "custom", message: getMessage("format", { format }) })
|
|
553
|
+
return
|
|
550
554
|
}
|
|
551
555
|
}
|
|
552
556
|
|
|
553
557
|
// String content checks
|
|
554
558
|
if (includes && !val.includes(includes)) {
|
|
555
|
-
|
|
559
|
+
ctx.addIssue({ code: "custom", message: getMessage("includes", { includes }) })
|
|
560
|
+
return
|
|
556
561
|
}
|
|
557
562
|
|
|
558
563
|
if (excludes) {
|
|
559
564
|
const excludeList = Array.isArray(excludes) ? excludes : [excludes]
|
|
560
565
|
for (const exclude of excludeList) {
|
|
561
566
|
if (val.includes(exclude)) {
|
|
562
|
-
|
|
567
|
+
ctx.addIssue({ code: "custom", message: getMessage("excludes", { excludes: exclude }) })
|
|
568
|
+
return
|
|
563
569
|
}
|
|
564
570
|
}
|
|
565
571
|
}
|
|
566
572
|
|
|
567
573
|
// Skip datetime parsing and range validation if using custom regex
|
|
568
574
|
if (regex) {
|
|
569
|
-
return
|
|
575
|
+
return
|
|
570
576
|
}
|
|
571
577
|
|
|
572
578
|
// Parse datetime for validation
|
|
@@ -575,32 +581,38 @@ export function datetime<IsRequired extends boolean = false>(required?: IsRequir
|
|
|
575
581
|
// Check if it's a format issue or parsing issue
|
|
576
582
|
const pattern = DATETIME_PATTERNS[format]
|
|
577
583
|
if (!pattern.test(val.trim())) {
|
|
578
|
-
|
|
584
|
+
ctx.addIssue({ code: "custom", message: getMessage("format", { format }) })
|
|
585
|
+
return
|
|
579
586
|
} else {
|
|
580
|
-
|
|
587
|
+
ctx.addIssue({ code: "custom", message: getMessage("invalid") })
|
|
588
|
+
return
|
|
581
589
|
}
|
|
582
590
|
}
|
|
583
591
|
|
|
584
592
|
// Hour validation
|
|
585
593
|
const hour = parsed.hour()
|
|
586
594
|
if (minHour !== undefined && hour < minHour) {
|
|
587
|
-
|
|
595
|
+
ctx.addIssue({ code: "custom", message: getMessage("hour", { minHour, maxHour: maxHour ?? 23 }) })
|
|
596
|
+
return
|
|
588
597
|
}
|
|
589
598
|
if (maxHour !== undefined && hour > maxHour) {
|
|
590
|
-
|
|
599
|
+
ctx.addIssue({ code: "custom", message: getMessage("hour", { minHour: minHour ?? 0, maxHour }) })
|
|
600
|
+
return
|
|
591
601
|
}
|
|
592
602
|
|
|
593
603
|
// Allowed hours check
|
|
594
604
|
if (allowedHours && allowedHours.length > 0) {
|
|
595
605
|
if (!allowedHours.includes(hour)) {
|
|
596
|
-
|
|
606
|
+
ctx.addIssue({ code: "custom", message: getMessage("hour", { allowedHours: allowedHours.join(", ") }) })
|
|
607
|
+
return
|
|
597
608
|
}
|
|
598
609
|
}
|
|
599
610
|
|
|
600
611
|
// Minute step validation
|
|
601
612
|
const minute = parsed.minute()
|
|
602
613
|
if (minuteStep !== undefined && minute % minuteStep !== 0) {
|
|
603
|
-
|
|
614
|
+
ctx.addIssue({ code: "custom", message: getMessage("minute", { minuteStep }) })
|
|
615
|
+
return
|
|
604
616
|
}
|
|
605
617
|
|
|
606
618
|
// DateTime range validation (min/max)
|
|
@@ -608,7 +620,8 @@ export function datetime<IsRequired extends boolean = false>(required?: IsRequir
|
|
|
608
620
|
const minParsed = typeof min === "string" ? parseDateTimeValue(min, format, timezone) : dayjs(min)
|
|
609
621
|
if (minParsed && parsed.isBefore(minParsed)) {
|
|
610
622
|
const minFormatted = typeof min === "string" ? min : minParsed.format(format)
|
|
611
|
-
|
|
623
|
+
ctx.addIssue({ code: "custom", message: getMessage("min", { min: minFormatted }) })
|
|
624
|
+
return
|
|
612
625
|
}
|
|
613
626
|
}
|
|
614
627
|
|
|
@@ -616,7 +629,8 @@ export function datetime<IsRequired extends boolean = false>(required?: IsRequir
|
|
|
616
629
|
const maxParsed = typeof max === "string" ? parseDateTimeValue(max, format, timezone) : dayjs(max)
|
|
617
630
|
if (maxParsed && parsed.isAfter(maxParsed)) {
|
|
618
631
|
const maxFormatted = typeof max === "string" ? max : maxParsed.format(format)
|
|
619
|
-
|
|
632
|
+
ctx.addIssue({ code: "custom", message: getMessage("max", { max: maxFormatted }) })
|
|
633
|
+
return
|
|
620
634
|
}
|
|
621
635
|
}
|
|
622
636
|
|
|
@@ -624,33 +638,37 @@ export function datetime<IsRequired extends boolean = false>(required?: IsRequir
|
|
|
624
638
|
const now = timezone ? dayjs().tz(timezone) : dayjs()
|
|
625
639
|
|
|
626
640
|
if (mustBePast && !parsed.isBefore(now)) {
|
|
627
|
-
|
|
641
|
+
ctx.addIssue({ code: "custom", message: getMessage("past") })
|
|
642
|
+
return
|
|
628
643
|
}
|
|
629
644
|
|
|
630
645
|
if (mustBeFuture && !parsed.isAfter(now)) {
|
|
631
|
-
|
|
646
|
+
ctx.addIssue({ code: "custom", message: getMessage("future") })
|
|
647
|
+
return
|
|
632
648
|
}
|
|
633
649
|
|
|
634
650
|
if (mustBeToday && !parsed.isSame(now, "day")) {
|
|
635
|
-
|
|
651
|
+
ctx.addIssue({ code: "custom", message: getMessage("today") })
|
|
652
|
+
return
|
|
636
653
|
}
|
|
637
654
|
|
|
638
655
|
if (mustNotBeToday && parsed.isSame(now, "day")) {
|
|
639
|
-
|
|
656
|
+
ctx.addIssue({ code: "custom", message: getMessage("notToday") })
|
|
657
|
+
return
|
|
640
658
|
}
|
|
641
659
|
|
|
642
660
|
// Weekday validations
|
|
643
661
|
const dayOfWeek = parsed.day() // 0 = Sunday, 1 = Monday, ..., 6 = Saturday
|
|
644
662
|
|
|
645
663
|
if (weekdaysOnly && (dayOfWeek === 0 || dayOfWeek === 6)) {
|
|
646
|
-
|
|
664
|
+
ctx.addIssue({ code: "custom", message: getMessage("weekday") })
|
|
665
|
+
return
|
|
647
666
|
}
|
|
648
667
|
|
|
649
668
|
if (weekendsOnly && dayOfWeek !== 0 && dayOfWeek !== 6) {
|
|
650
|
-
|
|
669
|
+
ctx.addIssue({ code: "custom", message: getMessage("weekend") })
|
|
670
|
+
return
|
|
651
671
|
}
|
|
652
|
-
|
|
653
|
-
return true
|
|
654
672
|
})
|
|
655
673
|
|
|
656
674
|
return schema as unknown as DateTimeSchema<IsRequired>
|
|
@@ -209,37 +209,61 @@ export function email<IsRequired extends boolean = false>(required?: IsRequired,
|
|
|
209
209
|
z.union([z.string().email(), z.null()])
|
|
210
210
|
)
|
|
211
211
|
|
|
212
|
-
const schema = baseSchema.
|
|
212
|
+
const schema = baseSchema.superRefine((val, ctx) => {
|
|
213
213
|
// Required check first
|
|
214
214
|
if (isRequired && val === null) {
|
|
215
|
-
|
|
215
|
+
ctx.addIssue({
|
|
216
|
+
code: "custom",
|
|
217
|
+
message: getMessage("required")
|
|
218
|
+
})
|
|
219
|
+
return
|
|
216
220
|
}
|
|
217
221
|
|
|
218
|
-
if (val === null) return
|
|
222
|
+
if (val === null) return
|
|
219
223
|
|
|
220
224
|
// Invalid email check
|
|
221
225
|
if (typeof val !== "string") {
|
|
222
|
-
|
|
226
|
+
ctx.addIssue({
|
|
227
|
+
code: "custom",
|
|
228
|
+
message: getMessage("invalid")
|
|
229
|
+
})
|
|
230
|
+
return
|
|
223
231
|
}
|
|
224
232
|
|
|
225
233
|
// Length checks
|
|
226
234
|
if (minLength !== undefined && val.length < minLength) {
|
|
227
|
-
|
|
235
|
+
ctx.addIssue({
|
|
236
|
+
code: "custom",
|
|
237
|
+
message: getMessage("minLength", { minLength })
|
|
238
|
+
})
|
|
239
|
+
return
|
|
228
240
|
}
|
|
229
241
|
if (maxLength !== undefined && val.length > maxLength) {
|
|
230
|
-
|
|
242
|
+
ctx.addIssue({
|
|
243
|
+
code: "custom",
|
|
244
|
+
message: getMessage("maxLength", { maxLength })
|
|
245
|
+
})
|
|
246
|
+
return
|
|
231
247
|
}
|
|
232
248
|
|
|
233
249
|
// Content checks
|
|
234
250
|
if (includes !== undefined && !val.includes(includes)) {
|
|
235
|
-
|
|
251
|
+
ctx.addIssue({
|
|
252
|
+
code: "custom",
|
|
253
|
+
message: getMessage("includes", { includes })
|
|
254
|
+
})
|
|
255
|
+
return
|
|
236
256
|
}
|
|
237
257
|
|
|
238
258
|
if (excludes !== undefined) {
|
|
239
259
|
const excludeList = Array.isArray(excludes) ? excludes : [excludes]
|
|
240
260
|
for (const exclude of excludeList) {
|
|
241
261
|
if (val.includes(exclude)) {
|
|
242
|
-
|
|
262
|
+
ctx.addIssue({
|
|
263
|
+
code: "custom",
|
|
264
|
+
message: getMessage("includes", { includes: exclude })
|
|
265
|
+
})
|
|
266
|
+
return
|
|
243
267
|
}
|
|
244
268
|
}
|
|
245
269
|
}
|
|
@@ -247,7 +271,11 @@ export function email<IsRequired extends boolean = false>(required?: IsRequired,
|
|
|
247
271
|
// Extract domain from email
|
|
248
272
|
const emailDomain = val.split("@")[1]?.toLowerCase()
|
|
249
273
|
if (!emailDomain) {
|
|
250
|
-
|
|
274
|
+
ctx.addIssue({
|
|
275
|
+
code: "custom",
|
|
276
|
+
message: getMessage("invalid")
|
|
277
|
+
})
|
|
278
|
+
return
|
|
251
279
|
}
|
|
252
280
|
|
|
253
281
|
// Business email check (should come before domain validation)
|
|
@@ -260,7 +288,11 @@ export function email<IsRequired extends boolean = false>(required?: IsRequired,
|
|
|
260
288
|
})
|
|
261
289
|
|
|
262
290
|
if (isFreeProvider) {
|
|
263
|
-
|
|
291
|
+
ctx.addIssue({
|
|
292
|
+
code: "custom",
|
|
293
|
+
message: getMessage("businessOnly")
|
|
294
|
+
})
|
|
295
|
+
return
|
|
264
296
|
}
|
|
265
297
|
}
|
|
266
298
|
|
|
@@ -275,7 +307,11 @@ export function email<IsRequired extends boolean = false>(required?: IsRequired,
|
|
|
275
307
|
})
|
|
276
308
|
|
|
277
309
|
if (isBlacklisted) {
|
|
278
|
-
|
|
310
|
+
ctx.addIssue({
|
|
311
|
+
code: "custom",
|
|
312
|
+
message: getMessage("domainBlacklist", { domain: emailDomain })
|
|
313
|
+
})
|
|
314
|
+
return
|
|
279
315
|
}
|
|
280
316
|
}
|
|
281
317
|
|
|
@@ -291,7 +327,11 @@ export function email<IsRequired extends boolean = false>(required?: IsRequired,
|
|
|
291
327
|
})
|
|
292
328
|
|
|
293
329
|
if (!isAllowed) {
|
|
294
|
-
|
|
330
|
+
ctx.addIssue({
|
|
331
|
+
code: "custom",
|
|
332
|
+
message: getMessage("domain", { domain: Array.isArray(domain) ? domain.join(", ") : domain })
|
|
333
|
+
})
|
|
334
|
+
return
|
|
295
335
|
}
|
|
296
336
|
}
|
|
297
337
|
|
|
@@ -305,11 +345,13 @@ export function email<IsRequired extends boolean = false>(required?: IsRequired,
|
|
|
305
345
|
})
|
|
306
346
|
|
|
307
347
|
if (isDisposable) {
|
|
308
|
-
|
|
348
|
+
ctx.addIssue({
|
|
349
|
+
code: "custom",
|
|
350
|
+
message: getMessage("noDisposable")
|
|
351
|
+
})
|
|
352
|
+
return
|
|
309
353
|
}
|
|
310
354
|
}
|
|
311
|
-
|
|
312
|
-
return true
|
|
313
355
|
})
|
|
314
356
|
|
|
315
357
|
return schema as unknown as EmailSchema<IsRequired>
|