@hy_ong/zod-kit 0.0.5 → 0.1.0
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 +9 -1
- package/README.md +465 -97
- package/dist/index.cjs +1690 -179
- package/dist/index.d.cts +2791 -28
- package/dist/index.d.ts +2791 -28
- package/dist/index.js +1672 -178
- package/package.json +2 -1
- package/src/i18n/locales/en.json +62 -0
- package/src/i18n/locales/zh-TW.json +62 -0
- package/src/index.ts +4 -0
- package/src/validators/common/boolean.ts +101 -4
- package/src/validators/common/date.ts +141 -6
- package/src/validators/common/datetime.ts +680 -0
- package/src/validators/common/email.ts +120 -4
- package/src/validators/common/file.ts +391 -0
- package/src/validators/common/id.ts +230 -18
- package/src/validators/common/number.ts +132 -4
- package/src/validators/common/password.ts +187 -8
- package/src/validators/common/text.ts +130 -6
- package/src/validators/common/time.ts +607 -0
- package/src/validators/common/url.ts +153 -6
- package/src/validators/taiwan/business-id.ts +138 -9
- package/src/validators/taiwan/fax.ts +164 -10
- package/src/validators/taiwan/mobile.ts +151 -10
- package/src/validators/taiwan/national-id.ts +233 -17
- package/src/validators/taiwan/postal-code.ts +1048 -0
- package/src/validators/taiwan/tel.ts +167 -10
- package/tests/common/boolean.test.ts +38 -38
- package/tests/common/date.test.ts +65 -65
- package/tests/common/datetime.test.ts +675 -0
- package/tests/common/email.test.ts +24 -28
- package/tests/common/file.test.ts +475 -0
- package/tests/common/id.test.ts +80 -113
- package/tests/common/number.test.ts +24 -25
- package/tests/common/password.test.ts +28 -35
- package/tests/common/text.test.ts +36 -37
- package/tests/common/time.test.ts +510 -0
- package/tests/common/url.test.ts +67 -67
- package/tests/taiwan/business-id.test.ts +22 -22
- package/tests/taiwan/fax.test.ts +33 -42
- package/tests/taiwan/mobile.test.ts +32 -41
- package/tests/taiwan/national-id.test.ts +31 -31
- package/tests/taiwan/postal-code.test.ts +751 -0
- package/tests/taiwan/tel.test.ts +33 -42
- package/debug.js +0 -21
- package/debug.ts +0 -16
package/tests/common/url.test.ts
CHANGED
|
@@ -4,123 +4,123 @@ import { setLocale, url } from "../../src"
|
|
|
4
4
|
describe("url", () => {
|
|
5
5
|
describe("basic validation", () => {
|
|
6
6
|
it("should validate valid URL", () => {
|
|
7
|
-
const schema = url()
|
|
7
|
+
const schema = url(true)
|
|
8
8
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
9
9
|
})
|
|
10
10
|
|
|
11
11
|
it("should validate http URL", () => {
|
|
12
|
-
const schema = url()
|
|
12
|
+
const schema = url(true)
|
|
13
13
|
expect(schema.parse("http://example.com")).toBe("http://example.com")
|
|
14
14
|
})
|
|
15
15
|
|
|
16
16
|
it("should validate URL with path", () => {
|
|
17
|
-
const schema = url()
|
|
17
|
+
const schema = url(true)
|
|
18
18
|
expect(schema.parse("https://example.com/path")).toBe("https://example.com/path")
|
|
19
19
|
})
|
|
20
20
|
|
|
21
21
|
it("should validate URL with query parameters", () => {
|
|
22
|
-
const schema = url()
|
|
22
|
+
const schema = url(true)
|
|
23
23
|
expect(schema.parse("https://example.com?param=value")).toBe("https://example.com?param=value")
|
|
24
24
|
})
|
|
25
25
|
|
|
26
26
|
it("should validate URL with fragment", () => {
|
|
27
|
-
const schema = url()
|
|
27
|
+
const schema = url(true)
|
|
28
28
|
expect(schema.parse("https://example.com#section")).toBe("https://example.com#section")
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
it("should reject invalid URL format", () => {
|
|
32
|
-
const schema = url()
|
|
32
|
+
const schema = url(true)
|
|
33
33
|
expect(() => schema.parse("invalid-url")).toThrow()
|
|
34
34
|
})
|
|
35
35
|
|
|
36
36
|
it("should reject empty string", () => {
|
|
37
|
-
const schema = url()
|
|
37
|
+
const schema = url(true)
|
|
38
38
|
expect(() => schema.parse("")).toThrow()
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
it("should reject null", () => {
|
|
42
|
-
const schema = url()
|
|
42
|
+
const schema = url(true)
|
|
43
43
|
expect(() => schema.parse(null)).toThrow()
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
it("should reject undefined", () => {
|
|
47
|
-
const schema = url()
|
|
47
|
+
const schema = url(true)
|
|
48
48
|
expect(() => schema.parse(undefined)).toThrow()
|
|
49
49
|
})
|
|
50
50
|
})
|
|
51
51
|
|
|
52
52
|
describe("optional", () => {
|
|
53
53
|
it("should allow null when not required", () => {
|
|
54
|
-
const schema = url(
|
|
54
|
+
const schema = url(false)
|
|
55
55
|
expect(schema.parse(null)).toBe(null)
|
|
56
56
|
})
|
|
57
57
|
|
|
58
58
|
it("should allow empty string when not required", () => {
|
|
59
|
-
const schema = url(
|
|
59
|
+
const schema = url(false)
|
|
60
60
|
expect(schema.parse("")).toBe(null)
|
|
61
61
|
})
|
|
62
62
|
|
|
63
63
|
it("should allow undefined when not required", () => {
|
|
64
|
-
const schema = url(
|
|
64
|
+
const schema = url(false)
|
|
65
65
|
expect(schema.parse(undefined)).toBe(null)
|
|
66
66
|
})
|
|
67
67
|
|
|
68
68
|
it("should validate valid URL when not required", () => {
|
|
69
|
-
const schema = url(
|
|
69
|
+
const schema = url(false)
|
|
70
70
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
71
71
|
})
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
describe("length validation", () => {
|
|
75
75
|
it("should accept URL with valid min length", () => {
|
|
76
|
-
const schema = url({ min: 10 })
|
|
76
|
+
const schema = url(true, { min: 10 })
|
|
77
77
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
78
78
|
})
|
|
79
79
|
|
|
80
80
|
it("should reject URL below min length", () => {
|
|
81
|
-
const schema = url({ min: 30 })
|
|
81
|
+
const schema = url(true, { min: 30 })
|
|
82
82
|
expect(() => schema.parse("https://short.co")).toThrow()
|
|
83
83
|
})
|
|
84
84
|
|
|
85
85
|
it("should accept URL with valid max length", () => {
|
|
86
|
-
const schema = url({ max: 30 })
|
|
86
|
+
const schema = url(true, { max: 30 })
|
|
87
87
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
88
88
|
})
|
|
89
89
|
|
|
90
90
|
it("should reject URL above max length", () => {
|
|
91
|
-
const schema = url({ max: 10 })
|
|
91
|
+
const schema = url(true, { max: 10 })
|
|
92
92
|
expect(() => schema.parse("https://verylongurl.com")).toThrow()
|
|
93
93
|
})
|
|
94
94
|
|
|
95
95
|
it("should accept URL within range", () => {
|
|
96
|
-
const schema = url({ min: 10, max: 30 })
|
|
96
|
+
const schema = url(true, { min: 10, max: 30 })
|
|
97
97
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
98
98
|
})
|
|
99
99
|
})
|
|
100
100
|
|
|
101
101
|
describe("includes/excludes validation", () => {
|
|
102
102
|
it("should accept URL containing required substring", () => {
|
|
103
|
-
const schema = url({ includes: "company" })
|
|
103
|
+
const schema = url(true, { includes: "company" })
|
|
104
104
|
expect(schema.parse("https://company.example.com")).toBe("https://company.example.com")
|
|
105
105
|
})
|
|
106
106
|
|
|
107
107
|
it("should reject URL not containing required substring", () => {
|
|
108
|
-
const schema = url({ includes: "company" })
|
|
108
|
+
const schema = url(true, { includes: "company" })
|
|
109
109
|
expect(() => schema.parse("https://other.com")).toThrow()
|
|
110
110
|
})
|
|
111
111
|
|
|
112
112
|
it("should accept URL not containing excluded substring", () => {
|
|
113
|
-
const schema = url({ excludes: "banned" })
|
|
113
|
+
const schema = url(true, { excludes: "banned" })
|
|
114
114
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
115
115
|
})
|
|
116
116
|
|
|
117
117
|
it("should reject URL containing excluded substring", () => {
|
|
118
|
-
const schema = url({ excludes: "banned" })
|
|
118
|
+
const schema = url(true, { excludes: "banned" })
|
|
119
119
|
expect(() => schema.parse("https://banned.com")).toThrow()
|
|
120
120
|
})
|
|
121
121
|
|
|
122
122
|
it("should handle multiple excluded substrings as array", () => {
|
|
123
|
-
const schema = url({ excludes: ["banned", "blocked"] })
|
|
123
|
+
const schema = url(true, { excludes: ["banned", "blocked"] })
|
|
124
124
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
125
125
|
expect(() => schema.parse("https://banned.com")).toThrow()
|
|
126
126
|
expect(() => schema.parse("https://blocked.com")).toThrow()
|
|
@@ -129,36 +129,36 @@ describe("url", () => {
|
|
|
129
129
|
|
|
130
130
|
describe("protocol validation", () => {
|
|
131
131
|
it("should accept allowed protocols", () => {
|
|
132
|
-
const schema = url({ protocols: ["https", "http"] })
|
|
132
|
+
const schema = url(true, { protocols: ["https", "http"] })
|
|
133
133
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
134
134
|
expect(schema.parse("http://example.com")).toBe("http://example.com")
|
|
135
135
|
})
|
|
136
136
|
|
|
137
137
|
it("should reject disallowed protocols", () => {
|
|
138
|
-
const schema = url({ protocols: ["https"] })
|
|
138
|
+
const schema = url(true, { protocols: ["https"] })
|
|
139
139
|
expect(() => schema.parse("http://example.com")).toThrow()
|
|
140
140
|
})
|
|
141
141
|
|
|
142
142
|
it("should accept ftp protocol when allowed", () => {
|
|
143
|
-
const schema = url({ protocols: ["ftp"] })
|
|
143
|
+
const schema = url(true, { protocols: ["ftp"] })
|
|
144
144
|
expect(schema.parse("ftp://files.example.com")).toBe("ftp://files.example.com")
|
|
145
145
|
})
|
|
146
146
|
})
|
|
147
147
|
|
|
148
148
|
describe("domain validation", () => {
|
|
149
149
|
it("should accept allowed domains", () => {
|
|
150
|
-
const schema = url({ allowedDomains: ["example.com", "test.org"] })
|
|
150
|
+
const schema = url(true, { allowedDomains: ["example.com", "test.org"] })
|
|
151
151
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
152
152
|
expect(schema.parse("https://sub.example.com")).toBe("https://sub.example.com")
|
|
153
153
|
})
|
|
154
154
|
|
|
155
155
|
it("should reject disallowed domains", () => {
|
|
156
|
-
const schema = url({ allowedDomains: ["example.com"] })
|
|
156
|
+
const schema = url(true, { allowedDomains: ["example.com"] })
|
|
157
157
|
expect(() => schema.parse("https://other.com")).toThrow()
|
|
158
158
|
})
|
|
159
159
|
|
|
160
160
|
it("should reject blocked domains", () => {
|
|
161
|
-
const schema = url({ blockedDomains: ["blocked.com", "spam.org"] })
|
|
161
|
+
const schema = url(true, { blockedDomains: ["blocked.com", "spam.org"] })
|
|
162
162
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
163
163
|
expect(() => schema.parse("https://blocked.com")).toThrow()
|
|
164
164
|
expect(() => schema.parse("https://sub.blocked.com")).toThrow()
|
|
@@ -167,19 +167,19 @@ describe("url", () => {
|
|
|
167
167
|
|
|
168
168
|
describe("port validation", () => {
|
|
169
169
|
it("should accept allowed ports", () => {
|
|
170
|
-
const schema = url({ allowedPorts: [80, 443, 8080] })
|
|
170
|
+
const schema = url(true, { allowedPorts: [80, 443, 8080] })
|
|
171
171
|
expect(schema.parse("https://example.com")).toBe("https://example.com") // 443
|
|
172
172
|
expect(schema.parse("http://example.com")).toBe("http://example.com") // 80
|
|
173
173
|
expect(schema.parse("https://example.com:8080")).toBe("https://example.com:8080")
|
|
174
174
|
})
|
|
175
175
|
|
|
176
176
|
it("should reject disallowed ports", () => {
|
|
177
|
-
const schema = url({ allowedPorts: [443] })
|
|
177
|
+
const schema = url(true, { allowedPorts: [443] })
|
|
178
178
|
expect(() => schema.parse("http://example.com")).toThrow() // Port 80
|
|
179
179
|
})
|
|
180
180
|
|
|
181
181
|
it("should reject blocked ports", () => {
|
|
182
|
-
const schema = url({ blockedPorts: [80] })
|
|
182
|
+
const schema = url(true, { blockedPorts: [80] })
|
|
183
183
|
expect(schema.parse("https://example.com")).toBe("https://example.com") // 443
|
|
184
184
|
expect(() => schema.parse("http://example.com")).toThrow() // Port 80
|
|
185
185
|
})
|
|
@@ -187,80 +187,80 @@ describe("url", () => {
|
|
|
187
187
|
|
|
188
188
|
describe("path validation", () => {
|
|
189
189
|
it("should accept path starting with required prefix", () => {
|
|
190
|
-
const schema = url({ pathStartsWith: "/api" })
|
|
190
|
+
const schema = url(true, { pathStartsWith: "/api" })
|
|
191
191
|
expect(schema.parse("https://example.com/api/users")).toBe("https://example.com/api/users")
|
|
192
192
|
})
|
|
193
193
|
|
|
194
194
|
it("should reject path not starting with required prefix", () => {
|
|
195
|
-
const schema = url({ pathStartsWith: "/api" })
|
|
195
|
+
const schema = url(true, { pathStartsWith: "/api" })
|
|
196
196
|
expect(() => schema.parse("https://example.com/web/users")).toThrow()
|
|
197
197
|
})
|
|
198
198
|
|
|
199
199
|
it("should accept path ending with required suffix", () => {
|
|
200
|
-
const schema = url({ pathEndsWith: ".json" })
|
|
200
|
+
const schema = url(true, { pathEndsWith: ".json" })
|
|
201
201
|
expect(schema.parse("https://example.com/api/data.json")).toBe("https://example.com/api/data.json")
|
|
202
202
|
})
|
|
203
203
|
|
|
204
204
|
it("should reject path not ending with required suffix", () => {
|
|
205
|
-
const schema = url({ pathEndsWith: ".json" })
|
|
205
|
+
const schema = url(true, { pathEndsWith: ".json" })
|
|
206
206
|
expect(() => schema.parse("https://example.com/api/data.xml")).toThrow()
|
|
207
207
|
})
|
|
208
208
|
})
|
|
209
209
|
|
|
210
210
|
describe("query validation", () => {
|
|
211
211
|
it("should accept URL with query when required", () => {
|
|
212
|
-
const schema = url({ mustHaveQuery: true })
|
|
212
|
+
const schema = url(true, { mustHaveQuery: true })
|
|
213
213
|
expect(schema.parse("https://example.com?param=value")).toBe("https://example.com?param=value")
|
|
214
214
|
})
|
|
215
215
|
|
|
216
216
|
it("should reject URL without query when required", () => {
|
|
217
|
-
const schema = url({ mustHaveQuery: true })
|
|
217
|
+
const schema = url(true, { mustHaveQuery: true })
|
|
218
218
|
expect(() => schema.parse("https://example.com")).toThrow()
|
|
219
219
|
})
|
|
220
220
|
|
|
221
221
|
it("should accept URL without query when forbidden", () => {
|
|
222
|
-
const schema = url({ mustNotHaveQuery: true })
|
|
222
|
+
const schema = url(true, { mustNotHaveQuery: true })
|
|
223
223
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
224
224
|
})
|
|
225
225
|
|
|
226
226
|
it("should reject URL with query when forbidden", () => {
|
|
227
|
-
const schema = url({ mustNotHaveQuery: true })
|
|
227
|
+
const schema = url(true, { mustNotHaveQuery: true })
|
|
228
228
|
expect(() => schema.parse("https://example.com?param=value")).toThrow()
|
|
229
229
|
})
|
|
230
230
|
})
|
|
231
231
|
|
|
232
232
|
describe("fragment validation", () => {
|
|
233
233
|
it("should accept URL with fragment when required", () => {
|
|
234
|
-
const schema = url({ mustHaveFragment: true })
|
|
234
|
+
const schema = url(true, { mustHaveFragment: true })
|
|
235
235
|
expect(schema.parse("https://example.com#section")).toBe("https://example.com#section")
|
|
236
236
|
})
|
|
237
237
|
|
|
238
238
|
it("should reject URL without fragment when required", () => {
|
|
239
|
-
const schema = url({ mustHaveFragment: true })
|
|
239
|
+
const schema = url(true, { mustHaveFragment: true })
|
|
240
240
|
expect(() => schema.parse("https://example.com")).toThrow()
|
|
241
241
|
})
|
|
242
242
|
|
|
243
243
|
it("should accept URL without fragment when forbidden", () => {
|
|
244
|
-
const schema = url({ mustNotHaveFragment: true })
|
|
244
|
+
const schema = url(true, { mustNotHaveFragment: true })
|
|
245
245
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
246
246
|
})
|
|
247
247
|
|
|
248
248
|
it("should reject URL with fragment when forbidden", () => {
|
|
249
|
-
const schema = url({ mustNotHaveFragment: true })
|
|
249
|
+
const schema = url(true, { mustNotHaveFragment: true })
|
|
250
250
|
expect(() => schema.parse("https://example.com#section")).toThrow()
|
|
251
251
|
})
|
|
252
252
|
})
|
|
253
253
|
|
|
254
254
|
describe("localhost validation", () => {
|
|
255
255
|
it("should allow localhost by default", () => {
|
|
256
|
-
const schema = url()
|
|
256
|
+
const schema = url(true)
|
|
257
257
|
expect(schema.parse("http://localhost:3000")).toBe("http://localhost:3000")
|
|
258
258
|
expect(schema.parse("http://127.0.0.1")).toBe("http://127.0.0.1")
|
|
259
259
|
expect(schema.parse("http://192.168.1.1")).toBe("http://192.168.1.1")
|
|
260
260
|
})
|
|
261
261
|
|
|
262
262
|
it("should block localhost when blockLocalhost is true", () => {
|
|
263
|
-
const schema = url({ blockLocalhost: true })
|
|
263
|
+
const schema = url(true, { blockLocalhost: true })
|
|
264
264
|
expect(schema.parse("https://example.com")).toBe("https://example.com")
|
|
265
265
|
expect(() => schema.parse("http://localhost:3000")).toThrow()
|
|
266
266
|
expect(() => schema.parse("http://127.0.0.1")).toThrow()
|
|
@@ -268,19 +268,19 @@ describe("url", () => {
|
|
|
268
268
|
})
|
|
269
269
|
|
|
270
270
|
it("should block localhost when allowLocalhost is false", () => {
|
|
271
|
-
const schema = url({ allowLocalhost: false })
|
|
271
|
+
const schema = url(true, { allowLocalhost: false })
|
|
272
272
|
expect(() => schema.parse("http://localhost:3000")).toThrow()
|
|
273
273
|
})
|
|
274
274
|
})
|
|
275
275
|
|
|
276
276
|
describe("transform function", () => {
|
|
277
277
|
it("should apply custom transform function", () => {
|
|
278
|
-
const schema = url({ transform: (val) => val.replace("http://", "https://") })
|
|
278
|
+
const schema = url(true, { transform: (val) => val.replace("http://", "https://") })
|
|
279
279
|
expect(schema.parse("http://example.com")).toBe("https://example.com")
|
|
280
280
|
})
|
|
281
281
|
|
|
282
282
|
it("should apply transform before validation", () => {
|
|
283
|
-
const schema = url({
|
|
283
|
+
const schema = url(true, {
|
|
284
284
|
protocols: ["https"],
|
|
285
285
|
transform: (val) => val.replace("http://", "https://"),
|
|
286
286
|
})
|
|
@@ -288,7 +288,7 @@ describe("url", () => {
|
|
|
288
288
|
})
|
|
289
289
|
|
|
290
290
|
it("should work with other validations after transform", () => {
|
|
291
|
-
const schema = url({
|
|
291
|
+
const schema = url(true, {
|
|
292
292
|
includes: "https",
|
|
293
293
|
transform: (val) => val.replace("http://", "https://"),
|
|
294
294
|
})
|
|
@@ -298,14 +298,14 @@ describe("url", () => {
|
|
|
298
298
|
|
|
299
299
|
describe("default value", () => {
|
|
300
300
|
it("should use default value when input is empty", () => {
|
|
301
|
-
const schema = url({ defaultValue: "https://example.com" })
|
|
301
|
+
const schema = url(true, { defaultValue: "https://example.com" })
|
|
302
302
|
expect(schema.parse("")).toBe("https://example.com")
|
|
303
303
|
expect(schema.parse(null)).toBe("https://example.com")
|
|
304
304
|
expect(schema.parse(undefined)).toBe("https://example.com")
|
|
305
305
|
})
|
|
306
306
|
|
|
307
307
|
it("should use default value when optional and input is empty", () => {
|
|
308
|
-
const schema = url(
|
|
308
|
+
const schema = url(false, { defaultValue: "https://example.com" })
|
|
309
309
|
expect(schema.parse("")).toBe("https://example.com")
|
|
310
310
|
expect(schema.parse(null)).toBe("https://example.com")
|
|
311
311
|
expect(schema.parse(undefined)).toBe("https://example.com")
|
|
@@ -314,7 +314,7 @@ describe("url", () => {
|
|
|
314
314
|
|
|
315
315
|
describe("i18n custom messages", () => {
|
|
316
316
|
it("should use custom English messages", () => {
|
|
317
|
-
const schema = url({
|
|
317
|
+
const schema = url(true, {
|
|
318
318
|
i18n: {
|
|
319
319
|
en: { invalid: "Custom URL format error" },
|
|
320
320
|
"zh-TW": { invalid: "自定義 URL 格式錯誤" },
|
|
@@ -330,7 +330,7 @@ describe("url", () => {
|
|
|
330
330
|
})
|
|
331
331
|
|
|
332
332
|
it("should use custom Chinese messages", () => {
|
|
333
|
-
const schema = url({
|
|
333
|
+
const schema = url(true, {
|
|
334
334
|
i18n: {
|
|
335
335
|
en: { invalid: "Custom URL format error" },
|
|
336
336
|
"zh-TW": { invalid: "自定義 URL 格式錯誤" },
|
|
@@ -346,7 +346,7 @@ describe("url", () => {
|
|
|
346
346
|
})
|
|
347
347
|
|
|
348
348
|
it("should fallback to default messages when custom not provided", () => {
|
|
349
|
-
const schema = url({
|
|
349
|
+
const schema = url(true, {
|
|
350
350
|
i18n: {
|
|
351
351
|
en: { invalid: "Custom invalid error" },
|
|
352
352
|
"zh-TW": { invalid: "自定義無效錯誤" },
|
|
@@ -362,7 +362,7 @@ describe("url", () => {
|
|
|
362
362
|
})
|
|
363
363
|
|
|
364
364
|
it("should handle protocol validation with custom message", () => {
|
|
365
|
-
const schema = url({
|
|
365
|
+
const schema = url(true, {
|
|
366
366
|
protocols: ["https"],
|
|
367
367
|
i18n: {
|
|
368
368
|
en: { protocol: "Only HTTPS allowed!" },
|
|
@@ -379,7 +379,7 @@ describe("url", () => {
|
|
|
379
379
|
})
|
|
380
380
|
|
|
381
381
|
it("should handle domain validation with custom message", () => {
|
|
382
|
-
const schema = url({
|
|
382
|
+
const schema = url(true, {
|
|
383
383
|
allowedDomains: ["example.com"],
|
|
384
384
|
i18n: {
|
|
385
385
|
en: { domain: "Only example.com allowed!" },
|
|
@@ -399,7 +399,7 @@ describe("url", () => {
|
|
|
399
399
|
describe("localization", () => {
|
|
400
400
|
it("should use English error messages", () => {
|
|
401
401
|
setLocale("en")
|
|
402
|
-
const schema = url()
|
|
402
|
+
const schema = url(true)
|
|
403
403
|
|
|
404
404
|
try {
|
|
405
405
|
schema.parse("invalid")
|
|
@@ -410,7 +410,7 @@ describe("url", () => {
|
|
|
410
410
|
|
|
411
411
|
it("should use Chinese error messages", () => {
|
|
412
412
|
setLocale("zh-TW")
|
|
413
|
-
const schema = url()
|
|
413
|
+
const schema = url(true)
|
|
414
414
|
|
|
415
415
|
try {
|
|
416
416
|
schema.parse("invalid")
|
|
@@ -421,7 +421,7 @@ describe("url", () => {
|
|
|
421
421
|
|
|
422
422
|
it("should use Chinese domain validation messages", () => {
|
|
423
423
|
setLocale("zh-TW")
|
|
424
|
-
const schema = url({ allowedDomains: ["example.com"] })
|
|
424
|
+
const schema = url(true, { allowedDomains: ["example.com"] })
|
|
425
425
|
|
|
426
426
|
try {
|
|
427
427
|
schema.parse("https://other.com")
|
|
@@ -433,7 +433,7 @@ describe("url", () => {
|
|
|
433
433
|
|
|
434
434
|
describe("complex scenarios", () => {
|
|
435
435
|
it("should handle multiple validations combined", () => {
|
|
436
|
-
const schema = url({
|
|
436
|
+
const schema = url(true, {
|
|
437
437
|
protocols: ["https"],
|
|
438
438
|
allowedDomains: ["example.com"],
|
|
439
439
|
min: 20,
|
|
@@ -451,7 +451,7 @@ describe("url", () => {
|
|
|
451
451
|
})
|
|
452
452
|
|
|
453
453
|
it("should handle localhost detection in private networks", () => {
|
|
454
|
-
const schema = url({ blockLocalhost: true })
|
|
454
|
+
const schema = url(true, { blockLocalhost: true })
|
|
455
455
|
|
|
456
456
|
expect(() => schema.parse("http://10.0.0.1")).toThrow()
|
|
457
457
|
expect(() => schema.parse("http://172.16.0.1")).toThrow()
|
|
@@ -460,7 +460,7 @@ describe("url", () => {
|
|
|
460
460
|
})
|
|
461
461
|
|
|
462
462
|
it("should handle edge cases with ports", () => {
|
|
463
|
-
const schema = url({ allowedPorts: [443, 8080] })
|
|
463
|
+
const schema = url(true, { allowedPorts: [443, 8080] })
|
|
464
464
|
|
|
465
465
|
expect(schema.parse("https://example.com")).toBe("https://example.com") // Default 443
|
|
466
466
|
expect(schema.parse("https://example.com:8080")).toBe("https://example.com:8080")
|
|
@@ -470,24 +470,24 @@ describe("url", () => {
|
|
|
470
470
|
|
|
471
471
|
describe("edge cases", () => {
|
|
472
472
|
it("should handle URLs with special characters", () => {
|
|
473
|
-
const schema = url()
|
|
473
|
+
const schema = url(true)
|
|
474
474
|
expect(schema.parse("https://example.com/path%20with%20spaces")).toBe("https://example.com/path%20with%20spaces")
|
|
475
475
|
})
|
|
476
476
|
|
|
477
477
|
it("should handle international domain names", () => {
|
|
478
|
-
const schema = url()
|
|
478
|
+
const schema = url(true)
|
|
479
479
|
expect(schema.parse("https://xn--fsq.com")).toBe("https://xn--fsq.com") // Internationalized domain
|
|
480
480
|
})
|
|
481
481
|
|
|
482
482
|
it("should handle URLs with authentication", () => {
|
|
483
|
-
const schema = url()
|
|
483
|
+
const schema = url(true)
|
|
484
484
|
expect(schema.parse("https://user:pass@example.com")).toBe("https://user:pass@example.com")
|
|
485
485
|
})
|
|
486
486
|
|
|
487
487
|
it("should handle very long URLs", () => {
|
|
488
488
|
const longPath = "/very/long/path/" + "segment/".repeat(100)
|
|
489
489
|
const longUrl = `https://example.com${longPath}`
|
|
490
|
-
const schema = url()
|
|
490
|
+
const schema = url(true)
|
|
491
491
|
expect(schema.parse(longUrl)).toBe(longUrl)
|
|
492
492
|
})
|
|
493
493
|
})
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from "vitest"
|
|
2
2
|
import { businessId, setLocale, validateTaiwanBusinessId } from "../../src"
|
|
3
3
|
|
|
4
|
-
describe("Taiwan businessId() validator", () => {
|
|
4
|
+
describe("Taiwan businessId(true) validator", () => {
|
|
5
5
|
beforeEach(() => setLocale("en"))
|
|
6
6
|
|
|
7
7
|
describe("basic functionality", () => {
|
|
8
8
|
it("should validate correct Taiwan business IDs", () => {
|
|
9
|
-
const schema = businessId()
|
|
9
|
+
const schema = businessId(true)
|
|
10
10
|
|
|
11
11
|
// Valid Taiwan business IDs (統一編號) - using calculation based on new/old rules
|
|
12
12
|
expect(schema.parse("22550077")).toBe("22550077") // Sum = 50, 50 % 5 = 0 (new rule)
|
|
@@ -15,7 +15,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
15
15
|
})
|
|
16
16
|
|
|
17
17
|
it("should reject invalid Taiwan business IDs", () => {
|
|
18
|
-
const schema = businessId()
|
|
18
|
+
const schema = businessId(true)
|
|
19
19
|
|
|
20
20
|
// Invalid checksums
|
|
21
21
|
expect(() => schema.parse("12345672")).toThrow("Invalid Taiwan Business ID")
|
|
@@ -24,7 +24,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
24
24
|
})
|
|
25
25
|
|
|
26
26
|
it("should reject non-numeric inputs", () => {
|
|
27
|
-
const schema = businessId()
|
|
27
|
+
const schema = businessId(true)
|
|
28
28
|
|
|
29
29
|
expect(() => schema.parse("1234567A")).toThrow("Invalid Taiwan Business ID")
|
|
30
30
|
expect(() => schema.parse("abcdefgh")).toThrow("Invalid Taiwan Business ID")
|
|
@@ -32,7 +32,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
it("should reject wrong length inputs", () => {
|
|
35
|
-
const schema = businessId()
|
|
35
|
+
const schema = businessId(true)
|
|
36
36
|
|
|
37
37
|
expect(() => schema.parse("1234567")).toThrow("Invalid Taiwan Business ID")
|
|
38
38
|
expect(() => schema.parse("123456789")).toThrow("Invalid Taiwan Business ID")
|
|
@@ -43,7 +43,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
43
43
|
|
|
44
44
|
describe("special case validation (7th digit = 7)", () => {
|
|
45
45
|
it("should handle special case where 7th digit is 7", () => {
|
|
46
|
-
const schema = businessId()
|
|
46
|
+
const schema = businessId(true)
|
|
47
47
|
|
|
48
48
|
// Valid number with 7th digit = 7 for testing special case
|
|
49
49
|
expect(schema.parse("12345670")).toBe("12345670") // Should be valid with special case
|
|
@@ -75,7 +75,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
75
75
|
|
|
76
76
|
describe("required/optional behavior", () => {
|
|
77
77
|
it("should handle required=true (default)", () => {
|
|
78
|
-
const schema = businessId()
|
|
78
|
+
const schema = businessId(true)
|
|
79
79
|
|
|
80
80
|
expect(() => schema.parse("")).toThrow("Required")
|
|
81
81
|
expect(() => schema.parse(null)).toThrow()
|
|
@@ -83,7 +83,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
83
83
|
})
|
|
84
84
|
|
|
85
85
|
it("should handle required=false", () => {
|
|
86
|
-
const schema = businessId(
|
|
86
|
+
const schema = businessId(false)
|
|
87
87
|
|
|
88
88
|
expect(schema.parse("")).toBe(null)
|
|
89
89
|
expect(schema.parse(null)).toBe(null)
|
|
@@ -92,8 +92,8 @@ describe("Taiwan businessId() validator", () => {
|
|
|
92
92
|
})
|
|
93
93
|
|
|
94
94
|
it("should use default values", () => {
|
|
95
|
-
const requiredSchema = businessId({ defaultValue: "12345675" })
|
|
96
|
-
const optionalSchema = businessId(
|
|
95
|
+
const requiredSchema = businessId(true, { defaultValue: "12345675" })
|
|
96
|
+
const optionalSchema = businessId(false, { defaultValue: "12345675" })
|
|
97
97
|
|
|
98
98
|
expect(requiredSchema.parse("")).toBe("12345675")
|
|
99
99
|
expect(optionalSchema.parse("")).toBe("12345675")
|
|
@@ -102,7 +102,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
102
102
|
|
|
103
103
|
describe("transform function", () => {
|
|
104
104
|
it("should apply custom transform", () => {
|
|
105
|
-
const schema = businessId({
|
|
105
|
+
const schema = businessId(true, {
|
|
106
106
|
transform: (val) => val.replace(/[-\s]/g, ""),
|
|
107
107
|
})
|
|
108
108
|
|
|
@@ -112,7 +112,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
112
112
|
})
|
|
113
113
|
|
|
114
114
|
it("should apply transform before validation", () => {
|
|
115
|
-
const schema = businessId({
|
|
115
|
+
const schema = businessId(true, {
|
|
116
116
|
transform: (val) => val.replace(/\s+/g, ""),
|
|
117
117
|
})
|
|
118
118
|
|
|
@@ -123,14 +123,14 @@ describe("Taiwan businessId() validator", () => {
|
|
|
123
123
|
|
|
124
124
|
describe("input preprocessing", () => {
|
|
125
125
|
it("should handle string conversion", () => {
|
|
126
|
-
const schema = businessId()
|
|
126
|
+
const schema = businessId(true)
|
|
127
127
|
|
|
128
128
|
expect(schema.parse(12345675)).toBe("12345675")
|
|
129
129
|
expect(() => schema.parse(12345672)).toThrow("Invalid Taiwan Business ID")
|
|
130
130
|
})
|
|
131
131
|
|
|
132
132
|
it("should trim whitespace", () => {
|
|
133
|
-
const schema = businessId()
|
|
133
|
+
const schema = businessId(true)
|
|
134
134
|
|
|
135
135
|
expect(schema.parse(" 12345675 ")).toBe("12345675")
|
|
136
136
|
expect(schema.parse("\t12345675\n")).toBe("12345675")
|
|
@@ -140,7 +140,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
140
140
|
describe("i18n support", () => {
|
|
141
141
|
it("should use English messages by default", () => {
|
|
142
142
|
setLocale("en")
|
|
143
|
-
const schema = businessId()
|
|
143
|
+
const schema = businessId(true)
|
|
144
144
|
|
|
145
145
|
expect(() => schema.parse("")).toThrow("Required")
|
|
146
146
|
expect(() => schema.parse("1234567")).toThrow("Invalid Taiwan Business ID")
|
|
@@ -149,7 +149,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
149
149
|
|
|
150
150
|
it("should use Chinese messages when locale is zh-TW", () => {
|
|
151
151
|
setLocale("zh-TW")
|
|
152
|
-
const schema = businessId()
|
|
152
|
+
const schema = businessId(true)
|
|
153
153
|
|
|
154
154
|
expect(() => schema.parse("")).toThrow("必填")
|
|
155
155
|
expect(() => schema.parse("1234567")).toThrow("無效的統一編號")
|
|
@@ -157,7 +157,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
157
157
|
})
|
|
158
158
|
|
|
159
159
|
it("should support custom i18n messages", () => {
|
|
160
|
-
const schema = businessId({
|
|
160
|
+
const schema = businessId(true, {
|
|
161
161
|
i18n: {
|
|
162
162
|
en: {
|
|
163
163
|
required: "Business ID is required",
|
|
@@ -182,7 +182,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
182
182
|
|
|
183
183
|
describe("real world Taiwan business IDs", () => {
|
|
184
184
|
it("should validate known real business IDs", () => {
|
|
185
|
-
const schema = businessId()
|
|
185
|
+
const schema = businessId(true)
|
|
186
186
|
|
|
187
187
|
// These are example valid Taiwan business IDs for testing
|
|
188
188
|
const validIds = [
|
|
@@ -198,7 +198,7 @@ describe("Taiwan businessId() validator", () => {
|
|
|
198
198
|
})
|
|
199
199
|
|
|
200
200
|
it("should reject common invalid patterns", () => {
|
|
201
|
-
const schema = businessId()
|
|
201
|
+
const schema = businessId(true)
|
|
202
202
|
|
|
203
203
|
const invalidIds = [
|
|
204
204
|
"00000001", // All zeros with bad checksum
|
|
@@ -218,15 +218,15 @@ describe("Taiwan businessId() validator", () => {
|
|
|
218
218
|
|
|
219
219
|
describe("edge cases", () => {
|
|
220
220
|
it("should handle leading zeros", () => {
|
|
221
|
-
const schema = businessId()
|
|
221
|
+
const schema = businessId(true)
|
|
222
222
|
|
|
223
223
|
expect(schema.parse("04595257")).toBe("04595257")
|
|
224
224
|
expect(() => schema.parse("00123456")).toThrow("Invalid Taiwan Business ID")
|
|
225
225
|
})
|
|
226
226
|
|
|
227
227
|
it("should handle empty and whitespace inputs", () => {
|
|
228
|
-
const schema = businessId()
|
|
229
|
-
const optionalSchema = businessId(
|
|
228
|
+
const schema = businessId(true)
|
|
229
|
+
const optionalSchema = businessId(false)
|
|
230
230
|
|
|
231
231
|
expect(() => schema.parse("")).toThrow("Required")
|
|
232
232
|
expect(() => schema.parse(" ")).toThrow("Required")
|