@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.
@@ -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.refine((val) => {
519
- if (val === null) return true
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
- throw new z.ZodError([{ code: "custom", message: getMessage("required"), path: [] }])
523
+ ctx.addIssue({ code: "custom", message: getMessage("required") })
524
+ return
524
525
  }
525
526
 
526
- if (val === null) return true
527
- if (!isRequired && val === "") return true
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 true
533
+ return
533
534
  }
534
535
  // If whitelistOnly is true, reject values not in whitelist
535
536
  if (whitelistOnly) {
536
- throw new z.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("customRegex"), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("format", { format }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }])
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 true
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
- throw new z.ZodError([{ code: "custom", message: getMessage("format", { format }), path: [] }])
584
+ ctx.addIssue({ code: "custom", message: getMessage("format", { format }) })
585
+ return
579
586
  } else {
580
- throw new z.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("hour", { minHour, maxHour: maxHour ?? 23 }), path: [] }])
595
+ ctx.addIssue({ code: "custom", message: getMessage("hour", { minHour, maxHour: maxHour ?? 23 }) })
596
+ return
588
597
  }
589
598
  if (maxHour !== undefined && hour > maxHour) {
590
- throw new z.ZodError([{ code: "custom", message: getMessage("hour", { minHour: minHour ?? 0, maxHour }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("hour", { allowedHours: allowedHours.join(", ") }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("minute", { minuteStep }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("min", { min: minFormatted }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("max", { max: maxFormatted }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("past"), path: [] }])
641
+ ctx.addIssue({ code: "custom", message: getMessage("past") })
642
+ return
628
643
  }
629
644
 
630
645
  if (mustBeFuture && !parsed.isAfter(now)) {
631
- throw new z.ZodError([{ code: "custom", message: getMessage("future"), path: [] }])
646
+ ctx.addIssue({ code: "custom", message: getMessage("future") })
647
+ return
632
648
  }
633
649
 
634
650
  if (mustBeToday && !parsed.isSame(now, "day")) {
635
- throw new z.ZodError([{ code: "custom", message: getMessage("today"), path: [] }])
651
+ ctx.addIssue({ code: "custom", message: getMessage("today") })
652
+ return
636
653
  }
637
654
 
638
655
  if (mustNotBeToday && parsed.isSame(now, "day")) {
639
- throw new z.ZodError([{ code: "custom", message: getMessage("notToday"), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("weekday"), path: [] }])
664
+ ctx.addIssue({ code: "custom", message: getMessage("weekday") })
665
+ return
647
666
  }
648
667
 
649
668
  if (weekendsOnly && dayOfWeek !== 0 && dayOfWeek !== 6) {
650
- throw new z.ZodError([{ code: "custom", message: getMessage("weekend"), path: [] }])
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.refine((val) => {
212
+ const schema = baseSchema.superRefine((val, ctx) => {
213
213
  // Required check first
214
214
  if (isRequired && val === null) {
215
- throw new z.ZodError([{ code: "custom", message: getMessage("required"), path: [] }])
215
+ ctx.addIssue({
216
+ code: "custom",
217
+ message: getMessage("required")
218
+ })
219
+ return
216
220
  }
217
221
 
218
- if (val === null) return true
222
+ if (val === null) return
219
223
 
220
224
  // Invalid email check
221
225
  if (typeof val !== "string") {
222
- throw new z.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("minLength", { minLength }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("maxLength", { maxLength }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("includes", { includes: exclude }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("businessOnly"), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("domainBlacklist", { domain: emailDomain }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("domain", { domain: Array.isArray(domain) ? domain.join(", ") : domain }), path: [] }])
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
- throw new z.ZodError([{ code: "custom", message: getMessage("noDisposable"), path: [] }])
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>