@hy_ong/zod-kit 0.0.4 → 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.
Files changed (51) hide show
  1. package/.claude/settings.local.json +23 -0
  2. package/LICENSE +21 -0
  3. package/README.md +5 -5
  4. package/debug.js +21 -0
  5. package/debug.ts +16 -0
  6. package/dist/index.cjs +1619 -145
  7. package/dist/index.d.cts +324 -25
  8. package/dist/index.d.ts +324 -25
  9. package/dist/index.js +1590 -143
  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 +99 -25
  14. package/src/i18n/locales/zh-TW.json +103 -26
  15. package/src/index.ts +13 -7
  16. package/src/validators/common/boolean.ts +97 -0
  17. package/src/validators/common/date.ts +171 -0
  18. package/src/validators/common/email.ts +200 -0
  19. package/src/validators/common/id.ts +259 -0
  20. package/src/validators/common/number.ts +194 -0
  21. package/src/validators/common/password.ts +214 -0
  22. package/src/validators/common/text.ts +151 -0
  23. package/src/validators/common/url.ts +207 -0
  24. package/src/validators/taiwan/business-id.ts +140 -0
  25. package/src/validators/taiwan/fax.ts +182 -0
  26. package/src/validators/taiwan/mobile.ts +110 -0
  27. package/src/validators/taiwan/national-id.ts +208 -0
  28. package/src/validators/taiwan/tel.ts +182 -0
  29. package/tests/common/boolean.test.ts +340 -92
  30. package/tests/common/date.test.ts +458 -0
  31. package/tests/common/email.test.ts +232 -60
  32. package/tests/common/id.test.ts +535 -0
  33. package/tests/common/number.test.ts +230 -60
  34. package/tests/common/password.test.ts +271 -44
  35. package/tests/common/text.test.ts +210 -13
  36. package/tests/common/url.test.ts +492 -67
  37. package/tests/taiwan/business-id.test.ts +240 -0
  38. package/tests/taiwan/fax.test.ts +463 -0
  39. package/tests/taiwan/mobile.test.ts +373 -0
  40. package/tests/taiwan/national-id.test.ts +435 -0
  41. package/tests/taiwan/tel.test.ts +467 -0
  42. package/eslint.config.mjs +0 -10
  43. package/src/common/boolean.ts +0 -36
  44. package/src/common/date.ts +0 -43
  45. package/src/common/email.ts +0 -44
  46. package/src/common/integer.ts +0 -46
  47. package/src/common/number.ts +0 -37
  48. package/src/common/password.ts +0 -33
  49. package/src/common/text.ts +0 -34
  50. package/src/common/url.ts +0 -37
  51. package/tests/common/integer.test.ts +0 -90
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // src/common/boolean.ts
1
+ // src/validators/common/boolean.ts
2
2
  import { z } from "zod";
3
3
 
4
4
  // src/i18n/locales/zh-TW.json
@@ -6,25 +6,40 @@ var zh_TW_default = {
6
6
  common: {
7
7
  boolean: {
8
8
  required: "\u5FC5\u586B",
9
- shouldBe: {
10
- true: "\u5FC5\u9808\u70BA\u662F",
11
- false: "\u5FC5\u9808\u70BA\u5426"
12
- }
9
+ shouldBeTrue: "\u5FC5\u9808\u70BA\u662F",
10
+ shouldBeFalse: "\u5FC5\u9808\u70BA\u5426",
11
+ invalid: "\u5FC5\u9808\u70BA\u5E03\u6797\u503C"
13
12
  },
14
13
  email: {
15
14
  required: "\u5FC5\u586B",
16
- min: "\u9577\u5EA6\u81F3\u5C11 ${min} \u5B57\u5143",
17
- max: "\u9577\u5EA6\u6700\u591A ${max} \u5B57\u5143",
15
+ invalid: "\u96FB\u5B50\u90F5\u4EF6\u683C\u5F0F\u932F\u8AA4",
16
+ minLength: "\u9577\u5EA6\u81F3\u5C11 ${minLength} \u5B57\u5143",
17
+ maxLength: "\u9577\u5EA6\u6700\u591A ${maxLength} \u5B57\u5143",
18
18
  includes: "\u5FC5\u9808\u5305\u542B\u300C${includes}\u300D",
19
- invalid: "\u683C\u5F0F\u932F\u8AA4",
20
- domain: "\u5FC5\u9808\u70BA @${domain} \u7DB2\u57DF"
19
+ domain: "\u5FC5\u9808\u70BA ${domain} \u7DB2\u57DF",
20
+ domainBlacklist: "\u4E0D\u5141\u8A31\u4F7F\u7528 ${domain} \u7DB2\u57DF",
21
+ businessOnly: "\u50C5\u5141\u8A31\u4F01\u696D\u90F5\u7BB1\u5730\u5740",
22
+ noDisposable: "\u4E0D\u5141\u8A31\u4F7F\u7528\u81E8\u6642\u90F5\u7BB1\u5730\u5740"
21
23
  },
22
24
  url: {
23
25
  required: "\u5FC5\u586B",
26
+ invalid: "\u7121\u6548\u7684 URL \u683C\u5F0F",
24
27
  min: "\u9577\u5EA6\u81F3\u5C11 ${min} \u5B57\u5143",
25
28
  max: "\u9577\u5EA6\u6700\u591A ${max} \u5B57\u5143",
26
29
  includes: "\u5FC5\u9808\u5305\u542B\u300C${includes}\u300D",
27
- invalid: "\u683C\u5F0F\u932F\u8AA4"
30
+ excludes: "\u4E0D\u5F97\u5305\u542B\u300C${excludes}\u300D",
31
+ protocol: "\u5354\u8B70\u5FC5\u9808\u70BA\uFF1A${protocols}",
32
+ domain: "\u7DB2\u57DF\u5FC5\u9808\u70BA\uFF1A${domains}",
33
+ domainBlacklist: "\u4E0D\u5141\u8A31\u4F7F\u7528\u7DB2\u57DF ${domain}",
34
+ port: "\u9023\u63A5\u57E0\u5FC5\u9808\u70BA\uFF1A${ports}",
35
+ pathStartsWith: "\u8DEF\u5F91\u5FC5\u9808\u4EE5\u300C${path}\u300D\u958B\u982D",
36
+ pathEndsWith: "\u8DEF\u5F91\u5FC5\u9808\u4EE5\u300C${path}\u300D\u7D50\u5C3E",
37
+ hasQuery: "URL \u5FC5\u9808\u5305\u542B\u67E5\u8A62\u53C3\u6578",
38
+ noQuery: "URL \u4E0D\u5F97\u5305\u542B\u67E5\u8A62\u53C3\u6578",
39
+ hasFragment: "URL \u5FC5\u9808\u5305\u542B\u9328\u9EDE",
40
+ noFragment: "URL \u4E0D\u5F97\u5305\u542B\u9328\u9EDE",
41
+ localhost: "\u4E0D\u5141\u8A31\u4F7F\u7528\u672C\u6A5F URL",
42
+ noLocalhost: "\u4E0D\u5141\u8A31\u4F7F\u7528\u672C\u6A5F URL"
28
43
  },
29
44
  password: {
30
45
  required: "\u5FC5\u586B",
@@ -33,37 +48,99 @@ var zh_TW_default = {
33
48
  uppercase: "\u5FC5\u9808\u5305\u542B\u81F3\u5C11\u4E00\u500B\u5927\u5BEB\u5B57\u6BCD",
34
49
  lowercase: "\u5FC5\u9808\u5305\u542B\u81F3\u5C11\u4E00\u500B\u5C0F\u5BEB\u5B57\u6BCD",
35
50
  digits: "\u5FC5\u9808\u5305\u542B\u81F3\u5C11\u4E00\u500B\u6578\u5B57",
36
- special: "\u5FC5\u9808\u5305\u542B\u81F3\u5C11\u4E00\u500B\u7279\u6B8A\u7B26\u865F"
51
+ special: "\u5FC5\u9808\u5305\u542B\u81F3\u5C11\u4E00\u500B\u7279\u6B8A\u7B26\u865F",
52
+ noRepeating: "\u4E0D\u53EF\u5305\u542B\u91CD\u8907\u5B57\u5143",
53
+ noSequential: "\u4E0D\u53EF\u5305\u542B\u9023\u7E8C\u5B57\u5143",
54
+ noCommonWords: "\u4E0D\u53EF\u5305\u542B\u5E38\u898B\u5BC6\u78BC\u6216\u6A21\u5F0F",
55
+ minStrength: "\u5BC6\u78BC\u5F37\u5EA6\u5FC5\u9808\u81F3\u5C11\u70BA ${minStrength}",
56
+ excludes: "\u4E0D\u5F97\u5305\u542B\u300C${excludes}\u300D",
57
+ includes: "\u5FC5\u9808\u5305\u542B\u300C${includes}\u300D",
58
+ invalid: "\u5BC6\u78BC\u683C\u5F0F\u932F\u8AA4"
37
59
  },
38
60
  number: {
39
61
  required: "\u5FC5\u586B",
40
- min: "\u6700\u5C0F\u503C ${min}",
41
- max: "\u6700\u5927\u503C ${max}"
42
- },
43
- id: {
44
- required: "\u8ACB\u9078\u64C7",
45
- invalid: "\u7121\u6548"
46
- },
47
- integer: {
48
- required: "\u5FC5\u586B",
62
+ invalid: "\u5FC5\u9808\u70BA\u6709\u6548\u6578\u5B57",
63
+ integer: "\u5FC5\u9808\u70BA\u6574\u6578",
64
+ float: "\u5FC5\u9808\u70BA\u5C0F\u6578",
49
65
  min: "\u6700\u5C0F\u503C ${min}",
50
66
  max: "\u6700\u5927\u503C ${max}",
51
- integer: "\u5FC5\u9808\u70BA\u6574\u6578"
67
+ positive: "\u5FC5\u9808\u70BA\u6B63\u6578",
68
+ negative: "\u5FC5\u9808\u70BA\u8CA0\u6578",
69
+ nonNegative: "\u4E0D\u53EF\u70BA\u8CA0\u6578",
70
+ nonPositive: "\u4E0D\u53EF\u70BA\u6B63\u6578",
71
+ multipleOf: "\u5FC5\u9808\u70BA ${multipleOf} \u7684\u500D\u6578",
72
+ finite: "\u5FC5\u9808\u70BA\u6709\u9650\u6578\u5B57",
73
+ precision: "\u5C0F\u6578\u4F4D\u6578\u4E0D\u53EF\u8D85\u904E ${precision} \u4F4D"
52
74
  },
53
- float: {
75
+ id: {
54
76
  required: "\u5FC5\u586B",
55
- float: "\u5FC5\u9808\u70BA\u6D6E\u9EDE\u6578",
56
- min: "\u6700\u5C0F\u503C ${min}",
57
- max: "\u6700\u5927\u503C ${max}"
77
+ invalid: "\u7121\u6548\u7684 ID \u683C\u5F0F",
78
+ minLength: "\u9577\u5EA6\u81F3\u5C11 ${minLength} \u5B57\u5143",
79
+ maxLength: "\u9577\u5EA6\u6700\u591A ${maxLength} \u5B57\u5143",
80
+ numeric: "\u5FC5\u9808\u70BA\u6578\u5B57 ID",
81
+ uuid: "\u5FC5\u9808\u70BA\u6709\u6548\u7684 UUID",
82
+ objectId: "\u5FC5\u9808\u70BA\u6709\u6548\u7684 MongoDB ObjectId",
83
+ nanoid: "\u5FC5\u9808\u70BA\u6709\u6548\u7684 Nano ID",
84
+ snowflake: "\u5FC5\u9808\u70BA\u6709\u6548\u7684 Snowflake ID",
85
+ cuid: "\u5FC5\u9808\u70BA\u6709\u6548\u7684 CUID",
86
+ ulid: "\u5FC5\u9808\u70BA\u6709\u6548\u7684 ULID",
87
+ shortid: "\u5FC5\u9808\u70BA\u6709\u6548\u7684 Short ID",
88
+ customFormat: "\u7121\u6548\u7684 ID \u683C\u5F0F",
89
+ includes: "\u5FC5\u9808\u5305\u542B\u300C${includes}\u300D",
90
+ excludes: "\u4E0D\u5F97\u5305\u542B\u300C${excludes}\u300D",
91
+ startsWith: "\u5FC5\u9808\u4EE5\u300C${startsWith}\u300D\u958B\u982D",
92
+ endsWith: "\u5FC5\u9808\u4EE5\u300C${endsWith}\u300D\u7D50\u5C3E"
58
93
  },
59
94
  text: {
60
95
  required: "\u5FC5\u586B",
61
- min: "\u9577\u5EA6\u81F3\u5C11 ${min} \u5B57\u5143",
62
- max: "\u9577\u5EA6\u6700\u591A ${max} \u5B57\u5143",
96
+ notEmpty: "\u4E0D\u53EF\u70BA\u7A7A\u767D\u6216\u50C5\u542B\u7A7A\u683C",
97
+ minLength: "\u9577\u5EA6\u81F3\u5C11 ${minLength} \u5B57\u5143",
98
+ maxLength: "\u9577\u5EA6\u6700\u591A ${maxLength} \u5B57\u5143",
63
99
  startsWith: "\u5FC5\u9808\u4EE5\u300C${startsWith}\u300D\u958B\u982D",
64
100
  endsWith: "\u5FC5\u9808\u4EE5\u300C${endsWith}\u300D\u7D50\u5C3E",
65
101
  includes: "\u5FC5\u9808\u5305\u542B\u300C${includes}\u300D",
102
+ excludes: "\u4E0D\u5F97\u5305\u542B\u300C${excludes}\u300D",
66
103
  invalid: "\u683C\u5F0F\u932F\u8AA4"
104
+ },
105
+ date: {
106
+ required: "\u5FC5\u586B",
107
+ invalid: "\u7121\u6548\u65E5\u671F",
108
+ format: "\u5FC5\u9808\u70BA ${format} \u683C\u5F0F",
109
+ min: "\u65E5\u671F\u4E0D\u5F97\u65E9\u65BC ${min}",
110
+ max: "\u65E5\u671F\u4E0D\u5F97\u665A\u65BC ${max}",
111
+ includes: "\u5FC5\u9808\u5305\u542B\u300C${includes}\u300D",
112
+ excludes: "\u4E0D\u5F97\u5305\u542B\u300C${excludes}\u300D",
113
+ past: "\u5FC5\u9808\u70BA\u904E\u53BB\u7684\u65E5\u671F",
114
+ future: "\u5FC5\u9808\u70BA\u672A\u4F86\u7684\u65E5\u671F",
115
+ today: "\u5FC5\u9808\u70BA\u4ECA\u5929",
116
+ notToday: "\u4E0D\u5F97\u70BA\u4ECA\u5929",
117
+ weekday: "\u5FC5\u9808\u70BA\u5DE5\u4F5C\u65E5\uFF08\u9031\u4E00\u81F3\u9031\u4E94\uFF09",
118
+ weekend: "\u5FC5\u9808\u70BA\u9031\u672B\uFF08\u9031\u516D\u81F3\u9031\u65E5\uFF09"
119
+ }
120
+ },
121
+ taiwan: {
122
+ businessId: {
123
+ required: "\u5FC5\u586B",
124
+ invalid: "\u7121\u6548\u7684\u7D71\u4E00\u7DE8\u865F"
125
+ },
126
+ nationalId: {
127
+ required: "\u5FC5\u586B",
128
+ invalid: "\u7121\u6548\u7684\u8EAB\u5206\u8B49\u5B57\u865F"
129
+ },
130
+ mobile: {
131
+ required: "\u5FC5\u586B",
132
+ invalid: "\u7121\u6548\u7684\u624B\u6A5F\u865F\u78BC\u683C\u5F0F",
133
+ notInWhitelist: "\u4E0D\u5728\u5141\u8A31\u7684\u624B\u6A5F\u865F\u78BC\u6E05\u55AE\u4E2D"
134
+ },
135
+ tel: {
136
+ required: "\u5FC5\u586B",
137
+ invalid: "\u7121\u6548\u7684\u5E02\u8A71\u865F\u78BC\u683C\u5F0F",
138
+ notInWhitelist: "\u4E0D\u5728\u5141\u8A31\u7684\u5E02\u8A71\u865F\u78BC\u6E05\u55AE\u4E2D"
139
+ },
140
+ fax: {
141
+ required: "\u5FC5\u586B",
142
+ invalid: "\u7121\u6548\u7684\u50B3\u771F\u865F\u78BC\u683C\u5F0F",
143
+ notInWhitelist: "\u4E0D\u5728\u5141\u8A31\u7684\u50B3\u771F\u865F\u78BC\u6E05\u55AE\u4E2D"
67
144
  }
68
145
  }
69
146
  };
@@ -73,25 +150,40 @@ var en_default = {
73
150
  common: {
74
151
  boolean: {
75
152
  required: "Required",
76
- shouldBe: {
77
- true: "Must be True",
78
- false: "Must be False"
79
- }
153
+ shouldBeTrue: "Must be True",
154
+ shouldBeFalse: "Must be False",
155
+ invalid: "Must be a boolean value"
80
156
  },
81
157
  email: {
82
158
  required: "Required",
83
- min: "Must be at least ${min} characters",
84
- max: "Must be at most ${max} characters",
159
+ invalid: "Invalid email format",
160
+ minLength: "Must be at least ${minLength} characters",
161
+ maxLength: "Must be at most ${maxLength} characters",
85
162
  includes: "Must include ${includes}",
86
- invalid: "Invalid format",
87
- domain: "Must be under the domain @${domain}"
163
+ domain: "Must be from domain: ${domain}",
164
+ domainBlacklist: "Domain ${domain} is not allowed",
165
+ businessOnly: "Only business email addresses are allowed",
166
+ noDisposable: "Disposable email addresses are not allowed"
88
167
  },
89
168
  url: {
90
169
  required: "Required",
170
+ invalid: "Invalid URL format",
91
171
  min: "Must be at least ${min} characters",
92
172
  max: "Must be at most ${max} characters",
93
173
  includes: "Must include ${includes}",
94
- invalid: "Invalid format"
174
+ excludes: "Must not contain ${excludes}",
175
+ protocol: "Protocol must be one of: ${protocols}",
176
+ domain: "Domain must be one of: ${domains}",
177
+ domainBlacklist: "Domain ${domain} is not allowed",
178
+ port: "Port must be one of: ${ports}",
179
+ pathStartsWith: "Path must start with ${path}",
180
+ pathEndsWith: "Path must end with ${path}",
181
+ hasQuery: "URL must have query parameters",
182
+ noQuery: "URL must not have query parameters",
183
+ hasFragment: "URL must have a fragment",
184
+ noFragment: "URL must not have a fragment",
185
+ localhost: "Localhost URLs are not allowed",
186
+ noLocalhost: "Localhost URLs are not allowed"
95
187
  },
96
188
  password: {
97
189
  required: "Required",
@@ -100,46 +192,105 @@ var en_default = {
100
192
  uppercase: "Must include at least one uppercase letter",
101
193
  lowercase: "Must include at least one lowercase letter",
102
194
  digits: "Must include at least one digit",
103
- special: "Must include at least one special character"
195
+ special: "Must include at least one special character",
196
+ noRepeating: "Must not contain repeating characters",
197
+ noSequential: "Must not contain sequential characters",
198
+ noCommonWords: "Must not contain common words or patterns",
199
+ minStrength: "Password strength must be at least ${minStrength}",
200
+ excludes: "Must not contain ${excludes}",
201
+ includes: "Must include ${includes}",
202
+ invalid: "Invalid password format"
104
203
  },
105
204
  number: {
106
205
  required: "Required",
107
- min: "Must be at least ${min}",
108
- max: "Must be at most ${max}"
109
- },
110
- integer: {
111
- required: "Required",
206
+ invalid: "Must be a valid number",
207
+ integer: "Must be an integer",
208
+ float: "Must be a decimal number",
112
209
  min: "Must be at least ${min}",
113
210
  max: "Must be at most ${max}",
114
- integer: "Must be an integer"
211
+ positive: "Must be positive",
212
+ negative: "Must be negative",
213
+ nonNegative: "Must be non-negative",
214
+ nonPositive: "Must be non-positive",
215
+ multipleOf: "Must be a multiple of ${multipleOf}",
216
+ finite: "Must be a finite number",
217
+ precision: "Must have at most ${precision} decimal places"
115
218
  },
116
- float: {
219
+ id: {
117
220
  required: "Required",
118
- min: "Must be at least ${min}",
119
- max: "Must be at most ${max}",
120
- float: "Must be an float"
221
+ invalid: "Invalid ID format",
222
+ minLength: "Must be at least ${minLength} characters",
223
+ maxLength: "Must be at most ${maxLength} characters",
224
+ numeric: "Must be a numeric ID",
225
+ uuid: "Must be a valid UUID",
226
+ objectId: "Must be a valid MongoDB ObjectId",
227
+ nanoid: "Must be a valid Nano ID",
228
+ snowflake: "Must be a valid Snowflake ID",
229
+ cuid: "Must be a valid CUID",
230
+ ulid: "Must be a valid ULID",
231
+ shortid: "Must be a valid Short ID",
232
+ customFormat: "Invalid ID format",
233
+ includes: "Must include ${includes}",
234
+ excludes: "Must not contain ${excludes}",
235
+ startsWith: "Must start with ${startsWith}",
236
+ endsWith: "Must end with ${endsWith}"
121
237
  },
122
238
  text: {
123
239
  required: "Required",
124
- min: "Must be at least ${min} characters",
125
- max: "Must be at most ${max} characters",
240
+ notEmpty: "Cannot be empty or whitespace only",
241
+ minLength: "Must be at least ${minLength} characters",
242
+ maxLength: "Must be at most ${maxLength} characters",
126
243
  startsWith: "Must start with ${startsWith}",
127
244
  endsWith: "Must end with ${endsWith}",
128
245
  includes: "Must include ${includes}",
246
+ excludes: "Must not contain ${excludes}",
129
247
  invalid: "Invalid format"
130
248
  },
131
249
  date: {
132
250
  required: "Required",
133
- min: "Must be at least ${min} characters",
134
- max: "Must be at most ${max} characters",
251
+ invalid: "Invalid date",
252
+ format: "Must be in ${format} format",
253
+ min: "Date must be on or after ${min}",
254
+ max: "Date must be on or before ${max}",
135
255
  includes: "Must include ${includes}",
136
- format: "Must be in ${format} format"
256
+ excludes: "Must not contain ${excludes}",
257
+ past: "Date must be in the past",
258
+ future: "Date must be in the future",
259
+ today: "Date must be today",
260
+ notToday: "Date must not be today",
261
+ weekday: "Date must be a weekday (Monday-Friday)",
262
+ weekend: "Date must be a weekend (Saturday-Sunday)"
263
+ }
264
+ },
265
+ taiwan: {
266
+ businessId: {
267
+ required: "Required",
268
+ invalid: "Invalid Taiwan Business ID"
269
+ },
270
+ nationalId: {
271
+ required: "Required",
272
+ invalid: "Invalid Taiwan National ID"
273
+ },
274
+ mobile: {
275
+ required: "Required",
276
+ invalid: "Invalid Taiwan mobile phone format",
277
+ notInWhitelist: "Not in allowed mobile phone list"
278
+ },
279
+ tel: {
280
+ required: "Required",
281
+ invalid: "Invalid Taiwan telephone format",
282
+ notInWhitelist: "Not in allowed telephone list"
283
+ },
284
+ fax: {
285
+ required: "Required",
286
+ invalid: "Invalid Taiwan fax format",
287
+ notInWhitelist: "Not in allowed fax list"
137
288
  }
138
289
  }
139
290
  };
140
291
 
141
292
  // src/config.ts
142
- var currentLocale = "zh-TW";
293
+ var currentLocale = "en";
143
294
  var setLocale = (locale) => {
144
295
  currentLocale = locale;
145
296
  };
@@ -161,148 +312,1444 @@ function getNestedValue(obj, path) {
161
312
  return typeof result === "string" ? result : void 0;
162
313
  }
163
314
 
164
- // src/common/boolean.ts
315
+ // src/validators/common/boolean.ts
165
316
  function boolean(options) {
166
- const { required = true, defaultValue = null, shouldBe } = options ?? {};
317
+ const {
318
+ required = true,
319
+ defaultValue = null,
320
+ shouldBe,
321
+ truthyValues = [true, "true", 1, "1", "yes", "on"],
322
+ falsyValues = [false, "false", 0, "0", "no", "off"],
323
+ strict = false,
324
+ transform,
325
+ i18n
326
+ } = options ?? {};
327
+ const getMessage = (key, params) => {
328
+ if (i18n) {
329
+ const currentLocale2 = getLocale();
330
+ const customMessages = i18n[currentLocale2];
331
+ if (customMessages && customMessages[key]) {
332
+ const template = customMessages[key];
333
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
334
+ }
335
+ }
336
+ return t(`common.boolean.${key}`, params);
337
+ };
167
338
  let result = z.preprocess(
168
339
  (val) => {
169
340
  if (val === "" || val === void 0 || val === null) return defaultValue;
170
- if (val === "true" || val === 1 || val === "1") return true;
171
- if (val === "false" || val === 0 || val === "0") return false;
341
+ if (strict && typeof val !== "boolean" && val !== null) {
342
+ return val;
343
+ }
344
+ if (truthyValues.includes(val)) {
345
+ let processed = true;
346
+ if (transform) processed = transform(processed);
347
+ return processed;
348
+ }
349
+ if (falsyValues.includes(val)) {
350
+ let processed = false;
351
+ if (transform) processed = transform(processed);
352
+ return processed;
353
+ }
172
354
  return val;
173
355
  },
174
356
  z.union([z.literal(true), z.literal(false), z.literal(null)])
175
357
  );
176
358
  if (required && defaultValue === null) {
177
- result = result.refine((val) => val !== null, { message: t("common.boolean.required") });
359
+ result = result.refine((val) => val !== null, { message: getMessage("required") });
178
360
  }
179
361
  if (shouldBe === true) {
180
- result = result.refine((val) => val === true, { message: t("common.boolean.shouldBe.true") });
362
+ result = result.refine((val) => val === true, { message: getMessage("shouldBeTrue") });
181
363
  } else if (shouldBe === false) {
182
- result = result.refine((val) => val === false, { message: t("common.boolean.shouldBe.false") });
364
+ result = result.refine((val) => val === false, { message: getMessage("shouldBeFalse") });
365
+ }
366
+ if (strict) {
367
+ result = result.refine(
368
+ (val) => {
369
+ return val === null || typeof val === "boolean";
370
+ },
371
+ { message: getMessage("invalid") }
372
+ );
183
373
  }
184
374
  return result;
185
375
  }
186
376
 
187
- // src/common/email.ts
377
+ // src/validators/common/date.ts
188
378
  import { z as z2 } from "zod";
189
- function email(options) {
190
- const { required = true, domain, min, max, includes } = options ?? {};
191
- const baseSchema = required ? z2.preprocess(
192
- (val) => val === "" || val === null || val === void 0 ? null : val,
193
- z2.email({
194
- error: (issue) => {
195
- if (issue.code === "invalid_type") return t("common.email.required");
196
- else if (issue.code === "invalid_format") return t("common.email.invalid");
197
- return t("common.email.invalid");
198
- }
199
- })
200
- ) : z2.preprocess((val) => val === "" || val === null || val === void 0 ? null : val, z2.email({ message: t("common.email.invalid") }).nullable());
201
- const schema = baseSchema.refine((val) => required ? val !== "" && val !== "null" && val !== "undefined" : true, { message: t("common.text.required") }).refine((val) => val === null || min === void 0 || val.length >= min, { message: t("common.text.min", { min }) }).refine((val) => val === null || max === void 0 || val.length <= max, { message: t("common.text.max", { max }) }).refine((val) => val === null || includes === void 0 || val.includes(includes), { message: t("common.text.includes", { includes }) }).refine(
202
- (val) => {
203
- if (val === null || domain === void 0) return true;
204
- return val.split("@")[1]?.toLowerCase() === domain.toLowerCase();
205
- },
206
- { message: t("common.email.domain", { domain }) }
207
- );
379
+ import dayjs from "dayjs";
380
+ import customParseFormat from "dayjs/plugin/customParseFormat";
381
+ import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
382
+ import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
383
+ import isToday from "dayjs/plugin/isToday";
384
+ import weekday from "dayjs/plugin/weekday";
385
+ dayjs.extend(isSameOrAfter);
386
+ dayjs.extend(isSameOrBefore);
387
+ dayjs.extend(customParseFormat);
388
+ dayjs.extend(isToday);
389
+ dayjs.extend(weekday);
390
+ function date(options) {
391
+ const {
392
+ required = true,
393
+ min,
394
+ max,
395
+ format = "YYYY-MM-DD",
396
+ includes,
397
+ excludes,
398
+ mustBePast,
399
+ mustBeFuture,
400
+ mustBeToday,
401
+ mustNotBeToday,
402
+ weekdaysOnly,
403
+ weekendsOnly,
404
+ transform,
405
+ defaultValue = null,
406
+ i18n
407
+ } = options ?? {};
408
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
409
+ const getMessage = (key, params) => {
410
+ if (i18n) {
411
+ const currentLocale2 = getLocale();
412
+ const customMessages = i18n[currentLocale2];
413
+ if (customMessages && customMessages[key]) {
414
+ const template = customMessages[key];
415
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
416
+ }
417
+ }
418
+ return t(`common.date.${key}`, params);
419
+ };
420
+ const preprocessFn = (val) => {
421
+ if (val === "" || val === null || val === void 0) {
422
+ return actualDefaultValue;
423
+ }
424
+ let processed = String(val).trim();
425
+ if (transform) {
426
+ processed = transform(processed);
427
+ }
428
+ return processed;
429
+ };
430
+ const baseSchema = required ? z2.preprocess(preprocessFn, z2.string()) : z2.preprocess(preprocessFn, z2.string().nullable());
431
+ const schema = baseSchema.refine((val) => {
432
+ if (val === null) return true;
433
+ if (required && (val === "" || val === "null" || val === "undefined")) {
434
+ throw new z2.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
435
+ }
436
+ if (val !== null && !dayjs(val, format, true).isValid()) {
437
+ throw new z2.ZodError([{ code: "custom", message: getMessage("format", { format }), path: [] }]);
438
+ }
439
+ const dateObj = dayjs(val, format);
440
+ if (val !== null && min !== void 0 && !dateObj.isSameOrAfter(dayjs(min, format))) {
441
+ throw new z2.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
442
+ }
443
+ if (val !== null && max !== void 0 && !dateObj.isSameOrBefore(dayjs(max, format))) {
444
+ throw new z2.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
445
+ }
446
+ if (val !== null && includes !== void 0 && !val.includes(includes)) {
447
+ throw new z2.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
448
+ }
449
+ if (val !== null && excludes !== void 0) {
450
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
451
+ for (const exclude of excludeList) {
452
+ if (val.includes(exclude)) {
453
+ throw new z2.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
454
+ }
455
+ }
456
+ }
457
+ const today = dayjs().startOf("day");
458
+ const targetDate = dateObj.startOf("day");
459
+ if (val !== null && mustBePast && !targetDate.isBefore(today)) {
460
+ throw new z2.ZodError([{ code: "custom", message: getMessage("past"), path: [] }]);
461
+ }
462
+ if (val !== null && mustBeFuture && !targetDate.isAfter(today)) {
463
+ throw new z2.ZodError([{ code: "custom", message: getMessage("future"), path: [] }]);
464
+ }
465
+ if (val !== null && mustBeToday && !targetDate.isSame(today)) {
466
+ throw new z2.ZodError([{ code: "custom", message: getMessage("today"), path: [] }]);
467
+ }
468
+ if (val !== null && mustNotBeToday && targetDate.isSame(today)) {
469
+ throw new z2.ZodError([{ code: "custom", message: getMessage("notToday"), path: [] }]);
470
+ }
471
+ if (val !== null && weekdaysOnly && (dateObj.day() === 0 || dateObj.day() === 6)) {
472
+ throw new z2.ZodError([{ code: "custom", message: getMessage("weekday"), path: [] }]);
473
+ }
474
+ if (val !== null && weekendsOnly && dateObj.day() !== 0 && dateObj.day() !== 6) {
475
+ throw new z2.ZodError([{ code: "custom", message: getMessage("weekend"), path: [] }]);
476
+ }
477
+ return true;
478
+ });
208
479
  return schema;
209
480
  }
210
481
 
211
- // src/common/url.ts
482
+ // src/validators/common/email.ts
212
483
  import { z as z3 } from "zod";
213
- function url(options) {
214
- const { required = true, min, max, includes } = options ?? {};
215
- const baseSchema = required ? z3.preprocess(
216
- (val) => val === "" || val === null || val === void 0 ? null : val,
217
- z3.url({
218
- error: (issue) => {
219
- if (issue.code === "invalid_type") return t("common.url.required");
220
- else if (issue.code === "invalid_format") return t("common.url.invalid");
221
- return t("common.url.invalid");
222
- }
223
- })
224
- ) : z3.preprocess((val) => val === "" || val === null || val === void 0 ? null : val, z3.url({ message: t("common.url.invalid") }).nullable());
225
- const schema = baseSchema.refine((val) => required ? val !== "" && val !== "null" && val !== "undefined" : true, { message: t("common.text.required") }).refine((val) => val === null || min === void 0 || val.length >= min, { message: t("common.text.min", { min }) }).refine((val) => val === null || max === void 0 || val.length <= max, { message: t("common.text.max", { max }) }).refine((val) => val === null || includes === void 0 || val.includes(includes), { message: t("common.text.includes", { includes }) });
484
+ function email(options) {
485
+ const {
486
+ required = true,
487
+ domain,
488
+ domainBlacklist,
489
+ minLength,
490
+ maxLength,
491
+ includes,
492
+ excludes,
493
+ allowSubdomains = true,
494
+ businessOnly = false,
495
+ noDisposable = false,
496
+ lowercase = true,
497
+ transform,
498
+ defaultValue,
499
+ i18n
500
+ } = options ?? {};
501
+ const getMessage = (key, params) => {
502
+ if (i18n) {
503
+ const currentLocale2 = getLocale();
504
+ const customMessages = i18n[currentLocale2];
505
+ if (customMessages && customMessages[key]) {
506
+ const template = customMessages[key];
507
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
508
+ }
509
+ }
510
+ return t(`common.email.${key}`, params);
511
+ };
512
+ const disposableDomains = ["10minutemail.com", "tempmail.org", "guerrillamail.com", "mailinator.com", "yopmail.com", "temp-mail.org", "throwaway.email", "getnada.com", "maildrop.cc"];
513
+ const freeEmailDomains = ["gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "icloud.com", "aol.com", "protonmail.com", "zoho.com"];
514
+ const actualDefaultValue = defaultValue ?? null;
515
+ const baseSchema = z3.preprocess(
516
+ (val) => {
517
+ if (val === "" || val === null || val === void 0) {
518
+ return actualDefaultValue;
519
+ }
520
+ let processed = String(val).trim();
521
+ if (lowercase) {
522
+ processed = processed.toLowerCase();
523
+ }
524
+ if (transform) {
525
+ processed = transform(processed);
526
+ }
527
+ return processed;
528
+ },
529
+ z3.union([z3.string().email(), z3.null()])
530
+ );
531
+ const schema = baseSchema.refine((val) => {
532
+ if (required && val === null) {
533
+ throw new z3.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
534
+ }
535
+ if (val === null) return true;
536
+ if (typeof val !== "string") {
537
+ throw new z3.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
538
+ }
539
+ if (minLength !== void 0 && val.length < minLength) {
540
+ throw new z3.ZodError([{ code: "custom", message: getMessage("minLength", { minLength }), path: [] }]);
541
+ }
542
+ if (maxLength !== void 0 && val.length > maxLength) {
543
+ throw new z3.ZodError([{ code: "custom", message: getMessage("maxLength", { maxLength }), path: [] }]);
544
+ }
545
+ if (includes !== void 0 && !val.includes(includes)) {
546
+ throw new z3.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
547
+ }
548
+ if (excludes !== void 0) {
549
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
550
+ for (const exclude of excludeList) {
551
+ if (val.includes(exclude)) {
552
+ throw new z3.ZodError([{ code: "custom", message: getMessage("includes", { includes: exclude }), path: [] }]);
553
+ }
554
+ }
555
+ }
556
+ const emailDomain = val.split("@")[1]?.toLowerCase();
557
+ if (!emailDomain) {
558
+ throw new z3.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
559
+ }
560
+ if (businessOnly) {
561
+ const isFreeProvider = freeEmailDomains.some((freeDomain) => {
562
+ if (allowSubdomains) {
563
+ return emailDomain === freeDomain || emailDomain.endsWith("." + freeDomain);
564
+ }
565
+ return emailDomain === freeDomain;
566
+ });
567
+ if (isFreeProvider) {
568
+ throw new z3.ZodError([{ code: "custom", message: getMessage("businessOnly"), path: [] }]);
569
+ }
570
+ }
571
+ if (domainBlacklist && domainBlacklist.length > 0) {
572
+ const isBlacklisted = domainBlacklist.some((blacklistedDomain) => {
573
+ const lowerDomain = blacklistedDomain.toLowerCase();
574
+ if (allowSubdomains) {
575
+ return emailDomain === lowerDomain || emailDomain.endsWith("." + lowerDomain);
576
+ }
577
+ return emailDomain === lowerDomain;
578
+ });
579
+ if (isBlacklisted) {
580
+ throw new z3.ZodError([{ code: "custom", message: getMessage("domainBlacklist", { domain: emailDomain }), path: [] }]);
581
+ }
582
+ }
583
+ if (domain !== void 0) {
584
+ const allowedDomains = Array.isArray(domain) ? domain : [domain];
585
+ const isAllowed = allowedDomains.some((allowedDomain) => {
586
+ const lowerDomain = allowedDomain.toLowerCase();
587
+ if (allowSubdomains) {
588
+ return emailDomain === lowerDomain || emailDomain.endsWith("." + lowerDomain);
589
+ }
590
+ return emailDomain === lowerDomain;
591
+ });
592
+ if (!isAllowed) {
593
+ throw new z3.ZodError([{ code: "custom", message: getMessage("domain", { domain: Array.isArray(domain) ? domain.join(", ") : domain }), path: [] }]);
594
+ }
595
+ }
596
+ if (noDisposable) {
597
+ const isDisposable = disposableDomains.some((disposableDomain) => {
598
+ if (allowSubdomains) {
599
+ return emailDomain === disposableDomain || emailDomain.endsWith("." + disposableDomain);
600
+ }
601
+ return emailDomain === disposableDomain;
602
+ });
603
+ if (isDisposable) {
604
+ throw new z3.ZodError([{ code: "custom", message: getMessage("noDisposable"), path: [] }]);
605
+ }
606
+ }
607
+ return true;
608
+ });
226
609
  return schema;
227
610
  }
228
611
 
229
- // src/common/text.ts
612
+ // src/validators/common/id.ts
230
613
  import { z as z4 } from "zod";
231
- function text(options) {
232
- const { required = true, min, max, startsWith, endsWith, includes, regex, defaultValue = null } = options ?? {};
233
- const baseSchema = required ? z4.preprocess((val) => val === "" || val === null || val === void 0 ? defaultValue : val, z4.coerce.string().trim()) : z4.preprocess((val) => val === "" || val === null || val === void 0 ? defaultValue : val, z4.coerce.string().trim().nullable());
234
- const schema = baseSchema.refine((val) => required ? val !== "" && val !== "null" && val !== "undefined" : true, { message: t("common.text.required") }).refine((val) => val === null || min === void 0 || val.length >= min, { message: t("common.text.min", { min }) }).refine((val) => val === null || max === void 0 || val.length <= max, { message: t("common.text.max", { max }) }).refine((val) => val === null || startsWith === void 0 || val.startsWith(startsWith), { message: t("common.text.startsWith", { startsWith }) }).refine((val) => val === null || endsWith === void 0 || val.endsWith(endsWith), { message: t("common.text.endsWith", { endsWith }) }).refine((val) => val === null || includes === void 0 || val.includes(includes), { message: t("common.text.includes", { includes }) }).refine((val) => val === null || regex === void 0 || regex.test(val), { message: t("common.text.invalid", { regex }) });
614
+ var ID_PATTERNS = {
615
+ numeric: /^\d+$/,
616
+ uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
617
+ objectId: /^[0-9a-f]{24}$/i,
618
+ nanoid: /^[A-Za-z0-9_-]{21}$/,
619
+ snowflake: /^\d{19}$/,
620
+ cuid: /^c[a-z0-9]{24}$/,
621
+ ulid: /^[0-9A-HJKMNP-TV-Z]{26}$/,
622
+ shortid: /^[A-Za-z0-9_-]{7,14}$/
623
+ };
624
+ var detectIdType = (value) => {
625
+ const orderedTypes = [
626
+ ["uuid", ID_PATTERNS.uuid],
627
+ ["objectId", ID_PATTERNS.objectId],
628
+ ["snowflake", ID_PATTERNS.snowflake],
629
+ ["cuid", ID_PATTERNS.cuid],
630
+ ["ulid", ID_PATTERNS.ulid],
631
+ ["nanoid", ID_PATTERNS.nanoid],
632
+ ["numeric", ID_PATTERNS.numeric],
633
+ ["shortid", ID_PATTERNS.shortid]
634
+ // 放最後,因為最通用
635
+ ];
636
+ for (const [type, pattern] of orderedTypes) {
637
+ if (pattern.test(value)) {
638
+ return type;
639
+ }
640
+ }
641
+ return null;
642
+ };
643
+ var validateIdType = (value, type) => {
644
+ if (type === "auto") {
645
+ return detectIdType(value) !== null;
646
+ }
647
+ const pattern = ID_PATTERNS[type];
648
+ return pattern ? pattern.test(value) : false;
649
+ };
650
+ function id(options) {
651
+ const {
652
+ required = true,
653
+ type = "auto",
654
+ minLength,
655
+ maxLength,
656
+ allowedTypes,
657
+ customRegex,
658
+ includes,
659
+ excludes,
660
+ startsWith,
661
+ endsWith,
662
+ caseSensitive = true,
663
+ transform,
664
+ defaultValue,
665
+ i18n
666
+ } = options ?? {};
667
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
668
+ const getMessage = (key, params) => {
669
+ if (i18n) {
670
+ const currentLocale2 = getLocale();
671
+ const customMessages = i18n[currentLocale2];
672
+ if (customMessages && customMessages[key]) {
673
+ const template = customMessages[key];
674
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
675
+ }
676
+ }
677
+ return t(`common.id.${key}`, params);
678
+ };
679
+ const preprocessFn = (val) => {
680
+ if (val === "" || val === null || val === void 0) {
681
+ return actualDefaultValue;
682
+ }
683
+ let processed = String(val);
684
+ if (transform) {
685
+ processed = transform(processed);
686
+ }
687
+ return processed;
688
+ };
689
+ const baseSchema = required ? z4.preprocess(preprocessFn, z4.string()) : z4.preprocess(preprocessFn, z4.string().nullable());
690
+ const schema = baseSchema.refine((val) => {
691
+ if (val === null) return true;
692
+ if (required && (val === "" || val === "null" || val === "undefined")) {
693
+ throw new z4.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
694
+ }
695
+ const comparisonVal = !caseSensitive ? val.toLowerCase() : val;
696
+ if (val !== null && minLength !== void 0 && val.length < minLength) {
697
+ throw new z4.ZodError([{ code: "custom", message: getMessage("minLength", { minLength }), path: [] }]);
698
+ }
699
+ if (val !== null && maxLength !== void 0 && val.length > maxLength) {
700
+ throw new z4.ZodError([{ code: "custom", message: getMessage("maxLength", { maxLength }), path: [] }]);
701
+ }
702
+ const hasContentValidations = customRegex !== void 0 || startsWith !== void 0 || endsWith !== void 0 || includes !== void 0 || excludes !== void 0;
703
+ if (val !== null && customRegex !== void 0) {
704
+ if (!customRegex.test(val)) {
705
+ throw new z4.ZodError([{ code: "custom", message: getMessage("customFormat"), path: [] }]);
706
+ }
707
+ } else if (val !== null && !hasContentValidations) {
708
+ let isValidId;
709
+ if (allowedTypes && allowedTypes.length > 0) {
710
+ isValidId = allowedTypes.some((allowedType) => validateIdType(val, allowedType));
711
+ if (!isValidId) {
712
+ const typeNames = allowedTypes.join(", ");
713
+ throw new z4.ZodError([{ code: "custom", message: getMessage("invalid") + ` (allowed types: ${typeNames})`, path: [] }]);
714
+ }
715
+ } else if (type !== "auto") {
716
+ isValidId = validateIdType(val, type);
717
+ if (!isValidId) {
718
+ throw new z4.ZodError([{ code: "custom", message: getMessage(type) || getMessage("invalid"), path: [] }]);
719
+ }
720
+ } else {
721
+ isValidId = detectIdType(val) !== null;
722
+ if (!isValidId) {
723
+ throw new z4.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
724
+ }
725
+ }
726
+ } else if (val !== null && hasContentValidations && type !== "auto" && !customRegex) {
727
+ if (allowedTypes && allowedTypes.length > 0) {
728
+ const isValidType = allowedTypes.some((allowedType) => validateIdType(val, allowedType));
729
+ if (!isValidType) {
730
+ const typeNames = allowedTypes.join(", ");
731
+ throw new z4.ZodError([{ code: "custom", message: getMessage("invalid") + ` (allowed types: ${typeNames})`, path: [] }]);
732
+ }
733
+ } else {
734
+ if (!validateIdType(val, type)) {
735
+ throw new z4.ZodError([{ code: "custom", message: getMessage(type) || getMessage("invalid"), path: [] }]);
736
+ }
737
+ }
738
+ }
739
+ const searchStartsWith = !caseSensitive && startsWith ? startsWith.toLowerCase() : startsWith;
740
+ const searchEndsWith = !caseSensitive && endsWith ? endsWith.toLowerCase() : endsWith;
741
+ const searchIncludes = !caseSensitive && includes ? includes.toLowerCase() : includes;
742
+ if (val !== null && startsWith !== void 0 && !comparisonVal.startsWith(searchStartsWith)) {
743
+ throw new z4.ZodError([{ code: "custom", message: getMessage("startsWith", { startsWith }), path: [] }]);
744
+ }
745
+ if (val !== null && endsWith !== void 0 && !comparisonVal.endsWith(searchEndsWith)) {
746
+ throw new z4.ZodError([{ code: "custom", message: getMessage("endsWith", { endsWith }), path: [] }]);
747
+ }
748
+ if (val !== null && includes !== void 0 && !comparisonVal.includes(searchIncludes)) {
749
+ throw new z4.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
750
+ }
751
+ if (val !== null && excludes !== void 0) {
752
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
753
+ for (const exclude of excludeList) {
754
+ const searchExclude = !caseSensitive ? exclude.toLowerCase() : exclude;
755
+ if (comparisonVal.includes(searchExclude)) {
756
+ throw new z4.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
757
+ }
758
+ }
759
+ }
760
+ return true;
761
+ }).transform((val) => {
762
+ if (val === null) return val;
763
+ const shouldPreserveCase = type === "uuid" || type === "objectId";
764
+ if (!caseSensitive && !shouldPreserveCase) {
765
+ return val.toLowerCase();
766
+ }
767
+ return val;
768
+ });
235
769
  return schema;
236
770
  }
237
771
 
238
- // src/common/number.ts
772
+ // src/validators/common/number.ts
239
773
  import { z as z5 } from "zod";
240
774
  function number(options) {
241
- const { required = true, min, max, defaultValue } = options ?? {};
775
+ const {
776
+ required = true,
777
+ min,
778
+ max,
779
+ defaultValue,
780
+ type = "both",
781
+ positive,
782
+ negative,
783
+ nonNegative,
784
+ nonPositive,
785
+ multipleOf,
786
+ precision,
787
+ finite = true,
788
+ transform,
789
+ parseCommas = false,
790
+ i18n
791
+ } = options ?? {};
792
+ const getMessage = (key, params) => {
793
+ if (i18n) {
794
+ const currentLocale2 = getLocale();
795
+ const customMessages = i18n[currentLocale2];
796
+ if (customMessages && customMessages[key]) {
797
+ const template = customMessages[key];
798
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
799
+ }
800
+ }
801
+ return t(`common.number.${key}`, params);
802
+ };
803
+ const actualDefaultValue = defaultValue ?? null;
242
804
  const schema = z5.preprocess(
243
805
  (val) => {
244
- if (val === "" || val === void 0 || val === null) return defaultValue ?? null;
245
- return typeof val === "string" ? Number(val) : val;
246
- },
247
- z5.union([
248
- z5.number({
249
- error: (issue) => {
250
- if (issue.code === "invalid_type") return t("common.number.integer");
251
- return t("common.number.required");
806
+ if (val === "" || val === void 0 || val === null) {
807
+ return actualDefaultValue;
808
+ }
809
+ if (typeof val === "string") {
810
+ let processedVal = val.trim();
811
+ if (parseCommas) {
812
+ processedVal = processedVal.replace(/,/g, "");
813
+ }
814
+ const parsed = Number(processedVal);
815
+ if (isNaN(parsed)) {
816
+ return parsed;
252
817
  }
253
- }),
254
- z5.null()
255
- ])
256
- ).refine((val) => !required || val !== null, { message: t("common.number.required") }).refine((val) => val === null || min === void 0 || val >= min, { message: t("common.number.min", { min }) }).refine((val) => val === null || max === void 0 || val <= max, { message: t("common.number.max", { max }) });
818
+ if (transform) {
819
+ return transform(parsed);
820
+ }
821
+ return parsed;
822
+ }
823
+ if (typeof val === "number") {
824
+ if (transform && Number.isFinite(val)) {
825
+ return transform(val);
826
+ }
827
+ return val;
828
+ }
829
+ return val;
830
+ },
831
+ z5.union([z5.number(), z5.null(), z5.nan(), z5.custom((val) => val === Infinity || val === -Infinity)])
832
+ ).refine((val) => {
833
+ if (required && val === null) {
834
+ throw new z5.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
835
+ }
836
+ if (val === null) return true;
837
+ if (typeof val === "number" && isNaN(val)) {
838
+ if (type === "integer") {
839
+ throw new z5.ZodError([{ code: "custom", message: getMessage("integer"), path: [] }]);
840
+ } else if (type === "float") {
841
+ throw new z5.ZodError([{ code: "custom", message: getMessage("float"), path: [] }]);
842
+ } else {
843
+ throw new z5.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
844
+ }
845
+ }
846
+ if (typeof val !== "number") {
847
+ throw new z5.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
848
+ }
849
+ if (finite && !Number.isFinite(val)) {
850
+ throw new z5.ZodError([{ code: "custom", message: getMessage("finite"), path: [] }]);
851
+ }
852
+ if (type === "integer" && !Number.isInteger(val)) {
853
+ throw new z5.ZodError([{ code: "custom", message: getMessage("integer"), path: [] }]);
854
+ }
855
+ if (type === "float" && Number.isInteger(val)) {
856
+ throw new z5.ZodError([{ code: "custom", message: getMessage("float"), path: [] }]);
857
+ }
858
+ if (positive && val <= 0) {
859
+ throw new z5.ZodError([{ code: "custom", message: getMessage("positive"), path: [] }]);
860
+ }
861
+ if (negative && val >= 0) {
862
+ throw new z5.ZodError([{ code: "custom", message: getMessage("negative"), path: [] }]);
863
+ }
864
+ if (nonNegative && val < 0) {
865
+ throw new z5.ZodError([{ code: "custom", message: getMessage("nonNegative"), path: [] }]);
866
+ }
867
+ if (nonPositive && val > 0) {
868
+ throw new z5.ZodError([{ code: "custom", message: getMessage("nonPositive"), path: [] }]);
869
+ }
870
+ if (min !== void 0 && val < min) {
871
+ throw new z5.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
872
+ }
873
+ if (max !== void 0 && val > max) {
874
+ throw new z5.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
875
+ }
876
+ if (multipleOf !== void 0 && val % multipleOf !== 0) {
877
+ throw new z5.ZodError([{ code: "custom", message: getMessage("multipleOf", { multipleOf }), path: [] }]);
878
+ }
879
+ if (precision !== void 0) {
880
+ const decimalPlaces = (val.toString().split(".")[1] || "").length;
881
+ if (decimalPlaces > precision) {
882
+ throw new z5.ZodError([{ code: "custom", message: getMessage("precision", { precision }), path: [] }]);
883
+ }
884
+ }
885
+ return true;
886
+ });
257
887
  return schema;
258
888
  }
259
889
 
260
- // src/common/password.ts
890
+ // src/validators/common/password.ts
261
891
  import { z as z6 } from "zod";
892
+ var COMMON_PASSWORDS = [
893
+ "password",
894
+ "123456",
895
+ "123456789",
896
+ "12345678",
897
+ "12345",
898
+ "1234567",
899
+ "admin",
900
+ "qwerty",
901
+ "abc123",
902
+ "password123",
903
+ "letmein",
904
+ "welcome",
905
+ "monkey",
906
+ "dragon",
907
+ "sunshine",
908
+ "princess"
909
+ ];
910
+ var calculatePasswordStrength = (password2) => {
911
+ let score = 0;
912
+ if (password2.length >= 8) score += 1;
913
+ if (password2.length >= 12) score += 1;
914
+ if (password2.length >= 16) score += 1;
915
+ if (/[a-z]/.test(password2)) score += 1;
916
+ if (/[A-Z]/.test(password2)) score += 1;
917
+ if (/[0-9]/.test(password2)) score += 1;
918
+ if (/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password2)) score += 1;
919
+ if (/(.)\1{2,}/.test(password2)) score -= 1;
920
+ if (/(?:abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz|012|123|234|345|456|567|678|789)/i.test(password2)) score -= 1;
921
+ if (score <= 2) return "weak";
922
+ if (score <= 4) return "medium";
923
+ if (score <= 6) return "strong";
924
+ return "very-strong";
925
+ };
262
926
  function password(options) {
263
- const { required = true, min, max, uppercase, lowercase, digits, special } = options ?? {};
264
- const baseSchema = required ? z6.preprocess((val) => val === "" || val === null || val === void 0 ? null : val, z6.coerce.string().trim()) : z6.preprocess((val) => val === "" || val === null || val === void 0 ? null : val, z6.coerce.string().trim().nullable());
265
- const schema = baseSchema.refine((val) => required ? val !== "" && val !== "null" && val !== "undefined" : true, { message: t("common.password.required") }).refine((val) => val === null || min === void 0 || val.length >= min, { message: t("common.password.min", { min }) }).refine((val) => val === null || max === void 0 || val.length <= max, { message: t("common.password.max", { max }) }).refine((val) => val === null || !uppercase || /[A-Z]/.test(val), { message: t("common.password.uppercase") }).refine((val) => val === null || !lowercase || /[a-z]/.test(val), { message: t("common.password.lowercase") }).refine((val) => val === null || !special || /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]+/.test(val), { message: t("common.password.special") }).refine((val) => val === null || !digits || /[0-9]/.test(val), { message: t("common.password.digits") });
927
+ const {
928
+ required = true,
929
+ min,
930
+ max,
931
+ uppercase,
932
+ lowercase,
933
+ digits,
934
+ special,
935
+ noRepeating,
936
+ noSequential,
937
+ noCommonWords,
938
+ minStrength,
939
+ excludes,
940
+ includes,
941
+ regex,
942
+ transform,
943
+ defaultValue,
944
+ i18n
945
+ } = options ?? {};
946
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
947
+ const getMessage = (key, params) => {
948
+ if (i18n) {
949
+ const currentLocale2 = getLocale();
950
+ const customMessages = i18n[currentLocale2];
951
+ if (customMessages && customMessages[key]) {
952
+ const template = customMessages[key];
953
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
954
+ }
955
+ }
956
+ return t(`common.password.${key}`, params);
957
+ };
958
+ const preprocessFn = (val) => {
959
+ if (val === "" || val === null || val === void 0) {
960
+ return actualDefaultValue;
961
+ }
962
+ let processed = String(val);
963
+ if (transform) {
964
+ processed = transform(processed);
965
+ }
966
+ return processed;
967
+ };
968
+ const baseSchema = required ? z6.preprocess(preprocessFn, z6.string()) : z6.preprocess(preprocessFn, z6.string().nullable());
969
+ const schema = baseSchema.refine((val) => {
970
+ if (val === null) return true;
971
+ if (required && (val === "" || val === "null" || val === "undefined")) {
972
+ throw new z6.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
973
+ }
974
+ if (val !== null && min !== void 0 && val.length < min) {
975
+ throw new z6.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
976
+ }
977
+ if (val !== null && max !== void 0 && val.length > max) {
978
+ throw new z6.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
979
+ }
980
+ if (val !== null && uppercase && !/[A-Z]/.test(val)) {
981
+ throw new z6.ZodError([{ code: "custom", message: getMessage("uppercase"), path: [] }]);
982
+ }
983
+ if (val !== null && lowercase && !/[a-z]/.test(val)) {
984
+ throw new z6.ZodError([{ code: "custom", message: getMessage("lowercase"), path: [] }]);
985
+ }
986
+ if (val !== null && digits && !/[0-9]/.test(val)) {
987
+ throw new z6.ZodError([{ code: "custom", message: getMessage("digits"), path: [] }]);
988
+ }
989
+ if (val !== null && special && !/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(val)) {
990
+ throw new z6.ZodError([{ code: "custom", message: getMessage("special"), path: [] }]);
991
+ }
992
+ if (val !== null && noRepeating && /(.)\1{2,}/.test(val)) {
993
+ throw new z6.ZodError([{ code: "custom", message: getMessage("noRepeating"), path: [] }]);
994
+ }
995
+ if (val !== null && noSequential && /(?:abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz|012|123|234|345|456|567|678|789)/i.test(val)) {
996
+ throw new z6.ZodError([{ code: "custom", message: getMessage("noSequential"), path: [] }]);
997
+ }
998
+ if (val !== null && noCommonWords && COMMON_PASSWORDS.some((common) => val.toLowerCase().includes(common.toLowerCase()))) {
999
+ throw new z6.ZodError([{ code: "custom", message: getMessage("noCommonWords"), path: [] }]);
1000
+ }
1001
+ if (val !== null && minStrength) {
1002
+ const strength = calculatePasswordStrength(val);
1003
+ const strengthLevels = ["weak", "medium", "strong", "very-strong"];
1004
+ const currentLevel = strengthLevels.indexOf(strength);
1005
+ const requiredLevel = strengthLevels.indexOf(minStrength);
1006
+ if (currentLevel < requiredLevel) {
1007
+ throw new z6.ZodError([{ code: "custom", message: getMessage("minStrength", { minStrength }), path: [] }]);
1008
+ }
1009
+ }
1010
+ if (val !== null && includes !== void 0 && !val.includes(includes)) {
1011
+ throw new z6.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
1012
+ }
1013
+ if (val !== null && excludes !== void 0) {
1014
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
1015
+ for (const exclude of excludeList) {
1016
+ if (val.includes(exclude)) {
1017
+ throw new z6.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
1018
+ }
1019
+ }
1020
+ }
1021
+ if (val !== null && regex !== void 0 && !regex.test(val)) {
1022
+ throw new z6.ZodError([{ code: "custom", message: getMessage("invalid", { regex }), path: [] }]);
1023
+ }
1024
+ return true;
1025
+ });
266
1026
  return schema;
267
1027
  }
268
1028
 
269
- // src/common/integer.ts
1029
+ // src/validators/common/text.ts
270
1030
  import { z as z7 } from "zod";
271
- function integer(options) {
272
- const { required = true, min, max, defaultValue } = options ?? {};
273
- const schema = z7.preprocess(
274
- (val) => {
275
- if (val === "" || val === void 0 || val === null) return defaultValue ?? null;
276
- return typeof val === "string" ? Number(val) : val;
277
- },
278
- z7.union([
279
- z7.number({
280
- error: (issue) => {
281
- if (issue.code === "invalid_type") return t("common.integer.integer");
282
- return t("common.integer.required");
1031
+ function text(options) {
1032
+ const { required = true, minLength, maxLength, startsWith, endsWith, includes, excludes, regex, trimMode = "trim", casing = "none", transform, notEmpty, defaultValue, i18n } = options ?? {};
1033
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
1034
+ const getMessage = (key, params) => {
1035
+ if (i18n) {
1036
+ const currentLocale2 = getLocale();
1037
+ const customMessages = i18n[currentLocale2];
1038
+ if (customMessages && customMessages[key]) {
1039
+ const template = customMessages[key];
1040
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
1041
+ }
1042
+ }
1043
+ return t(`common.text.${key}`, params);
1044
+ };
1045
+ const applyTrim = (str) => {
1046
+ switch (trimMode) {
1047
+ case "trimStart":
1048
+ return str.trimStart();
1049
+ case "trimEnd":
1050
+ return str.trimEnd();
1051
+ case "none":
1052
+ return str;
1053
+ default:
1054
+ return str.trim();
1055
+ }
1056
+ };
1057
+ const applyCasing = (str) => {
1058
+ switch (casing) {
1059
+ case "upper":
1060
+ return str.toUpperCase();
1061
+ case "lower":
1062
+ return str.toLowerCase();
1063
+ case "title":
1064
+ return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase());
1065
+ default:
1066
+ return str;
1067
+ }
1068
+ };
1069
+ const preprocessFn = (val) => {
1070
+ if (val === "" || val === null || val === void 0) {
1071
+ return actualDefaultValue;
1072
+ }
1073
+ let processed = String(val);
1074
+ processed = applyTrim(processed);
1075
+ processed = applyCasing(processed);
1076
+ if (transform) {
1077
+ processed = transform(processed);
1078
+ }
1079
+ return processed;
1080
+ };
1081
+ const baseSchema = required ? z7.preprocess(preprocessFn, z7.string()) : z7.preprocess(preprocessFn, z7.string().nullable());
1082
+ const schema = baseSchema.refine((val) => {
1083
+ if (val === null) return true;
1084
+ if (required && (val === "" || val === "null" || val === "undefined")) {
1085
+ throw new z7.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
1086
+ }
1087
+ if (notEmpty && val !== null && val.trim() === "") {
1088
+ throw new z7.ZodError([{ code: "custom", message: getMessage("notEmpty"), path: [] }]);
1089
+ }
1090
+ if (val !== null && minLength !== void 0 && val.length < minLength) {
1091
+ throw new z7.ZodError([{ code: "custom", message: getMessage("minLength", { minLength }), path: [] }]);
1092
+ }
1093
+ if (val !== null && maxLength !== void 0 && val.length > maxLength) {
1094
+ throw new z7.ZodError([{ code: "custom", message: getMessage("maxLength", { maxLength }), path: [] }]);
1095
+ }
1096
+ if (val !== null && startsWith !== void 0 && !val.startsWith(startsWith)) {
1097
+ throw new z7.ZodError([{ code: "custom", message: getMessage("startsWith", { startsWith }), path: [] }]);
1098
+ }
1099
+ if (val !== null && endsWith !== void 0 && !val.endsWith(endsWith)) {
1100
+ throw new z7.ZodError([{ code: "custom", message: getMessage("endsWith", { endsWith }), path: [] }]);
1101
+ }
1102
+ if (val !== null && includes !== void 0 && !val.includes(includes)) {
1103
+ throw new z7.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
1104
+ }
1105
+ if (val !== null && excludes !== void 0) {
1106
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
1107
+ for (const exclude of excludeList) {
1108
+ if (val.includes(exclude)) {
1109
+ throw new z7.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
283
1110
  }
284
- }),
285
- z7.null()
286
- ])
287
- ).refine((val) => !required || val !== null, {
288
- message: t("common.integer.required")
289
- }).refine((val) => val === null || Number.isInteger(val), {
290
- message: t("common.integer.integer")
291
- }).refine((val) => val === null || min === void 0 || val >= min, {
292
- message: t("common.integer.min", { min })
293
- }).refine((val) => val === null || max === void 0 || val <= max, {
294
- message: t("common.integer.max", { max })
1111
+ }
1112
+ }
1113
+ if (val !== null && regex !== void 0 && !regex.test(val)) {
1114
+ throw new z7.ZodError([{ code: "custom", message: getMessage("invalid", { regex }), path: [] }]);
1115
+ }
1116
+ return true;
1117
+ });
1118
+ return schema;
1119
+ }
1120
+
1121
+ // src/validators/common/url.ts
1122
+ import { z as z8 } from "zod";
1123
+ function url(options) {
1124
+ const {
1125
+ required = true,
1126
+ min,
1127
+ max,
1128
+ includes,
1129
+ excludes,
1130
+ protocols,
1131
+ allowedDomains,
1132
+ blockedDomains,
1133
+ allowedPorts,
1134
+ blockedPorts,
1135
+ pathStartsWith,
1136
+ pathEndsWith,
1137
+ mustHaveQuery,
1138
+ mustNotHaveQuery,
1139
+ mustHaveFragment,
1140
+ mustNotHaveFragment,
1141
+ allowLocalhost = true,
1142
+ blockLocalhost,
1143
+ transform,
1144
+ defaultValue = null,
1145
+ i18n
1146
+ } = options ?? {};
1147
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
1148
+ const getMessage = (key, params) => {
1149
+ if (i18n) {
1150
+ const currentLocale2 = getLocale();
1151
+ const customMessages = i18n[currentLocale2];
1152
+ if (customMessages && customMessages[key]) {
1153
+ const template = customMessages[key];
1154
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
1155
+ }
1156
+ }
1157
+ return t(`common.url.${key}`, params);
1158
+ };
1159
+ const preprocessFn = (val) => {
1160
+ if (val === "" || val === null || val === void 0) {
1161
+ return actualDefaultValue;
1162
+ }
1163
+ let processed = String(val).trim();
1164
+ if (transform) {
1165
+ processed = transform(processed);
1166
+ }
1167
+ return processed;
1168
+ };
1169
+ const baseSchema = required ? z8.preprocess(preprocessFn, z8.string()) : z8.preprocess(preprocessFn, z8.string().nullable());
1170
+ const schema = baseSchema.refine((val) => {
1171
+ if (val === null) return true;
1172
+ if (required && (val === "" || val === "null" || val === "undefined")) {
1173
+ throw new z8.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
1174
+ }
1175
+ let urlObj;
1176
+ try {
1177
+ urlObj = new URL(val);
1178
+ } catch {
1179
+ throw new z8.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
1180
+ }
1181
+ if (val !== null && min !== void 0 && val.length < min) {
1182
+ throw new z8.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
1183
+ }
1184
+ if (val !== null && max !== void 0 && val.length > max) {
1185
+ throw new z8.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
1186
+ }
1187
+ if (val !== null && includes !== void 0 && !val.includes(includes)) {
1188
+ throw new z8.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
1189
+ }
1190
+ if (val !== null && excludes !== void 0) {
1191
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
1192
+ for (const exclude of excludeList) {
1193
+ if (val.includes(exclude)) {
1194
+ throw new z8.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
1195
+ }
1196
+ }
1197
+ }
1198
+ if (protocols && !protocols.includes(urlObj.protocol.slice(0, -1))) {
1199
+ throw new z8.ZodError([{ code: "custom", message: getMessage("protocol", { protocols: protocols.join(", ") }), path: [] }]);
1200
+ }
1201
+ const hostname = urlObj.hostname.toLowerCase();
1202
+ if (allowedDomains && !allowedDomains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) {
1203
+ throw new z8.ZodError([{ code: "custom", message: getMessage("domain", { domains: allowedDomains.join(", ") }), path: [] }]);
1204
+ }
1205
+ if (blockedDomains && blockedDomains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) {
1206
+ const blockedDomain = blockedDomains.find((domain) => hostname === domain || hostname.endsWith(`.${domain}`));
1207
+ throw new z8.ZodError([{ code: "custom", message: getMessage("domainBlacklist", { domain: blockedDomain }), path: [] }]);
1208
+ }
1209
+ const port = urlObj.port ? parseInt(urlObj.port) : urlObj.protocol === "https:" ? 443 : 80;
1210
+ if (allowedPorts && !allowedPorts.includes(port)) {
1211
+ throw new z8.ZodError([{ code: "custom", message: getMessage("port", { ports: allowedPorts.join(", ") }), path: [] }]);
1212
+ }
1213
+ if (blockedPorts && blockedPorts.includes(port)) {
1214
+ throw new z8.ZodError([{ code: "custom", message: getMessage("port", { port }), path: [] }]);
1215
+ }
1216
+ if (pathStartsWith && !urlObj.pathname.startsWith(pathStartsWith)) {
1217
+ throw new z8.ZodError([{ code: "custom", message: getMessage("pathStartsWith", { path: pathStartsWith }), path: [] }]);
1218
+ }
1219
+ if (pathEndsWith && !urlObj.pathname.endsWith(pathEndsWith)) {
1220
+ throw new z8.ZodError([{ code: "custom", message: getMessage("pathEndsWith", { path: pathEndsWith }), path: [] }]);
1221
+ }
1222
+ if (mustHaveQuery && !urlObj.search) {
1223
+ throw new z8.ZodError([{ code: "custom", message: getMessage("hasQuery"), path: [] }]);
1224
+ }
1225
+ if (mustNotHaveQuery && urlObj.search) {
1226
+ throw new z8.ZodError([{ code: "custom", message: getMessage("noQuery"), path: [] }]);
1227
+ }
1228
+ if (mustHaveFragment && !urlObj.hash) {
1229
+ throw new z8.ZodError([{ code: "custom", message: getMessage("hasFragment"), path: [] }]);
1230
+ }
1231
+ if (mustNotHaveFragment && urlObj.hash) {
1232
+ throw new z8.ZodError([{ code: "custom", message: getMessage("noFragment"), path: [] }]);
1233
+ }
1234
+ const isLocalhost = hostname === "localhost" || hostname === "127.0.0.1" || hostname.startsWith("192.168.") || hostname.startsWith("10.") || hostname.match(/^172\.(1[6-9]|2[0-9]|3[0-1])\./);
1235
+ if (blockLocalhost && isLocalhost) {
1236
+ throw new z8.ZodError([{ code: "custom", message: getMessage("noLocalhost"), path: [] }]);
1237
+ }
1238
+ if (!allowLocalhost && isLocalhost) {
1239
+ throw new z8.ZodError([{ code: "custom", message: getMessage("localhost"), path: [] }]);
1240
+ }
1241
+ return true;
1242
+ });
1243
+ return schema;
1244
+ }
1245
+
1246
+ // src/validators/taiwan/business-id.ts
1247
+ import { z as z9 } from "zod";
1248
+ var validateTaiwanBusinessId = (value) => {
1249
+ if (!/^\d{8}$/.test(value)) {
1250
+ return false;
1251
+ }
1252
+ const digits = value.split("").map(Number);
1253
+ const coefficients = [1, 2, 1, 2, 1, 2, 4];
1254
+ let sum = 0;
1255
+ for (let i = 0; i < 7; i++) {
1256
+ const product = digits[i] * coefficients[i];
1257
+ sum += Math.floor(product / 10) + product % 10;
1258
+ }
1259
+ sum += digits[7];
1260
+ if (sum % 5 === 0) {
1261
+ return true;
1262
+ }
1263
+ if (sum % 10 === 0) {
1264
+ return true;
1265
+ }
1266
+ if (digits[6] === 7) {
1267
+ let altSum = 0;
1268
+ for (let i = 0; i < 7; i++) {
1269
+ const product = digits[i] * coefficients[i];
1270
+ altSum += Math.floor(product / 10) + product % 10;
1271
+ }
1272
+ altSum += 1 + digits[7];
1273
+ if (altSum % 5 === 0 || altSum % 10 === 0) {
1274
+ return true;
1275
+ }
1276
+ }
1277
+ return false;
1278
+ };
1279
+ function businessId(options) {
1280
+ const {
1281
+ required = true,
1282
+ transform,
1283
+ defaultValue,
1284
+ i18n
1285
+ } = options ?? {};
1286
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
1287
+ const getMessage = (key, params) => {
1288
+ if (i18n) {
1289
+ const currentLocale2 = getLocale();
1290
+ const customMessages = i18n[currentLocale2];
1291
+ if (customMessages && customMessages[key]) {
1292
+ const template = customMessages[key];
1293
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
1294
+ }
1295
+ }
1296
+ return t(`taiwan.businessId.${key}`, params);
1297
+ };
1298
+ const preprocessFn = (val) => {
1299
+ if (val === "" || val === null || val === void 0) {
1300
+ return actualDefaultValue;
1301
+ }
1302
+ let processed = String(val).trim();
1303
+ if (processed === "" && !required) {
1304
+ return null;
1305
+ }
1306
+ if (transform) {
1307
+ processed = transform(processed);
1308
+ }
1309
+ return processed;
1310
+ };
1311
+ const baseSchema = required ? z9.preprocess(preprocessFn, z9.string()) : z9.preprocess(preprocessFn, z9.string().nullable());
1312
+ const schema = baseSchema.refine((val) => {
1313
+ if (val === null) return true;
1314
+ if (required && (val === "" || val === "null" || val === "undefined")) {
1315
+ throw new z9.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
1316
+ }
1317
+ if (val === null) return true;
1318
+ if (!required && val === "") return true;
1319
+ if (!validateTaiwanBusinessId(val)) {
1320
+ throw new z9.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
1321
+ }
1322
+ return true;
1323
+ });
1324
+ return schema;
1325
+ }
1326
+
1327
+ // src/validators/taiwan/national-id.ts
1328
+ import { z as z10 } from "zod";
1329
+ var CITY_CODES = {
1330
+ "A": 10,
1331
+ "B": 11,
1332
+ "C": 12,
1333
+ "D": 13,
1334
+ "E": 14,
1335
+ "F": 15,
1336
+ "G": 16,
1337
+ "H": 17,
1338
+ "I": 34,
1339
+ "J": 18,
1340
+ "K": 19,
1341
+ "L": 20,
1342
+ "M": 21,
1343
+ "N": 22,
1344
+ "O": 35,
1345
+ "P": 23,
1346
+ "Q": 24,
1347
+ "R": 25,
1348
+ "S": 26,
1349
+ "T": 27,
1350
+ "U": 28,
1351
+ "V": 29,
1352
+ "W": 32,
1353
+ "X": 30,
1354
+ "Y": 31,
1355
+ "Z": 33
1356
+ };
1357
+ var validateCitizenId = (value) => {
1358
+ if (!/^[A-Z][1-2]\d{8}$/.test(value)) {
1359
+ return false;
1360
+ }
1361
+ const letter = value[0];
1362
+ const digits = value.slice(1).split("").map(Number);
1363
+ const cityCode = CITY_CODES[letter];
1364
+ if (!cityCode) return false;
1365
+ const cityDigits = [Math.floor(cityCode / 10), cityCode % 10];
1366
+ const coefficients = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1];
1367
+ let sum = cityDigits[0] * coefficients[0] + cityDigits[1] * coefficients[1];
1368
+ for (let i = 0; i < 8; i++) {
1369
+ sum += digits[i] * coefficients[i + 2];
1370
+ }
1371
+ const checksum = (10 - sum % 10) % 10;
1372
+ return checksum === digits[8];
1373
+ };
1374
+ var validateOldResidentId = (value) => {
1375
+ if (!/^[A-Z][ABCD]\d{8}$/.test(value)) {
1376
+ return false;
1377
+ }
1378
+ const letter = value[0];
1379
+ const genderCode = value[1];
1380
+ const digits = value.slice(2).split("").map(Number);
1381
+ const cityCode = CITY_CODES[letter];
1382
+ if (!cityCode) return false;
1383
+ const genderValue = genderCode === "A" || genderCode === "C" ? 1 : 0;
1384
+ const cityDigits = [Math.floor(cityCode / 10), cityCode % 10];
1385
+ const coefficients = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1];
1386
+ let sum = cityDigits[0] * coefficients[0] + cityDigits[1] * coefficients[1] + genderValue * coefficients[2];
1387
+ for (let i = 0; i < 7; i++) {
1388
+ sum += digits[i] * coefficients[i + 3];
1389
+ }
1390
+ const checksum = (10 - sum % 10) % 10;
1391
+ return checksum === digits[7];
1392
+ };
1393
+ var validateNewResidentId = (value) => {
1394
+ if (!/^[A-Z][89]\d{8}$/.test(value)) {
1395
+ return false;
1396
+ }
1397
+ const letter = value[0];
1398
+ const digits = value.slice(1).split("").map(Number);
1399
+ const cityCode = CITY_CODES[letter];
1400
+ if (!cityCode) return false;
1401
+ const cityDigits = [Math.floor(cityCode / 10), cityCode % 10];
1402
+ const coefficients = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1];
1403
+ let sum = cityDigits[0] * coefficients[0] + cityDigits[1] * coefficients[1];
1404
+ for (let i = 0; i < 8; i++) {
1405
+ sum += digits[i] * coefficients[i + 2];
1406
+ }
1407
+ const checksum = (10 - sum % 10) % 10;
1408
+ return checksum === digits[8];
1409
+ };
1410
+ var validateTaiwanNationalId = (value, type = "both", allowOldResident = true) => {
1411
+ if (!/^[A-Z].{9}$/.test(value)) {
1412
+ return false;
1413
+ }
1414
+ switch (type) {
1415
+ case "citizen":
1416
+ return validateCitizenId(value);
1417
+ case "resident":
1418
+ return (allowOldResident ? validateOldResidentId(value) : false) || validateNewResidentId(value);
1419
+ case "both":
1420
+ return validateCitizenId(value) || (allowOldResident ? validateOldResidentId(value) : false) || validateNewResidentId(value);
1421
+ default:
1422
+ return false;
1423
+ }
1424
+ };
1425
+ function nationalId(options) {
1426
+ const {
1427
+ required = true,
1428
+ type = "both",
1429
+ allowOldResident = true,
1430
+ transform,
1431
+ defaultValue,
1432
+ i18n
1433
+ } = options ?? {};
1434
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
1435
+ const getMessage = (key, params) => {
1436
+ if (i18n) {
1437
+ const currentLocale2 = getLocale();
1438
+ const customMessages = i18n[currentLocale2];
1439
+ if (customMessages && customMessages[key]) {
1440
+ const template = customMessages[key];
1441
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
1442
+ }
1443
+ }
1444
+ return t(`taiwan.nationalId.${key}`, params);
1445
+ };
1446
+ const preprocessFn = (val) => {
1447
+ if (val === "" || val === null || val === void 0) {
1448
+ return actualDefaultValue;
1449
+ }
1450
+ let processed = String(val).trim().toUpperCase();
1451
+ if (processed === "" && !required) {
1452
+ return null;
1453
+ }
1454
+ if (transform) {
1455
+ processed = transform(processed);
1456
+ }
1457
+ return processed;
1458
+ };
1459
+ const baseSchema = required ? z10.preprocess(preprocessFn, z10.string()) : z10.preprocess(preprocessFn, z10.string().nullable());
1460
+ const schema = baseSchema.refine((val) => {
1461
+ if (val === null) return true;
1462
+ if (required && (val === "" || val === "null" || val === "undefined")) {
1463
+ throw new z10.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
1464
+ }
1465
+ if (val === null) return true;
1466
+ if (!required && val === "") return true;
1467
+ if (!validateTaiwanNationalId(val, type, allowOldResident)) {
1468
+ throw new z10.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
1469
+ }
1470
+ return true;
1471
+ });
1472
+ return schema;
1473
+ }
1474
+
1475
+ // src/validators/taiwan/mobile.ts
1476
+ import { z as z11 } from "zod";
1477
+ var validateTaiwanMobile = (value) => {
1478
+ return /^09[0-9]\d{7}$/.test(value);
1479
+ };
1480
+ function mobile(options) {
1481
+ const { required = true, whitelist, transform, defaultValue, i18n } = options ?? {};
1482
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
1483
+ const getMessage = (key, params) => {
1484
+ if (i18n) {
1485
+ const currentLocale2 = getLocale();
1486
+ const customMessages = i18n[currentLocale2];
1487
+ if (customMessages && customMessages[key]) {
1488
+ const template = customMessages[key];
1489
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
1490
+ }
1491
+ }
1492
+ return t(`taiwan.mobile.${key}`, params);
1493
+ };
1494
+ const preprocessFn = (val) => {
1495
+ if (val === null || val === void 0) {
1496
+ return actualDefaultValue;
1497
+ }
1498
+ let processed = String(val).trim();
1499
+ if (processed === "") {
1500
+ if (whitelist && whitelist.includes("")) {
1501
+ return "";
1502
+ }
1503
+ if (!required) {
1504
+ return actualDefaultValue;
1505
+ }
1506
+ return actualDefaultValue;
1507
+ }
1508
+ if (transform) {
1509
+ processed = transform(processed);
1510
+ }
1511
+ return processed;
1512
+ };
1513
+ const baseSchema = required ? z11.preprocess(preprocessFn, z11.string()) : z11.preprocess(preprocessFn, z11.string().nullable());
1514
+ const schema = baseSchema.refine((val) => {
1515
+ if (val === null) return true;
1516
+ if (required && (val === "" || val === "null" || val === "undefined")) {
1517
+ throw new z11.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
1518
+ }
1519
+ if (val === null) return true;
1520
+ if (!required && val === "") return true;
1521
+ if (whitelist && whitelist.length > 0) {
1522
+ if (whitelist.includes(val)) {
1523
+ return true;
1524
+ }
1525
+ throw new z11.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }]);
1526
+ }
1527
+ if (!validateTaiwanMobile(val)) {
1528
+ throw new z11.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
1529
+ }
1530
+ return true;
1531
+ });
1532
+ return schema;
1533
+ }
1534
+
1535
+ // src/validators/taiwan/tel.ts
1536
+ import { z as z12 } from "zod";
1537
+ var validateTaiwanTel = (value) => {
1538
+ const cleanValue = value.replace(/[-\s]/g, "");
1539
+ if (!/^0\d{7,10}$/.test(cleanValue)) {
1540
+ return false;
1541
+ }
1542
+ const areaCode4 = cleanValue.substring(0, 4);
1543
+ if (areaCode4 === "0826") {
1544
+ return cleanValue.length === 9 && /^0826[6]\d{4}$/.test(cleanValue);
1545
+ }
1546
+ if (areaCode4 === "0836") {
1547
+ return cleanValue.length === 9 && /^0836[2-9]\d{4}$/.test(cleanValue);
1548
+ }
1549
+ const areaCode3 = cleanValue.substring(0, 3);
1550
+ if (areaCode3 === "037") {
1551
+ return cleanValue.length === 9 && /^037[2-9]\d{5}$/.test(cleanValue);
1552
+ }
1553
+ if (areaCode3 === "049") {
1554
+ return cleanValue.length === 10 && /^049[2-9]\d{6}$/.test(cleanValue);
1555
+ }
1556
+ if (areaCode3 === "082") {
1557
+ return cleanValue.length === 9 && /^082[2-57-9]\d{5}$/.test(cleanValue);
1558
+ }
1559
+ if (areaCode3 === "089") {
1560
+ return cleanValue.length === 9 && /^089[2-9]\d{5}$/.test(cleanValue);
1561
+ }
1562
+ const areaCode2 = cleanValue.substring(0, 2);
1563
+ if (areaCode2 === "02") {
1564
+ return cleanValue.length === 10 && /^02[235-8]\d{7}$/.test(cleanValue);
1565
+ }
1566
+ if (["03", "04", "05", "06"].includes(areaCode2)) {
1567
+ return cleanValue.length === 9;
1568
+ }
1569
+ if (areaCode2 === "07") {
1570
+ return cleanValue.length === 9 && /^07[2-9]\d{6}$/.test(cleanValue);
1571
+ }
1572
+ if (areaCode2 === "08") {
1573
+ return cleanValue.length === 9 && /^08[478]\d{6}$/.test(cleanValue);
1574
+ }
1575
+ return false;
1576
+ };
1577
+ function tel(options) {
1578
+ const { required = true, whitelist, transform, defaultValue, i18n } = options ?? {};
1579
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
1580
+ const getMessage = (key, params) => {
1581
+ if (i18n) {
1582
+ const currentLocale2 = getLocale();
1583
+ const customMessages = i18n[currentLocale2];
1584
+ if (customMessages && customMessages[key]) {
1585
+ const template = customMessages[key];
1586
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
1587
+ }
1588
+ }
1589
+ return t(`taiwan.tel.${key}`, params);
1590
+ };
1591
+ const preprocessFn = (val) => {
1592
+ if (val === null || val === void 0) {
1593
+ return actualDefaultValue;
1594
+ }
1595
+ let processed = String(val).trim();
1596
+ if (processed === "") {
1597
+ if (whitelist && whitelist.includes("")) {
1598
+ return "";
1599
+ }
1600
+ if (!required) {
1601
+ return actualDefaultValue;
1602
+ }
1603
+ return actualDefaultValue;
1604
+ }
1605
+ if (transform) {
1606
+ processed = transform(processed);
1607
+ }
1608
+ return processed;
1609
+ };
1610
+ const baseSchema = required ? z12.preprocess(preprocessFn, z12.string()) : z12.preprocess(preprocessFn, z12.string().nullable());
1611
+ const schema = baseSchema.refine((val) => {
1612
+ if (val === null) return true;
1613
+ if (required && (val === "" || val === "null" || val === "undefined")) {
1614
+ throw new z12.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
1615
+ }
1616
+ if (val === null) return true;
1617
+ if (!required && val === "") return true;
1618
+ if (whitelist && whitelist.length > 0) {
1619
+ if (whitelist.includes(val)) {
1620
+ return true;
1621
+ }
1622
+ throw new z12.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }]);
1623
+ }
1624
+ if (!validateTaiwanTel(val)) {
1625
+ throw new z12.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
1626
+ }
1627
+ return true;
1628
+ });
1629
+ return schema;
1630
+ }
1631
+
1632
+ // src/validators/taiwan/fax.ts
1633
+ import { z as z13 } from "zod";
1634
+ var validateTaiwanFax = (value) => {
1635
+ const cleanValue = value.replace(/[-\s]/g, "");
1636
+ if (!/^0\d{7,10}$/.test(cleanValue)) {
1637
+ return false;
1638
+ }
1639
+ const areaCode4 = cleanValue.substring(0, 4);
1640
+ if (areaCode4 === "0826") {
1641
+ return cleanValue.length === 9 && /^0826[6]\d{4}$/.test(cleanValue);
1642
+ }
1643
+ if (areaCode4 === "0836") {
1644
+ return cleanValue.length === 9 && /^0836[2-9]\d{4}$/.test(cleanValue);
1645
+ }
1646
+ const areaCode3 = cleanValue.substring(0, 3);
1647
+ if (areaCode3 === "037") {
1648
+ return cleanValue.length === 9 && /^037[2-9]\d{5}$/.test(cleanValue);
1649
+ }
1650
+ if (areaCode3 === "049") {
1651
+ return cleanValue.length === 10 && /^049[2-9]\d{6}$/.test(cleanValue);
1652
+ }
1653
+ if (areaCode3 === "082") {
1654
+ return cleanValue.length === 9 && /^082[2-57-9]\d{5}$/.test(cleanValue);
1655
+ }
1656
+ if (areaCode3 === "089") {
1657
+ return cleanValue.length === 9 && /^089[2-9]\d{5}$/.test(cleanValue);
1658
+ }
1659
+ const areaCode2 = cleanValue.substring(0, 2);
1660
+ if (areaCode2 === "02") {
1661
+ return cleanValue.length === 10 && /^02[235-8]\d{7}$/.test(cleanValue);
1662
+ }
1663
+ if (["03", "04", "05", "06"].includes(areaCode2)) {
1664
+ return cleanValue.length === 9;
1665
+ }
1666
+ if (areaCode2 === "07") {
1667
+ return cleanValue.length === 9 && /^07[2-9]\d{6}$/.test(cleanValue);
1668
+ }
1669
+ if (areaCode2 === "08") {
1670
+ return cleanValue.length === 9 && /^08[478]\d{6}$/.test(cleanValue);
1671
+ }
1672
+ return false;
1673
+ };
1674
+ function fax(options) {
1675
+ const { required = true, whitelist, transform, defaultValue, i18n } = options ?? {};
1676
+ const actualDefaultValue = defaultValue ?? (required ? "" : null);
1677
+ const getMessage = (key, params) => {
1678
+ if (i18n) {
1679
+ const currentLocale2 = getLocale();
1680
+ const customMessages = i18n[currentLocale2];
1681
+ if (customMessages && customMessages[key]) {
1682
+ const template = customMessages[key];
1683
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
1684
+ }
1685
+ }
1686
+ return t(`taiwan.fax.${key}`, params);
1687
+ };
1688
+ const preprocessFn = (val) => {
1689
+ if (val === null || val === void 0) {
1690
+ return actualDefaultValue;
1691
+ }
1692
+ let processed = String(val).trim();
1693
+ if (processed === "") {
1694
+ if (whitelist && whitelist.includes("")) {
1695
+ return "";
1696
+ }
1697
+ if (!required) {
1698
+ return actualDefaultValue;
1699
+ }
1700
+ return actualDefaultValue;
1701
+ }
1702
+ if (transform) {
1703
+ processed = transform(processed);
1704
+ }
1705
+ return processed;
1706
+ };
1707
+ const baseSchema = required ? z13.preprocess(preprocessFn, z13.string()) : z13.preprocess(preprocessFn, z13.string().nullable());
1708
+ const schema = baseSchema.refine((val) => {
1709
+ if (val === null) return true;
1710
+ if (required && (val === "" || val === "null" || val === "undefined")) {
1711
+ throw new z13.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
1712
+ }
1713
+ if (val === null) return true;
1714
+ if (!required && val === "") return true;
1715
+ if (whitelist && whitelist.length > 0) {
1716
+ if (whitelist.includes(val)) {
1717
+ return true;
1718
+ }
1719
+ throw new z13.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }]);
1720
+ }
1721
+ if (!validateTaiwanFax(val)) {
1722
+ throw new z13.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
1723
+ }
1724
+ return true;
295
1725
  });
296
1726
  return schema;
297
1727
  }
298
1728
  export {
1729
+ ID_PATTERNS,
299
1730
  boolean,
1731
+ businessId,
1732
+ date,
1733
+ detectIdType,
300
1734
  email,
1735
+ fax,
301
1736
  getLocale,
302
- integer,
1737
+ id,
1738
+ mobile,
1739
+ nationalId,
303
1740
  number,
304
1741
  password,
305
1742
  setLocale,
1743
+ tel,
306
1744
  text,
307
- url
1745
+ url,
1746
+ validateCitizenId,
1747
+ validateIdType,
1748
+ validateNewResidentId,
1749
+ validateOldResidentId,
1750
+ validateTaiwanBusinessId,
1751
+ validateTaiwanFax,
1752
+ validateTaiwanMobile,
1753
+ validateTaiwanNationalId,
1754
+ validateTaiwanTel
308
1755
  };