@hy_ong/zod-kit 0.0.4 → 0.0.6

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.
Files changed (59) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +465 -97
  4. package/debug.js +21 -0
  5. package/debug.ts +16 -0
  6. package/dist/index.cjs +3127 -146
  7. package/dist/index.d.cts +3021 -25
  8. package/dist/index.d.ts +3021 -25
  9. package/dist/index.js +3081 -144
  10. package/eslint.config.mts +8 -0
  11. package/package.json +10 -9
  12. package/src/config.ts +1 -1
  13. package/src/i18n/locales/en.json +161 -25
  14. package/src/i18n/locales/zh-TW.json +165 -26
  15. package/src/index.ts +17 -7
  16. package/src/validators/common/boolean.ts +191 -0
  17. package/src/validators/common/date.ts +299 -0
  18. package/src/validators/common/datetime.ts +673 -0
  19. package/src/validators/common/email.ts +313 -0
  20. package/src/validators/common/file.ts +384 -0
  21. package/src/validators/common/id.ts +471 -0
  22. package/src/validators/common/number.ts +319 -0
  23. package/src/validators/common/password.ts +386 -0
  24. package/src/validators/common/text.ts +271 -0
  25. package/src/validators/common/time.ts +600 -0
  26. package/src/validators/common/url.ts +347 -0
  27. package/src/validators/taiwan/business-id.ts +262 -0
  28. package/src/validators/taiwan/fax.ts +327 -0
  29. package/src/validators/taiwan/mobile.ts +242 -0
  30. package/src/validators/taiwan/national-id.ts +425 -0
  31. package/src/validators/taiwan/postal-code.ts +1049 -0
  32. package/src/validators/taiwan/tel.ts +330 -0
  33. package/tests/common/boolean.test.ts +340 -92
  34. package/tests/common/date.test.ts +458 -0
  35. package/tests/common/datetime.test.ts +693 -0
  36. package/tests/common/email.test.ts +232 -60
  37. package/tests/common/file.test.ts +479 -0
  38. package/tests/common/id.test.ts +535 -0
  39. package/tests/common/number.test.ts +230 -60
  40. package/tests/common/password.test.ts +271 -44
  41. package/tests/common/text.test.ts +210 -13
  42. package/tests/common/time.test.ts +528 -0
  43. package/tests/common/url.test.ts +492 -67
  44. package/tests/taiwan/business-id.test.ts +240 -0
  45. package/tests/taiwan/fax.test.ts +463 -0
  46. package/tests/taiwan/mobile.test.ts +373 -0
  47. package/tests/taiwan/national-id.test.ts +435 -0
  48. package/tests/taiwan/postal-code.test.ts +705 -0
  49. package/tests/taiwan/tel.test.ts +467 -0
  50. package/eslint.config.mjs +0 -10
  51. package/src/common/boolean.ts +0 -36
  52. package/src/common/date.ts +0 -43
  53. package/src/common/email.ts +0 -44
  54. package/src/common/integer.ts +0 -46
  55. package/src/common/number.ts +0 -37
  56. package/src/common/password.ts +0 -33
  57. package/src/common/text.ts +0 -34
  58. package/src/common/url.ts +0 -37
  59. package/tests/common/integer.test.ts +0 -90
@@ -1,37 +0,0 @@
1
- import { z, ZodNullable, ZodNumber } from "zod"
2
- import { t } from "../i18n"
3
-
4
- export type NumberOptions<IsRequired extends boolean = true> = {
5
- required?: IsRequired
6
- min?: number
7
- max?: number
8
- defaultValue?: IsRequired extends true ? number : number | null
9
- }
10
-
11
- export type NumberSchema<IsRequired extends boolean> = IsRequired extends true ? ZodNumber : ZodNullable<ZodNumber>
12
-
13
- export function number<IsRequired extends boolean = true>(options?: NumberOptions<IsRequired>): NumberSchema<IsRequired> {
14
- const { required = true, min, max, defaultValue } = options ?? {}
15
-
16
- const schema = z
17
- .preprocess(
18
- (val) => {
19
- if (val === "" || val === undefined || val === null) return defaultValue ?? null
20
- return typeof val === "string" ? Number(val) : val
21
- },
22
- z.union([
23
- z.number({
24
- error: (issue) => {
25
- if (issue.code === "invalid_type") return t("common.number.integer")
26
- return t("common.number.required")
27
- },
28
- }),
29
- z.null(),
30
- ])
31
- )
32
- .refine((val) => !required || val !== null, { message: t("common.number.required") })
33
- .refine((val) => val === null || min === undefined || val >= min, { message: t("common.number.min", { min }) })
34
- .refine((val) => val === null || max === undefined || val <= max, { message: t("common.number.max", { max }) })
35
-
36
- return schema as unknown as NumberSchema<IsRequired>
37
- }
@@ -1,33 +0,0 @@
1
- import { z, ZodNullable, ZodString } from "zod"
2
- import { t } from "../i18n"
3
-
4
- export type PasswordOptions<IsRequired extends boolean = true> = {
5
- required?: IsRequired
6
- min?: number
7
- max?: number
8
- uppercase?: boolean
9
- lowercase?: boolean
10
- digits?: boolean
11
- special?: boolean
12
- }
13
-
14
- export type PasswordSchema<IsRequired extends boolean> = IsRequired extends true ? ZodString : ZodNullable<ZodString>
15
-
16
- export function password<IsRequired extends boolean = true>(options?: PasswordOptions<IsRequired>): PasswordSchema<IsRequired> {
17
- const { required = true, min, max, uppercase, lowercase, digits, special } = options ?? {}
18
-
19
- const baseSchema = required
20
- ? z.preprocess((val) => (val === "" || val === null || val === undefined ? null : val), z.coerce.string().trim())
21
- : z.preprocess((val) => (val === "" || val === null || val === undefined ? null : val), z.coerce.string().trim().nullable())
22
-
23
- const schema = baseSchema
24
- .refine((val) => (required ? val !== "" && val !== "null" && val !== "undefined" : true), { message: t("common.password.required") })
25
- .refine((val) => val === null || min === undefined || val.length >= min, { message: t("common.password.min", { min }) })
26
- .refine((val) => val === null || max === undefined || val.length <= max, { message: t("common.password.max", { max }) })
27
- .refine((val) => val === null || !uppercase || /[A-Z]/.test(val), { message: t("common.password.uppercase") })
28
- .refine((val) => val === null || !lowercase || /[a-z]/.test(val), { message: t("common.password.lowercase") })
29
- .refine((val) => val === null || !special || /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]+/.test(val), { message: t("common.password.special") })
30
- .refine((val) => val === null || !digits || /[0-9]/.test(val), { message: t("common.password.digits") })
31
-
32
- return schema as unknown as PasswordSchema<IsRequired>
33
- }
@@ -1,34 +0,0 @@
1
- import { z, ZodNullable, ZodString } from "zod"
2
- import { t } from "../i18n"
3
-
4
- export type TextOptions<IsRequired extends boolean = true> = {
5
- required?: IsRequired
6
- min?: number
7
- max?: number
8
- startsWith?: string
9
- endsWith?: string
10
- includes?: string
11
- regex?: RegExp
12
- defaultValue?: IsRequired extends true ? string : string | null
13
- }
14
-
15
- export type TextSchema<IsRequired extends boolean> = IsRequired extends true ? ZodString : ZodNullable<ZodString>
16
-
17
- export function text<IsRequired extends boolean = true>(options?: TextOptions<IsRequired>): TextSchema<IsRequired> {
18
- const { required = true, min, max, startsWith, endsWith, includes, regex, defaultValue = null } = options ?? {}
19
-
20
- const baseSchema = required
21
- ? z.preprocess((val) => (val === "" || val === null || val === undefined ? defaultValue : val), z.coerce.string().trim())
22
- : z.preprocess((val) => (val === "" || val === null || val === undefined ? defaultValue : val), z.coerce.string().trim().nullable())
23
-
24
- const schema = baseSchema
25
- .refine((val) => (required ? val !== "" && val !== "null" && val !== "undefined" : true), { message: t("common.text.required") })
26
- .refine((val) => val === null || min === undefined || val.length >= min, { message: t("common.text.min", { min }) })
27
- .refine((val) => val === null || max === undefined || val.length <= max, { message: t("common.text.max", { max }) })
28
- .refine((val) => val === null || startsWith === undefined || val.startsWith(startsWith), { message: t("common.text.startsWith", { startsWith }) })
29
- .refine((val) => val === null || endsWith === undefined || val.endsWith(endsWith), { message: t("common.text.endsWith", { endsWith }) })
30
- .refine((val) => val === null || includes === undefined || val.includes(includes), { message: t("common.text.includes", { includes }) })
31
- .refine((val) => val === null || regex === undefined || regex.test(val), { message: t("common.text.invalid", { regex }) })
32
-
33
- return schema as unknown as TextSchema<IsRequired>
34
- }
package/src/common/url.ts DELETED
@@ -1,37 +0,0 @@
1
- import { z, ZodNullable, ZodString } from "zod"
2
- import { t } from "../i18n"
3
- import { TextSchema } from "./text"
4
-
5
- export type UrlOptions<IsRequired extends boolean = true> = {
6
- required?: IsRequired
7
- min?: number
8
- max?: number
9
- includes?: string
10
- }
11
-
12
- export type UrlSchema<IsRequired extends boolean> = IsRequired extends true ? ZodString : ZodNullable<ZodString>
13
-
14
- export function url<IsRequired extends boolean = true>(options?: UrlOptions<IsRequired>): UrlSchema<IsRequired> {
15
- const { required = true, min, max, includes } = options ?? {}
16
-
17
- const baseSchema = required
18
- ? z.preprocess(
19
- (val) => (val === "" || val === null || val === undefined ? null : val),
20
- z.url({
21
- error: (issue) => {
22
- if (issue.code === "invalid_type") return t("common.url.required")
23
- else if (issue.code === "invalid_format") return t("common.url.invalid")
24
- return t("common.url.invalid")
25
- },
26
- })
27
- )
28
- : z.preprocess((val) => (val === "" || val === null || val === undefined ? null : val), z.url({ message: t("common.url.invalid") }).nullable())
29
-
30
- const schema = baseSchema
31
- .refine((val) => (required ? val !== "" && val !== "null" && val !== "undefined" : true), { message: t("common.text.required") })
32
- .refine((val) => val === null || min === undefined || val.length >= min, { message: t("common.text.min", { min }) })
33
- .refine((val) => val === null || max === undefined || val.length <= max, { message: t("common.text.max", { max }) })
34
- .refine((val) => val === null || includes === undefined || val.includes(includes), { message: t("common.text.includes", { includes }) })
35
-
36
- return schema as unknown as TextSchema<IsRequired>
37
- }
@@ -1,90 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest"
2
- import { setLocale, integer, Locale } from "../../src"
3
-
4
- const locales = [
5
- {
6
- locale: "en",
7
- messages: {
8
- required: "Required",
9
- min: "Must be at least 3",
10
- max: "Must be at most 10",
11
- integer: "Must be an integer",
12
- },
13
- },
14
- {
15
- locale: "zh-TW",
16
- messages: {
17
- required: "必填",
18
- min: "最小值 3",
19
- max: "最大值 10",
20
- integer: "必須為整數",
21
- },
22
- },
23
- ] as const
24
-
25
- describe.each(locales)("integer() locale: $locale", ({ locale, messages }) => {
26
- beforeEach(() => setLocale(locale as Locale))
27
-
28
- it("should pass with valid integer", () => {
29
- const schema = integer()
30
- expect(schema.parse(42)).toBe(42)
31
- })
32
-
33
- it("should parse string number as integer", () => {
34
- const schema = integer()
35
- expect(schema.parse("100")).toBe(100)
36
- })
37
-
38
- it("should fail for decimal value", () => {
39
- const schema = integer()
40
- expect(() => schema.parse(3.14)).toThrow(messages.integer)
41
- })
42
-
43
- it("should fail for non-numeric string", () => {
44
- const schema = integer()
45
- expect(() => schema.parse("abc")).toThrow(messages.integer)
46
- expect(() => schema.parse("10a")).toThrow(messages.integer)
47
- })
48
-
49
- it("should allow null if not required", () => {
50
- const schema = integer({ required: false })
51
- expect(schema.parse("")).toBe(null)
52
- expect(schema.parse(null)).toBe(null)
53
- expect(schema.parse(undefined)).toBe(null)
54
- })
55
-
56
- it("should fail when required and input is empty", () => {
57
- const schema = integer({ required: true })
58
- expect(() => schema.parse("")).toThrow(messages.required)
59
- expect(() => schema.parse(null)).toThrow(messages.required)
60
- expect(() => schema.parse(undefined)).toThrow(messages.required)
61
- })
62
-
63
- it("should apply default value even when required", () => {
64
- const schema = integer({ required: true, defaultValue: 5 })
65
- expect(schema.parse("")).toBe(5)
66
- expect(schema.parse(null)).toBe(5)
67
- expect(schema.parse(undefined)).toBe(5)
68
- })
69
-
70
- it("should apply default value even when required", () => {
71
- const schema = integer({ required: false, defaultValue: 5 })
72
- expect(schema.parse("")).toBe(5)
73
- expect(schema.parse(null)).toBe(5)
74
- expect(schema.parse(undefined)).toBe(5)
75
- })
76
-
77
- it("should enforce min", () => {
78
- const schema = integer({ min: 3 })
79
- expect(schema.parse(3)).toBe(3)
80
- expect(() => schema.parse(2)).toThrow(messages.min)
81
- expect(() => schema.parse("2")).toThrow(messages.min)
82
- })
83
-
84
- it("should enforce max", () => {
85
- const schema = integer({ max: 10 })
86
- expect(schema.parse(10)).toBe(10)
87
- expect(() => schema.parse(11)).toThrow(messages.max)
88
- expect(() => schema.parse("11")).toThrow(messages.max)
89
- })
90
- })