@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.
- package/.claude/settings.local.json +28 -0
- package/LICENSE +21 -0
- package/README.md +465 -97
- package/debug.js +21 -0
- package/debug.ts +16 -0
- package/dist/index.cjs +3127 -146
- package/dist/index.d.cts +3021 -25
- package/dist/index.d.ts +3021 -25
- package/dist/index.js +3081 -144
- package/eslint.config.mts +8 -0
- package/package.json +10 -9
- package/src/config.ts +1 -1
- package/src/i18n/locales/en.json +161 -25
- package/src/i18n/locales/zh-TW.json +165 -26
- package/src/index.ts +17 -7
- package/src/validators/common/boolean.ts +191 -0
- package/src/validators/common/date.ts +299 -0
- package/src/validators/common/datetime.ts +673 -0
- package/src/validators/common/email.ts +313 -0
- package/src/validators/common/file.ts +384 -0
- package/src/validators/common/id.ts +471 -0
- package/src/validators/common/number.ts +319 -0
- package/src/validators/common/password.ts +386 -0
- package/src/validators/common/text.ts +271 -0
- package/src/validators/common/time.ts +600 -0
- package/src/validators/common/url.ts +347 -0
- package/src/validators/taiwan/business-id.ts +262 -0
- package/src/validators/taiwan/fax.ts +327 -0
- package/src/validators/taiwan/mobile.ts +242 -0
- package/src/validators/taiwan/national-id.ts +425 -0
- package/src/validators/taiwan/postal-code.ts +1049 -0
- package/src/validators/taiwan/tel.ts +330 -0
- package/tests/common/boolean.test.ts +340 -92
- package/tests/common/date.test.ts +458 -0
- package/tests/common/datetime.test.ts +693 -0
- package/tests/common/email.test.ts +232 -60
- package/tests/common/file.test.ts +479 -0
- package/tests/common/id.test.ts +535 -0
- package/tests/common/number.test.ts +230 -60
- package/tests/common/password.test.ts +271 -44
- package/tests/common/text.test.ts +210 -13
- package/tests/common/time.test.ts +528 -0
- package/tests/common/url.test.ts +492 -67
- package/tests/taiwan/business-id.test.ts +240 -0
- package/tests/taiwan/fax.test.ts +463 -0
- package/tests/taiwan/mobile.test.ts +373 -0
- package/tests/taiwan/national-id.test.ts +435 -0
- package/tests/taiwan/postal-code.test.ts +705 -0
- package/tests/taiwan/tel.test.ts +467 -0
- package/eslint.config.mjs +0 -10
- package/src/common/boolean.ts +0 -36
- package/src/common/date.ts +0 -43
- package/src/common/email.ts +0 -44
- package/src/common/integer.ts +0 -46
- package/src/common/number.ts +0 -37
- package/src/common/password.ts +0 -33
- package/src/common/text.ts +0 -34
- package/src/common/url.ts +0 -37
- package/tests/common/integer.test.ts +0 -90
package/src/common/number.ts
DELETED
|
@@ -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
|
-
}
|
package/src/common/password.ts
DELETED
|
@@ -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
|
-
}
|
package/src/common/text.ts
DELETED
|
@@ -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
|
-
})
|