@hy_ong/zod-kit 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +28 -0
- package/LICENSE +21 -0
- package/README.md +465 -97
- package/debug.js +21 -0
- package/debug.ts +16 -0
- package/dist/index.cjs +3127 -146
- package/dist/index.d.cts +3021 -25
- package/dist/index.d.ts +3021 -25
- package/dist/index.js +3081 -144
- package/eslint.config.mts +8 -0
- package/package.json +10 -9
- package/src/config.ts +1 -1
- package/src/i18n/locales/en.json +161 -25
- package/src/i18n/locales/zh-TW.json +165 -26
- package/src/index.ts +17 -7
- package/src/validators/common/boolean.ts +191 -0
- package/src/validators/common/date.ts +299 -0
- package/src/validators/common/datetime.ts +673 -0
- package/src/validators/common/email.ts +313 -0
- package/src/validators/common/file.ts +384 -0
- package/src/validators/common/id.ts +471 -0
- package/src/validators/common/number.ts +319 -0
- package/src/validators/common/password.ts +386 -0
- package/src/validators/common/text.ts +271 -0
- package/src/validators/common/time.ts +600 -0
- package/src/validators/common/url.ts +347 -0
- package/src/validators/taiwan/business-id.ts +262 -0
- package/src/validators/taiwan/fax.ts +327 -0
- package/src/validators/taiwan/mobile.ts +242 -0
- package/src/validators/taiwan/national-id.ts +425 -0
- package/src/validators/taiwan/postal-code.ts +1049 -0
- package/src/validators/taiwan/tel.ts +330 -0
- package/tests/common/boolean.test.ts +340 -92
- package/tests/common/date.test.ts +458 -0
- package/tests/common/datetime.test.ts +693 -0
- package/tests/common/email.test.ts +232 -60
- package/tests/common/file.test.ts +479 -0
- package/tests/common/id.test.ts +535 -0
- package/tests/common/number.test.ts +230 -60
- package/tests/common/password.test.ts +271 -44
- package/tests/common/text.test.ts +210 -13
- package/tests/common/time.test.ts +528 -0
- package/tests/common/url.test.ts +492 -67
- package/tests/taiwan/business-id.test.ts +240 -0
- package/tests/taiwan/fax.test.ts +463 -0
- package/tests/taiwan/mobile.test.ts +373 -0
- package/tests/taiwan/national-id.test.ts +435 -0
- package/tests/taiwan/postal-code.test.ts +705 -0
- package/tests/taiwan/tel.test.ts +467 -0
- package/eslint.config.mjs +0 -10
- package/src/common/boolean.ts +0 -36
- package/src/common/date.ts +0 -43
- package/src/common/email.ts +0 -44
- package/src/common/integer.ts +0 -46
- package/src/common/number.ts +0 -37
- package/src/common/password.ts +0 -33
- package/src/common/text.ts +0 -34
- package/src/common/url.ts +0 -37
- package/tests/common/integer.test.ts +0 -90
package/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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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,161 @@ 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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
75
|
+
id: {
|
|
54
76
|
required: "\u5FC5\u586B",
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
+
time: {
|
|
121
|
+
required: "\u5FC5\u586B",
|
|
122
|
+
invalid: "\u7121\u6548\u7684\u6642\u9593\u683C\u5F0F",
|
|
123
|
+
format: "\u5FC5\u9808\u70BA ${format} \u683C\u5F0F",
|
|
124
|
+
min: "\u6642\u9593\u4E0D\u5F97\u65E9\u65BC ${min}",
|
|
125
|
+
max: "\u6642\u9593\u4E0D\u5F97\u665A\u65BC ${max}",
|
|
126
|
+
hour: "\u5C0F\u6642\u5FC5\u9808\u4ECB\u65BC ${minHour} \u8207 ${maxHour} \u4E4B\u9593",
|
|
127
|
+
minute: "\u5206\u9418\u5FC5\u9808\u70BA ${minuteStep} \u5206\u9418\u9593\u9694",
|
|
128
|
+
second: "\u79D2\u6578\u5FC5\u9808\u70BA ${secondStep} \u79D2\u9593\u9694",
|
|
129
|
+
includes: "\u5FC5\u9808\u5305\u542B\u300C${includes}\u300D",
|
|
130
|
+
excludes: "\u4E0D\u5F97\u5305\u542B\u300C${excludes}\u300D",
|
|
131
|
+
customRegex: "\u7121\u6548\u7684\u6642\u9593\u683C\u5F0F",
|
|
132
|
+
notInWhitelist: "\u6642\u9593\u4E0D\u5728\u5141\u8A31\u6E05\u55AE\u4E2D"
|
|
133
|
+
},
|
|
134
|
+
datetime: {
|
|
135
|
+
required: "\u5FC5\u586B",
|
|
136
|
+
invalid: "\u7121\u6548\u7684\u65E5\u671F\u6642\u9593\u683C\u5F0F",
|
|
137
|
+
format: "\u5FC5\u9808\u70BA ${format} \u683C\u5F0F",
|
|
138
|
+
min: "\u65E5\u671F\u6642\u9593\u4E0D\u5F97\u65E9\u65BC ${min}",
|
|
139
|
+
max: "\u65E5\u671F\u6642\u9593\u4E0D\u5F97\u665A\u65BC ${max}",
|
|
140
|
+
hour: "\u5C0F\u6642\u5FC5\u9808\u4ECB\u65BC ${minHour} \u8207 ${maxHour} \u4E4B\u9593",
|
|
141
|
+
minute: "\u5206\u9418\u5FC5\u9808\u70BA ${minuteStep} \u5206\u9418\u9593\u9694",
|
|
142
|
+
includes: "\u5FC5\u9808\u5305\u542B\u300C${includes}\u300D",
|
|
143
|
+
excludes: "\u4E0D\u5F97\u5305\u542B\u300C${excludes}\u300D",
|
|
144
|
+
past: "\u5FC5\u9808\u70BA\u904E\u53BB\u7684\u65E5\u671F\u6642\u9593",
|
|
145
|
+
future: "\u5FC5\u9808\u70BA\u672A\u4F86\u7684\u65E5\u671F\u6642\u9593",
|
|
146
|
+
today: "\u5FC5\u9808\u70BA\u4ECA\u5929",
|
|
147
|
+
notToday: "\u4E0D\u5F97\u70BA\u4ECA\u5929",
|
|
148
|
+
weekday: "\u5FC5\u9808\u70BA\u5DE5\u4F5C\u65E5\uFF08\u9031\u4E00\u81F3\u9031\u4E94\uFF09",
|
|
149
|
+
weekend: "\u5FC5\u9808\u70BA\u9031\u672B\uFF08\u9031\u516D\u81F3\u9031\u65E5\uFF09",
|
|
150
|
+
customRegex: "\u7121\u6548\u7684\u65E5\u671F\u6642\u9593\u683C\u5F0F",
|
|
151
|
+
notInWhitelist: "\u65E5\u671F\u6642\u9593\u4E0D\u5728\u5141\u8A31\u6E05\u55AE\u4E2D"
|
|
152
|
+
},
|
|
153
|
+
file: {
|
|
154
|
+
required: "\u5FC5\u586B",
|
|
155
|
+
invalid: "\u7121\u6548\u7684\u6A94\u6848\u683C\u5F0F",
|
|
156
|
+
size: "\u6A94\u6848\u5927\u5C0F\u4E0D\u5F97\u8D85\u904E ${size}",
|
|
157
|
+
minSize: "\u6A94\u6848\u5927\u5C0F\u81F3\u5C11 ${minSize}",
|
|
158
|
+
maxSize: "\u6A94\u6848\u5927\u5C0F\u4E0D\u5F97\u8D85\u904E ${maxSize}",
|
|
159
|
+
type: "\u6A94\u6848\u985E\u578B\u5FC5\u9808\u70BA\uFF1A${type}",
|
|
160
|
+
extension: "\u526F\u6A94\u540D\u5FC5\u9808\u70BA\uFF1A${extension}",
|
|
161
|
+
extensionBlacklist: "\u4E0D\u5141\u8A31\u4F7F\u7528\u526F\u6A94\u540D ${extension}",
|
|
162
|
+
name: "\u6A94\u6848\u540D\u7A31\u5FC5\u9808\u7B26\u5408\u683C\u5F0F ${pattern}",
|
|
163
|
+
nameBlacklist: "\u6A94\u6848\u540D\u7A31\u4E0D\u5F97\u7B26\u5408\u683C\u5F0F ${pattern}",
|
|
164
|
+
imageOnly: "\u50C5\u5141\u8A31\u5716\u7247\u6A94\u6848",
|
|
165
|
+
documentOnly: "\u50C5\u5141\u8A31\u6587\u4EF6\u6A94\u6848",
|
|
166
|
+
videoOnly: "\u50C5\u5141\u8A31\u5F71\u7247\u6A94\u6848",
|
|
167
|
+
audioOnly: "\u50C5\u5141\u8A31\u97F3\u8A0A\u6A94\u6848",
|
|
168
|
+
archiveOnly: "\u50C5\u5141\u8A31\u58D3\u7E2E\u6A94\u6848"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
taiwan: {
|
|
172
|
+
businessId: {
|
|
173
|
+
required: "\u5FC5\u586B",
|
|
174
|
+
invalid: "\u7121\u6548\u7684\u7D71\u4E00\u7DE8\u865F"
|
|
175
|
+
},
|
|
176
|
+
nationalId: {
|
|
177
|
+
required: "\u5FC5\u586B",
|
|
178
|
+
invalid: "\u7121\u6548\u7684\u8EAB\u5206\u8B49\u5B57\u865F"
|
|
179
|
+
},
|
|
180
|
+
mobile: {
|
|
181
|
+
required: "\u5FC5\u586B",
|
|
182
|
+
invalid: "\u7121\u6548\u7684\u624B\u6A5F\u865F\u78BC\u683C\u5F0F",
|
|
183
|
+
notInWhitelist: "\u4E0D\u5728\u5141\u8A31\u7684\u624B\u6A5F\u865F\u78BC\u6E05\u55AE\u4E2D"
|
|
184
|
+
},
|
|
185
|
+
tel: {
|
|
186
|
+
required: "\u5FC5\u586B",
|
|
187
|
+
invalid: "\u7121\u6548\u7684\u5E02\u8A71\u865F\u78BC\u683C\u5F0F",
|
|
188
|
+
notInWhitelist: "\u4E0D\u5728\u5141\u8A31\u7684\u5E02\u8A71\u865F\u78BC\u6E05\u55AE\u4E2D"
|
|
189
|
+
},
|
|
190
|
+
fax: {
|
|
191
|
+
required: "\u5FC5\u586B",
|
|
192
|
+
invalid: "\u7121\u6548\u7684\u50B3\u771F\u865F\u78BC\u683C\u5F0F",
|
|
193
|
+
notInWhitelist: "\u4E0D\u5728\u5141\u8A31\u7684\u50B3\u771F\u865F\u78BC\u6E05\u55AE\u4E2D"
|
|
194
|
+
},
|
|
195
|
+
postalCode: {
|
|
196
|
+
required: "\u5FC5\u586B",
|
|
197
|
+
invalid: "\u7121\u6548\u7684\u90F5\u905E\u5340\u865F",
|
|
198
|
+
invalidFormat: "\u90F5\u905E\u5340\u865F\u683C\u5F0F\u932F\u8AA4",
|
|
199
|
+
invalidRange: "\u90F5\u905E\u5340\u865F\u8D85\u51FA\u6709\u6548\u7BC4\u570D",
|
|
200
|
+
legacy5DigitWarning: "5 \u78BC\u90F5\u905E\u5340\u865F\u70BA\u820A\u5F0F\u683C\u5F0F\uFF0C\u5EFA\u8B70\u4F7F\u7528 6 \u78BC\u683C\u5F0F",
|
|
201
|
+
format3Only: "\u50C5\u5141\u8A31 3 \u78BC\u90F5\u905E\u5340\u865F",
|
|
202
|
+
format5Only: "\u50C5\u5141\u8A31 5 \u78BC\u90F5\u905E\u5340\u865F",
|
|
203
|
+
format6Only: "\u50C5\u5141\u8A31 6 \u78BC\u90F5\u905E\u5340\u865F",
|
|
204
|
+
invalidSuffix: "\u7121\u6548\u7684\u90F5\u905E\u5340\u865F\u5F8C\u78BC - 5 \u78BC\u683C\u5F0F\u9808\u70BA 01-99\uFF0C6 \u78BC\u683C\u5F0F\u9808\u70BA 001-999",
|
|
205
|
+
deprecated5Digit: "5 \u78BC\u90F5\u905E\u5340\u865F\u5DF2\u68C4\u7528\u4E14\u4E0D\u518D\u652F\u63F4"
|
|
67
206
|
}
|
|
68
207
|
}
|
|
69
208
|
};
|
|
@@ -73,25 +212,40 @@ var en_default = {
|
|
|
73
212
|
common: {
|
|
74
213
|
boolean: {
|
|
75
214
|
required: "Required",
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
215
|
+
shouldBeTrue: "Must be True",
|
|
216
|
+
shouldBeFalse: "Must be False",
|
|
217
|
+
invalid: "Must be a boolean value"
|
|
80
218
|
},
|
|
81
219
|
email: {
|
|
82
220
|
required: "Required",
|
|
83
|
-
|
|
84
|
-
|
|
221
|
+
invalid: "Invalid email format",
|
|
222
|
+
minLength: "Must be at least ${minLength} characters",
|
|
223
|
+
maxLength: "Must be at most ${maxLength} characters",
|
|
85
224
|
includes: "Must include ${includes}",
|
|
86
|
-
|
|
87
|
-
|
|
225
|
+
domain: "Must be from domain: ${domain}",
|
|
226
|
+
domainBlacklist: "Domain ${domain} is not allowed",
|
|
227
|
+
businessOnly: "Only business email addresses are allowed",
|
|
228
|
+
noDisposable: "Disposable email addresses are not allowed"
|
|
88
229
|
},
|
|
89
230
|
url: {
|
|
90
231
|
required: "Required",
|
|
232
|
+
invalid: "Invalid URL format",
|
|
91
233
|
min: "Must be at least ${min} characters",
|
|
92
234
|
max: "Must be at most ${max} characters",
|
|
93
235
|
includes: "Must include ${includes}",
|
|
94
|
-
|
|
236
|
+
excludes: "Must not contain ${excludes}",
|
|
237
|
+
protocol: "Protocol must be one of: ${protocols}",
|
|
238
|
+
domain: "Domain must be one of: ${domains}",
|
|
239
|
+
domainBlacklist: "Domain ${domain} is not allowed",
|
|
240
|
+
port: "Port must be one of: ${ports}",
|
|
241
|
+
pathStartsWith: "Path must start with ${path}",
|
|
242
|
+
pathEndsWith: "Path must end with ${path}",
|
|
243
|
+
hasQuery: "URL must have query parameters",
|
|
244
|
+
noQuery: "URL must not have query parameters",
|
|
245
|
+
hasFragment: "URL must have a fragment",
|
|
246
|
+
noFragment: "URL must not have a fragment",
|
|
247
|
+
localhost: "Localhost URLs are not allowed",
|
|
248
|
+
noLocalhost: "Localhost URLs are not allowed"
|
|
95
249
|
},
|
|
96
250
|
password: {
|
|
97
251
|
required: "Required",
|
|
@@ -100,46 +254,167 @@ var en_default = {
|
|
|
100
254
|
uppercase: "Must include at least one uppercase letter",
|
|
101
255
|
lowercase: "Must include at least one lowercase letter",
|
|
102
256
|
digits: "Must include at least one digit",
|
|
103
|
-
special: "Must include at least one special character"
|
|
257
|
+
special: "Must include at least one special character",
|
|
258
|
+
noRepeating: "Must not contain repeating characters",
|
|
259
|
+
noSequential: "Must not contain sequential characters",
|
|
260
|
+
noCommonWords: "Must not contain common words or patterns",
|
|
261
|
+
minStrength: "Password strength must be at least ${minStrength}",
|
|
262
|
+
excludes: "Must not contain ${excludes}",
|
|
263
|
+
includes: "Must include ${includes}",
|
|
264
|
+
invalid: "Invalid password format"
|
|
104
265
|
},
|
|
105
266
|
number: {
|
|
106
267
|
required: "Required",
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
integer: {
|
|
111
|
-
required: "Required",
|
|
268
|
+
invalid: "Must be a valid number",
|
|
269
|
+
integer: "Must be an integer",
|
|
270
|
+
float: "Must be a decimal number",
|
|
112
271
|
min: "Must be at least ${min}",
|
|
113
272
|
max: "Must be at most ${max}",
|
|
114
|
-
|
|
273
|
+
positive: "Must be positive",
|
|
274
|
+
negative: "Must be negative",
|
|
275
|
+
nonNegative: "Must be non-negative",
|
|
276
|
+
nonPositive: "Must be non-positive",
|
|
277
|
+
multipleOf: "Must be a multiple of ${multipleOf}",
|
|
278
|
+
finite: "Must be a finite number",
|
|
279
|
+
precision: "Must have at most ${precision} decimal places"
|
|
115
280
|
},
|
|
116
|
-
|
|
281
|
+
id: {
|
|
117
282
|
required: "Required",
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
283
|
+
invalid: "Invalid ID format",
|
|
284
|
+
minLength: "Must be at least ${minLength} characters",
|
|
285
|
+
maxLength: "Must be at most ${maxLength} characters",
|
|
286
|
+
numeric: "Must be a numeric ID",
|
|
287
|
+
uuid: "Must be a valid UUID",
|
|
288
|
+
objectId: "Must be a valid MongoDB ObjectId",
|
|
289
|
+
nanoid: "Must be a valid Nano ID",
|
|
290
|
+
snowflake: "Must be a valid Snowflake ID",
|
|
291
|
+
cuid: "Must be a valid CUID",
|
|
292
|
+
ulid: "Must be a valid ULID",
|
|
293
|
+
shortid: "Must be a valid Short ID",
|
|
294
|
+
customFormat: "Invalid ID format",
|
|
295
|
+
includes: "Must include ${includes}",
|
|
296
|
+
excludes: "Must not contain ${excludes}",
|
|
297
|
+
startsWith: "Must start with ${startsWith}",
|
|
298
|
+
endsWith: "Must end with ${endsWith}"
|
|
121
299
|
},
|
|
122
300
|
text: {
|
|
123
301
|
required: "Required",
|
|
124
|
-
|
|
125
|
-
|
|
302
|
+
notEmpty: "Cannot be empty or whitespace only",
|
|
303
|
+
minLength: "Must be at least ${minLength} characters",
|
|
304
|
+
maxLength: "Must be at most ${maxLength} characters",
|
|
126
305
|
startsWith: "Must start with ${startsWith}",
|
|
127
306
|
endsWith: "Must end with ${endsWith}",
|
|
128
307
|
includes: "Must include ${includes}",
|
|
308
|
+
excludes: "Must not contain ${excludes}",
|
|
129
309
|
invalid: "Invalid format"
|
|
130
310
|
},
|
|
131
311
|
date: {
|
|
132
312
|
required: "Required",
|
|
133
|
-
|
|
134
|
-
|
|
313
|
+
invalid: "Invalid date",
|
|
314
|
+
format: "Must be in ${format} format",
|
|
315
|
+
min: "Date must be on or after ${min}",
|
|
316
|
+
max: "Date must be on or before ${max}",
|
|
317
|
+
includes: "Must include ${includes}",
|
|
318
|
+
excludes: "Must not contain ${excludes}",
|
|
319
|
+
past: "Date must be in the past",
|
|
320
|
+
future: "Date must be in the future",
|
|
321
|
+
today: "Date must be today",
|
|
322
|
+
notToday: "Date must not be today",
|
|
323
|
+
weekday: "Date must be a weekday (Monday-Friday)",
|
|
324
|
+
weekend: "Date must be a weekend (Saturday-Sunday)"
|
|
325
|
+
},
|
|
326
|
+
time: {
|
|
327
|
+
required: "Required",
|
|
328
|
+
invalid: "Invalid time format",
|
|
329
|
+
format: "Must be in ${format} format",
|
|
330
|
+
min: "Time must be after ${min}",
|
|
331
|
+
max: "Time must be before ${max}",
|
|
332
|
+
hour: "Hour must be between ${minHour} and ${maxHour}",
|
|
333
|
+
minute: "Minutes must be in ${minuteStep}-minute intervals",
|
|
334
|
+
second: "Seconds must be in ${secondStep}-second intervals",
|
|
135
335
|
includes: "Must include ${includes}",
|
|
136
|
-
|
|
336
|
+
excludes: "Must not contain ${excludes}",
|
|
337
|
+
customRegex: "Invalid time format",
|
|
338
|
+
notInWhitelist: "Time is not in the allowed list"
|
|
339
|
+
},
|
|
340
|
+
datetime: {
|
|
341
|
+
required: "Required",
|
|
342
|
+
invalid: "Invalid datetime format",
|
|
343
|
+
format: "Must be in ${format} format",
|
|
344
|
+
min: "DateTime must be after ${min}",
|
|
345
|
+
max: "DateTime must be before ${max}",
|
|
346
|
+
hour: "Hour must be between ${minHour} and ${maxHour}",
|
|
347
|
+
minute: "Minutes must be in ${minuteStep}-minute intervals",
|
|
348
|
+
includes: "Must include ${includes}",
|
|
349
|
+
excludes: "Must not contain ${excludes}",
|
|
350
|
+
past: "DateTime must be in the past",
|
|
351
|
+
future: "DateTime must be in the future",
|
|
352
|
+
today: "DateTime must be today",
|
|
353
|
+
notToday: "DateTime must not be today",
|
|
354
|
+
weekday: "DateTime must be a weekday (Monday-Friday)",
|
|
355
|
+
weekend: "DateTime must be a weekend (Saturday-Sunday)",
|
|
356
|
+
customRegex: "Invalid datetime format",
|
|
357
|
+
notInWhitelist: "DateTime is not in the allowed list"
|
|
358
|
+
},
|
|
359
|
+
file: {
|
|
360
|
+
required: "Required",
|
|
361
|
+
invalid: "Invalid file format",
|
|
362
|
+
size: "File size must not exceed ${size}",
|
|
363
|
+
minSize: "File size must be at least ${minSize}",
|
|
364
|
+
maxSize: "File size must not exceed ${maxSize}",
|
|
365
|
+
type: "File type must be one of: ${type}",
|
|
366
|
+
extension: "File extension must be one of: ${extension}",
|
|
367
|
+
extensionBlacklist: "File extension ${extension} is not allowed",
|
|
368
|
+
name: "File name must match pattern ${pattern}",
|
|
369
|
+
nameBlacklist: "File name must not match pattern ${pattern}",
|
|
370
|
+
imageOnly: "Only image files are allowed",
|
|
371
|
+
documentOnly: "Only document files are allowed",
|
|
372
|
+
videoOnly: "Only video files are allowed",
|
|
373
|
+
audioOnly: "Only audio files are allowed",
|
|
374
|
+
archiveOnly: "Only archive files are allowed"
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
taiwan: {
|
|
378
|
+
businessId: {
|
|
379
|
+
required: "Required",
|
|
380
|
+
invalid: "Invalid Taiwan Business ID"
|
|
381
|
+
},
|
|
382
|
+
nationalId: {
|
|
383
|
+
required: "Required",
|
|
384
|
+
invalid: "Invalid Taiwan National ID"
|
|
385
|
+
},
|
|
386
|
+
mobile: {
|
|
387
|
+
required: "Required",
|
|
388
|
+
invalid: "Invalid Taiwan mobile phone format",
|
|
389
|
+
notInWhitelist: "Not in allowed mobile phone list"
|
|
390
|
+
},
|
|
391
|
+
tel: {
|
|
392
|
+
required: "Required",
|
|
393
|
+
invalid: "Invalid Taiwan telephone format",
|
|
394
|
+
notInWhitelist: "Not in allowed telephone list"
|
|
395
|
+
},
|
|
396
|
+
fax: {
|
|
397
|
+
required: "Required",
|
|
398
|
+
invalid: "Invalid Taiwan fax format",
|
|
399
|
+
notInWhitelist: "Not in allowed fax list"
|
|
400
|
+
},
|
|
401
|
+
postalCode: {
|
|
402
|
+
required: "Required",
|
|
403
|
+
invalid: "Invalid Taiwan postal code",
|
|
404
|
+
invalidFormat: "Invalid postal code format",
|
|
405
|
+
invalidRange: "Postal code is outside valid range",
|
|
406
|
+
legacy5DigitWarning: "5-digit postal codes are legacy format, consider using 6-digit format",
|
|
407
|
+
format3Only: "Only 3-digit postal codes are allowed",
|
|
408
|
+
format5Only: "Only 5-digit postal codes are allowed",
|
|
409
|
+
format6Only: "Only 6-digit postal codes are allowed",
|
|
410
|
+
invalidSuffix: "Invalid postal code suffix - must be 01-99 for 5-digit or 001-999 for 6-digit codes",
|
|
411
|
+
deprecated5Digit: "5-digit postal codes are deprecated and no longer supported"
|
|
137
412
|
}
|
|
138
413
|
}
|
|
139
414
|
};
|
|
140
415
|
|
|
141
416
|
// src/config.ts
|
|
142
|
-
var currentLocale = "
|
|
417
|
+
var currentLocale = "en";
|
|
143
418
|
var setLocale = (locale) => {
|
|
144
419
|
currentLocale = locale;
|
|
145
420
|
};
|
|
@@ -161,148 +436,2810 @@ function getNestedValue(obj, path) {
|
|
|
161
436
|
return typeof result === "string" ? result : void 0;
|
|
162
437
|
}
|
|
163
438
|
|
|
164
|
-
// src/common/boolean.ts
|
|
439
|
+
// src/validators/common/boolean.ts
|
|
165
440
|
function boolean(options) {
|
|
166
|
-
const {
|
|
441
|
+
const {
|
|
442
|
+
required = true,
|
|
443
|
+
defaultValue = null,
|
|
444
|
+
shouldBe,
|
|
445
|
+
truthyValues = [true, "true", 1, "1", "yes", "on"],
|
|
446
|
+
falsyValues = [false, "false", 0, "0", "no", "off"],
|
|
447
|
+
strict = false,
|
|
448
|
+
transform,
|
|
449
|
+
i18n
|
|
450
|
+
} = options ?? {};
|
|
451
|
+
const getMessage = (key, params) => {
|
|
452
|
+
if (i18n) {
|
|
453
|
+
const currentLocale2 = getLocale();
|
|
454
|
+
const customMessages = i18n[currentLocale2];
|
|
455
|
+
if (customMessages && customMessages[key]) {
|
|
456
|
+
const template = customMessages[key];
|
|
457
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return t(`common.boolean.${key}`, params);
|
|
461
|
+
};
|
|
167
462
|
let result = z.preprocess(
|
|
168
463
|
(val) => {
|
|
169
464
|
if (val === "" || val === void 0 || val === null) return defaultValue;
|
|
170
|
-
if (
|
|
171
|
-
|
|
465
|
+
if (strict && typeof val !== "boolean" && val !== null) {
|
|
466
|
+
return val;
|
|
467
|
+
}
|
|
468
|
+
if (truthyValues.includes(val)) {
|
|
469
|
+
let processed = true;
|
|
470
|
+
if (transform) processed = transform(processed);
|
|
471
|
+
return processed;
|
|
472
|
+
}
|
|
473
|
+
if (falsyValues.includes(val)) {
|
|
474
|
+
let processed = false;
|
|
475
|
+
if (transform) processed = transform(processed);
|
|
476
|
+
return processed;
|
|
477
|
+
}
|
|
172
478
|
return val;
|
|
173
479
|
},
|
|
174
480
|
z.union([z.literal(true), z.literal(false), z.literal(null)])
|
|
175
481
|
);
|
|
176
482
|
if (required && defaultValue === null) {
|
|
177
|
-
result = result.refine((val) => val !== null, { message:
|
|
483
|
+
result = result.refine((val) => val !== null, { message: getMessage("required") });
|
|
178
484
|
}
|
|
179
485
|
if (shouldBe === true) {
|
|
180
|
-
result = result.refine((val) => val === true, { message:
|
|
486
|
+
result = result.refine((val) => val === true, { message: getMessage("shouldBeTrue") });
|
|
181
487
|
} else if (shouldBe === false) {
|
|
182
|
-
result = result.refine((val) => val === false, { message:
|
|
488
|
+
result = result.refine((val) => val === false, { message: getMessage("shouldBeFalse") });
|
|
489
|
+
}
|
|
490
|
+
if (strict) {
|
|
491
|
+
result = result.refine(
|
|
492
|
+
(val) => {
|
|
493
|
+
return val === null || typeof val === "boolean";
|
|
494
|
+
},
|
|
495
|
+
{ message: getMessage("invalid") }
|
|
496
|
+
);
|
|
183
497
|
}
|
|
184
498
|
return result;
|
|
185
499
|
}
|
|
186
500
|
|
|
187
|
-
// src/common/
|
|
501
|
+
// src/validators/common/date.ts
|
|
188
502
|
import { z as z2 } from "zod";
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
503
|
+
import dayjs from "dayjs";
|
|
504
|
+
import customParseFormat from "dayjs/plugin/customParseFormat";
|
|
505
|
+
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
|
|
506
|
+
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
|
|
507
|
+
import isToday from "dayjs/plugin/isToday";
|
|
508
|
+
import weekday from "dayjs/plugin/weekday";
|
|
509
|
+
dayjs.extend(isSameOrAfter);
|
|
510
|
+
dayjs.extend(isSameOrBefore);
|
|
511
|
+
dayjs.extend(customParseFormat);
|
|
512
|
+
dayjs.extend(isToday);
|
|
513
|
+
dayjs.extend(weekday);
|
|
514
|
+
function date(options) {
|
|
515
|
+
const {
|
|
516
|
+
required = true,
|
|
517
|
+
min,
|
|
518
|
+
max,
|
|
519
|
+
format = "YYYY-MM-DD",
|
|
520
|
+
includes,
|
|
521
|
+
excludes,
|
|
522
|
+
mustBePast,
|
|
523
|
+
mustBeFuture,
|
|
524
|
+
mustBeToday,
|
|
525
|
+
mustNotBeToday,
|
|
526
|
+
weekdaysOnly,
|
|
527
|
+
weekendsOnly,
|
|
528
|
+
transform,
|
|
529
|
+
defaultValue = null,
|
|
530
|
+
i18n
|
|
531
|
+
} = options ?? {};
|
|
532
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
533
|
+
const getMessage = (key, params) => {
|
|
534
|
+
if (i18n) {
|
|
535
|
+
const currentLocale2 = getLocale();
|
|
536
|
+
const customMessages = i18n[currentLocale2];
|
|
537
|
+
if (customMessages && customMessages[key]) {
|
|
538
|
+
const template = customMessages[key];
|
|
539
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return t(`common.date.${key}`, params);
|
|
543
|
+
};
|
|
544
|
+
const preprocessFn = (val) => {
|
|
545
|
+
if (val === "" || val === null || val === void 0) {
|
|
546
|
+
return actualDefaultValue;
|
|
547
|
+
}
|
|
548
|
+
let processed = String(val).trim();
|
|
549
|
+
if (transform) {
|
|
550
|
+
processed = transform(processed);
|
|
551
|
+
}
|
|
552
|
+
return processed;
|
|
553
|
+
};
|
|
554
|
+
const baseSchema = required ? z2.preprocess(preprocessFn, z2.string()) : z2.preprocess(preprocessFn, z2.string().nullable());
|
|
555
|
+
const schema = baseSchema.refine((val) => {
|
|
556
|
+
if (val === null) return true;
|
|
557
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
558
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
559
|
+
}
|
|
560
|
+
if (val !== null && !dayjs(val, format, true).isValid()) {
|
|
561
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("format", { format }), path: [] }]);
|
|
562
|
+
}
|
|
563
|
+
const dateObj = dayjs(val, format);
|
|
564
|
+
if (val !== null && min !== void 0 && !dateObj.isSameOrAfter(dayjs(min, format))) {
|
|
565
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
|
|
566
|
+
}
|
|
567
|
+
if (val !== null && max !== void 0 && !dateObj.isSameOrBefore(dayjs(max, format))) {
|
|
568
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
|
|
569
|
+
}
|
|
570
|
+
if (val !== null && includes !== void 0 && !val.includes(includes)) {
|
|
571
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
|
|
572
|
+
}
|
|
573
|
+
if (val !== null && excludes !== void 0) {
|
|
574
|
+
const excludeList = Array.isArray(excludes) ? excludes : [excludes];
|
|
575
|
+
for (const exclude of excludeList) {
|
|
576
|
+
if (val.includes(exclude)) {
|
|
577
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
const today = dayjs().startOf("day");
|
|
582
|
+
const targetDate = dateObj.startOf("day");
|
|
583
|
+
if (val !== null && mustBePast && !targetDate.isBefore(today)) {
|
|
584
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("past"), path: [] }]);
|
|
585
|
+
}
|
|
586
|
+
if (val !== null && mustBeFuture && !targetDate.isAfter(today)) {
|
|
587
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("future"), path: [] }]);
|
|
588
|
+
}
|
|
589
|
+
if (val !== null && mustBeToday && !targetDate.isSame(today)) {
|
|
590
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("today"), path: [] }]);
|
|
591
|
+
}
|
|
592
|
+
if (val !== null && mustNotBeToday && targetDate.isSame(today)) {
|
|
593
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("notToday"), path: [] }]);
|
|
594
|
+
}
|
|
595
|
+
if (val !== null && weekdaysOnly && (dateObj.day() === 0 || dateObj.day() === 6)) {
|
|
596
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("weekday"), path: [] }]);
|
|
597
|
+
}
|
|
598
|
+
if (val !== null && weekendsOnly && dateObj.day() !== 0 && dateObj.day() !== 6) {
|
|
599
|
+
throw new z2.ZodError([{ code: "custom", message: getMessage("weekend"), path: [] }]);
|
|
600
|
+
}
|
|
601
|
+
return true;
|
|
602
|
+
});
|
|
208
603
|
return schema;
|
|
209
604
|
}
|
|
210
605
|
|
|
211
|
-
// src/common/
|
|
606
|
+
// src/validators/common/datetime.ts
|
|
212
607
|
import { z as z3 } from "zod";
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
608
|
+
import dayjs2 from "dayjs";
|
|
609
|
+
import customParseFormat2 from "dayjs/plugin/customParseFormat";
|
|
610
|
+
import isSameOrAfter2 from "dayjs/plugin/isSameOrAfter";
|
|
611
|
+
import isSameOrBefore2 from "dayjs/plugin/isSameOrBefore";
|
|
612
|
+
import isToday2 from "dayjs/plugin/isToday";
|
|
613
|
+
import weekday2 from "dayjs/plugin/weekday";
|
|
614
|
+
import timezone from "dayjs/plugin/timezone";
|
|
615
|
+
import utc from "dayjs/plugin/utc";
|
|
616
|
+
dayjs2.extend(isSameOrAfter2);
|
|
617
|
+
dayjs2.extend(isSameOrBefore2);
|
|
618
|
+
dayjs2.extend(customParseFormat2);
|
|
619
|
+
dayjs2.extend(isToday2);
|
|
620
|
+
dayjs2.extend(weekday2);
|
|
621
|
+
dayjs2.extend(timezone);
|
|
622
|
+
dayjs2.extend(utc);
|
|
623
|
+
var DATETIME_PATTERNS = {
|
|
624
|
+
"YYYY-MM-DD HH:mm": /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/,
|
|
625
|
+
"YYYY-MM-DD HH:mm:ss": /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/,
|
|
626
|
+
"YYYY-MM-DD hh:mm A": /^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2} (AM|PM)$/i,
|
|
627
|
+
"YYYY-MM-DD hh:mm:ss A": /^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}:\d{2} (AM|PM)$/i,
|
|
628
|
+
"DD/MM/YYYY HH:mm": /^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}$/,
|
|
629
|
+
"DD/MM/YYYY HH:mm:ss": /^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}:\d{2}$/,
|
|
630
|
+
"DD/MM/YYYY hh:mm A": /^\d{1,2}\/\d{1,2}\/\d{4} \d{1,2}:\d{2} (AM|PM)$/i,
|
|
631
|
+
"MM/DD/YYYY HH:mm": /^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}$/,
|
|
632
|
+
"MM/DD/YYYY hh:mm A": /^\d{1,2}\/\d{1,2}\/\d{4} \d{1,2}:\d{2} (AM|PM)$/i,
|
|
633
|
+
"YYYY/MM/DD HH:mm": /^\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}$/,
|
|
634
|
+
"DD-MM-YYYY HH:mm": /^\d{1,2}-\d{1,2}-\d{4} \d{2}:\d{2}$/,
|
|
635
|
+
"MM-DD-YYYY HH:mm": /^\d{1,2}-\d{1,2}-\d{4} \d{2}:\d{2}$/,
|
|
636
|
+
ISO: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/,
|
|
637
|
+
RFC: /^[A-Za-z]{3}, \d{1,2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} [A-Z]{3}$/,
|
|
638
|
+
UNIX: /^\d{10}$/
|
|
639
|
+
};
|
|
640
|
+
var validateDateTimeFormat = (value, format) => {
|
|
641
|
+
const pattern = DATETIME_PATTERNS[format];
|
|
642
|
+
if (!pattern.test(value.trim())) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
const parsed = parseDateTimeValue(value, format);
|
|
646
|
+
return parsed !== null;
|
|
647
|
+
};
|
|
648
|
+
var parseDateTimeValue = (value, format, timezone2) => {
|
|
649
|
+
try {
|
|
650
|
+
const cleanValue = value.trim();
|
|
651
|
+
let parsed;
|
|
652
|
+
switch (format) {
|
|
653
|
+
case "ISO":
|
|
654
|
+
parsed = dayjs2(cleanValue);
|
|
655
|
+
break;
|
|
656
|
+
case "RFC":
|
|
657
|
+
parsed = dayjs2(cleanValue);
|
|
658
|
+
break;
|
|
659
|
+
case "UNIX":
|
|
660
|
+
parsed = dayjs2.unix(parseInt(cleanValue, 10));
|
|
661
|
+
break;
|
|
662
|
+
default:
|
|
663
|
+
parsed = dayjs2(cleanValue, format, true);
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
if (!parsed.isValid()) {
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
if (timezone2) {
|
|
670
|
+
parsed = parsed.tz(timezone2);
|
|
671
|
+
}
|
|
672
|
+
return parsed;
|
|
673
|
+
} catch {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
var normalizeDateTimeValue = (value, format, timezone2) => {
|
|
678
|
+
const parsed = parseDateTimeValue(value, format, timezone2);
|
|
679
|
+
if (!parsed) return null;
|
|
680
|
+
switch (format) {
|
|
681
|
+
case "ISO":
|
|
682
|
+
return parsed.toISOString();
|
|
683
|
+
case "RFC":
|
|
684
|
+
return parsed.format("ddd, DD MMM YYYY HH:mm:ss [GMT]");
|
|
685
|
+
case "UNIX":
|
|
686
|
+
return parsed.unix().toString();
|
|
687
|
+
default:
|
|
688
|
+
return parsed.format(format);
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
function datetime(options) {
|
|
692
|
+
const {
|
|
693
|
+
required = true,
|
|
694
|
+
format = "YYYY-MM-DD HH:mm",
|
|
695
|
+
min,
|
|
696
|
+
max,
|
|
697
|
+
minHour,
|
|
698
|
+
maxHour,
|
|
699
|
+
allowedHours,
|
|
700
|
+
minuteStep,
|
|
701
|
+
timezone: timezone2,
|
|
702
|
+
includes,
|
|
703
|
+
excludes,
|
|
704
|
+
regex,
|
|
705
|
+
trimMode = "trim",
|
|
706
|
+
casing = "none",
|
|
707
|
+
mustBePast,
|
|
708
|
+
mustBeFuture,
|
|
709
|
+
mustBeToday,
|
|
710
|
+
mustNotBeToday,
|
|
711
|
+
weekdaysOnly,
|
|
712
|
+
weekendsOnly,
|
|
713
|
+
whitelist,
|
|
714
|
+
whitelistOnly = false,
|
|
715
|
+
transform,
|
|
716
|
+
defaultValue,
|
|
717
|
+
i18n
|
|
718
|
+
} = options ?? {};
|
|
719
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
720
|
+
const getMessage = (key, params) => {
|
|
721
|
+
if (i18n) {
|
|
722
|
+
const currentLocale2 = getLocale();
|
|
723
|
+
const customMessages = i18n[currentLocale2];
|
|
724
|
+
if (customMessages && customMessages[key]) {
|
|
725
|
+
const template = customMessages[key];
|
|
726
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return t(`common.datetime.${key}`, params);
|
|
730
|
+
};
|
|
731
|
+
const preprocessFn = (val) => {
|
|
732
|
+
if (val === null || val === void 0) {
|
|
733
|
+
return actualDefaultValue;
|
|
734
|
+
}
|
|
735
|
+
let processed = String(val);
|
|
736
|
+
switch (trimMode) {
|
|
737
|
+
case "trim":
|
|
738
|
+
processed = processed.trim();
|
|
739
|
+
break;
|
|
740
|
+
case "trimStart":
|
|
741
|
+
processed = processed.trimStart();
|
|
742
|
+
break;
|
|
743
|
+
case "trimEnd":
|
|
744
|
+
processed = processed.trimEnd();
|
|
745
|
+
break;
|
|
746
|
+
case "none":
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
if (processed === "") {
|
|
750
|
+
if (whitelist && whitelist.includes("")) {
|
|
751
|
+
return "";
|
|
752
|
+
}
|
|
753
|
+
if (!required) {
|
|
754
|
+
return actualDefaultValue;
|
|
755
|
+
}
|
|
756
|
+
return actualDefaultValue;
|
|
757
|
+
}
|
|
758
|
+
switch (casing) {
|
|
759
|
+
case "upper":
|
|
760
|
+
processed = processed.toUpperCase();
|
|
761
|
+
break;
|
|
762
|
+
case "lower":
|
|
763
|
+
processed = processed.toLowerCase();
|
|
764
|
+
break;
|
|
765
|
+
case "none":
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
if (transform) {
|
|
769
|
+
processed = transform(processed);
|
|
770
|
+
}
|
|
771
|
+
return processed;
|
|
772
|
+
};
|
|
773
|
+
const baseSchema = required ? z3.preprocess(preprocessFn, z3.string()) : z3.preprocess(preprocessFn, z3.string().nullable());
|
|
774
|
+
const schema = baseSchema.refine((val) => {
|
|
775
|
+
if (val === null) return true;
|
|
776
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
777
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
778
|
+
}
|
|
779
|
+
if (val === null) return true;
|
|
780
|
+
if (!required && val === "") return true;
|
|
781
|
+
if (whitelist && whitelist.length > 0) {
|
|
782
|
+
if (whitelist.includes(val)) {
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
if (whitelistOnly) {
|
|
786
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }]);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (regex) {
|
|
790
|
+
if (!regex.test(val)) {
|
|
791
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("customRegex"), path: [] }]);
|
|
792
|
+
}
|
|
793
|
+
} else {
|
|
794
|
+
if (!validateDateTimeFormat(val, format)) {
|
|
795
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("format", { format }), path: [] }]);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (includes && !val.includes(includes)) {
|
|
799
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
|
|
800
|
+
}
|
|
801
|
+
if (excludes) {
|
|
802
|
+
const excludeList = Array.isArray(excludes) ? excludes : [excludes];
|
|
803
|
+
for (const exclude of excludeList) {
|
|
804
|
+
if (val.includes(exclude)) {
|
|
805
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (regex) {
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
const parsed = parseDateTimeValue(val, format, timezone2);
|
|
813
|
+
if (!parsed) {
|
|
814
|
+
const pattern = DATETIME_PATTERNS[format];
|
|
815
|
+
if (!pattern.test(val.trim())) {
|
|
816
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("format", { format }), path: [] }]);
|
|
817
|
+
} else {
|
|
818
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
const hour = parsed.hour();
|
|
822
|
+
if (minHour !== void 0 && hour < minHour) {
|
|
823
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("hour", { minHour, maxHour: maxHour ?? 23 }), path: [] }]);
|
|
824
|
+
}
|
|
825
|
+
if (maxHour !== void 0 && hour > maxHour) {
|
|
826
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("hour", { minHour: minHour ?? 0, maxHour }), path: [] }]);
|
|
827
|
+
}
|
|
828
|
+
if (allowedHours && allowedHours.length > 0) {
|
|
829
|
+
if (!allowedHours.includes(hour)) {
|
|
830
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("hour", { allowedHours: allowedHours.join(", ") }), path: [] }]);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const minute = parsed.minute();
|
|
834
|
+
if (minuteStep !== void 0 && minute % minuteStep !== 0) {
|
|
835
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("minute", { minuteStep }), path: [] }]);
|
|
836
|
+
}
|
|
837
|
+
if (min) {
|
|
838
|
+
const minParsed = typeof min === "string" ? parseDateTimeValue(min, format, timezone2) : dayjs2(min);
|
|
839
|
+
if (minParsed && parsed.isBefore(minParsed)) {
|
|
840
|
+
const minFormatted = typeof min === "string" ? min : minParsed.format(format);
|
|
841
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("min", { min: minFormatted }), path: [] }]);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (max) {
|
|
845
|
+
const maxParsed = typeof max === "string" ? parseDateTimeValue(max, format, timezone2) : dayjs2(max);
|
|
846
|
+
if (maxParsed && parsed.isAfter(maxParsed)) {
|
|
847
|
+
const maxFormatted = typeof max === "string" ? max : maxParsed.format(format);
|
|
848
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("max", { max: maxFormatted }), path: [] }]);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const now = timezone2 ? dayjs2().tz(timezone2) : dayjs2();
|
|
852
|
+
if (mustBePast && !parsed.isBefore(now)) {
|
|
853
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("past"), path: [] }]);
|
|
854
|
+
}
|
|
855
|
+
if (mustBeFuture && !parsed.isAfter(now)) {
|
|
856
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("future"), path: [] }]);
|
|
857
|
+
}
|
|
858
|
+
if (mustBeToday && !parsed.isSame(now, "day")) {
|
|
859
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("today"), path: [] }]);
|
|
860
|
+
}
|
|
861
|
+
if (mustNotBeToday && parsed.isSame(now, "day")) {
|
|
862
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("notToday"), path: [] }]);
|
|
863
|
+
}
|
|
864
|
+
const dayOfWeek = parsed.day();
|
|
865
|
+
if (weekdaysOnly && (dayOfWeek === 0 || dayOfWeek === 6)) {
|
|
866
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("weekday"), path: [] }]);
|
|
867
|
+
}
|
|
868
|
+
if (weekendsOnly && dayOfWeek !== 0 && dayOfWeek !== 6) {
|
|
869
|
+
throw new z3.ZodError([{ code: "custom", message: getMessage("weekend"), path: [] }]);
|
|
870
|
+
}
|
|
871
|
+
return true;
|
|
872
|
+
});
|
|
226
873
|
return schema;
|
|
227
874
|
}
|
|
228
875
|
|
|
229
|
-
// src/common/
|
|
876
|
+
// src/validators/common/email.ts
|
|
230
877
|
import { z as z4 } from "zod";
|
|
231
|
-
function
|
|
232
|
-
const {
|
|
233
|
-
|
|
234
|
-
|
|
878
|
+
function email(options) {
|
|
879
|
+
const {
|
|
880
|
+
required = true,
|
|
881
|
+
domain,
|
|
882
|
+
domainBlacklist,
|
|
883
|
+
minLength,
|
|
884
|
+
maxLength,
|
|
885
|
+
includes,
|
|
886
|
+
excludes,
|
|
887
|
+
allowSubdomains = true,
|
|
888
|
+
businessOnly = false,
|
|
889
|
+
noDisposable = false,
|
|
890
|
+
lowercase = true,
|
|
891
|
+
transform,
|
|
892
|
+
defaultValue,
|
|
893
|
+
i18n
|
|
894
|
+
} = options ?? {};
|
|
895
|
+
const getMessage = (key, params) => {
|
|
896
|
+
if (i18n) {
|
|
897
|
+
const currentLocale2 = getLocale();
|
|
898
|
+
const customMessages = i18n[currentLocale2];
|
|
899
|
+
if (customMessages && customMessages[key]) {
|
|
900
|
+
const template = customMessages[key];
|
|
901
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
return t(`common.email.${key}`, params);
|
|
905
|
+
};
|
|
906
|
+
const disposableDomains = ["10minutemail.com", "tempmail.org", "guerrillamail.com", "mailinator.com", "yopmail.com", "temp-mail.org", "throwaway.email", "getnada.com", "maildrop.cc"];
|
|
907
|
+
const freeEmailDomains = ["gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "icloud.com", "aol.com", "protonmail.com", "zoho.com"];
|
|
908
|
+
const actualDefaultValue = defaultValue ?? null;
|
|
909
|
+
const baseSchema = z4.preprocess(
|
|
910
|
+
(val) => {
|
|
911
|
+
if (val === "" || val === null || val === void 0) {
|
|
912
|
+
return actualDefaultValue;
|
|
913
|
+
}
|
|
914
|
+
let processed = String(val).trim();
|
|
915
|
+
if (lowercase) {
|
|
916
|
+
processed = processed.toLowerCase();
|
|
917
|
+
}
|
|
918
|
+
if (transform) {
|
|
919
|
+
processed = transform(processed);
|
|
920
|
+
}
|
|
921
|
+
return processed;
|
|
922
|
+
},
|
|
923
|
+
z4.union([z4.string().email(), z4.null()])
|
|
924
|
+
);
|
|
925
|
+
const schema = baseSchema.refine((val) => {
|
|
926
|
+
if (required && val === null) {
|
|
927
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
928
|
+
}
|
|
929
|
+
if (val === null) return true;
|
|
930
|
+
if (typeof val !== "string") {
|
|
931
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
932
|
+
}
|
|
933
|
+
if (minLength !== void 0 && val.length < minLength) {
|
|
934
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("minLength", { minLength }), path: [] }]);
|
|
935
|
+
}
|
|
936
|
+
if (maxLength !== void 0 && val.length > maxLength) {
|
|
937
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("maxLength", { maxLength }), path: [] }]);
|
|
938
|
+
}
|
|
939
|
+
if (includes !== void 0 && !val.includes(includes)) {
|
|
940
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
|
|
941
|
+
}
|
|
942
|
+
if (excludes !== void 0) {
|
|
943
|
+
const excludeList = Array.isArray(excludes) ? excludes : [excludes];
|
|
944
|
+
for (const exclude of excludeList) {
|
|
945
|
+
if (val.includes(exclude)) {
|
|
946
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("includes", { includes: exclude }), path: [] }]);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
const emailDomain = val.split("@")[1]?.toLowerCase();
|
|
951
|
+
if (!emailDomain) {
|
|
952
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
953
|
+
}
|
|
954
|
+
if (businessOnly) {
|
|
955
|
+
const isFreeProvider = freeEmailDomains.some((freeDomain) => {
|
|
956
|
+
if (allowSubdomains) {
|
|
957
|
+
return emailDomain === freeDomain || emailDomain.endsWith("." + freeDomain);
|
|
958
|
+
}
|
|
959
|
+
return emailDomain === freeDomain;
|
|
960
|
+
});
|
|
961
|
+
if (isFreeProvider) {
|
|
962
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("businessOnly"), path: [] }]);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (domainBlacklist && domainBlacklist.length > 0) {
|
|
966
|
+
const isBlacklisted = domainBlacklist.some((blacklistedDomain) => {
|
|
967
|
+
const lowerDomain = blacklistedDomain.toLowerCase();
|
|
968
|
+
if (allowSubdomains) {
|
|
969
|
+
return emailDomain === lowerDomain || emailDomain.endsWith("." + lowerDomain);
|
|
970
|
+
}
|
|
971
|
+
return emailDomain === lowerDomain;
|
|
972
|
+
});
|
|
973
|
+
if (isBlacklisted) {
|
|
974
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("domainBlacklist", { domain: emailDomain }), path: [] }]);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (domain !== void 0) {
|
|
978
|
+
const allowedDomains = Array.isArray(domain) ? domain : [domain];
|
|
979
|
+
const isAllowed = allowedDomains.some((allowedDomain) => {
|
|
980
|
+
const lowerDomain = allowedDomain.toLowerCase();
|
|
981
|
+
if (allowSubdomains) {
|
|
982
|
+
return emailDomain === lowerDomain || emailDomain.endsWith("." + lowerDomain);
|
|
983
|
+
}
|
|
984
|
+
return emailDomain === lowerDomain;
|
|
985
|
+
});
|
|
986
|
+
if (!isAllowed) {
|
|
987
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("domain", { domain: Array.isArray(domain) ? domain.join(", ") : domain }), path: [] }]);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
if (noDisposable) {
|
|
991
|
+
const isDisposable = disposableDomains.some((disposableDomain) => {
|
|
992
|
+
if (allowSubdomains) {
|
|
993
|
+
return emailDomain === disposableDomain || emailDomain.endsWith("." + disposableDomain);
|
|
994
|
+
}
|
|
995
|
+
return emailDomain === disposableDomain;
|
|
996
|
+
});
|
|
997
|
+
if (isDisposable) {
|
|
998
|
+
throw new z4.ZodError([{ code: "custom", message: getMessage("noDisposable"), path: [] }]);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return true;
|
|
1002
|
+
});
|
|
235
1003
|
return schema;
|
|
236
1004
|
}
|
|
237
1005
|
|
|
238
|
-
// src/common/
|
|
1006
|
+
// src/validators/common/file.ts
|
|
239
1007
|
import { z as z5 } from "zod";
|
|
240
|
-
function
|
|
241
|
-
const {
|
|
242
|
-
|
|
1008
|
+
function file(options) {
|
|
1009
|
+
const {
|
|
1010
|
+
required = true,
|
|
1011
|
+
maxSize,
|
|
1012
|
+
minSize,
|
|
1013
|
+
type,
|
|
1014
|
+
typeBlacklist,
|
|
1015
|
+
extension,
|
|
1016
|
+
extensionBlacklist,
|
|
1017
|
+
namePattern,
|
|
1018
|
+
nameBlacklist,
|
|
1019
|
+
imageOnly = false,
|
|
1020
|
+
documentOnly = false,
|
|
1021
|
+
videoOnly = false,
|
|
1022
|
+
audioOnly = false,
|
|
1023
|
+
archiveOnly = false,
|
|
1024
|
+
caseSensitive = false,
|
|
1025
|
+
transform,
|
|
1026
|
+
defaultValue,
|
|
1027
|
+
i18n
|
|
1028
|
+
} = options ?? {};
|
|
1029
|
+
const getMessage = (key, params) => {
|
|
1030
|
+
if (i18n) {
|
|
1031
|
+
const currentLocale2 = getLocale();
|
|
1032
|
+
const customMessages = i18n[currentLocale2];
|
|
1033
|
+
if (customMessages && customMessages[key]) {
|
|
1034
|
+
const template = customMessages[key];
|
|
1035
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return t(`common.file.${key}`, params);
|
|
1039
|
+
};
|
|
1040
|
+
const imageTypes = ["image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp", "image/svg+xml", "image/bmp", "image/tiff"];
|
|
1041
|
+
const documentTypes = [
|
|
1042
|
+
"application/pdf",
|
|
1043
|
+
"application/msword",
|
|
1044
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1045
|
+
"application/vnd.ms-excel",
|
|
1046
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1047
|
+
"application/vnd.ms-powerpoint",
|
|
1048
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
1049
|
+
"text/plain",
|
|
1050
|
+
"text/csv"
|
|
1051
|
+
];
|
|
1052
|
+
const videoTypes = ["video/mp4", "video/mpeg", "video/quicktime", "video/x-msvideo", "video/x-ms-wmv", "video/webm", "video/ogg"];
|
|
1053
|
+
const audioTypes = ["audio/mpeg", "audio/wav", "audio/ogg", "audio/aac", "audio/webm", "audio/mp3", "audio/x-wav"];
|
|
1054
|
+
const archiveTypes = ["application/zip", "application/x-rar-compressed", "application/x-7z-compressed", "application/x-tar", "application/gzip"];
|
|
1055
|
+
const actualDefaultValue = defaultValue ?? null;
|
|
1056
|
+
const baseSchema = z5.preprocess(
|
|
1057
|
+
(val) => {
|
|
1058
|
+
if (val === "" || val === null || val === void 0) {
|
|
1059
|
+
return actualDefaultValue;
|
|
1060
|
+
}
|
|
1061
|
+
if (!(val instanceof File)) {
|
|
1062
|
+
return val;
|
|
1063
|
+
}
|
|
1064
|
+
let processed = val;
|
|
1065
|
+
if (transform) {
|
|
1066
|
+
processed = transform(processed);
|
|
1067
|
+
}
|
|
1068
|
+
return processed;
|
|
1069
|
+
},
|
|
1070
|
+
z5.union([z5.instanceof(File).refine(() => true, { message: getMessage("invalid") }), z5.null()])
|
|
1071
|
+
);
|
|
1072
|
+
const schema = baseSchema.refine((val) => required === false || val !== null, {
|
|
1073
|
+
message: getMessage("required")
|
|
1074
|
+
}).refine((val) => val === null || val instanceof File, {
|
|
1075
|
+
message: getMessage("invalid")
|
|
1076
|
+
}).refine((val) => val === null || minSize === void 0 || val.size >= minSize, {
|
|
1077
|
+
message: getMessage("minSize", { minSize: formatFileSize(minSize || 0) })
|
|
1078
|
+
}).refine((val) => val === null || maxSize === void 0 || val.size <= maxSize, {
|
|
1079
|
+
message: getMessage("maxSize", { maxSize: formatFileSize(maxSize || 0) })
|
|
1080
|
+
}).refine((val) => val === null || !imageOnly || imageTypes.includes(val.type), {
|
|
1081
|
+
message: getMessage("imageOnly")
|
|
1082
|
+
}).refine((val) => val === null || !documentOnly || documentTypes.includes(val.type), {
|
|
1083
|
+
message: getMessage("documentOnly")
|
|
1084
|
+
}).refine((val) => val === null || !videoOnly || videoTypes.includes(val.type), {
|
|
1085
|
+
message: getMessage("videoOnly")
|
|
1086
|
+
}).refine((val) => val === null || !audioOnly || audioTypes.includes(val.type), {
|
|
1087
|
+
message: getMessage("audioOnly")
|
|
1088
|
+
}).refine((val) => val === null || !archiveOnly || archiveTypes.includes(val.type), {
|
|
1089
|
+
message: getMessage("archiveOnly")
|
|
1090
|
+
}).refine(
|
|
1091
|
+
(val) => {
|
|
1092
|
+
if (val === null || !typeBlacklist || typeBlacklist.length === 0) return true;
|
|
1093
|
+
return !typeBlacklist.includes(val.type);
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
message: getMessage("type", { type: typeBlacklist?.join(", ") || "" })
|
|
1097
|
+
}
|
|
1098
|
+
).refine(
|
|
1099
|
+
(val) => {
|
|
1100
|
+
if (val === null || type === void 0) return true;
|
|
1101
|
+
const allowedTypes = Array.isArray(type) ? type : [type];
|
|
1102
|
+
return allowedTypes.includes(val.type);
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
message: getMessage("type", { type: Array.isArray(type) ? type.join(", ") : type || "" })
|
|
1106
|
+
}
|
|
1107
|
+
).refine(
|
|
1108
|
+
(val) => {
|
|
1109
|
+
if (val === null || extensionBlacklist === void 0 || extensionBlacklist.length === 0) return true;
|
|
1110
|
+
const fileExtension = getFileExtension(val.name, caseSensitive);
|
|
1111
|
+
const normalizedBlacklist = extensionBlacklist.map((ext) => normalizeExtension(ext, caseSensitive));
|
|
1112
|
+
return !normalizedBlacklist.includes(fileExtension);
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
message: getMessage("extensionBlacklist", { extension: extensionBlacklist?.join(", ") || "" })
|
|
1116
|
+
}
|
|
1117
|
+
).refine(
|
|
1118
|
+
(val) => {
|
|
1119
|
+
if (val === null || extension === void 0) return true;
|
|
1120
|
+
const fileName = val.name;
|
|
1121
|
+
const fileExtension = getFileExtension(fileName, caseSensitive);
|
|
1122
|
+
const allowedExtensions = Array.isArray(extension) ? extension : [extension];
|
|
1123
|
+
const normalizedExtensions = allowedExtensions.map((ext) => normalizeExtension(ext, caseSensitive));
|
|
1124
|
+
return normalizedExtensions.includes(fileExtension);
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
message: getMessage("extension", { extension: Array.isArray(extension) ? extension.join(", ") : extension || "" })
|
|
1128
|
+
}
|
|
1129
|
+
).refine(
|
|
1130
|
+
(val) => {
|
|
1131
|
+
if (val === null || namePattern === void 0) return true;
|
|
1132
|
+
const pattern = typeof namePattern === "string" ? new RegExp(namePattern) : namePattern;
|
|
1133
|
+
return pattern.test(val.name);
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
message: getMessage("name", { pattern: namePattern?.toString() || "" })
|
|
1137
|
+
}
|
|
1138
|
+
).refine(
|
|
243
1139
|
(val) => {
|
|
244
|
-
if (val ===
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (issue.code === "invalid_type") return t("common.number.integer");
|
|
251
|
-
return t("common.number.required");
|
|
1140
|
+
if (val === null || nameBlacklist === void 0) return true;
|
|
1141
|
+
const blacklistPatterns = Array.isArray(nameBlacklist) ? nameBlacklist : [nameBlacklist];
|
|
1142
|
+
for (const blacklistPattern of blacklistPatterns) {
|
|
1143
|
+
const pattern = typeof blacklistPattern === "string" ? new RegExp(blacklistPattern) : blacklistPattern;
|
|
1144
|
+
if (pattern.test(val.name)) {
|
|
1145
|
+
return false;
|
|
252
1146
|
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
1147
|
+
}
|
|
1148
|
+
return true;
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
message: getMessage("nameBlacklist", { pattern: "" })
|
|
1152
|
+
}
|
|
1153
|
+
);
|
|
257
1154
|
return schema;
|
|
258
1155
|
}
|
|
1156
|
+
function getFileExtension(fileName, caseSensitive) {
|
|
1157
|
+
const lastDotIndex = fileName.lastIndexOf(".");
|
|
1158
|
+
if (lastDotIndex === -1) return "";
|
|
1159
|
+
const extension = fileName.substring(lastDotIndex);
|
|
1160
|
+
return caseSensitive ? extension : extension.toLowerCase();
|
|
1161
|
+
}
|
|
1162
|
+
function normalizeExtension(extension, caseSensitive) {
|
|
1163
|
+
const normalized = extension.startsWith(".") ? extension : `.${extension}`;
|
|
1164
|
+
return caseSensitive ? normalized : normalized.toLowerCase();
|
|
1165
|
+
}
|
|
1166
|
+
function formatFileSize(bytes) {
|
|
1167
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
1168
|
+
let size = bytes;
|
|
1169
|
+
let unitIndex = 0;
|
|
1170
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
1171
|
+
size /= 1024;
|
|
1172
|
+
unitIndex++;
|
|
1173
|
+
}
|
|
1174
|
+
return `${Math.round(size * 100) / 100} ${units[unitIndex]}`;
|
|
1175
|
+
}
|
|
259
1176
|
|
|
260
|
-
// src/common/
|
|
1177
|
+
// src/validators/common/id.ts
|
|
261
1178
|
import { z as z6 } from "zod";
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
1179
|
+
var ID_PATTERNS = {
|
|
1180
|
+
numeric: /^\d+$/,
|
|
1181
|
+
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,
|
|
1182
|
+
objectId: /^[0-9a-f]{24}$/i,
|
|
1183
|
+
nanoid: /^[A-Za-z0-9_-]{21}$/,
|
|
1184
|
+
snowflake: /^\d{19}$/,
|
|
1185
|
+
cuid: /^c[a-z0-9]{24}$/,
|
|
1186
|
+
ulid: /^[0-9A-HJKMNP-TV-Z]{26}$/,
|
|
1187
|
+
shortid: /^[A-Za-z0-9_-]{7,14}$/
|
|
1188
|
+
};
|
|
1189
|
+
var detectIdType = (value) => {
|
|
1190
|
+
const orderedTypes = [
|
|
1191
|
+
["uuid", ID_PATTERNS.uuid],
|
|
1192
|
+
["objectId", ID_PATTERNS.objectId],
|
|
1193
|
+
["snowflake", ID_PATTERNS.snowflake],
|
|
1194
|
+
["cuid", ID_PATTERNS.cuid],
|
|
1195
|
+
["ulid", ID_PATTERNS.ulid],
|
|
1196
|
+
["nanoid", ID_PATTERNS.nanoid],
|
|
1197
|
+
["numeric", ID_PATTERNS.numeric],
|
|
1198
|
+
["shortid", ID_PATTERNS.shortid]
|
|
1199
|
+
// 放最後,因為最通用
|
|
1200
|
+
];
|
|
1201
|
+
for (const [type, pattern] of orderedTypes) {
|
|
1202
|
+
if (pattern.test(value)) {
|
|
1203
|
+
return type;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return null;
|
|
1207
|
+
};
|
|
1208
|
+
var validateIdType = (value, type) => {
|
|
1209
|
+
if (type === "auto") {
|
|
1210
|
+
return detectIdType(value) !== null;
|
|
1211
|
+
}
|
|
1212
|
+
const pattern = ID_PATTERNS[type];
|
|
1213
|
+
return pattern ? pattern.test(value) : false;
|
|
1214
|
+
};
|
|
1215
|
+
function id(options) {
|
|
1216
|
+
const {
|
|
1217
|
+
required = true,
|
|
1218
|
+
type = "auto",
|
|
1219
|
+
minLength,
|
|
1220
|
+
maxLength,
|
|
1221
|
+
allowedTypes,
|
|
1222
|
+
customRegex,
|
|
1223
|
+
includes,
|
|
1224
|
+
excludes,
|
|
1225
|
+
startsWith,
|
|
1226
|
+
endsWith,
|
|
1227
|
+
caseSensitive = true,
|
|
1228
|
+
transform,
|
|
1229
|
+
defaultValue,
|
|
1230
|
+
i18n
|
|
1231
|
+
} = options ?? {};
|
|
1232
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
1233
|
+
const getMessage = (key, params) => {
|
|
1234
|
+
if (i18n) {
|
|
1235
|
+
const currentLocale2 = getLocale();
|
|
1236
|
+
const customMessages = i18n[currentLocale2];
|
|
1237
|
+
if (customMessages && customMessages[key]) {
|
|
1238
|
+
const template = customMessages[key];
|
|
1239
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
return t(`common.id.${key}`, params);
|
|
1243
|
+
};
|
|
1244
|
+
const preprocessFn = (val) => {
|
|
1245
|
+
if (val === "" || val === null || val === void 0) {
|
|
1246
|
+
return actualDefaultValue;
|
|
1247
|
+
}
|
|
1248
|
+
let processed = String(val);
|
|
1249
|
+
if (transform) {
|
|
1250
|
+
processed = transform(processed);
|
|
1251
|
+
}
|
|
1252
|
+
return processed;
|
|
1253
|
+
};
|
|
1254
|
+
const baseSchema = required ? z6.preprocess(preprocessFn, z6.string()) : z6.preprocess(preprocessFn, z6.string().nullable());
|
|
1255
|
+
const schema = baseSchema.refine((val) => {
|
|
1256
|
+
if (val === null) return true;
|
|
1257
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
1258
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
1259
|
+
}
|
|
1260
|
+
const comparisonVal = !caseSensitive ? val.toLowerCase() : val;
|
|
1261
|
+
if (val !== null && minLength !== void 0 && val.length < minLength) {
|
|
1262
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("minLength", { minLength }), path: [] }]);
|
|
1263
|
+
}
|
|
1264
|
+
if (val !== null && maxLength !== void 0 && val.length > maxLength) {
|
|
1265
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("maxLength", { maxLength }), path: [] }]);
|
|
1266
|
+
}
|
|
1267
|
+
const hasContentValidations = customRegex !== void 0 || startsWith !== void 0 || endsWith !== void 0 || includes !== void 0 || excludes !== void 0;
|
|
1268
|
+
if (val !== null && customRegex !== void 0) {
|
|
1269
|
+
if (!customRegex.test(val)) {
|
|
1270
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("customFormat"), path: [] }]);
|
|
1271
|
+
}
|
|
1272
|
+
} else if (val !== null && !hasContentValidations) {
|
|
1273
|
+
let isValidId;
|
|
1274
|
+
if (allowedTypes && allowedTypes.length > 0) {
|
|
1275
|
+
isValidId = allowedTypes.some((allowedType) => validateIdType(val, allowedType));
|
|
1276
|
+
if (!isValidId) {
|
|
1277
|
+
const typeNames = allowedTypes.join(", ");
|
|
1278
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("invalid") + ` (allowed types: ${typeNames})`, path: [] }]);
|
|
1279
|
+
}
|
|
1280
|
+
} else if (type !== "auto") {
|
|
1281
|
+
isValidId = validateIdType(val, type);
|
|
1282
|
+
if (!isValidId) {
|
|
1283
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage(type) || getMessage("invalid"), path: [] }]);
|
|
1284
|
+
}
|
|
1285
|
+
} else {
|
|
1286
|
+
isValidId = detectIdType(val) !== null;
|
|
1287
|
+
if (!isValidId) {
|
|
1288
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
} else if (val !== null && hasContentValidations && type !== "auto" && !customRegex) {
|
|
1292
|
+
if (allowedTypes && allowedTypes.length > 0) {
|
|
1293
|
+
const isValidType = allowedTypes.some((allowedType) => validateIdType(val, allowedType));
|
|
1294
|
+
if (!isValidType) {
|
|
1295
|
+
const typeNames = allowedTypes.join(", ");
|
|
1296
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("invalid") + ` (allowed types: ${typeNames})`, path: [] }]);
|
|
1297
|
+
}
|
|
1298
|
+
} else {
|
|
1299
|
+
if (!validateIdType(val, type)) {
|
|
1300
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage(type) || getMessage("invalid"), path: [] }]);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
const searchStartsWith = !caseSensitive && startsWith ? startsWith.toLowerCase() : startsWith;
|
|
1305
|
+
const searchEndsWith = !caseSensitive && endsWith ? endsWith.toLowerCase() : endsWith;
|
|
1306
|
+
const searchIncludes = !caseSensitive && includes ? includes.toLowerCase() : includes;
|
|
1307
|
+
if (val !== null && startsWith !== void 0 && !comparisonVal.startsWith(searchStartsWith)) {
|
|
1308
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("startsWith", { startsWith }), path: [] }]);
|
|
1309
|
+
}
|
|
1310
|
+
if (val !== null && endsWith !== void 0 && !comparisonVal.endsWith(searchEndsWith)) {
|
|
1311
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("endsWith", { endsWith }), path: [] }]);
|
|
1312
|
+
}
|
|
1313
|
+
if (val !== null && includes !== void 0 && !comparisonVal.includes(searchIncludes)) {
|
|
1314
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
|
|
1315
|
+
}
|
|
1316
|
+
if (val !== null && excludes !== void 0) {
|
|
1317
|
+
const excludeList = Array.isArray(excludes) ? excludes : [excludes];
|
|
1318
|
+
for (const exclude of excludeList) {
|
|
1319
|
+
const searchExclude = !caseSensitive ? exclude.toLowerCase() : exclude;
|
|
1320
|
+
if (comparisonVal.includes(searchExclude)) {
|
|
1321
|
+
throw new z6.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
return true;
|
|
1326
|
+
}).transform((val) => {
|
|
1327
|
+
if (val === null) return val;
|
|
1328
|
+
const shouldPreserveCase = type === "uuid" || type === "objectId";
|
|
1329
|
+
if (!caseSensitive && !shouldPreserveCase) {
|
|
1330
|
+
return val.toLowerCase();
|
|
1331
|
+
}
|
|
1332
|
+
return val;
|
|
1333
|
+
});
|
|
266
1334
|
return schema;
|
|
267
1335
|
}
|
|
268
1336
|
|
|
269
|
-
// src/common/
|
|
1337
|
+
// src/validators/common/number.ts
|
|
270
1338
|
import { z as z7 } from "zod";
|
|
271
|
-
function
|
|
272
|
-
const {
|
|
1339
|
+
function number(options) {
|
|
1340
|
+
const {
|
|
1341
|
+
required = true,
|
|
1342
|
+
min,
|
|
1343
|
+
max,
|
|
1344
|
+
defaultValue,
|
|
1345
|
+
type = "both",
|
|
1346
|
+
positive,
|
|
1347
|
+
negative,
|
|
1348
|
+
nonNegative,
|
|
1349
|
+
nonPositive,
|
|
1350
|
+
multipleOf,
|
|
1351
|
+
precision,
|
|
1352
|
+
finite = true,
|
|
1353
|
+
transform,
|
|
1354
|
+
parseCommas = false,
|
|
1355
|
+
i18n
|
|
1356
|
+
} = options ?? {};
|
|
1357
|
+
const getMessage = (key, params) => {
|
|
1358
|
+
if (i18n) {
|
|
1359
|
+
const currentLocale2 = getLocale();
|
|
1360
|
+
const customMessages = i18n[currentLocale2];
|
|
1361
|
+
if (customMessages && customMessages[key]) {
|
|
1362
|
+
const template = customMessages[key];
|
|
1363
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return t(`common.number.${key}`, params);
|
|
1367
|
+
};
|
|
1368
|
+
const actualDefaultValue = defaultValue ?? null;
|
|
273
1369
|
const schema = z7.preprocess(
|
|
274
1370
|
(val) => {
|
|
275
|
-
if (val === "" || val === void 0 || val === null)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
1371
|
+
if (val === "" || val === void 0 || val === null) {
|
|
1372
|
+
return actualDefaultValue;
|
|
1373
|
+
}
|
|
1374
|
+
if (typeof val === "string") {
|
|
1375
|
+
let processedVal = val.trim();
|
|
1376
|
+
if (parseCommas) {
|
|
1377
|
+
processedVal = processedVal.replace(/,/g, "");
|
|
1378
|
+
}
|
|
1379
|
+
const parsed = Number(processedVal);
|
|
1380
|
+
if (isNaN(parsed)) {
|
|
1381
|
+
return parsed;
|
|
283
1382
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
1383
|
+
if (transform) {
|
|
1384
|
+
return transform(parsed);
|
|
1385
|
+
}
|
|
1386
|
+
return parsed;
|
|
1387
|
+
}
|
|
1388
|
+
if (typeof val === "number") {
|
|
1389
|
+
if (transform && Number.isFinite(val)) {
|
|
1390
|
+
return transform(val);
|
|
1391
|
+
}
|
|
1392
|
+
return val;
|
|
1393
|
+
}
|
|
1394
|
+
return val;
|
|
1395
|
+
},
|
|
1396
|
+
z7.union([z7.number(), z7.null(), z7.nan(), z7.custom((val) => val === Infinity || val === -Infinity)])
|
|
1397
|
+
).refine((val) => {
|
|
1398
|
+
if (required && val === null) {
|
|
1399
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
1400
|
+
}
|
|
1401
|
+
if (val === null) return true;
|
|
1402
|
+
if (typeof val === "number" && isNaN(val)) {
|
|
1403
|
+
if (type === "integer") {
|
|
1404
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("integer"), path: [] }]);
|
|
1405
|
+
} else if (type === "float") {
|
|
1406
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("float"), path: [] }]);
|
|
1407
|
+
} else {
|
|
1408
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
if (typeof val !== "number") {
|
|
1412
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
1413
|
+
}
|
|
1414
|
+
if (finite && !Number.isFinite(val)) {
|
|
1415
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("finite"), path: [] }]);
|
|
1416
|
+
}
|
|
1417
|
+
if (type === "integer" && !Number.isInteger(val)) {
|
|
1418
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("integer"), path: [] }]);
|
|
1419
|
+
}
|
|
1420
|
+
if (type === "float" && Number.isInteger(val)) {
|
|
1421
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("float"), path: [] }]);
|
|
1422
|
+
}
|
|
1423
|
+
if (positive && val <= 0) {
|
|
1424
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("positive"), path: [] }]);
|
|
1425
|
+
}
|
|
1426
|
+
if (negative && val >= 0) {
|
|
1427
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("negative"), path: [] }]);
|
|
1428
|
+
}
|
|
1429
|
+
if (nonNegative && val < 0) {
|
|
1430
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("nonNegative"), path: [] }]);
|
|
1431
|
+
}
|
|
1432
|
+
if (nonPositive && val > 0) {
|
|
1433
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("nonPositive"), path: [] }]);
|
|
1434
|
+
}
|
|
1435
|
+
if (min !== void 0 && val < min) {
|
|
1436
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
|
|
1437
|
+
}
|
|
1438
|
+
if (max !== void 0 && val > max) {
|
|
1439
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
|
|
1440
|
+
}
|
|
1441
|
+
if (multipleOf !== void 0 && val % multipleOf !== 0) {
|
|
1442
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("multipleOf", { multipleOf }), path: [] }]);
|
|
1443
|
+
}
|
|
1444
|
+
if (precision !== void 0) {
|
|
1445
|
+
const decimalPlaces = (val.toString().split(".")[1] || "").length;
|
|
1446
|
+
if (decimalPlaces > precision) {
|
|
1447
|
+
throw new z7.ZodError([{ code: "custom", message: getMessage("precision", { precision }), path: [] }]);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
return true;
|
|
1451
|
+
});
|
|
1452
|
+
return schema;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// src/validators/common/password.ts
|
|
1456
|
+
import { z as z8 } from "zod";
|
|
1457
|
+
var COMMON_PASSWORDS = [
|
|
1458
|
+
"password",
|
|
1459
|
+
"123456",
|
|
1460
|
+
"123456789",
|
|
1461
|
+
"12345678",
|
|
1462
|
+
"12345",
|
|
1463
|
+
"1234567",
|
|
1464
|
+
"admin",
|
|
1465
|
+
"qwerty",
|
|
1466
|
+
"abc123",
|
|
1467
|
+
"password123",
|
|
1468
|
+
"letmein",
|
|
1469
|
+
"welcome",
|
|
1470
|
+
"monkey",
|
|
1471
|
+
"dragon",
|
|
1472
|
+
"sunshine",
|
|
1473
|
+
"princess"
|
|
1474
|
+
];
|
|
1475
|
+
var calculatePasswordStrength = (password2) => {
|
|
1476
|
+
let score = 0;
|
|
1477
|
+
if (password2.length >= 8) score += 1;
|
|
1478
|
+
if (password2.length >= 12) score += 1;
|
|
1479
|
+
if (password2.length >= 16) score += 1;
|
|
1480
|
+
if (/[a-z]/.test(password2)) score += 1;
|
|
1481
|
+
if (/[A-Z]/.test(password2)) score += 1;
|
|
1482
|
+
if (/[0-9]/.test(password2)) score += 1;
|
|
1483
|
+
if (/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password2)) score += 1;
|
|
1484
|
+
if (/(.)\1{2,}/.test(password2)) score -= 1;
|
|
1485
|
+
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;
|
|
1486
|
+
if (score <= 2) return "weak";
|
|
1487
|
+
if (score <= 4) return "medium";
|
|
1488
|
+
if (score <= 6) return "strong";
|
|
1489
|
+
return "very-strong";
|
|
1490
|
+
};
|
|
1491
|
+
function password(options) {
|
|
1492
|
+
const {
|
|
1493
|
+
required = true,
|
|
1494
|
+
min,
|
|
1495
|
+
max,
|
|
1496
|
+
uppercase,
|
|
1497
|
+
lowercase,
|
|
1498
|
+
digits,
|
|
1499
|
+
special,
|
|
1500
|
+
noRepeating,
|
|
1501
|
+
noSequential,
|
|
1502
|
+
noCommonWords,
|
|
1503
|
+
minStrength,
|
|
1504
|
+
excludes,
|
|
1505
|
+
includes,
|
|
1506
|
+
regex,
|
|
1507
|
+
transform,
|
|
1508
|
+
defaultValue,
|
|
1509
|
+
i18n
|
|
1510
|
+
} = options ?? {};
|
|
1511
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
1512
|
+
const getMessage = (key, params) => {
|
|
1513
|
+
if (i18n) {
|
|
1514
|
+
const currentLocale2 = getLocale();
|
|
1515
|
+
const customMessages = i18n[currentLocale2];
|
|
1516
|
+
if (customMessages && customMessages[key]) {
|
|
1517
|
+
const template = customMessages[key];
|
|
1518
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return t(`common.password.${key}`, params);
|
|
1522
|
+
};
|
|
1523
|
+
const preprocessFn = (val) => {
|
|
1524
|
+
if (val === "" || val === null || val === void 0) {
|
|
1525
|
+
return actualDefaultValue;
|
|
1526
|
+
}
|
|
1527
|
+
let processed = String(val);
|
|
1528
|
+
if (transform) {
|
|
1529
|
+
processed = transform(processed);
|
|
1530
|
+
}
|
|
1531
|
+
return processed;
|
|
1532
|
+
};
|
|
1533
|
+
const baseSchema = required ? z8.preprocess(preprocessFn, z8.string()) : z8.preprocess(preprocessFn, z8.string().nullable());
|
|
1534
|
+
const schema = baseSchema.refine((val) => {
|
|
1535
|
+
if (val === null) return true;
|
|
1536
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
1537
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
1538
|
+
}
|
|
1539
|
+
if (val !== null && min !== void 0 && val.length < min) {
|
|
1540
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
|
|
1541
|
+
}
|
|
1542
|
+
if (val !== null && max !== void 0 && val.length > max) {
|
|
1543
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
|
|
1544
|
+
}
|
|
1545
|
+
if (val !== null && uppercase && !/[A-Z]/.test(val)) {
|
|
1546
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("uppercase"), path: [] }]);
|
|
1547
|
+
}
|
|
1548
|
+
if (val !== null && lowercase && !/[a-z]/.test(val)) {
|
|
1549
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("lowercase"), path: [] }]);
|
|
1550
|
+
}
|
|
1551
|
+
if (val !== null && digits && !/[0-9]/.test(val)) {
|
|
1552
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("digits"), path: [] }]);
|
|
1553
|
+
}
|
|
1554
|
+
if (val !== null && special && !/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(val)) {
|
|
1555
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("special"), path: [] }]);
|
|
1556
|
+
}
|
|
1557
|
+
if (val !== null && noRepeating && /(.)\1{2,}/.test(val)) {
|
|
1558
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("noRepeating"), path: [] }]);
|
|
1559
|
+
}
|
|
1560
|
+
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)) {
|
|
1561
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("noSequential"), path: [] }]);
|
|
1562
|
+
}
|
|
1563
|
+
if (val !== null && noCommonWords && COMMON_PASSWORDS.some((common) => val.toLowerCase().includes(common.toLowerCase()))) {
|
|
1564
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("noCommonWords"), path: [] }]);
|
|
1565
|
+
}
|
|
1566
|
+
if (val !== null && minStrength) {
|
|
1567
|
+
const strength = calculatePasswordStrength(val);
|
|
1568
|
+
const strengthLevels = ["weak", "medium", "strong", "very-strong"];
|
|
1569
|
+
const currentLevel = strengthLevels.indexOf(strength);
|
|
1570
|
+
const requiredLevel = strengthLevels.indexOf(minStrength);
|
|
1571
|
+
if (currentLevel < requiredLevel) {
|
|
1572
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("minStrength", { minStrength }), path: [] }]);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
if (val !== null && includes !== void 0 && !val.includes(includes)) {
|
|
1576
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
|
|
1577
|
+
}
|
|
1578
|
+
if (val !== null && excludes !== void 0) {
|
|
1579
|
+
const excludeList = Array.isArray(excludes) ? excludes : [excludes];
|
|
1580
|
+
for (const exclude of excludeList) {
|
|
1581
|
+
if (val.includes(exclude)) {
|
|
1582
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
if (val !== null && regex !== void 0 && !regex.test(val)) {
|
|
1587
|
+
throw new z8.ZodError([{ code: "custom", message: getMessage("invalid", { regex }), path: [] }]);
|
|
1588
|
+
}
|
|
1589
|
+
return true;
|
|
1590
|
+
});
|
|
1591
|
+
return schema;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// src/validators/common/text.ts
|
|
1595
|
+
import { z as z9 } from "zod";
|
|
1596
|
+
function text(options) {
|
|
1597
|
+
const { required = true, minLength, maxLength, startsWith, endsWith, includes, excludes, regex, trimMode = "trim", casing = "none", transform, notEmpty, defaultValue, i18n } = options ?? {};
|
|
1598
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
1599
|
+
const getMessage = (key, params) => {
|
|
1600
|
+
if (i18n) {
|
|
1601
|
+
const currentLocale2 = getLocale();
|
|
1602
|
+
const customMessages = i18n[currentLocale2];
|
|
1603
|
+
if (customMessages && customMessages[key]) {
|
|
1604
|
+
const template = customMessages[key];
|
|
1605
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return t(`common.text.${key}`, params);
|
|
1609
|
+
};
|
|
1610
|
+
const applyTrim = (str) => {
|
|
1611
|
+
switch (trimMode) {
|
|
1612
|
+
case "trimStart":
|
|
1613
|
+
return str.trimStart();
|
|
1614
|
+
case "trimEnd":
|
|
1615
|
+
return str.trimEnd();
|
|
1616
|
+
case "none":
|
|
1617
|
+
return str;
|
|
1618
|
+
default:
|
|
1619
|
+
return str.trim();
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
const applyCasing = (str) => {
|
|
1623
|
+
switch (casing) {
|
|
1624
|
+
case "upper":
|
|
1625
|
+
return str.toUpperCase();
|
|
1626
|
+
case "lower":
|
|
1627
|
+
return str.toLowerCase();
|
|
1628
|
+
case "title":
|
|
1629
|
+
return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase());
|
|
1630
|
+
default:
|
|
1631
|
+
return str;
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
const preprocessFn = (val) => {
|
|
1635
|
+
if (val === "" || val === null || val === void 0) {
|
|
1636
|
+
return actualDefaultValue;
|
|
1637
|
+
}
|
|
1638
|
+
let processed = String(val);
|
|
1639
|
+
processed = applyTrim(processed);
|
|
1640
|
+
processed = applyCasing(processed);
|
|
1641
|
+
if (transform) {
|
|
1642
|
+
processed = transform(processed);
|
|
1643
|
+
}
|
|
1644
|
+
return processed;
|
|
1645
|
+
};
|
|
1646
|
+
const baseSchema = required ? z9.preprocess(preprocessFn, z9.string()) : z9.preprocess(preprocessFn, z9.string().nullable());
|
|
1647
|
+
const schema = baseSchema.refine((val) => {
|
|
1648
|
+
if (val === null) return true;
|
|
1649
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
1650
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
1651
|
+
}
|
|
1652
|
+
if (notEmpty && val !== null && val.trim() === "") {
|
|
1653
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("notEmpty"), path: [] }]);
|
|
1654
|
+
}
|
|
1655
|
+
if (val !== null && minLength !== void 0 && val.length < minLength) {
|
|
1656
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("minLength", { minLength }), path: [] }]);
|
|
1657
|
+
}
|
|
1658
|
+
if (val !== null && maxLength !== void 0 && val.length > maxLength) {
|
|
1659
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("maxLength", { maxLength }), path: [] }]);
|
|
1660
|
+
}
|
|
1661
|
+
if (val !== null && startsWith !== void 0 && !val.startsWith(startsWith)) {
|
|
1662
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("startsWith", { startsWith }), path: [] }]);
|
|
1663
|
+
}
|
|
1664
|
+
if (val !== null && endsWith !== void 0 && !val.endsWith(endsWith)) {
|
|
1665
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("endsWith", { endsWith }), path: [] }]);
|
|
1666
|
+
}
|
|
1667
|
+
if (val !== null && includes !== void 0 && !val.includes(includes)) {
|
|
1668
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
|
|
1669
|
+
}
|
|
1670
|
+
if (val !== null && excludes !== void 0) {
|
|
1671
|
+
const excludeList = Array.isArray(excludes) ? excludes : [excludes];
|
|
1672
|
+
for (const exclude of excludeList) {
|
|
1673
|
+
if (val.includes(exclude)) {
|
|
1674
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
if (val !== null && regex !== void 0 && !regex.test(val)) {
|
|
1679
|
+
throw new z9.ZodError([{ code: "custom", message: getMessage("invalid", { regex }), path: [] }]);
|
|
1680
|
+
}
|
|
1681
|
+
return true;
|
|
1682
|
+
});
|
|
1683
|
+
return schema;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// src/validators/common/time.ts
|
|
1687
|
+
import { z as z10 } from "zod";
|
|
1688
|
+
var TIME_PATTERNS = {
|
|
1689
|
+
"HH:mm": /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/,
|
|
1690
|
+
"HH:mm:ss": /^([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/,
|
|
1691
|
+
"hh:mm A": /^(0?[1-9]|1[0-2]):[0-5][0-9]\s?(AM|PM)$/i,
|
|
1692
|
+
"hh:mm:ss A": /^(0?[1-9]|1[0-2]):[0-5][0-9]:[0-5][0-9]\s?(AM|PM)$/i,
|
|
1693
|
+
"H:mm": /^([0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/,
|
|
1694
|
+
"h:mm A": /^([1-9]|1[0-2]):[0-5][0-9]\s?(AM|PM)$/i
|
|
1695
|
+
};
|
|
1696
|
+
var parseTimeToMinutes = (timeStr, format) => {
|
|
1697
|
+
const cleanTime = timeStr.trim();
|
|
1698
|
+
try {
|
|
1699
|
+
if (format.includes("A")) {
|
|
1700
|
+
const match = cleanTime.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?\s?(AM|PM)$/i);
|
|
1701
|
+
if (!match) return null;
|
|
1702
|
+
const [, hourStr, minuteStr, , period] = match;
|
|
1703
|
+
let hour = parseInt(hourStr, 10);
|
|
1704
|
+
const minute = parseInt(minuteStr, 10);
|
|
1705
|
+
if (hour < 1 || hour > 12 || minute < 0 || minute > 59) {
|
|
1706
|
+
return null;
|
|
1707
|
+
}
|
|
1708
|
+
if (period.toUpperCase() === "PM" && hour !== 12) {
|
|
1709
|
+
hour += 12;
|
|
1710
|
+
} else if (period.toUpperCase() === "AM" && hour === 12) {
|
|
1711
|
+
hour = 0;
|
|
1712
|
+
}
|
|
1713
|
+
return hour * 60 + minute;
|
|
1714
|
+
} else {
|
|
1715
|
+
const match = cleanTime.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
|
|
1716
|
+
if (!match) return null;
|
|
1717
|
+
const [, hourStr, minuteStr] = match;
|
|
1718
|
+
const hour = parseInt(hourStr, 10);
|
|
1719
|
+
const minute = parseInt(minuteStr, 10);
|
|
1720
|
+
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
|
|
1721
|
+
return null;
|
|
1722
|
+
}
|
|
1723
|
+
return hour * 60 + minute;
|
|
1724
|
+
}
|
|
1725
|
+
} catch {
|
|
1726
|
+
return null;
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
var validateTimeFormat = (value, format) => {
|
|
1730
|
+
const pattern = TIME_PATTERNS[format];
|
|
1731
|
+
return pattern.test(value.trim());
|
|
1732
|
+
};
|
|
1733
|
+
var normalizeTime = (timeStr, format) => {
|
|
1734
|
+
const cleanTime = timeStr.trim();
|
|
1735
|
+
if (format.includes("A")) {
|
|
1736
|
+
const match = cleanTime.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?\s?(AM|PM)$/i);
|
|
1737
|
+
if (!match) return null;
|
|
1738
|
+
const [, hourStr, minuteStr, secondStr = "00", period] = match;
|
|
1739
|
+
let hour = parseInt(hourStr, 10);
|
|
1740
|
+
const minute = parseInt(minuteStr, 10);
|
|
1741
|
+
const second = parseInt(secondStr, 10);
|
|
1742
|
+
if (period.toUpperCase() === "PM" && hour !== 12) {
|
|
1743
|
+
hour += 12;
|
|
1744
|
+
} else if (period.toUpperCase() === "AM" && hour === 12) {
|
|
1745
|
+
hour = 0;
|
|
1746
|
+
}
|
|
1747
|
+
return format.includes("ss") ? `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}:${second.toString().padStart(2, "0")}` : `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
|
|
1748
|
+
}
|
|
1749
|
+
if (format === "H:mm") {
|
|
1750
|
+
const match = cleanTime.match(/^(\d{1,2}):(\d{2})$/);
|
|
1751
|
+
if (!match) return null;
|
|
1752
|
+
const [, hourStr, minuteStr] = match;
|
|
1753
|
+
return `${hourStr.padStart(2, "0")}:${minuteStr}`;
|
|
1754
|
+
}
|
|
1755
|
+
return cleanTime;
|
|
1756
|
+
};
|
|
1757
|
+
function time(options) {
|
|
1758
|
+
const {
|
|
1759
|
+
required = true,
|
|
1760
|
+
format = "HH:mm",
|
|
1761
|
+
min,
|
|
1762
|
+
max,
|
|
1763
|
+
minHour,
|
|
1764
|
+
maxHour,
|
|
1765
|
+
allowedHours,
|
|
1766
|
+
minuteStep,
|
|
1767
|
+
secondStep,
|
|
1768
|
+
includes,
|
|
1769
|
+
excludes,
|
|
1770
|
+
regex,
|
|
1771
|
+
trimMode = "trim",
|
|
1772
|
+
casing = "none",
|
|
1773
|
+
whitelist,
|
|
1774
|
+
whitelistOnly = false,
|
|
1775
|
+
transform,
|
|
1776
|
+
defaultValue,
|
|
1777
|
+
i18n
|
|
1778
|
+
} = options ?? {};
|
|
1779
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
1780
|
+
const getMessage = (key, params) => {
|
|
1781
|
+
if (i18n) {
|
|
1782
|
+
const currentLocale2 = getLocale();
|
|
1783
|
+
const customMessages = i18n[currentLocale2];
|
|
1784
|
+
if (customMessages && customMessages[key]) {
|
|
1785
|
+
const template = customMessages[key];
|
|
1786
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
return t(`common.time.${key}`, params);
|
|
1790
|
+
};
|
|
1791
|
+
const preprocessFn = (val) => {
|
|
1792
|
+
if (val === null || val === void 0) {
|
|
1793
|
+
return actualDefaultValue;
|
|
1794
|
+
}
|
|
1795
|
+
let processed = String(val);
|
|
1796
|
+
switch (trimMode) {
|
|
1797
|
+
case "trim":
|
|
1798
|
+
processed = processed.trim();
|
|
1799
|
+
break;
|
|
1800
|
+
case "trimStart":
|
|
1801
|
+
processed = processed.trimStart();
|
|
1802
|
+
break;
|
|
1803
|
+
case "trimEnd":
|
|
1804
|
+
processed = processed.trimEnd();
|
|
1805
|
+
break;
|
|
1806
|
+
case "none":
|
|
1807
|
+
break;
|
|
1808
|
+
}
|
|
1809
|
+
if (processed === "") {
|
|
1810
|
+
if (whitelist && whitelist.includes("")) {
|
|
1811
|
+
return "";
|
|
1812
|
+
}
|
|
1813
|
+
if (!required) {
|
|
1814
|
+
return actualDefaultValue;
|
|
1815
|
+
}
|
|
1816
|
+
return actualDefaultValue;
|
|
1817
|
+
}
|
|
1818
|
+
switch (casing) {
|
|
1819
|
+
case "upper":
|
|
1820
|
+
processed = processed.toUpperCase();
|
|
1821
|
+
break;
|
|
1822
|
+
case "lower":
|
|
1823
|
+
processed = processed.toLowerCase();
|
|
1824
|
+
break;
|
|
1825
|
+
case "none":
|
|
1826
|
+
break;
|
|
1827
|
+
}
|
|
1828
|
+
if (transform) {
|
|
1829
|
+
processed = transform(processed);
|
|
1830
|
+
}
|
|
1831
|
+
return processed;
|
|
1832
|
+
};
|
|
1833
|
+
const baseSchema = required ? z10.preprocess(preprocessFn, z10.string()) : z10.preprocess(preprocessFn, z10.string().nullable());
|
|
1834
|
+
const schema = baseSchema.refine((val) => {
|
|
1835
|
+
if (val === null) return true;
|
|
1836
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
1837
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
1838
|
+
}
|
|
1839
|
+
if (val === null) return true;
|
|
1840
|
+
if (!required && val === "") return true;
|
|
1841
|
+
if (whitelist && whitelist.length > 0) {
|
|
1842
|
+
if (whitelist.includes(val)) {
|
|
1843
|
+
return true;
|
|
1844
|
+
}
|
|
1845
|
+
if (whitelistOnly) {
|
|
1846
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }]);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
if (regex) {
|
|
1850
|
+
if (!regex.test(val)) {
|
|
1851
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("customRegex"), path: [] }]);
|
|
1852
|
+
}
|
|
1853
|
+
} else {
|
|
1854
|
+
if (!validateTimeFormat(val, format)) {
|
|
1855
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("format", { format }), path: [] }]);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
if (includes && !val.includes(includes)) {
|
|
1859
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
|
|
1860
|
+
}
|
|
1861
|
+
if (excludes) {
|
|
1862
|
+
const excludeList = Array.isArray(excludes) ? excludes : [excludes];
|
|
1863
|
+
for (const exclude of excludeList) {
|
|
1864
|
+
if (val.includes(exclude)) {
|
|
1865
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
if (regex) {
|
|
1870
|
+
return true;
|
|
1871
|
+
}
|
|
1872
|
+
const timeMinutes = parseTimeToMinutes(val, format);
|
|
1873
|
+
if (timeMinutes === null) {
|
|
1874
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
1875
|
+
}
|
|
1876
|
+
const hour = Math.floor(timeMinutes / 60);
|
|
1877
|
+
if (minHour !== void 0 && hour < minHour) {
|
|
1878
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("hour", { minHour, maxHour: maxHour ?? 23 }), path: [] }]);
|
|
1879
|
+
}
|
|
1880
|
+
if (maxHour !== void 0 && hour > maxHour) {
|
|
1881
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("hour", { minHour: minHour ?? 0, maxHour }), path: [] }]);
|
|
1882
|
+
}
|
|
1883
|
+
if (allowedHours && allowedHours.length > 0) {
|
|
1884
|
+
if (!allowedHours.includes(hour)) {
|
|
1885
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("hour", { allowedHours: allowedHours.join(", ") }), path: [] }]);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
const minute = timeMinutes % 60;
|
|
1889
|
+
if (minuteStep !== void 0 && minute % minuteStep !== 0) {
|
|
1890
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("minute", { minuteStep }), path: [] }]);
|
|
1891
|
+
}
|
|
1892
|
+
if (secondStep !== void 0 && format.includes("ss")) {
|
|
1893
|
+
const secondMatch = val.match(/:(\d{2})$/);
|
|
1894
|
+
if (secondMatch) {
|
|
1895
|
+
const seconds = parseInt(secondMatch[1], 10);
|
|
1896
|
+
if (seconds % secondStep !== 0) {
|
|
1897
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("second", { secondStep }), path: [] }]);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
if (min) {
|
|
1902
|
+
const minMinutes = parseTimeToMinutes(min, format);
|
|
1903
|
+
if (minMinutes !== null && timeMinutes < minMinutes) {
|
|
1904
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
if (max) {
|
|
1908
|
+
const maxMinutes = parseTimeToMinutes(max, format);
|
|
1909
|
+
if (maxMinutes !== null && timeMinutes > maxMinutes) {
|
|
1910
|
+
throw new z10.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
return true;
|
|
1914
|
+
});
|
|
1915
|
+
return schema;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
// src/validators/common/url.ts
|
|
1919
|
+
import { z as z11 } from "zod";
|
|
1920
|
+
function url(options) {
|
|
1921
|
+
const {
|
|
1922
|
+
required = true,
|
|
1923
|
+
min,
|
|
1924
|
+
max,
|
|
1925
|
+
includes,
|
|
1926
|
+
excludes,
|
|
1927
|
+
protocols,
|
|
1928
|
+
allowedDomains,
|
|
1929
|
+
blockedDomains,
|
|
1930
|
+
allowedPorts,
|
|
1931
|
+
blockedPorts,
|
|
1932
|
+
pathStartsWith,
|
|
1933
|
+
pathEndsWith,
|
|
1934
|
+
mustHaveQuery,
|
|
1935
|
+
mustNotHaveQuery,
|
|
1936
|
+
mustHaveFragment,
|
|
1937
|
+
mustNotHaveFragment,
|
|
1938
|
+
allowLocalhost = true,
|
|
1939
|
+
blockLocalhost,
|
|
1940
|
+
transform,
|
|
1941
|
+
defaultValue = null,
|
|
1942
|
+
i18n
|
|
1943
|
+
} = options ?? {};
|
|
1944
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
1945
|
+
const getMessage = (key, params) => {
|
|
1946
|
+
if (i18n) {
|
|
1947
|
+
const currentLocale2 = getLocale();
|
|
1948
|
+
const customMessages = i18n[currentLocale2];
|
|
1949
|
+
if (customMessages && customMessages[key]) {
|
|
1950
|
+
const template = customMessages[key];
|
|
1951
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
return t(`common.url.${key}`, params);
|
|
1955
|
+
};
|
|
1956
|
+
const preprocessFn = (val) => {
|
|
1957
|
+
if (val === "" || val === null || val === void 0) {
|
|
1958
|
+
return actualDefaultValue;
|
|
1959
|
+
}
|
|
1960
|
+
let processed = String(val).trim();
|
|
1961
|
+
if (transform) {
|
|
1962
|
+
processed = transform(processed);
|
|
1963
|
+
}
|
|
1964
|
+
return processed;
|
|
1965
|
+
};
|
|
1966
|
+
const baseSchema = required ? z11.preprocess(preprocessFn, z11.string()) : z11.preprocess(preprocessFn, z11.string().nullable());
|
|
1967
|
+
const schema = baseSchema.refine((val) => {
|
|
1968
|
+
if (val === null) return true;
|
|
1969
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
1970
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
1971
|
+
}
|
|
1972
|
+
let urlObj;
|
|
1973
|
+
try {
|
|
1974
|
+
urlObj = new URL(val);
|
|
1975
|
+
} catch {
|
|
1976
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
1977
|
+
}
|
|
1978
|
+
if (val !== null && min !== void 0 && val.length < min) {
|
|
1979
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("min", { min }), path: [] }]);
|
|
1980
|
+
}
|
|
1981
|
+
if (val !== null && max !== void 0 && val.length > max) {
|
|
1982
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("max", { max }), path: [] }]);
|
|
1983
|
+
}
|
|
1984
|
+
if (val !== null && includes !== void 0 && !val.includes(includes)) {
|
|
1985
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("includes", { includes }), path: [] }]);
|
|
1986
|
+
}
|
|
1987
|
+
if (val !== null && excludes !== void 0) {
|
|
1988
|
+
const excludeList = Array.isArray(excludes) ? excludes : [excludes];
|
|
1989
|
+
for (const exclude of excludeList) {
|
|
1990
|
+
if (val.includes(exclude)) {
|
|
1991
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("excludes", { excludes: exclude }), path: [] }]);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
if (protocols && !protocols.includes(urlObj.protocol.slice(0, -1))) {
|
|
1996
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("protocol", { protocols: protocols.join(", ") }), path: [] }]);
|
|
1997
|
+
}
|
|
1998
|
+
const hostname = urlObj.hostname.toLowerCase();
|
|
1999
|
+
if (allowedDomains && !allowedDomains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) {
|
|
2000
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("domain", { domains: allowedDomains.join(", ") }), path: [] }]);
|
|
2001
|
+
}
|
|
2002
|
+
if (blockedDomains && blockedDomains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) {
|
|
2003
|
+
const blockedDomain = blockedDomains.find((domain) => hostname === domain || hostname.endsWith(`.${domain}`));
|
|
2004
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("domainBlacklist", { domain: blockedDomain }), path: [] }]);
|
|
2005
|
+
}
|
|
2006
|
+
const port = urlObj.port ? parseInt(urlObj.port) : urlObj.protocol === "https:" ? 443 : 80;
|
|
2007
|
+
if (allowedPorts && !allowedPorts.includes(port)) {
|
|
2008
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("port", { ports: allowedPorts.join(", ") }), path: [] }]);
|
|
2009
|
+
}
|
|
2010
|
+
if (blockedPorts && blockedPorts.includes(port)) {
|
|
2011
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("port", { port }), path: [] }]);
|
|
2012
|
+
}
|
|
2013
|
+
if (pathStartsWith && !urlObj.pathname.startsWith(pathStartsWith)) {
|
|
2014
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("pathStartsWith", { path: pathStartsWith }), path: [] }]);
|
|
2015
|
+
}
|
|
2016
|
+
if (pathEndsWith && !urlObj.pathname.endsWith(pathEndsWith)) {
|
|
2017
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("pathEndsWith", { path: pathEndsWith }), path: [] }]);
|
|
2018
|
+
}
|
|
2019
|
+
if (mustHaveQuery && !urlObj.search) {
|
|
2020
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("hasQuery"), path: [] }]);
|
|
2021
|
+
}
|
|
2022
|
+
if (mustNotHaveQuery && urlObj.search) {
|
|
2023
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("noQuery"), path: [] }]);
|
|
2024
|
+
}
|
|
2025
|
+
if (mustHaveFragment && !urlObj.hash) {
|
|
2026
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("hasFragment"), path: [] }]);
|
|
2027
|
+
}
|
|
2028
|
+
if (mustNotHaveFragment && urlObj.hash) {
|
|
2029
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("noFragment"), path: [] }]);
|
|
2030
|
+
}
|
|
2031
|
+
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])\./);
|
|
2032
|
+
if (blockLocalhost && isLocalhost) {
|
|
2033
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("noLocalhost"), path: [] }]);
|
|
2034
|
+
}
|
|
2035
|
+
if (!allowLocalhost && isLocalhost) {
|
|
2036
|
+
throw new z11.ZodError([{ code: "custom", message: getMessage("localhost"), path: [] }]);
|
|
2037
|
+
}
|
|
2038
|
+
return true;
|
|
2039
|
+
});
|
|
2040
|
+
return schema;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// src/validators/taiwan/business-id.ts
|
|
2044
|
+
import { z as z12 } from "zod";
|
|
2045
|
+
var validateTaiwanBusinessId = (value) => {
|
|
2046
|
+
if (!/^\d{8}$/.test(value)) {
|
|
2047
|
+
return false;
|
|
2048
|
+
}
|
|
2049
|
+
const digits = value.split("").map(Number);
|
|
2050
|
+
const coefficients = [1, 2, 1, 2, 1, 2, 4];
|
|
2051
|
+
let sum = 0;
|
|
2052
|
+
for (let i = 0; i < 7; i++) {
|
|
2053
|
+
const product = digits[i] * coefficients[i];
|
|
2054
|
+
sum += Math.floor(product / 10) + product % 10;
|
|
2055
|
+
}
|
|
2056
|
+
sum += digits[7];
|
|
2057
|
+
if (sum % 5 === 0) {
|
|
2058
|
+
return true;
|
|
2059
|
+
}
|
|
2060
|
+
if (sum % 10 === 0) {
|
|
2061
|
+
return true;
|
|
2062
|
+
}
|
|
2063
|
+
if (digits[6] === 7) {
|
|
2064
|
+
let altSum = 0;
|
|
2065
|
+
for (let i = 0; i < 7; i++) {
|
|
2066
|
+
const product = digits[i] * coefficients[i];
|
|
2067
|
+
altSum += Math.floor(product / 10) + product % 10;
|
|
2068
|
+
}
|
|
2069
|
+
altSum += 1 + digits[7];
|
|
2070
|
+
if (altSum % 5 === 0 || altSum % 10 === 0) {
|
|
2071
|
+
return true;
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
return false;
|
|
2075
|
+
};
|
|
2076
|
+
function businessId(options) {
|
|
2077
|
+
const {
|
|
2078
|
+
required = true,
|
|
2079
|
+
transform,
|
|
2080
|
+
defaultValue,
|
|
2081
|
+
i18n
|
|
2082
|
+
} = options ?? {};
|
|
2083
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
2084
|
+
const getMessage = (key, params) => {
|
|
2085
|
+
if (i18n) {
|
|
2086
|
+
const currentLocale2 = getLocale();
|
|
2087
|
+
const customMessages = i18n[currentLocale2];
|
|
2088
|
+
if (customMessages && customMessages[key]) {
|
|
2089
|
+
const template = customMessages[key];
|
|
2090
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
return t(`taiwan.businessId.${key}`, params);
|
|
2094
|
+
};
|
|
2095
|
+
const preprocessFn = (val) => {
|
|
2096
|
+
if (val === "" || val === null || val === void 0) {
|
|
2097
|
+
return actualDefaultValue;
|
|
2098
|
+
}
|
|
2099
|
+
let processed = String(val).trim();
|
|
2100
|
+
if (processed === "" && !required) {
|
|
2101
|
+
return null;
|
|
2102
|
+
}
|
|
2103
|
+
if (transform) {
|
|
2104
|
+
processed = transform(processed);
|
|
2105
|
+
}
|
|
2106
|
+
return processed;
|
|
2107
|
+
};
|
|
2108
|
+
const baseSchema = required ? z12.preprocess(preprocessFn, z12.string()) : z12.preprocess(preprocessFn, z12.string().nullable());
|
|
2109
|
+
const schema = baseSchema.refine((val) => {
|
|
2110
|
+
if (val === null) return true;
|
|
2111
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
2112
|
+
throw new z12.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
2113
|
+
}
|
|
2114
|
+
if (val === null) return true;
|
|
2115
|
+
if (!required && val === "") return true;
|
|
2116
|
+
if (!validateTaiwanBusinessId(val)) {
|
|
2117
|
+
throw new z12.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
2118
|
+
}
|
|
2119
|
+
return true;
|
|
2120
|
+
});
|
|
2121
|
+
return schema;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
// src/validators/taiwan/national-id.ts
|
|
2125
|
+
import { z as z13 } from "zod";
|
|
2126
|
+
var CITY_CODES = {
|
|
2127
|
+
"A": 10,
|
|
2128
|
+
"B": 11,
|
|
2129
|
+
"C": 12,
|
|
2130
|
+
"D": 13,
|
|
2131
|
+
"E": 14,
|
|
2132
|
+
"F": 15,
|
|
2133
|
+
"G": 16,
|
|
2134
|
+
"H": 17,
|
|
2135
|
+
"I": 34,
|
|
2136
|
+
"J": 18,
|
|
2137
|
+
"K": 19,
|
|
2138
|
+
"L": 20,
|
|
2139
|
+
"M": 21,
|
|
2140
|
+
"N": 22,
|
|
2141
|
+
"O": 35,
|
|
2142
|
+
"P": 23,
|
|
2143
|
+
"Q": 24,
|
|
2144
|
+
"R": 25,
|
|
2145
|
+
"S": 26,
|
|
2146
|
+
"T": 27,
|
|
2147
|
+
"U": 28,
|
|
2148
|
+
"V": 29,
|
|
2149
|
+
"W": 32,
|
|
2150
|
+
"X": 30,
|
|
2151
|
+
"Y": 31,
|
|
2152
|
+
"Z": 33
|
|
2153
|
+
};
|
|
2154
|
+
var validateCitizenId = (value) => {
|
|
2155
|
+
if (!/^[A-Z][1-2]\d{8}$/.test(value)) {
|
|
2156
|
+
return false;
|
|
2157
|
+
}
|
|
2158
|
+
const letter = value[0];
|
|
2159
|
+
const digits = value.slice(1).split("").map(Number);
|
|
2160
|
+
const cityCode = CITY_CODES[letter];
|
|
2161
|
+
if (!cityCode) return false;
|
|
2162
|
+
const cityDigits = [Math.floor(cityCode / 10), cityCode % 10];
|
|
2163
|
+
const coefficients = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1];
|
|
2164
|
+
let sum = cityDigits[0] * coefficients[0] + cityDigits[1] * coefficients[1];
|
|
2165
|
+
for (let i = 0; i < 8; i++) {
|
|
2166
|
+
sum += digits[i] * coefficients[i + 2];
|
|
2167
|
+
}
|
|
2168
|
+
const checksum = (10 - sum % 10) % 10;
|
|
2169
|
+
return checksum === digits[8];
|
|
2170
|
+
};
|
|
2171
|
+
var validateOldResidentId = (value) => {
|
|
2172
|
+
if (!/^[A-Z][ABCD]\d{8}$/.test(value)) {
|
|
2173
|
+
return false;
|
|
2174
|
+
}
|
|
2175
|
+
const letter = value[0];
|
|
2176
|
+
const genderCode = value[1];
|
|
2177
|
+
const digits = value.slice(2).split("").map(Number);
|
|
2178
|
+
const cityCode = CITY_CODES[letter];
|
|
2179
|
+
if (!cityCode) return false;
|
|
2180
|
+
const genderValue = genderCode === "A" || genderCode === "C" ? 1 : 0;
|
|
2181
|
+
const cityDigits = [Math.floor(cityCode / 10), cityCode % 10];
|
|
2182
|
+
const coefficients = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1];
|
|
2183
|
+
let sum = cityDigits[0] * coefficients[0] + cityDigits[1] * coefficients[1] + genderValue * coefficients[2];
|
|
2184
|
+
for (let i = 0; i < 7; i++) {
|
|
2185
|
+
sum += digits[i] * coefficients[i + 3];
|
|
2186
|
+
}
|
|
2187
|
+
const checksum = (10 - sum % 10) % 10;
|
|
2188
|
+
return checksum === digits[7];
|
|
2189
|
+
};
|
|
2190
|
+
var validateNewResidentId = (value) => {
|
|
2191
|
+
if (!/^[A-Z][89]\d{8}$/.test(value)) {
|
|
2192
|
+
return false;
|
|
2193
|
+
}
|
|
2194
|
+
const letter = value[0];
|
|
2195
|
+
const digits = value.slice(1).split("").map(Number);
|
|
2196
|
+
const cityCode = CITY_CODES[letter];
|
|
2197
|
+
if (!cityCode) return false;
|
|
2198
|
+
const cityDigits = [Math.floor(cityCode / 10), cityCode % 10];
|
|
2199
|
+
const coefficients = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1];
|
|
2200
|
+
let sum = cityDigits[0] * coefficients[0] + cityDigits[1] * coefficients[1];
|
|
2201
|
+
for (let i = 0; i < 8; i++) {
|
|
2202
|
+
sum += digits[i] * coefficients[i + 2];
|
|
2203
|
+
}
|
|
2204
|
+
const checksum = (10 - sum % 10) % 10;
|
|
2205
|
+
return checksum === digits[8];
|
|
2206
|
+
};
|
|
2207
|
+
var validateTaiwanNationalId = (value, type = "both", allowOldResident = true) => {
|
|
2208
|
+
if (!/^[A-Z].{9}$/.test(value)) {
|
|
2209
|
+
return false;
|
|
2210
|
+
}
|
|
2211
|
+
switch (type) {
|
|
2212
|
+
case "citizen":
|
|
2213
|
+
return validateCitizenId(value);
|
|
2214
|
+
case "resident":
|
|
2215
|
+
return (allowOldResident ? validateOldResidentId(value) : false) || validateNewResidentId(value);
|
|
2216
|
+
case "both":
|
|
2217
|
+
return validateCitizenId(value) || (allowOldResident ? validateOldResidentId(value) : false) || validateNewResidentId(value);
|
|
2218
|
+
default:
|
|
2219
|
+
return false;
|
|
2220
|
+
}
|
|
2221
|
+
};
|
|
2222
|
+
function nationalId(options) {
|
|
2223
|
+
const {
|
|
2224
|
+
required = true,
|
|
2225
|
+
type = "both",
|
|
2226
|
+
allowOldResident = true,
|
|
2227
|
+
transform,
|
|
2228
|
+
defaultValue,
|
|
2229
|
+
i18n
|
|
2230
|
+
} = options ?? {};
|
|
2231
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
2232
|
+
const getMessage = (key, params) => {
|
|
2233
|
+
if (i18n) {
|
|
2234
|
+
const currentLocale2 = getLocale();
|
|
2235
|
+
const customMessages = i18n[currentLocale2];
|
|
2236
|
+
if (customMessages && customMessages[key]) {
|
|
2237
|
+
const template = customMessages[key];
|
|
2238
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
return t(`taiwan.nationalId.${key}`, params);
|
|
2242
|
+
};
|
|
2243
|
+
const preprocessFn = (val) => {
|
|
2244
|
+
if (val === "" || val === null || val === void 0) {
|
|
2245
|
+
return actualDefaultValue;
|
|
2246
|
+
}
|
|
2247
|
+
let processed = String(val).trim().toUpperCase();
|
|
2248
|
+
if (processed === "" && !required) {
|
|
2249
|
+
return null;
|
|
2250
|
+
}
|
|
2251
|
+
if (transform) {
|
|
2252
|
+
processed = transform(processed);
|
|
2253
|
+
}
|
|
2254
|
+
return processed;
|
|
2255
|
+
};
|
|
2256
|
+
const baseSchema = required ? z13.preprocess(preprocessFn, z13.string()) : z13.preprocess(preprocessFn, z13.string().nullable());
|
|
2257
|
+
const schema = baseSchema.refine((val) => {
|
|
2258
|
+
if (val === null) return true;
|
|
2259
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
2260
|
+
throw new z13.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
2261
|
+
}
|
|
2262
|
+
if (val === null) return true;
|
|
2263
|
+
if (!required && val === "") return true;
|
|
2264
|
+
if (!validateTaiwanNationalId(val, type, allowOldResident)) {
|
|
2265
|
+
throw new z13.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
2266
|
+
}
|
|
2267
|
+
return true;
|
|
2268
|
+
});
|
|
2269
|
+
return schema;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
// src/validators/taiwan/mobile.ts
|
|
2273
|
+
import { z as z14 } from "zod";
|
|
2274
|
+
var validateTaiwanMobile = (value) => {
|
|
2275
|
+
return /^09[0-9]\d{7}$/.test(value);
|
|
2276
|
+
};
|
|
2277
|
+
function mobile(options) {
|
|
2278
|
+
const { required = true, whitelist, transform, defaultValue, i18n } = options ?? {};
|
|
2279
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
2280
|
+
const getMessage = (key, params) => {
|
|
2281
|
+
if (i18n) {
|
|
2282
|
+
const currentLocale2 = getLocale();
|
|
2283
|
+
const customMessages = i18n[currentLocale2];
|
|
2284
|
+
if (customMessages && customMessages[key]) {
|
|
2285
|
+
const template = customMessages[key];
|
|
2286
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
return t(`taiwan.mobile.${key}`, params);
|
|
2290
|
+
};
|
|
2291
|
+
const preprocessFn = (val) => {
|
|
2292
|
+
if (val === null || val === void 0) {
|
|
2293
|
+
return actualDefaultValue;
|
|
2294
|
+
}
|
|
2295
|
+
let processed = String(val).trim();
|
|
2296
|
+
if (processed === "") {
|
|
2297
|
+
if (whitelist && whitelist.includes("")) {
|
|
2298
|
+
return "";
|
|
2299
|
+
}
|
|
2300
|
+
if (!required) {
|
|
2301
|
+
return actualDefaultValue;
|
|
2302
|
+
}
|
|
2303
|
+
return actualDefaultValue;
|
|
2304
|
+
}
|
|
2305
|
+
if (transform) {
|
|
2306
|
+
processed = transform(processed);
|
|
2307
|
+
}
|
|
2308
|
+
return processed;
|
|
2309
|
+
};
|
|
2310
|
+
const baseSchema = required ? z14.preprocess(preprocessFn, z14.string()) : z14.preprocess(preprocessFn, z14.string().nullable());
|
|
2311
|
+
const schema = baseSchema.refine((val) => {
|
|
2312
|
+
if (val === null) return true;
|
|
2313
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
2314
|
+
throw new z14.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
2315
|
+
}
|
|
2316
|
+
if (val === null) return true;
|
|
2317
|
+
if (!required && val === "") return true;
|
|
2318
|
+
if (whitelist && whitelist.length > 0) {
|
|
2319
|
+
if (whitelist.includes(val)) {
|
|
2320
|
+
return true;
|
|
2321
|
+
}
|
|
2322
|
+
throw new z14.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }]);
|
|
2323
|
+
}
|
|
2324
|
+
if (!validateTaiwanMobile(val)) {
|
|
2325
|
+
throw new z14.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
2326
|
+
}
|
|
2327
|
+
return true;
|
|
2328
|
+
});
|
|
2329
|
+
return schema;
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
// src/validators/taiwan/postal-code.ts
|
|
2333
|
+
import { z as z15 } from "zod";
|
|
2334
|
+
var VALID_3_DIGIT_PREFIXES = [
|
|
2335
|
+
// Taipei City (台北市) - 100-116
|
|
2336
|
+
"100",
|
|
2337
|
+
"103",
|
|
2338
|
+
"104",
|
|
2339
|
+
"105",
|
|
2340
|
+
"106",
|
|
2341
|
+
"108",
|
|
2342
|
+
"110",
|
|
2343
|
+
"111",
|
|
2344
|
+
"112",
|
|
2345
|
+
"114",
|
|
2346
|
+
"115",
|
|
2347
|
+
"116",
|
|
2348
|
+
// New Taipei City (新北市) - 200-253
|
|
2349
|
+
"200",
|
|
2350
|
+
"201",
|
|
2351
|
+
"202",
|
|
2352
|
+
"203",
|
|
2353
|
+
"204",
|
|
2354
|
+
"205",
|
|
2355
|
+
"206",
|
|
2356
|
+
"207",
|
|
2357
|
+
"208",
|
|
2358
|
+
"220",
|
|
2359
|
+
"221",
|
|
2360
|
+
"222",
|
|
2361
|
+
"223",
|
|
2362
|
+
"224",
|
|
2363
|
+
"226",
|
|
2364
|
+
"227",
|
|
2365
|
+
"228",
|
|
2366
|
+
"231",
|
|
2367
|
+
"232",
|
|
2368
|
+
"233",
|
|
2369
|
+
"234",
|
|
2370
|
+
"235",
|
|
2371
|
+
"236",
|
|
2372
|
+
"237",
|
|
2373
|
+
"238",
|
|
2374
|
+
"239",
|
|
2375
|
+
"241",
|
|
2376
|
+
"242",
|
|
2377
|
+
"243",
|
|
2378
|
+
"244",
|
|
2379
|
+
"247",
|
|
2380
|
+
"248",
|
|
2381
|
+
"249",
|
|
2382
|
+
"251",
|
|
2383
|
+
"252",
|
|
2384
|
+
"253",
|
|
2385
|
+
// Keelung City (基隆市) - 200-206
|
|
2386
|
+
"200",
|
|
2387
|
+
"201",
|
|
2388
|
+
"202",
|
|
2389
|
+
"203",
|
|
2390
|
+
"204",
|
|
2391
|
+
"205",
|
|
2392
|
+
"206",
|
|
2393
|
+
// Taoyuan City (桃園市) - 300-338
|
|
2394
|
+
"300",
|
|
2395
|
+
"302",
|
|
2396
|
+
"303",
|
|
2397
|
+
"304",
|
|
2398
|
+
"305",
|
|
2399
|
+
"306",
|
|
2400
|
+
"307",
|
|
2401
|
+
"308",
|
|
2402
|
+
"310",
|
|
2403
|
+
"311",
|
|
2404
|
+
"312",
|
|
2405
|
+
"313",
|
|
2406
|
+
"314",
|
|
2407
|
+
"315",
|
|
2408
|
+
"316",
|
|
2409
|
+
"317",
|
|
2410
|
+
"318",
|
|
2411
|
+
"320",
|
|
2412
|
+
"324",
|
|
2413
|
+
"325",
|
|
2414
|
+
"326",
|
|
2415
|
+
"327",
|
|
2416
|
+
"328",
|
|
2417
|
+
"330",
|
|
2418
|
+
"333",
|
|
2419
|
+
"334",
|
|
2420
|
+
"335",
|
|
2421
|
+
"336",
|
|
2422
|
+
"337",
|
|
2423
|
+
"338",
|
|
2424
|
+
// Hsinchu County (新竹縣) - 300-315
|
|
2425
|
+
"300",
|
|
2426
|
+
"302",
|
|
2427
|
+
"303",
|
|
2428
|
+
"304",
|
|
2429
|
+
"305",
|
|
2430
|
+
"306",
|
|
2431
|
+
"307",
|
|
2432
|
+
"308",
|
|
2433
|
+
"310",
|
|
2434
|
+
"311",
|
|
2435
|
+
"312",
|
|
2436
|
+
"313",
|
|
2437
|
+
"314",
|
|
2438
|
+
"315",
|
|
2439
|
+
// Hsinchu City (新竹市) - 300
|
|
2440
|
+
"300",
|
|
2441
|
+
// Miaoli County (苗栗縣) - 350-369
|
|
2442
|
+
"350",
|
|
2443
|
+
"351",
|
|
2444
|
+
"352",
|
|
2445
|
+
"353",
|
|
2446
|
+
"354",
|
|
2447
|
+
"356",
|
|
2448
|
+
"357",
|
|
2449
|
+
"358",
|
|
2450
|
+
"360",
|
|
2451
|
+
"361",
|
|
2452
|
+
"362",
|
|
2453
|
+
"363",
|
|
2454
|
+
"364",
|
|
2455
|
+
"365",
|
|
2456
|
+
"366",
|
|
2457
|
+
"367",
|
|
2458
|
+
"368",
|
|
2459
|
+
"369",
|
|
2460
|
+
// Taichung City (台中市) - 400-439
|
|
2461
|
+
"400",
|
|
2462
|
+
"401",
|
|
2463
|
+
"402",
|
|
2464
|
+
"403",
|
|
2465
|
+
"404",
|
|
2466
|
+
"406",
|
|
2467
|
+
"407",
|
|
2468
|
+
"408",
|
|
2469
|
+
"411",
|
|
2470
|
+
"412",
|
|
2471
|
+
"413",
|
|
2472
|
+
"414",
|
|
2473
|
+
"420",
|
|
2474
|
+
"421",
|
|
2475
|
+
"422",
|
|
2476
|
+
"423",
|
|
2477
|
+
"424",
|
|
2478
|
+
"426",
|
|
2479
|
+
"427",
|
|
2480
|
+
"428",
|
|
2481
|
+
"429",
|
|
2482
|
+
"432",
|
|
2483
|
+
"433",
|
|
2484
|
+
"434",
|
|
2485
|
+
"435",
|
|
2486
|
+
"436",
|
|
2487
|
+
"437",
|
|
2488
|
+
"438",
|
|
2489
|
+
"439",
|
|
2490
|
+
// Changhua County (彰化縣) - 500-530
|
|
2491
|
+
"500",
|
|
2492
|
+
"502",
|
|
2493
|
+
"503",
|
|
2494
|
+
"504",
|
|
2495
|
+
"505",
|
|
2496
|
+
"506",
|
|
2497
|
+
"507",
|
|
2498
|
+
"508",
|
|
2499
|
+
"509",
|
|
2500
|
+
"510",
|
|
2501
|
+
"511",
|
|
2502
|
+
"512",
|
|
2503
|
+
"513",
|
|
2504
|
+
"514",
|
|
2505
|
+
"515",
|
|
2506
|
+
"516",
|
|
2507
|
+
"520",
|
|
2508
|
+
"521",
|
|
2509
|
+
"522",
|
|
2510
|
+
"523",
|
|
2511
|
+
"524",
|
|
2512
|
+
"525",
|
|
2513
|
+
"526",
|
|
2514
|
+
"527",
|
|
2515
|
+
"528",
|
|
2516
|
+
"530",
|
|
2517
|
+
// Nantou County (南投縣) - 540-558
|
|
2518
|
+
"540",
|
|
2519
|
+
"541",
|
|
2520
|
+
"542",
|
|
2521
|
+
"544",
|
|
2522
|
+
"545",
|
|
2523
|
+
"546",
|
|
2524
|
+
"551",
|
|
2525
|
+
"552",
|
|
2526
|
+
"553",
|
|
2527
|
+
"555",
|
|
2528
|
+
"556",
|
|
2529
|
+
"557",
|
|
2530
|
+
"558",
|
|
2531
|
+
// Yunlin County (雲林縣) - 630-655
|
|
2532
|
+
"630",
|
|
2533
|
+
"631",
|
|
2534
|
+
"632",
|
|
2535
|
+
"633",
|
|
2536
|
+
"634",
|
|
2537
|
+
"635",
|
|
2538
|
+
"636",
|
|
2539
|
+
"637",
|
|
2540
|
+
"638",
|
|
2541
|
+
"640",
|
|
2542
|
+
"643",
|
|
2543
|
+
"646",
|
|
2544
|
+
"647",
|
|
2545
|
+
"648",
|
|
2546
|
+
"649",
|
|
2547
|
+
"651",
|
|
2548
|
+
"652",
|
|
2549
|
+
"653",
|
|
2550
|
+
"654",
|
|
2551
|
+
"655",
|
|
2552
|
+
// Chiayi County (嘉義縣) - 600-625
|
|
2553
|
+
"600",
|
|
2554
|
+
"602",
|
|
2555
|
+
"603",
|
|
2556
|
+
"604",
|
|
2557
|
+
"605",
|
|
2558
|
+
"606",
|
|
2559
|
+
"607",
|
|
2560
|
+
"608",
|
|
2561
|
+
"611",
|
|
2562
|
+
"612",
|
|
2563
|
+
"613",
|
|
2564
|
+
"614",
|
|
2565
|
+
"615",
|
|
2566
|
+
"616",
|
|
2567
|
+
"621",
|
|
2568
|
+
"622",
|
|
2569
|
+
"623",
|
|
2570
|
+
"624",
|
|
2571
|
+
"625",
|
|
2572
|
+
// Chiayi City (嘉義市) - 600
|
|
2573
|
+
"600",
|
|
2574
|
+
// Tainan City (台南市) - 700-745
|
|
2575
|
+
"700",
|
|
2576
|
+
"701",
|
|
2577
|
+
"702",
|
|
2578
|
+
"704",
|
|
2579
|
+
"708",
|
|
2580
|
+
"709",
|
|
2581
|
+
"710",
|
|
2582
|
+
"711",
|
|
2583
|
+
"712",
|
|
2584
|
+
"713",
|
|
2585
|
+
"714",
|
|
2586
|
+
"715",
|
|
2587
|
+
"716",
|
|
2588
|
+
"717",
|
|
2589
|
+
"718",
|
|
2590
|
+
"719",
|
|
2591
|
+
"720",
|
|
2592
|
+
"721",
|
|
2593
|
+
"722",
|
|
2594
|
+
"723",
|
|
2595
|
+
"724",
|
|
2596
|
+
"725",
|
|
2597
|
+
"726",
|
|
2598
|
+
"727",
|
|
2599
|
+
"730",
|
|
2600
|
+
"731",
|
|
2601
|
+
"732",
|
|
2602
|
+
"733",
|
|
2603
|
+
"734",
|
|
2604
|
+
"735",
|
|
2605
|
+
"736",
|
|
2606
|
+
"737",
|
|
2607
|
+
"741",
|
|
2608
|
+
"742",
|
|
2609
|
+
"743",
|
|
2610
|
+
"744",
|
|
2611
|
+
"745",
|
|
2612
|
+
// Kaohsiung City (高雄市) - 800-852
|
|
2613
|
+
"800",
|
|
2614
|
+
"801",
|
|
2615
|
+
"802",
|
|
2616
|
+
"803",
|
|
2617
|
+
"804",
|
|
2618
|
+
"805",
|
|
2619
|
+
"806",
|
|
2620
|
+
"807",
|
|
2621
|
+
"811",
|
|
2622
|
+
"812",
|
|
2623
|
+
"813",
|
|
2624
|
+
"814",
|
|
2625
|
+
"815",
|
|
2626
|
+
"820",
|
|
2627
|
+
"821",
|
|
2628
|
+
"822",
|
|
2629
|
+
"823",
|
|
2630
|
+
"824",
|
|
2631
|
+
"825",
|
|
2632
|
+
"826",
|
|
2633
|
+
"827",
|
|
2634
|
+
"828",
|
|
2635
|
+
"829",
|
|
2636
|
+
"830",
|
|
2637
|
+
"831",
|
|
2638
|
+
"832",
|
|
2639
|
+
"833",
|
|
2640
|
+
"840",
|
|
2641
|
+
"842",
|
|
2642
|
+
"843",
|
|
2643
|
+
"844",
|
|
2644
|
+
"845",
|
|
2645
|
+
"846",
|
|
2646
|
+
"847",
|
|
2647
|
+
"848",
|
|
2648
|
+
"849",
|
|
2649
|
+
"851",
|
|
2650
|
+
"852",
|
|
2651
|
+
// Pingtung County (屏東縣) - 900-947
|
|
2652
|
+
"900",
|
|
2653
|
+
"901",
|
|
2654
|
+
"902",
|
|
2655
|
+
"903",
|
|
2656
|
+
"904",
|
|
2657
|
+
"905",
|
|
2658
|
+
"906",
|
|
2659
|
+
"907",
|
|
2660
|
+
"908",
|
|
2661
|
+
"909",
|
|
2662
|
+
"911",
|
|
2663
|
+
"912",
|
|
2664
|
+
"913",
|
|
2665
|
+
"920",
|
|
2666
|
+
"921",
|
|
2667
|
+
"922",
|
|
2668
|
+
"923",
|
|
2669
|
+
"924",
|
|
2670
|
+
"925",
|
|
2671
|
+
"926",
|
|
2672
|
+
"927",
|
|
2673
|
+
"928",
|
|
2674
|
+
"929",
|
|
2675
|
+
"931",
|
|
2676
|
+
"932",
|
|
2677
|
+
"940",
|
|
2678
|
+
"941",
|
|
2679
|
+
"942",
|
|
2680
|
+
"943",
|
|
2681
|
+
"944",
|
|
2682
|
+
"945",
|
|
2683
|
+
"946",
|
|
2684
|
+
"947",
|
|
2685
|
+
// Yilan County (宜蘭縣) - 260-269
|
|
2686
|
+
"260",
|
|
2687
|
+
"261",
|
|
2688
|
+
"262",
|
|
2689
|
+
"263",
|
|
2690
|
+
"264",
|
|
2691
|
+
"265",
|
|
2692
|
+
"266",
|
|
2693
|
+
"267",
|
|
2694
|
+
"268",
|
|
2695
|
+
"269",
|
|
2696
|
+
// Hualien County (花蓮縣) - 970-983
|
|
2697
|
+
"970",
|
|
2698
|
+
"971",
|
|
2699
|
+
"972",
|
|
2700
|
+
"973",
|
|
2701
|
+
"974",
|
|
2702
|
+
"975",
|
|
2703
|
+
"976",
|
|
2704
|
+
"977",
|
|
2705
|
+
"978",
|
|
2706
|
+
"979",
|
|
2707
|
+
"981",
|
|
2708
|
+
"982",
|
|
2709
|
+
"983",
|
|
2710
|
+
// Taitung County (台東縣) - 950-966
|
|
2711
|
+
"950",
|
|
2712
|
+
"951",
|
|
2713
|
+
"952",
|
|
2714
|
+
"953",
|
|
2715
|
+
"954",
|
|
2716
|
+
"955",
|
|
2717
|
+
"956",
|
|
2718
|
+
"957",
|
|
2719
|
+
"958",
|
|
2720
|
+
"959",
|
|
2721
|
+
"961",
|
|
2722
|
+
"962",
|
|
2723
|
+
"963",
|
|
2724
|
+
"964",
|
|
2725
|
+
"965",
|
|
2726
|
+
"966",
|
|
2727
|
+
// Penghu County (澎湖縣) - 880-885
|
|
2728
|
+
"880",
|
|
2729
|
+
"881",
|
|
2730
|
+
"882",
|
|
2731
|
+
"883",
|
|
2732
|
+
"884",
|
|
2733
|
+
"885",
|
|
2734
|
+
// Kinmen County (金門縣) - 890-896
|
|
2735
|
+
"890",
|
|
2736
|
+
"891",
|
|
2737
|
+
"892",
|
|
2738
|
+
"893",
|
|
2739
|
+
"894",
|
|
2740
|
+
"895",
|
|
2741
|
+
"896",
|
|
2742
|
+
// Lienchiang County (連江縣/馬祖) - 209-212
|
|
2743
|
+
"209",
|
|
2744
|
+
"210",
|
|
2745
|
+
"211",
|
|
2746
|
+
"212"
|
|
2747
|
+
];
|
|
2748
|
+
var POSTAL_CODE_RANGES = {
|
|
2749
|
+
// Major cities with extensive postal networks
|
|
2750
|
+
"100": { range5: [1, 99], range6: [1, 999] },
|
|
2751
|
+
// Taipei City - Zhongzheng
|
|
2752
|
+
"103": { range5: [1, 99], range6: [1, 999] },
|
|
2753
|
+
// Taipei City - Datong
|
|
2754
|
+
"104": { range5: [1, 99], range6: [1, 999] },
|
|
2755
|
+
// Taipei City - Zhongshan
|
|
2756
|
+
"105": { range5: [1, 99], range6: [1, 999] },
|
|
2757
|
+
// Taipei City - Songshan
|
|
2758
|
+
"106": { range5: [1, 99], range6: [1, 999] },
|
|
2759
|
+
// Taipei City - Da'an
|
|
2760
|
+
"108": { range5: [1, 99], range6: [1, 999] },
|
|
2761
|
+
// Taipei City - Wanhua
|
|
2762
|
+
"110": { range5: [1, 99], range6: [1, 999] },
|
|
2763
|
+
// Taipei City - Xinyi
|
|
2764
|
+
"111": { range5: [1, 99], range6: [1, 999] },
|
|
2765
|
+
// Taipei City - Shilin
|
|
2766
|
+
"112": { range5: [1, 99], range6: [1, 999] },
|
|
2767
|
+
// Taipei City - Beitou
|
|
2768
|
+
"114": { range5: [1, 99], range6: [1, 999] },
|
|
2769
|
+
// Taipei City - Neihu
|
|
2770
|
+
"115": { range5: [1, 99], range6: [1, 999] },
|
|
2771
|
+
// Taipei City - Nangang
|
|
2772
|
+
"116": { range5: [1, 99], range6: [1, 999] },
|
|
2773
|
+
// Taipei City - Wenshan
|
|
2774
|
+
// New Taipei City major areas
|
|
2775
|
+
"220": { range5: [1, 99], range6: [1, 999] },
|
|
2776
|
+
// Banqiao
|
|
2777
|
+
"221": { range5: [1, 99], range6: [1, 999] },
|
|
2778
|
+
// Xizhi
|
|
2779
|
+
"222": { range5: [1, 99], range6: [1, 999] },
|
|
2780
|
+
// Shenkeng
|
|
2781
|
+
"223": { range5: [1, 99], range6: [1, 999] },
|
|
2782
|
+
// Shiding
|
|
2783
|
+
"224": { range5: [1, 99], range6: [1, 999] },
|
|
2784
|
+
// Ruifang
|
|
2785
|
+
// Taoyuan City
|
|
2786
|
+
"320": { range5: [1, 99], range6: [1, 999] },
|
|
2787
|
+
// Zhongli
|
|
2788
|
+
"324": { range5: [1, 99], range6: [1, 999] },
|
|
2789
|
+
// Pingzhen
|
|
2790
|
+
"330": { range5: [1, 99], range6: [1, 999] },
|
|
2791
|
+
// Taoyuan
|
|
2792
|
+
// Taichung City
|
|
2793
|
+
"400": { range5: [1, 99], range6: [1, 999] },
|
|
2794
|
+
// Central District
|
|
2795
|
+
"401": { range5: [1, 99], range6: [1, 999] },
|
|
2796
|
+
// East District
|
|
2797
|
+
"402": { range5: [1, 99], range6: [1, 999] },
|
|
2798
|
+
// South District
|
|
2799
|
+
"403": { range5: [1, 99], range6: [1, 999] },
|
|
2800
|
+
// West District
|
|
2801
|
+
"404": { range5: [1, 99], range6: [1, 999] },
|
|
2802
|
+
// North District
|
|
2803
|
+
// Tainan City
|
|
2804
|
+
"700": { range5: [1, 99], range6: [1, 999] },
|
|
2805
|
+
// Central District
|
|
2806
|
+
"701": { range5: [1, 99], range6: [1, 999] },
|
|
2807
|
+
// East District
|
|
2808
|
+
"702": { range5: [1, 99], range6: [1, 999] },
|
|
2809
|
+
// South District
|
|
2810
|
+
// Kaohsiung City
|
|
2811
|
+
"800": { range5: [1, 99], range6: [1, 999] },
|
|
2812
|
+
// Xinxing
|
|
2813
|
+
"801": { range5: [1, 99], range6: [1, 999] },
|
|
2814
|
+
// Qianjin
|
|
2815
|
+
"802": { range5: [1, 99], range6: [1, 999] },
|
|
2816
|
+
// Lingya
|
|
2817
|
+
"803": { range5: [1, 99], range6: [1, 999] },
|
|
2818
|
+
// Yancheng
|
|
2819
|
+
// Smaller areas with more limited ranges
|
|
2820
|
+
"880": { range5: [1, 50], range6: [1, 500] },
|
|
2821
|
+
// Penghu (smaller population)
|
|
2822
|
+
"890": { range5: [1, 30], range6: [1, 300] },
|
|
2823
|
+
// Kinmen (smaller population)
|
|
2824
|
+
"209": { range5: [1, 20], range6: [1, 200] }
|
|
2825
|
+
// Lienchiang/Matsu (smallest population)
|
|
2826
|
+
};
|
|
2827
|
+
var getPostalCodeRanges = (prefix) => {
|
|
2828
|
+
return POSTAL_CODE_RANGES[prefix] || {
|
|
2829
|
+
range5: [1, 99],
|
|
2830
|
+
// Default range for 5-digit
|
|
2831
|
+
range6: [1, 999]
|
|
2832
|
+
// Default range for 6-digit
|
|
2833
|
+
};
|
|
2834
|
+
};
|
|
2835
|
+
var validate3DigitPostalCode = (value, strictValidation = true, allowedPrefixes, blockedPrefixes) => {
|
|
2836
|
+
if (!/^\d{3}$/.test(value)) {
|
|
2837
|
+
return false;
|
|
2838
|
+
}
|
|
2839
|
+
if (blockedPrefixes && blockedPrefixes.includes(value)) {
|
|
2840
|
+
return false;
|
|
2841
|
+
}
|
|
2842
|
+
if (allowedPrefixes) {
|
|
2843
|
+
return allowedPrefixes.includes(value);
|
|
2844
|
+
}
|
|
2845
|
+
if (strictValidation) {
|
|
2846
|
+
return VALID_3_DIGIT_PREFIXES.includes(value);
|
|
2847
|
+
}
|
|
2848
|
+
const num = parseInt(value, 10);
|
|
2849
|
+
return num >= 100 && num <= 999;
|
|
2850
|
+
};
|
|
2851
|
+
var validate5DigitPostalCode = (value, strictValidation = true, strictSuffixValidation = false, allowedPrefixes, blockedPrefixes) => {
|
|
2852
|
+
if (!/^\d{5}$/.test(value)) {
|
|
2853
|
+
return false;
|
|
2854
|
+
}
|
|
2855
|
+
const prefix = value.substring(0, 3);
|
|
2856
|
+
const suffix = value.substring(3, 5);
|
|
2857
|
+
if (!validate3DigitPostalCode(prefix, strictValidation, allowedPrefixes, blockedPrefixes)) {
|
|
2858
|
+
return false;
|
|
2859
|
+
}
|
|
2860
|
+
if (strictSuffixValidation) {
|
|
2861
|
+
const suffixNum = parseInt(suffix, 10);
|
|
2862
|
+
const ranges = getPostalCodeRanges(prefix);
|
|
2863
|
+
if (suffixNum < ranges.range5[0] || suffixNum > ranges.range5[1]) {
|
|
2864
|
+
return false;
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
return true;
|
|
2868
|
+
};
|
|
2869
|
+
var validate6DigitPostalCode = (value, strictValidation = true, strictSuffixValidation = false, allowedPrefixes, blockedPrefixes) => {
|
|
2870
|
+
if (!/^\d{6}$/.test(value)) {
|
|
2871
|
+
return false;
|
|
2872
|
+
}
|
|
2873
|
+
const prefix = value.substring(0, 3);
|
|
2874
|
+
const suffix = value.substring(3, 6);
|
|
2875
|
+
if (!validate3DigitPostalCode(prefix, strictValidation, allowedPrefixes, blockedPrefixes)) {
|
|
2876
|
+
return false;
|
|
2877
|
+
}
|
|
2878
|
+
if (strictSuffixValidation) {
|
|
2879
|
+
const suffixNum = parseInt(suffix, 10);
|
|
2880
|
+
const ranges = getPostalCodeRanges(prefix);
|
|
2881
|
+
if (suffixNum < ranges.range6[0] || suffixNum > ranges.range6[1]) {
|
|
2882
|
+
return false;
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
return true;
|
|
2886
|
+
};
|
|
2887
|
+
var validateTaiwanPostalCode = (value, format = "3+6", strictValidation = true, strictSuffixValidation = false, allowDashes = true, allowedPrefixes, blockedPrefixes) => {
|
|
2888
|
+
if (!value || typeof value !== "string") {
|
|
2889
|
+
return false;
|
|
2890
|
+
}
|
|
2891
|
+
const cleanValue = allowDashes ? value.replace(/[-\s]/g, "") : value;
|
|
2892
|
+
if (!allowDashes && /[-\s]/.test(value)) {
|
|
2893
|
+
return false;
|
|
2894
|
+
}
|
|
2895
|
+
switch (format) {
|
|
2896
|
+
case "3":
|
|
2897
|
+
return cleanValue.length === 3 && validate3DigitPostalCode(cleanValue, strictValidation, allowedPrefixes, blockedPrefixes);
|
|
2898
|
+
case "5":
|
|
2899
|
+
return cleanValue.length === 5 && validate5DigitPostalCode(cleanValue, strictValidation, strictSuffixValidation, allowedPrefixes, blockedPrefixes);
|
|
2900
|
+
case "6":
|
|
2901
|
+
return cleanValue.length === 6 && validate6DigitPostalCode(cleanValue, strictValidation, strictSuffixValidation, allowedPrefixes, blockedPrefixes);
|
|
2902
|
+
case "3+5":
|
|
2903
|
+
return cleanValue.length === 3 && validate3DigitPostalCode(cleanValue, strictValidation, allowedPrefixes, blockedPrefixes) || cleanValue.length === 5 && validate5DigitPostalCode(cleanValue, strictValidation, strictSuffixValidation, allowedPrefixes, blockedPrefixes);
|
|
2904
|
+
case "3+6":
|
|
2905
|
+
return cleanValue.length === 3 && validate3DigitPostalCode(cleanValue, strictValidation, allowedPrefixes, blockedPrefixes) || cleanValue.length === 6 && validate6DigitPostalCode(cleanValue, strictValidation, strictSuffixValidation, allowedPrefixes, blockedPrefixes);
|
|
2906
|
+
case "5+6":
|
|
2907
|
+
return cleanValue.length === 5 && validate5DigitPostalCode(cleanValue, strictValidation, strictSuffixValidation, allowedPrefixes, blockedPrefixes) || cleanValue.length === 6 && validate6DigitPostalCode(cleanValue, strictValidation, strictSuffixValidation, allowedPrefixes, blockedPrefixes);
|
|
2908
|
+
case "all":
|
|
2909
|
+
return cleanValue.length === 3 && validate3DigitPostalCode(cleanValue, strictValidation, allowedPrefixes, blockedPrefixes) || cleanValue.length === 5 && validate5DigitPostalCode(cleanValue, strictValidation, strictSuffixValidation, allowedPrefixes, blockedPrefixes) || cleanValue.length === 6 && validate6DigitPostalCode(cleanValue, strictValidation, strictSuffixValidation, allowedPrefixes, blockedPrefixes);
|
|
2910
|
+
default:
|
|
2911
|
+
return false;
|
|
2912
|
+
}
|
|
2913
|
+
};
|
|
2914
|
+
function postalCode(options) {
|
|
2915
|
+
const {
|
|
2916
|
+
required = true,
|
|
2917
|
+
format = "3+6",
|
|
2918
|
+
strictValidation = true,
|
|
2919
|
+
allowDashes = true,
|
|
2920
|
+
warn5Digit = true,
|
|
2921
|
+
allowedPrefixes,
|
|
2922
|
+
blockedPrefixes,
|
|
2923
|
+
transform,
|
|
2924
|
+
defaultValue,
|
|
2925
|
+
i18n,
|
|
2926
|
+
strictSuffixValidation = false,
|
|
2927
|
+
deprecate5Digit = false
|
|
2928
|
+
} = options ?? {};
|
|
2929
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
2930
|
+
const getMessage = (key, params) => {
|
|
2931
|
+
if (i18n) {
|
|
2932
|
+
const currentLocale2 = getLocale();
|
|
2933
|
+
const customMessages = i18n[currentLocale2];
|
|
2934
|
+
if (customMessages && customMessages[key]) {
|
|
2935
|
+
const template = customMessages[key];
|
|
2936
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
return t(`taiwan.postalCode.${key}`, params);
|
|
2940
|
+
};
|
|
2941
|
+
const preprocessFn = (val) => {
|
|
2942
|
+
if (val === "" || val === null || val === void 0) {
|
|
2943
|
+
return actualDefaultValue;
|
|
2944
|
+
}
|
|
2945
|
+
let processed = String(val).trim();
|
|
2946
|
+
if (allowDashes) {
|
|
2947
|
+
processed = processed.replace(/[-\s]/g, "");
|
|
2948
|
+
}
|
|
2949
|
+
if (processed === "" && !required) {
|
|
2950
|
+
return null;
|
|
2951
|
+
}
|
|
2952
|
+
if (transform) {
|
|
2953
|
+
processed = transform(processed);
|
|
2954
|
+
}
|
|
2955
|
+
return processed;
|
|
2956
|
+
};
|
|
2957
|
+
const baseSchema = required ? z15.preprocess(preprocessFn, z15.string()) : z15.preprocess(preprocessFn, z15.string().nullable());
|
|
2958
|
+
const schema = baseSchema.refine((val) => {
|
|
2959
|
+
if (val === null) return true;
|
|
2960
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
2961
|
+
throw new z15.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
2962
|
+
}
|
|
2963
|
+
if (val === null) return true;
|
|
2964
|
+
if (!required && val === "") return true;
|
|
2965
|
+
const cleanValue = val.replace(/[-\s]/g, "");
|
|
2966
|
+
if (format === "3" && cleanValue.length !== 3) {
|
|
2967
|
+
throw new z15.ZodError([{ code: "custom", message: getMessage("format3Only"), path: [] }]);
|
|
2968
|
+
}
|
|
2969
|
+
if (format === "5" && cleanValue.length !== 5) {
|
|
2970
|
+
throw new z15.ZodError([{ code: "custom", message: getMessage("format5Only"), path: [] }]);
|
|
2971
|
+
}
|
|
2972
|
+
if (format === "6" && cleanValue.length !== 6) {
|
|
2973
|
+
throw new z15.ZodError([{ code: "custom", message: getMessage("format6Only"), path: [] }]);
|
|
2974
|
+
}
|
|
2975
|
+
if (deprecate5Digit && cleanValue.length === 5) {
|
|
2976
|
+
throw new z15.ZodError([{ code: "custom", message: getMessage("deprecated5Digit"), path: [] }]);
|
|
2977
|
+
}
|
|
2978
|
+
if (strictSuffixValidation) {
|
|
2979
|
+
if (cleanValue.length === 5) {
|
|
2980
|
+
const prefix = cleanValue.substring(0, 3);
|
|
2981
|
+
const suffix = cleanValue.substring(3, 5);
|
|
2982
|
+
const suffixNum = parseInt(suffix, 10);
|
|
2983
|
+
const ranges = getPostalCodeRanges(prefix);
|
|
2984
|
+
if (suffixNum < ranges.range5[0] || suffixNum > ranges.range5[1]) {
|
|
2985
|
+
throw new z15.ZodError([{ code: "custom", message: getMessage("invalidSuffix"), path: [] }]);
|
|
2986
|
+
}
|
|
2987
|
+
} else if (cleanValue.length === 6) {
|
|
2988
|
+
const prefix = cleanValue.substring(0, 3);
|
|
2989
|
+
const suffix = cleanValue.substring(3, 6);
|
|
2990
|
+
const suffixNum = parseInt(suffix, 10);
|
|
2991
|
+
const ranges = getPostalCodeRanges(prefix);
|
|
2992
|
+
if (suffixNum < ranges.range6[0] || suffixNum > ranges.range6[1]) {
|
|
2993
|
+
throw new z15.ZodError([{ code: "custom", message: getMessage("invalidSuffix"), path: [] }]);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
if (!validateTaiwanPostalCode(val, format, strictValidation, strictSuffixValidation, allowDashes, allowedPrefixes, blockedPrefixes)) {
|
|
2998
|
+
throw new z15.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
2999
|
+
}
|
|
3000
|
+
if (warn5Digit && cleanValue.length === 5 && format !== "5" && !deprecate5Digit) {
|
|
3001
|
+
console.warn(getMessage("legacy5DigitWarning"));
|
|
3002
|
+
}
|
|
3003
|
+
return true;
|
|
3004
|
+
});
|
|
3005
|
+
return schema;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
// src/validators/taiwan/tel.ts
|
|
3009
|
+
import { z as z16 } from "zod";
|
|
3010
|
+
var validateTaiwanTel = (value) => {
|
|
3011
|
+
const cleanValue = value.replace(/[-\s]/g, "");
|
|
3012
|
+
if (!/^0\d{7,10}$/.test(cleanValue)) {
|
|
3013
|
+
return false;
|
|
3014
|
+
}
|
|
3015
|
+
const areaCode4 = cleanValue.substring(0, 4);
|
|
3016
|
+
if (areaCode4 === "0826") {
|
|
3017
|
+
return cleanValue.length === 9 && /^0826[6]\d{4}$/.test(cleanValue);
|
|
3018
|
+
}
|
|
3019
|
+
if (areaCode4 === "0836") {
|
|
3020
|
+
return cleanValue.length === 9 && /^0836[2-9]\d{4}$/.test(cleanValue);
|
|
3021
|
+
}
|
|
3022
|
+
const areaCode3 = cleanValue.substring(0, 3);
|
|
3023
|
+
if (areaCode3 === "037") {
|
|
3024
|
+
return cleanValue.length === 9 && /^037[2-9]\d{5}$/.test(cleanValue);
|
|
3025
|
+
}
|
|
3026
|
+
if (areaCode3 === "049") {
|
|
3027
|
+
return cleanValue.length === 10 && /^049[2-9]\d{6}$/.test(cleanValue);
|
|
3028
|
+
}
|
|
3029
|
+
if (areaCode3 === "082") {
|
|
3030
|
+
return cleanValue.length === 9 && /^082[2-57-9]\d{5}$/.test(cleanValue);
|
|
3031
|
+
}
|
|
3032
|
+
if (areaCode3 === "089") {
|
|
3033
|
+
return cleanValue.length === 9 && /^089[2-9]\d{5}$/.test(cleanValue);
|
|
3034
|
+
}
|
|
3035
|
+
const areaCode2 = cleanValue.substring(0, 2);
|
|
3036
|
+
if (areaCode2 === "02") {
|
|
3037
|
+
return cleanValue.length === 10 && /^02[235-8]\d{7}$/.test(cleanValue);
|
|
3038
|
+
}
|
|
3039
|
+
if (["03", "04", "05", "06"].includes(areaCode2)) {
|
|
3040
|
+
return cleanValue.length === 9;
|
|
3041
|
+
}
|
|
3042
|
+
if (areaCode2 === "07") {
|
|
3043
|
+
return cleanValue.length === 9 && /^07[2-9]\d{6}$/.test(cleanValue);
|
|
3044
|
+
}
|
|
3045
|
+
if (areaCode2 === "08") {
|
|
3046
|
+
return cleanValue.length === 9 && /^08[478]\d{6}$/.test(cleanValue);
|
|
3047
|
+
}
|
|
3048
|
+
return false;
|
|
3049
|
+
};
|
|
3050
|
+
function tel(options) {
|
|
3051
|
+
const { required = true, whitelist, transform, defaultValue, i18n } = options ?? {};
|
|
3052
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
3053
|
+
const getMessage = (key, params) => {
|
|
3054
|
+
if (i18n) {
|
|
3055
|
+
const currentLocale2 = getLocale();
|
|
3056
|
+
const customMessages = i18n[currentLocale2];
|
|
3057
|
+
if (customMessages && customMessages[key]) {
|
|
3058
|
+
const template = customMessages[key];
|
|
3059
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
return t(`taiwan.tel.${key}`, params);
|
|
3063
|
+
};
|
|
3064
|
+
const preprocessFn = (val) => {
|
|
3065
|
+
if (val === null || val === void 0) {
|
|
3066
|
+
return actualDefaultValue;
|
|
3067
|
+
}
|
|
3068
|
+
let processed = String(val).trim();
|
|
3069
|
+
if (processed === "") {
|
|
3070
|
+
if (whitelist && whitelist.includes("")) {
|
|
3071
|
+
return "";
|
|
3072
|
+
}
|
|
3073
|
+
if (!required) {
|
|
3074
|
+
return actualDefaultValue;
|
|
3075
|
+
}
|
|
3076
|
+
return actualDefaultValue;
|
|
3077
|
+
}
|
|
3078
|
+
if (transform) {
|
|
3079
|
+
processed = transform(processed);
|
|
3080
|
+
}
|
|
3081
|
+
return processed;
|
|
3082
|
+
};
|
|
3083
|
+
const baseSchema = required ? z16.preprocess(preprocessFn, z16.string()) : z16.preprocess(preprocessFn, z16.string().nullable());
|
|
3084
|
+
const schema = baseSchema.refine((val) => {
|
|
3085
|
+
if (val === null) return true;
|
|
3086
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
3087
|
+
throw new z16.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
3088
|
+
}
|
|
3089
|
+
if (val === null) return true;
|
|
3090
|
+
if (!required && val === "") return true;
|
|
3091
|
+
if (whitelist && whitelist.length > 0) {
|
|
3092
|
+
if (whitelist.includes(val)) {
|
|
3093
|
+
return true;
|
|
3094
|
+
}
|
|
3095
|
+
throw new z16.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }]);
|
|
3096
|
+
}
|
|
3097
|
+
if (!validateTaiwanTel(val)) {
|
|
3098
|
+
throw new z16.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
3099
|
+
}
|
|
3100
|
+
return true;
|
|
3101
|
+
});
|
|
3102
|
+
return schema;
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
// src/validators/taiwan/fax.ts
|
|
3106
|
+
import { z as z17 } from "zod";
|
|
3107
|
+
var validateTaiwanFax = (value) => {
|
|
3108
|
+
const cleanValue = value.replace(/[-\s]/g, "");
|
|
3109
|
+
if (!/^0\d{7,10}$/.test(cleanValue)) {
|
|
3110
|
+
return false;
|
|
3111
|
+
}
|
|
3112
|
+
const areaCode4 = cleanValue.substring(0, 4);
|
|
3113
|
+
if (areaCode4 === "0826") {
|
|
3114
|
+
return cleanValue.length === 9 && /^0826[6]\d{4}$/.test(cleanValue);
|
|
3115
|
+
}
|
|
3116
|
+
if (areaCode4 === "0836") {
|
|
3117
|
+
return cleanValue.length === 9 && /^0836[2-9]\d{4}$/.test(cleanValue);
|
|
3118
|
+
}
|
|
3119
|
+
const areaCode3 = cleanValue.substring(0, 3);
|
|
3120
|
+
if (areaCode3 === "037") {
|
|
3121
|
+
return cleanValue.length === 9 && /^037[2-9]\d{5}$/.test(cleanValue);
|
|
3122
|
+
}
|
|
3123
|
+
if (areaCode3 === "049") {
|
|
3124
|
+
return cleanValue.length === 10 && /^049[2-9]\d{6}$/.test(cleanValue);
|
|
3125
|
+
}
|
|
3126
|
+
if (areaCode3 === "082") {
|
|
3127
|
+
return cleanValue.length === 9 && /^082[2-57-9]\d{5}$/.test(cleanValue);
|
|
3128
|
+
}
|
|
3129
|
+
if (areaCode3 === "089") {
|
|
3130
|
+
return cleanValue.length === 9 && /^089[2-9]\d{5}$/.test(cleanValue);
|
|
3131
|
+
}
|
|
3132
|
+
const areaCode2 = cleanValue.substring(0, 2);
|
|
3133
|
+
if (areaCode2 === "02") {
|
|
3134
|
+
return cleanValue.length === 10 && /^02[235-8]\d{7}$/.test(cleanValue);
|
|
3135
|
+
}
|
|
3136
|
+
if (["03", "04", "05", "06"].includes(areaCode2)) {
|
|
3137
|
+
return cleanValue.length === 9;
|
|
3138
|
+
}
|
|
3139
|
+
if (areaCode2 === "07") {
|
|
3140
|
+
return cleanValue.length === 9 && /^07[2-9]\d{6}$/.test(cleanValue);
|
|
3141
|
+
}
|
|
3142
|
+
if (areaCode2 === "08") {
|
|
3143
|
+
return cleanValue.length === 9 && /^08[478]\d{6}$/.test(cleanValue);
|
|
3144
|
+
}
|
|
3145
|
+
return false;
|
|
3146
|
+
};
|
|
3147
|
+
function fax(options) {
|
|
3148
|
+
const { required = true, whitelist, transform, defaultValue, i18n } = options ?? {};
|
|
3149
|
+
const actualDefaultValue = defaultValue ?? (required ? "" : null);
|
|
3150
|
+
const getMessage = (key, params) => {
|
|
3151
|
+
if (i18n) {
|
|
3152
|
+
const currentLocale2 = getLocale();
|
|
3153
|
+
const customMessages = i18n[currentLocale2];
|
|
3154
|
+
if (customMessages && customMessages[key]) {
|
|
3155
|
+
const template = customMessages[key];
|
|
3156
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
return t(`taiwan.fax.${key}`, params);
|
|
3160
|
+
};
|
|
3161
|
+
const preprocessFn = (val) => {
|
|
3162
|
+
if (val === null || val === void 0) {
|
|
3163
|
+
return actualDefaultValue;
|
|
3164
|
+
}
|
|
3165
|
+
let processed = String(val).trim();
|
|
3166
|
+
if (processed === "") {
|
|
3167
|
+
if (whitelist && whitelist.includes("")) {
|
|
3168
|
+
return "";
|
|
3169
|
+
}
|
|
3170
|
+
if (!required) {
|
|
3171
|
+
return actualDefaultValue;
|
|
3172
|
+
}
|
|
3173
|
+
return actualDefaultValue;
|
|
3174
|
+
}
|
|
3175
|
+
if (transform) {
|
|
3176
|
+
processed = transform(processed);
|
|
3177
|
+
}
|
|
3178
|
+
return processed;
|
|
3179
|
+
};
|
|
3180
|
+
const baseSchema = required ? z17.preprocess(preprocessFn, z17.string()) : z17.preprocess(preprocessFn, z17.string().nullable());
|
|
3181
|
+
const schema = baseSchema.refine((val) => {
|
|
3182
|
+
if (val === null) return true;
|
|
3183
|
+
if (required && (val === "" || val === "null" || val === "undefined")) {
|
|
3184
|
+
throw new z17.ZodError([{ code: "custom", message: getMessage("required"), path: [] }]);
|
|
3185
|
+
}
|
|
3186
|
+
if (val === null) return true;
|
|
3187
|
+
if (!required && val === "") return true;
|
|
3188
|
+
if (whitelist && whitelist.length > 0) {
|
|
3189
|
+
if (whitelist.includes(val)) {
|
|
3190
|
+
return true;
|
|
3191
|
+
}
|
|
3192
|
+
throw new z17.ZodError([{ code: "custom", message: getMessage("notInWhitelist"), path: [] }]);
|
|
3193
|
+
}
|
|
3194
|
+
if (!validateTaiwanFax(val)) {
|
|
3195
|
+
throw new z17.ZodError([{ code: "custom", message: getMessage("invalid"), path: [] }]);
|
|
3196
|
+
}
|
|
3197
|
+
return true;
|
|
295
3198
|
});
|
|
296
3199
|
return schema;
|
|
297
3200
|
}
|
|
298
3201
|
export {
|
|
3202
|
+
DATETIME_PATTERNS,
|
|
3203
|
+
ID_PATTERNS,
|
|
3204
|
+
TIME_PATTERNS,
|
|
3205
|
+
VALID_3_DIGIT_PREFIXES,
|
|
299
3206
|
boolean,
|
|
3207
|
+
businessId,
|
|
3208
|
+
date,
|
|
3209
|
+
datetime,
|
|
3210
|
+
detectIdType,
|
|
300
3211
|
email,
|
|
3212
|
+
fax,
|
|
3213
|
+
file,
|
|
301
3214
|
getLocale,
|
|
302
|
-
|
|
3215
|
+
id,
|
|
3216
|
+
mobile,
|
|
3217
|
+
nationalId,
|
|
3218
|
+
normalizeDateTimeValue,
|
|
3219
|
+
normalizeTime,
|
|
303
3220
|
number,
|
|
3221
|
+
parseDateTimeValue,
|
|
3222
|
+
parseTimeToMinutes,
|
|
304
3223
|
password,
|
|
3224
|
+
postalCode,
|
|
305
3225
|
setLocale,
|
|
3226
|
+
tel,
|
|
306
3227
|
text,
|
|
307
|
-
|
|
3228
|
+
time,
|
|
3229
|
+
url,
|
|
3230
|
+
validate3DigitPostalCode,
|
|
3231
|
+
validate5DigitPostalCode,
|
|
3232
|
+
validate6DigitPostalCode,
|
|
3233
|
+
validateCitizenId,
|
|
3234
|
+
validateDateTimeFormat,
|
|
3235
|
+
validateIdType,
|
|
3236
|
+
validateNewResidentId,
|
|
3237
|
+
validateOldResidentId,
|
|
3238
|
+
validateTaiwanBusinessId,
|
|
3239
|
+
validateTaiwanFax,
|
|
3240
|
+
validateTaiwanMobile,
|
|
3241
|
+
validateTaiwanNationalId,
|
|
3242
|
+
validateTaiwanPostalCode,
|
|
3243
|
+
validateTaiwanTel,
|
|
3244
|
+
validateTimeFormat
|
|
308
3245
|
};
|