@hy_ong/zod-kit 0.0.2 → 0.0.5
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 +23 -0
- package/LICENSE +21 -0
- package/README.md +7 -7
- package/debug.js +21 -0
- package/debug.ts +16 -0
- package/dist/index.cjs +1663 -189
- package/dist/index.d.cts +324 -32
- package/dist/index.d.ts +324 -32
- package/dist/index.js +1634 -187
- package/eslint.config.mts +8 -0
- package/package.json +10 -9
- package/src/config.ts +1 -1
- package/src/i18n/locales/en.json +123 -49
- package/src/i18n/locales/zh-TW.json +123 -46
- package/src/index.ts +13 -7
- package/src/validators/common/boolean.ts +97 -0
- package/src/validators/common/date.ts +171 -0
- package/src/validators/common/email.ts +200 -0
- package/src/validators/common/id.ts +259 -0
- package/src/validators/common/number.ts +194 -0
- package/src/validators/common/password.ts +214 -0
- package/src/validators/common/text.ts +151 -0
- package/src/validators/common/url.ts +207 -0
- package/src/validators/taiwan/business-id.ts +140 -0
- package/src/validators/taiwan/fax.ts +182 -0
- package/src/validators/taiwan/mobile.ts +110 -0
- package/src/validators/taiwan/national-id.ts +208 -0
- package/src/validators/taiwan/tel.ts +182 -0
- package/tests/common/boolean.test.ts +340 -92
- package/tests/common/date.test.ts +458 -0
- package/tests/common/email.test.ts +232 -60
- package/tests/common/id.test.ts +535 -0
- package/tests/common/number.test.ts +230 -60
- package/tests/common/password.test.ts +281 -54
- package/tests/common/text.test.ts +227 -30
- 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/tel.test.ts +467 -0
- package/eslint.config.mjs +0 -10
- package/src/common/boolean.ts +0 -37
- package/src/common/date.ts +0 -44
- package/src/common/email.ts +0 -45
- package/src/common/integer.ts +0 -47
- package/src/common/number.ts +0 -38
- package/src/common/password.ts +0 -34
- package/src/common/text.ts +0 -35
- package/src/common/url.ts +0 -38
- package/tests/common/integer.test.ts +0 -90
|
@@ -1,77 +1,247 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from "vitest"
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
2
|
+
import { number, setLocale } from "../../src"
|
|
3
|
+
|
|
4
|
+
describe("number() features", () => {
|
|
5
|
+
beforeEach(() => setLocale("en"))
|
|
6
|
+
|
|
7
|
+
describe("type validation", () => {
|
|
8
|
+
it("should accept integers when type='integer'", () => {
|
|
9
|
+
const schema = number({ type: "integer" })
|
|
10
|
+
expect(schema.parse(42)).toBe(42)
|
|
11
|
+
expect(schema.parse("123")).toBe(123)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it("should reject floats when type='integer'", () => {
|
|
15
|
+
const schema = number({ type: "integer" })
|
|
16
|
+
expect(() => schema.parse(3.14)).toThrow("Must be an integer")
|
|
17
|
+
expect(() => schema.parse("3.14")).toThrow("Must be an integer")
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it("should accept floats when type='float'", () => {
|
|
21
|
+
const schema = number({ type: "float" })
|
|
22
|
+
expect(schema.parse(3.14)).toBe(3.14)
|
|
23
|
+
expect(schema.parse("3.14")).toBe(3.14)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it("should reject integers when type='float'", () => {
|
|
27
|
+
const schema = number({ type: "float" })
|
|
28
|
+
expect(() => schema.parse(42)).toThrow("Must be a decimal number")
|
|
29
|
+
expect(() => schema.parse("42")).toThrow("Must be a decimal number")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("should accept both when type='both' (default)", () => {
|
|
33
|
+
const schema = number({ type: "both" })
|
|
34
|
+
expect(schema.parse(42)).toBe(42)
|
|
35
|
+
expect(schema.parse(3.14)).toBe(3.14)
|
|
36
|
+
})
|
|
29
37
|
})
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
describe("sign validation", () => {
|
|
40
|
+
it("should enforce positive numbers", () => {
|
|
41
|
+
const schema = number({ positive: true })
|
|
42
|
+
expect(schema.parse(5)).toBe(5)
|
|
43
|
+
expect(() => schema.parse(0)).toThrow("Must be positive")
|
|
44
|
+
expect(() => schema.parse(-5)).toThrow("Must be positive")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("should enforce negative numbers", () => {
|
|
48
|
+
const schema = number({ negative: true })
|
|
49
|
+
expect(schema.parse(-5)).toBe(-5)
|
|
50
|
+
expect(() => schema.parse(0)).toThrow("Must be negative")
|
|
51
|
+
expect(() => schema.parse(5)).toThrow("Must be negative")
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("should enforce non-negative numbers", () => {
|
|
55
|
+
const schema = number({ nonNegative: true })
|
|
56
|
+
expect(schema.parse(0)).toBe(0)
|
|
57
|
+
expect(schema.parse(5)).toBe(5)
|
|
58
|
+
expect(() => schema.parse(-5)).toThrow("Must be non-negative")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("should enforce non-positive numbers", () => {
|
|
62
|
+
const schema = number({ nonPositive: true })
|
|
63
|
+
expect(schema.parse(0)).toBe(0)
|
|
64
|
+
expect(schema.parse(-5)).toBe(-5)
|
|
65
|
+
expect(() => schema.parse(5)).toThrow("Must be non-positive")
|
|
66
|
+
})
|
|
34
67
|
})
|
|
35
68
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
69
|
+
describe("multipleOf validation", () => {
|
|
70
|
+
it("should enforce multiple of constraint", () => {
|
|
71
|
+
const schema = number({ multipleOf: 5 })
|
|
72
|
+
expect(schema.parse(0)).toBe(0)
|
|
73
|
+
expect(schema.parse(5)).toBe(5)
|
|
74
|
+
expect(schema.parse(10)).toBe(10)
|
|
75
|
+
expect(schema.parse(-5)).toBe(-5)
|
|
76
|
+
expect(() => schema.parse(3)).toThrow("Must be a multiple of 5")
|
|
77
|
+
expect(() => schema.parse(7)).toThrow("Must be a multiple of 5")
|
|
78
|
+
})
|
|
41
79
|
})
|
|
42
80
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
81
|
+
describe("precision validation", () => {
|
|
82
|
+
it("should enforce decimal precision", () => {
|
|
83
|
+
const schema = number({ precision: 2 })
|
|
84
|
+
expect(schema.parse(3.14)).toBe(3.14)
|
|
85
|
+
expect(schema.parse(3.1)).toBe(3.1)
|
|
86
|
+
expect(schema.parse(3)).toBe(3)
|
|
87
|
+
expect(() => schema.parse(3.141)).toThrow("Must have at most 2 decimal places")
|
|
88
|
+
expect(() => schema.parse(3.1234)).toThrow("Must have at most 2 decimal places")
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe("finite validation", () => {
|
|
93
|
+
it("should reject infinite values by default", () => {
|
|
94
|
+
const schema = number()
|
|
95
|
+
expect(() => schema.parse(Infinity)).toThrow("Must be a finite number")
|
|
96
|
+
expect(() => schema.parse(-Infinity)).toThrow("Must be a finite number")
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it("should allow infinite values when finite=false", () => {
|
|
100
|
+
const schema = number({ finite: false })
|
|
101
|
+
expect(schema.parse(Infinity)).toBe(Infinity)
|
|
102
|
+
expect(schema.parse(-Infinity)).toBe(-Infinity)
|
|
103
|
+
})
|
|
48
104
|
})
|
|
49
105
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
106
|
+
describe("parseCommas functionality", () => {
|
|
107
|
+
it("should parse comma-separated numbers", () => {
|
|
108
|
+
const schema = number({ parseCommas: true })
|
|
109
|
+
expect(schema.parse("1,234")).toBe(1234)
|
|
110
|
+
expect(schema.parse("1,234.56")).toBe(1234.56)
|
|
111
|
+
expect(schema.parse("12,345,678")).toBe(12345678)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("should not parse commas by default", () => {
|
|
115
|
+
const schema = number()
|
|
116
|
+
expect(() => schema.parse("1,234")).toThrow("Must be a valid number")
|
|
117
|
+
})
|
|
55
118
|
})
|
|
56
119
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
120
|
+
describe("transform functionality", () => {
|
|
121
|
+
it("should apply transform function", () => {
|
|
122
|
+
const schema = number({
|
|
123
|
+
transform: (val) => Math.round(val * 100) / 100, // Round to 2 decimals
|
|
124
|
+
})
|
|
125
|
+
expect(schema.parse(3.14159)).toBe(3.14)
|
|
126
|
+
expect(schema.parse("2.71828")).toBe(2.72)
|
|
127
|
+
})
|
|
62
128
|
})
|
|
63
129
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
130
|
+
describe("custom i18n messages", () => {
|
|
131
|
+
it("should use custom messages when provided", () => {
|
|
132
|
+
const schema = number({
|
|
133
|
+
type: "integer",
|
|
134
|
+
min: 0,
|
|
135
|
+
i18n: {
|
|
136
|
+
en: {
|
|
137
|
+
required: "Custom required message",
|
|
138
|
+
integer: "Custom integer message",
|
|
139
|
+
min: "Custom min: at least ${min}",
|
|
140
|
+
},
|
|
141
|
+
"zh-TW": {
|
|
142
|
+
required: "客製化必填訊息",
|
|
143
|
+
integer: "客製化整數訊息",
|
|
144
|
+
min: "客製化最小值: 至少 ${min}",
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
expect(() => schema.parse("")).toThrow("Custom required message")
|
|
150
|
+
expect(() => schema.parse(3.14)).toThrow("Custom integer message")
|
|
151
|
+
expect(() => schema.parse(-1)).toThrow("Custom min: at least 0")
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it("should fallback to default messages when custom not provided", () => {
|
|
155
|
+
const schema = number({
|
|
156
|
+
type: "integer",
|
|
157
|
+
max: 10,
|
|
158
|
+
i18n: {
|
|
159
|
+
en: {
|
|
160
|
+
required: "Custom required message",
|
|
161
|
+
},
|
|
162
|
+
"zh-TW": {
|
|
163
|
+
required: "客製化必填訊息",
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
expect(() => schema.parse("")).toThrow("Custom required message")
|
|
169
|
+
expect(() => schema.parse(3.14)).toThrow("Must be an integer")
|
|
170
|
+
expect(() => schema.parse(15)).toThrow("Must be at most 10")
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it("should use correct locale for custom messages", () => {
|
|
174
|
+
setLocale("en")
|
|
175
|
+
const schemaEn = number({
|
|
176
|
+
type: "integer",
|
|
177
|
+
i18n: {
|
|
178
|
+
en: {
|
|
179
|
+
integer: "English integer message",
|
|
180
|
+
},
|
|
181
|
+
"zh-TW": {
|
|
182
|
+
integer: "繁體中文整數訊息",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
expect(() => schemaEn.parse(3.14)).toThrow("English integer message")
|
|
187
|
+
|
|
188
|
+
setLocale("zh-TW")
|
|
189
|
+
const schemaZh = number({
|
|
190
|
+
type: "integer",
|
|
191
|
+
i18n: {
|
|
192
|
+
en: {
|
|
193
|
+
integer: "English integer message",
|
|
194
|
+
},
|
|
195
|
+
"zh-TW": {
|
|
196
|
+
integer: "繁體中文整數訊息",
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
})
|
|
200
|
+
expect(() => schemaZh.parse(3.14)).toThrow("繁體中文整數訊息")
|
|
201
|
+
})
|
|
69
202
|
})
|
|
70
203
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
204
|
+
describe("complex scenarios", () => {
|
|
205
|
+
it("should work with multiple validations", () => {
|
|
206
|
+
const schema = number({
|
|
207
|
+
type: "integer",
|
|
208
|
+
min: 1,
|
|
209
|
+
max: 100,
|
|
210
|
+
multipleOf: 5,
|
|
211
|
+
positive: true,
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
expect(schema.parse(5)).toBe(5)
|
|
215
|
+
expect(schema.parse(10)).toBe(10)
|
|
216
|
+
expect(schema.parse(95)).toBe(95)
|
|
217
|
+
|
|
218
|
+
expect(() => schema.parse(0)).toThrow("Must be positive")
|
|
219
|
+
expect(() => schema.parse(3)).toThrow("Must be a multiple of 5")
|
|
220
|
+
expect(() => schema.parse(105)).toThrow("Must be at most 100")
|
|
221
|
+
expect(() => schema.parse(5.5)).toThrow("Must be an integer")
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it("should handle null values correctly when not required", () => {
|
|
225
|
+
const schema = number({
|
|
226
|
+
required: false,
|
|
227
|
+
type: "integer",
|
|
228
|
+
min: 0,
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
expect(schema.parse(null)).toBe(null)
|
|
232
|
+
expect(schema.parse("")).toBe(null)
|
|
233
|
+
expect(schema.parse(undefined)).toBe(null)
|
|
234
|
+
expect(schema.parse(5)).toBe(5)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it("should work with parseCommas and transform together", () => {
|
|
238
|
+
const schema = number({
|
|
239
|
+
parseCommas: true,
|
|
240
|
+
transform: (val) => Math.floor(val / 100) * 100, // Round down to nearest 100
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
expect(schema.parse("1,234")).toBe(1200)
|
|
244
|
+
expect(schema.parse("5,678.90")).toBe(5600)
|
|
245
|
+
})
|
|
76
246
|
})
|
|
77
247
|
})
|