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