@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
@@ -0,0 +1,240 @@
1
+ import { describe, it, expect, beforeEach } from "vitest"
2
+ import { businessId, setLocale, validateTaiwanBusinessId } from "../../src"
3
+
4
+ describe("Taiwan businessId() validator", () => {
5
+ beforeEach(() => setLocale("en"))
6
+
7
+ describe("basic functionality", () => {
8
+ it("should validate correct Taiwan business IDs", () => {
9
+ const schema = businessId()
10
+
11
+ // Valid Taiwan business IDs (統一編號) - using calculation based on new/old rules
12
+ expect(schema.parse("22550077")).toBe("22550077") // Sum = 50, 50 % 5 = 0 (new rule)
13
+ expect(schema.parse("12345675")).toBe("12345675") // Should be valid with our algorithm
14
+ expect(schema.parse("04595257")).toBe("04595257") // Should be valid
15
+ })
16
+
17
+ it("should reject invalid Taiwan business IDs", () => {
18
+ const schema = businessId()
19
+
20
+ // Invalid checksums
21
+ expect(() => schema.parse("12345672")).toThrow("Invalid Taiwan Business ID")
22
+ expect(() => schema.parse("12345673")).toThrow("Invalid Taiwan Business ID")
23
+ expect(() => schema.parse("12345674")).toThrow("Invalid Taiwan Business ID")
24
+ })
25
+
26
+ it("should reject non-numeric inputs", () => {
27
+ const schema = businessId()
28
+
29
+ expect(() => schema.parse("1234567A")).toThrow("Invalid Taiwan Business ID")
30
+ expect(() => schema.parse("abcdefgh")).toThrow("Invalid Taiwan Business ID")
31
+ expect(() => schema.parse("123-45-67")).toThrow("Invalid Taiwan Business ID")
32
+ })
33
+
34
+ it("should reject wrong length inputs", () => {
35
+ const schema = businessId()
36
+
37
+ expect(() => schema.parse("1234567")).toThrow("Invalid Taiwan Business ID")
38
+ expect(() => schema.parse("123456789")).toThrow("Invalid Taiwan Business ID")
39
+ expect(() => schema.parse("12")).toThrow("Invalid Taiwan Business ID")
40
+ expect(() => schema.parse("")).toThrow("Required")
41
+ })
42
+ })
43
+
44
+ describe("special case validation (7th digit = 7)", () => {
45
+ it("should handle special case where 7th digit is 7", () => {
46
+ const schema = businessId()
47
+
48
+ // Valid number with 7th digit = 7 for testing special case
49
+ expect(schema.parse("12345670")).toBe("12345670") // Should be valid with special case
50
+ })
51
+ })
52
+
53
+ describe("utility function validateTaiwanBusinessId", () => {
54
+ it("should correctly validate business IDs", () => {
55
+ // Valid IDs (confirmed by our algorithm)
56
+ expect(validateTaiwanBusinessId("12345675")).toBe(true)
57
+ expect(validateTaiwanBusinessId("22550077")).toBe(true)
58
+ expect(validateTaiwanBusinessId("04595257")).toBe(true)
59
+ expect(validateTaiwanBusinessId("53885486")).toBe(true)
60
+
61
+ // Invalid IDs
62
+ expect(validateTaiwanBusinessId("12345672")).toBe(false)
63
+ expect(validateTaiwanBusinessId("12345673")).toBe(false)
64
+ expect(validateTaiwanBusinessId("12345674")).toBe(false)
65
+ expect(validateTaiwanBusinessId("1234567")).toBe(false)
66
+ expect(validateTaiwanBusinessId("123456789")).toBe(false)
67
+ expect(validateTaiwanBusinessId("abcdefgh")).toBe(false)
68
+ })
69
+
70
+ it("should handle special case for 7th digit = 7", () => {
71
+ // Test with 7th digit = 7 (requires special calculation)
72
+ expect(validateTaiwanBusinessId("12345670")).toBe(true) // Should be valid with special case
73
+ })
74
+ })
75
+
76
+ describe("required/optional behavior", () => {
77
+ it("should handle required=true (default)", () => {
78
+ const schema = businessId()
79
+
80
+ expect(() => schema.parse("")).toThrow("Required")
81
+ expect(() => schema.parse(null)).toThrow()
82
+ expect(() => schema.parse(undefined)).toThrow()
83
+ })
84
+
85
+ it("should handle required=false", () => {
86
+ const schema = businessId({ required: false })
87
+
88
+ expect(schema.parse("")).toBe(null)
89
+ expect(schema.parse(null)).toBe(null)
90
+ expect(schema.parse(undefined)).toBe(null)
91
+ expect(schema.parse("12345675")).toBe("12345675")
92
+ })
93
+
94
+ it("should use default values", () => {
95
+ const requiredSchema = businessId({ defaultValue: "12345675" })
96
+ const optionalSchema = businessId({ required: false, defaultValue: "12345675" })
97
+
98
+ expect(requiredSchema.parse("")).toBe("12345675")
99
+ expect(optionalSchema.parse("")).toBe("12345675")
100
+ })
101
+ })
102
+
103
+ describe("transform function", () => {
104
+ it("should apply custom transform", () => {
105
+ const schema = businessId({
106
+ transform: (val) => val.replace(/[-\s]/g, ""),
107
+ })
108
+
109
+ expect(schema.parse("1234-5675")).toBe("12345675")
110
+ // Note: This will fail validation since the cleaned value has wrong checksum
111
+ expect(() => schema.parse("1234-5672")).toThrow("Invalid Taiwan Business ID")
112
+ })
113
+
114
+ it("should apply transform before validation", () => {
115
+ const schema = businessId({
116
+ transform: (val) => val.replace(/\s+/g, ""),
117
+ })
118
+
119
+ expect(schema.parse(" 12345675 ")).toBe("12345675")
120
+ expect(() => schema.parse(" 1234567 2 ")).toThrow("Invalid Taiwan Business ID")
121
+ })
122
+ })
123
+
124
+ describe("input preprocessing", () => {
125
+ it("should handle string conversion", () => {
126
+ const schema = businessId()
127
+
128
+ expect(schema.parse(12345675)).toBe("12345675")
129
+ expect(() => schema.parse(12345672)).toThrow("Invalid Taiwan Business ID")
130
+ })
131
+
132
+ it("should trim whitespace", () => {
133
+ const schema = businessId()
134
+
135
+ expect(schema.parse(" 12345675 ")).toBe("12345675")
136
+ expect(schema.parse("\t12345675\n")).toBe("12345675")
137
+ })
138
+ })
139
+
140
+ describe("i18n support", () => {
141
+ it("should use English messages by default", () => {
142
+ setLocale("en")
143
+ const schema = businessId()
144
+
145
+ expect(() => schema.parse("")).toThrow("Required")
146
+ expect(() => schema.parse("1234567")).toThrow("Invalid Taiwan Business ID")
147
+ expect(() => schema.parse("12345672")).toThrow("Invalid Taiwan Business ID")
148
+ })
149
+
150
+ it("should use Chinese messages when locale is zh-TW", () => {
151
+ setLocale("zh-TW")
152
+ const schema = businessId()
153
+
154
+ expect(() => schema.parse("")).toThrow("必填")
155
+ expect(() => schema.parse("1234567")).toThrow("無效的統一編號")
156
+ expect(() => schema.parse("12345672")).toThrow("無效的統一編號")
157
+ })
158
+
159
+ it("should support custom i18n messages", () => {
160
+ const schema = businessId({
161
+ i18n: {
162
+ en: {
163
+ required: "Business ID is required",
164
+ invalid: "Business ID is invalid",
165
+ },
166
+ "zh-TW": {
167
+ required: "請輸入統一編號",
168
+ invalid: "統一編號格式錯誤",
169
+ },
170
+ },
171
+ })
172
+
173
+ setLocale("en")
174
+ expect(() => schema.parse("")).toThrow("Business ID is required")
175
+ expect(() => schema.parse("12345672")).toThrow("Business ID is invalid")
176
+
177
+ setLocale("zh-TW")
178
+ expect(() => schema.parse("")).toThrow("請輸入統一編號")
179
+ expect(() => schema.parse("12345672")).toThrow("統一編號格式錯誤")
180
+ })
181
+ })
182
+
183
+ describe("real world Taiwan business IDs", () => {
184
+ it("should validate known real business IDs", () => {
185
+ const schema = businessId()
186
+
187
+ // These are example valid Taiwan business IDs for testing
188
+ const validIds = [
189
+ "12345675", // Calculated valid example
190
+ "22550077", // Calculated valid example
191
+ "04595257", // Calculated valid example
192
+ "53885486", // Calculated valid example
193
+ ]
194
+
195
+ validIds.forEach((id) => {
196
+ expect(schema.parse(id)).toBe(id)
197
+ })
198
+ })
199
+
200
+ it("should reject common invalid patterns", () => {
201
+ const schema = businessId()
202
+
203
+ const invalidIds = [
204
+ "00000001", // All zeros with bad checksum
205
+ "11111111", // All ones
206
+ "12345672", // Sequential but wrong checksum
207
+ "12345673", // Almost valid but wrong checksum
208
+ "12345674", // Almost valid but wrong checksum
209
+ "98765432", // Reverse sequential but wrong checksum
210
+ "87654321", // Another invalid pattern
211
+ ]
212
+
213
+ invalidIds.forEach((id) => {
214
+ expect(() => schema.parse(id)).toThrow()
215
+ })
216
+ })
217
+ })
218
+
219
+ describe("edge cases", () => {
220
+ it("should handle leading zeros", () => {
221
+ const schema = businessId()
222
+
223
+ expect(schema.parse("04595257")).toBe("04595257")
224
+ expect(() => schema.parse("00123456")).toThrow("Invalid Taiwan Business ID")
225
+ })
226
+
227
+ it("should handle empty and whitespace inputs", () => {
228
+ const schema = businessId()
229
+ const optionalSchema = businessId({ required: false })
230
+
231
+ expect(() => schema.parse("")).toThrow("Required")
232
+ expect(() => schema.parse(" ")).toThrow("Required")
233
+ expect(() => schema.parse("\t\n")).toThrow("Required")
234
+
235
+ expect(optionalSchema.parse("")).toBe(null)
236
+ expect(optionalSchema.parse(" ")).toBe(null)
237
+ expect(optionalSchema.parse("\t\n")).toBe(null)
238
+ })
239
+ })
240
+ })