@hy_ong/zod-kit 0.2.1 → 0.2.3
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/dist/{chunk-MCDESS3T.js → chunk-2JGRV3JO.js} +1 -1
- package/dist/{chunk-4LYZAO3P.js → chunk-3QLUXIY2.js} +1 -1
- package/dist/chunk-3ZF5JO3F.js +61 -0
- package/dist/{chunk-OP4KV3BY.cjs → chunk-4AQB4RSU.cjs} +3 -3
- package/dist/{chunk-UFNVCUPQ.cjs → chunk-53EEWALQ.cjs} +3 -3
- package/dist/{chunk-R5G4V7C6.cjs → chunk-5ZMTAI4G.cjs} +3 -3
- package/dist/{chunk-RKHX3DGH.js → chunk-6DFP7XY2.js} +1 -1
- package/dist/{chunk-P2NONIMS.js → chunk-6IAPM7BP.js} +1 -1
- package/dist/{chunk-AWV2IT66.js → chunk-7SKH66CM.js} +1 -1
- package/dist/chunk-7X3XPK6A.js +92 -0
- package/dist/{chunk-FVO4743A.cjs → chunk-A2GAEU4O.cjs} +3 -3
- package/dist/{chunk-P364KRO5.js → chunk-AI4U42JV.js} +1 -1
- package/dist/{chunk-5ZEKWPSE.cjs → chunk-AYCXAJRA.cjs} +3 -3
- package/dist/{chunk-5LS4DSRQ.cjs → chunk-BZSPJJYT.cjs} +3 -3
- package/dist/chunk-BZWQPSHO.cjs +92 -0
- package/dist/{chunk-KARFFIMP.js → chunk-D55YFP5R.js} +1 -1
- package/dist/{chunk-I2RJMDXN.js → chunk-E52GRXYY.js} +1 -1
- package/dist/{chunk-VDOAPLA6.cjs → chunk-EDTNS2XL.cjs} +3 -3
- package/dist/{chunk-YALLOVNO.cjs → chunk-FEL432I2.cjs} +3 -3
- package/dist/chunk-FMPHW7ID.cjs +61 -0
- package/dist/{chunk-OMFQ7Z63.cjs → chunk-FP4O2ICM.cjs} +3 -3
- package/dist/{chunk-AANSHH2O.cjs → chunk-G747FHUZ.cjs} +3 -3
- package/dist/{chunk-JZ2SHRGZ.js → chunk-G7FLGJYD.js} +1 -1
- package/dist/{chunk-LKPXHW5N.cjs → chunk-H25N5GP6.cjs} +3 -3
- package/dist/{chunk-FC6VDOC7.js → chunk-H6STFX4I.js} +11 -2
- package/dist/{chunk-MAQRXYE6.js → chunk-HQ4RAMSD.js} +1 -1
- package/dist/{chunk-6X22I6NQ.cjs → chunk-HZ2WESSL.cjs} +3 -3
- package/dist/{chunk-W2EWMV3A.cjs → chunk-IWR3H7IH.cjs} +3 -3
- package/dist/{chunk-5GAZQDVS.cjs → chunk-JZEF5Q3W.cjs} +13 -4
- package/dist/{chunk-77KZUPPN.cjs → chunk-KIUO2HIR.cjs} +3 -3
- package/dist/{chunk-OEK7QSQP.js → chunk-LBH5U2DZ.js} +1 -1
- package/dist/{chunk-PGSDXR2I.js → chunk-LC4RNKBM.js} +1 -1
- package/dist/{chunk-YAU6JCYL.cjs → chunk-LNWEJED7.cjs} +3 -3
- package/dist/{chunk-WWRFBLCR.cjs → chunk-LXFRQLH4.cjs} +3 -3
- package/dist/{chunk-B3U5G3AA.js → chunk-NWQSOSNF.js} +1 -1
- package/dist/{chunk-LIQSVJLS.js → chunk-OGU7AIZF.js} +1 -1
- package/dist/{chunk-NKCYXBGX.js → chunk-OKO6WO6M.js} +1 -1
- package/dist/{chunk-6OGDPSWT.js → chunk-OSUPJCBA.js} +1 -1
- package/dist/{chunk-ZBOQCXD4.js → chunk-POIDES2L.js} +190 -0
- package/dist/{chunk-MG25BEV4.cjs → chunk-Q24GYUTO.cjs} +3 -3
- package/dist/{chunk-YWV2BBXN.cjs → chunk-Q7TUNJD4.cjs} +190 -0
- package/dist/{chunk-DRXPGQM6.cjs → chunk-QQWX3ICK.cjs} +3 -3
- package/dist/{chunk-EAU42EVH.js → chunk-RFWCYULE.js} +1 -1
- package/dist/{chunk-VCRKYMJM.js → chunk-RHKBT3M2.js} +1 -1
- package/dist/{chunk-36NWHESN.js → chunk-RVGCMQ4J.js} +1 -1
- package/dist/{chunk-IJEEM3DI.js → chunk-T7PG4JDW.js} +1 -1
- package/dist/{chunk-JBNCMS42.cjs → chunk-TDEXEIHH.cjs} +3 -3
- package/dist/{chunk-VP5CCP5F.cjs → chunk-TPXRQT2H.cjs} +3 -3
- package/dist/{chunk-DPXRMSB2.js → chunk-TRQMRHFM.js} +1 -1
- package/dist/{chunk-ZFQQXWNB.js → chunk-U2PB6XEO.js} +1 -1
- package/dist/{chunk-G6DV7LX7.cjs → chunk-UCPKW43K.cjs} +3 -3
- package/dist/{chunk-PL2GERLG.cjs → chunk-V2KKGSKQ.cjs} +3 -3
- package/dist/{chunk-AI72FMOF.cjs → chunk-VKBNKPFO.cjs} +3 -3
- package/dist/{chunk-5OGW2ERW.js → chunk-XAN4CAVH.js} +1 -1
- package/dist/{chunk-CFFCBWYL.cjs → chunk-ZCX22PY4.cjs} +3 -3
- package/dist/{chunk-TSHL7ZO2.js → chunk-ZXPRRNZR.js} +1 -1
- package/dist/common/boolean.cjs +3 -3
- package/dist/common/boolean.js +2 -2
- package/dist/common/color.cjs +3 -3
- package/dist/common/color.js +2 -2
- package/dist/common/coordinate.cjs +3 -3
- package/dist/common/coordinate.js +2 -2
- package/dist/common/credit-card.cjs +3 -3
- package/dist/common/credit-card.js +2 -2
- package/dist/common/date.cjs +3 -3
- package/dist/common/date.js +2 -2
- package/dist/common/datetime.cjs +3 -3
- package/dist/common/datetime.js +2 -2
- package/dist/common/email.cjs +3 -3
- package/dist/common/email.js +2 -2
- package/dist/common/file.cjs +3 -3
- package/dist/common/file.js +2 -2
- package/dist/common/id.cjs +3 -3
- package/dist/common/id.d.cts +34 -8
- package/dist/common/id.d.ts +34 -8
- package/dist/common/id.js +2 -2
- package/dist/common/ip.cjs +3 -3
- package/dist/common/ip.js +2 -2
- package/dist/common/many-of.cjs +7 -0
- package/dist/common/many-of.d.cts +111 -0
- package/dist/common/many-of.d.ts +111 -0
- package/dist/common/many-of.js +7 -0
- package/dist/common/number.cjs +3 -3
- package/dist/common/number.js +2 -2
- package/dist/common/one-of.cjs +7 -0
- package/dist/common/one-of.d.cts +104 -0
- package/dist/common/one-of.d.ts +104 -0
- package/dist/common/one-of.js +7 -0
- package/dist/common/password.cjs +3 -3
- package/dist/common/password.js +2 -2
- package/dist/common/text.cjs +3 -3
- package/dist/common/text.js +2 -2
- package/dist/common/time.cjs +3 -3
- package/dist/common/time.js +2 -2
- package/dist/common/url.cjs +3 -3
- package/dist/common/url.js +2 -2
- package/dist/index.cjs +35 -27
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +46 -38
- package/dist/taiwan/bank-account.cjs +3 -3
- package/dist/taiwan/bank-account.js +2 -2
- package/dist/taiwan/business-id.cjs +3 -3
- package/dist/taiwan/business-id.js +2 -2
- package/dist/taiwan/fax.cjs +3 -3
- package/dist/taiwan/fax.js +2 -2
- package/dist/taiwan/invoice.cjs +3 -3
- package/dist/taiwan/invoice.js +2 -2
- package/dist/taiwan/license-plate.cjs +3 -3
- package/dist/taiwan/license-plate.js +2 -2
- package/dist/taiwan/mobile.cjs +3 -3
- package/dist/taiwan/mobile.js +2 -2
- package/dist/taiwan/national-id.cjs +3 -3
- package/dist/taiwan/national-id.js +2 -2
- package/dist/taiwan/passport.cjs +3 -3
- package/dist/taiwan/passport.js +2 -2
- package/dist/taiwan/postal-code.cjs +3 -3
- package/dist/taiwan/postal-code.js +2 -2
- package/dist/taiwan/tel.cjs +3 -3
- package/dist/taiwan/tel.js +2 -2
- package/package.json +15 -5
- package/src/i18n/locales/en-GB.json +19 -0
- package/src/i18n/locales/en-US.json +20 -1
- package/src/i18n/locales/id-ID.json +19 -0
- package/src/i18n/locales/ja-JP.json +19 -0
- package/src/i18n/locales/ko-KR.json +19 -0
- package/src/i18n/locales/ms-MY.json +19 -0
- package/src/i18n/locales/th-TH.json +19 -0
- package/src/i18n/locales/vi-VN.json +19 -0
- package/src/i18n/locales/zh-CN.json +19 -0
- package/src/i18n/locales/zh-TW.json +19 -0
- package/src/index.ts +2 -0
- package/src/validators/common/id.ts +45 -3
- package/src/validators/common/many-of.ts +219 -0
- package/src/validators/common/one-of.ts +172 -0
- package/tests/common/id.test.ts +68 -3
- package/tests/common/many-of.test.ts +198 -0
- package/tests/common/one-of.test.ts +136 -0
- package/tsup.config.ts +2 -0
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "ビジネスメールアドレスのみ使用可能です",
|
|
18
18
|
"noDisposable": "使い捨てメールアドレスは使用できません"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "必須項目です",
|
|
22
|
+
"invalid": "次のいずれかである必要があります:${values}",
|
|
23
|
+
"minSelect": "${min}個以上選択してください",
|
|
24
|
+
"maxSelect": "${max}個以下で選択してください",
|
|
25
|
+
"duplicate": "重複した値は許可されていません"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "必須項目です",
|
|
22
29
|
"invalid": "無効なURL形式です",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "有限の数である必要があります",
|
|
69
76
|
"precision": "小数点以下は ${precision} 桁以内である必要があります"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "必須項目です",
|
|
80
|
+
"invalid": "次のいずれかである必要があります:${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "必須項目です",
|
|
73
84
|
"invalid": "無効なID形式です",
|
|
@@ -75,6 +86,14 @@
|
|
|
75
86
|
"maxLength": "${maxLength}文字以下で入力してください",
|
|
76
87
|
"numeric": "数値IDである必要があります",
|
|
77
88
|
"uuid": "有効なUUIDである必要があります",
|
|
89
|
+
"uuidv1": "有効なUUID v1である必要があります",
|
|
90
|
+
"uuidv2": "有効なUUID v2である必要があります",
|
|
91
|
+
"uuidv3": "有効なUUID v3である必要があります",
|
|
92
|
+
"uuidv4": "有効なUUID v4である必要があります",
|
|
93
|
+
"uuidv5": "有効なUUID v5である必要があります",
|
|
94
|
+
"uuidv6": "有効なUUID v6である必要があります",
|
|
95
|
+
"uuidv7": "有効なUUID v7である必要があります",
|
|
96
|
+
"uuidv8": "有効なUUID v8である必要があります",
|
|
78
97
|
"objectId": "有効なMongoDB ObjectIdである必要があります",
|
|
79
98
|
"nanoid": "有効なNano IDである必要があります",
|
|
80
99
|
"snowflake": "有効なSnowflake IDである必要があります",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "비즈니스 이메일 주소만 허용됩니다",
|
|
18
18
|
"noDisposable": "일회용 이메일 주소는 허용되지 않습니다"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "필수 항목입니다",
|
|
22
|
+
"invalid": "다음 중 하나여야 합니다: ${values}",
|
|
23
|
+
"minSelect": "최소 ${min}개를 선택해야 합니다",
|
|
24
|
+
"maxSelect": "최대 ${max}개까지 선택할 수 있습니다",
|
|
25
|
+
"duplicate": "중복된 값은 허용되지 않습니다"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "필수 항목입니다",
|
|
22
29
|
"invalid": "유효하지 않은 URL 형식입니다",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "유한한 수여야 합니다",
|
|
69
76
|
"precision": "소수점 이하 ${precision}자리 이내여야 합니다"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "필수 항목입니다",
|
|
80
|
+
"invalid": "다음 중 하나여야 합니다: ${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "필수 항목입니다",
|
|
73
84
|
"invalid": "유효하지 않은 ID 형식입니다",
|
|
@@ -75,6 +86,14 @@
|
|
|
75
86
|
"maxLength": "최대 ${maxLength}자 이하여야 합니다",
|
|
76
87
|
"numeric": "숫자 ID여야 합니다",
|
|
77
88
|
"uuid": "유효한 UUID여야 합니다",
|
|
89
|
+
"uuidv1": "유효한 UUID v1이어야 합니다",
|
|
90
|
+
"uuidv2": "유효한 UUID v2이어야 합니다",
|
|
91
|
+
"uuidv3": "유효한 UUID v3이어야 합니다",
|
|
92
|
+
"uuidv4": "유효한 UUID v4이어야 합니다",
|
|
93
|
+
"uuidv5": "유효한 UUID v5이어야 합니다",
|
|
94
|
+
"uuidv6": "유효한 UUID v6이어야 합니다",
|
|
95
|
+
"uuidv7": "유효한 UUID v7이어야 합니다",
|
|
96
|
+
"uuidv8": "유효한 UUID v8이어야 합니다",
|
|
78
97
|
"objectId": "유효한 MongoDB ObjectId여야 합니다",
|
|
79
98
|
"nanoid": "유효한 Nano ID여야 합니다",
|
|
80
99
|
"snowflake": "유효한 Snowflake ID여야 합니다",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "Hanya alamat e-mel perniagaan dibenarkan",
|
|
18
18
|
"noDisposable": "Alamat e-mel pakai buang tidak dibenarkan"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "Diperlukan",
|
|
22
|
+
"invalid": "Mesti daripada: ${values}",
|
|
23
|
+
"minSelect": "Mesti pilih sekurang-kurangnya ${min} item",
|
|
24
|
+
"maxSelect": "Mesti pilih paling banyak ${max} item",
|
|
25
|
+
"duplicate": "Nilai pendua tidak dibenarkan"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "Wajib diisi",
|
|
22
29
|
"invalid": "Format URL tidak sah",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "Mesti nombor terhingga",
|
|
69
76
|
"precision": "Mesti tidak melebihi ${precision} tempat perpuluhan"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "Diperlukan",
|
|
80
|
+
"invalid": "Mesti salah satu daripada: ${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "Wajib diisi",
|
|
73
84
|
"invalid": "Format ID tidak sah",
|
|
@@ -75,6 +86,14 @@
|
|
|
75
86
|
"maxLength": "Mesti tidak melebihi ${maxLength} aksara",
|
|
76
87
|
"numeric": "Mesti ID numerik",
|
|
77
88
|
"uuid": "Mesti UUID yang sah",
|
|
89
|
+
"uuidv1": "Mesti UUID v1 yang sah",
|
|
90
|
+
"uuidv2": "Mesti UUID v2 yang sah",
|
|
91
|
+
"uuidv3": "Mesti UUID v3 yang sah",
|
|
92
|
+
"uuidv4": "Mesti UUID v4 yang sah",
|
|
93
|
+
"uuidv5": "Mesti UUID v5 yang sah",
|
|
94
|
+
"uuidv6": "Mesti UUID v6 yang sah",
|
|
95
|
+
"uuidv7": "Mesti UUID v7 yang sah",
|
|
96
|
+
"uuidv8": "Mesti UUID v8 yang sah",
|
|
78
97
|
"objectId": "Mesti MongoDB ObjectId yang sah",
|
|
79
98
|
"nanoid": "Mesti Nano ID yang sah",
|
|
80
99
|
"snowflake": "Mesti Snowflake ID yang sah",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "อนุญาตเฉพาะอีเมลธุรกิจเท่านั้น",
|
|
18
18
|
"noDisposable": "ไม่อนุญาตอีเมลแบบใช้แล้วทิ้ง"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "จำเป็น",
|
|
22
|
+
"invalid": "ต้องเป็นหนึ่งใน: ${values}",
|
|
23
|
+
"minSelect": "ต้องเลือกอย่างน้อย ${min} รายการ",
|
|
24
|
+
"maxSelect": "เลือกได้มากสุด ${max} รายการ",
|
|
25
|
+
"duplicate": "ไม่อนุญาตให้มีค่าซ้ำ"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "จำเป็นต้องกรอก",
|
|
22
29
|
"invalid": "รูปแบบ URL ไม่ถูกต้อง",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "ต้องเป็นจำนวนจำกัด",
|
|
69
76
|
"precision": "ต้องมีทศนิยมไม่เกิน ${precision} ตำแหน่ง"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "จำเป็น",
|
|
80
|
+
"invalid": "ต้องเป็นหนึ่งใน: ${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "จำเป็นต้องกรอก",
|
|
73
84
|
"invalid": "รูปแบบ ID ไม่ถูกต้อง",
|
|
@@ -75,6 +86,14 @@
|
|
|
75
86
|
"maxLength": "ต้องไม่เกิน ${maxLength} ตัวอักษร",
|
|
76
87
|
"numeric": "ต้องเป็น ID ตัวเลข",
|
|
77
88
|
"uuid": "ต้องเป็น UUID ที่ถูกต้อง",
|
|
89
|
+
"uuidv1": "ต้องเป็น UUID v1 ที่ถูกต้อง",
|
|
90
|
+
"uuidv2": "ต้องเป็น UUID v2 ที่ถูกต้อง",
|
|
91
|
+
"uuidv3": "ต้องเป็น UUID v3 ที่ถูกต้อง",
|
|
92
|
+
"uuidv4": "ต้องเป็น UUID v4 ที่ถูกต้อง",
|
|
93
|
+
"uuidv5": "ต้องเป็น UUID v5 ที่ถูกต้อง",
|
|
94
|
+
"uuidv6": "ต้องเป็น UUID v6 ที่ถูกต้อง",
|
|
95
|
+
"uuidv7": "ต้องเป็น UUID v7 ที่ถูกต้อง",
|
|
96
|
+
"uuidv8": "ต้องเป็น UUID v8 ที่ถูกต้อง",
|
|
78
97
|
"objectId": "ต้องเป็น MongoDB ObjectId ที่ถูกต้อง",
|
|
79
98
|
"nanoid": "ต้องเป็น Nano ID ที่ถูกต้อง",
|
|
80
99
|
"snowflake": "ต้องเป็น Snowflake ID ที่ถูกต้อง",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "Chỉ cho phép địa chỉ email doanh nghiệp",
|
|
18
18
|
"noDisposable": "Không cho phép địa chỉ email dùng một lần"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "Bắt buộc",
|
|
22
|
+
"invalid": "Phải thuộc: ${values}",
|
|
23
|
+
"minSelect": "Phải chọn ít nhất ${min} mục",
|
|
24
|
+
"maxSelect": "Chỉ được chọn tối đa ${max} mục",
|
|
25
|
+
"duplicate": "Không được phép có giá trị trùng lặp"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "Bắt buộc",
|
|
22
29
|
"invalid": "Định dạng URL không hợp lệ",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "Phải là số hữu hạn",
|
|
69
76
|
"precision": "Phải có tối đa ${precision} chữ số thập phân"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "Bắt buộc",
|
|
80
|
+
"invalid": "Phải là một trong: ${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "Bắt buộc",
|
|
73
84
|
"invalid": "Định dạng ID không hợp lệ",
|
|
@@ -75,6 +86,14 @@
|
|
|
75
86
|
"maxLength": "Phải có tối đa ${maxLength} ký tự",
|
|
76
87
|
"numeric": "Phải là ID dạng số",
|
|
77
88
|
"uuid": "Phải là UUID hợp lệ",
|
|
89
|
+
"uuidv1": "Phải là UUID v1 hợp lệ",
|
|
90
|
+
"uuidv2": "Phải là UUID v2 hợp lệ",
|
|
91
|
+
"uuidv3": "Phải là UUID v3 hợp lệ",
|
|
92
|
+
"uuidv4": "Phải là UUID v4 hợp lệ",
|
|
93
|
+
"uuidv5": "Phải là UUID v5 hợp lệ",
|
|
94
|
+
"uuidv6": "Phải là UUID v6 hợp lệ",
|
|
95
|
+
"uuidv7": "Phải là UUID v7 hợp lệ",
|
|
96
|
+
"uuidv8": "Phải là UUID v8 hợp lệ",
|
|
78
97
|
"objectId": "Phải là MongoDB ObjectId hợp lệ",
|
|
79
98
|
"nanoid": "Phải là Nano ID hợp lệ",
|
|
80
99
|
"snowflake": "Phải là Snowflake ID hợp lệ",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "仅允许企业邮箱地址",
|
|
18
18
|
"noDisposable": "不允许使用临时邮箱地址"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "必填",
|
|
22
|
+
"invalid": "必须为以下值之一:${values}",
|
|
23
|
+
"minSelect": "至少需选择 ${min} 项",
|
|
24
|
+
"maxSelect": "最多只能选择 ${max} 项",
|
|
25
|
+
"duplicate": "不允许重复的值"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "必填",
|
|
22
29
|
"invalid": "无效的 URL 格式",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "必须为有限数字",
|
|
69
76
|
"precision": "小数位数不可超过 ${precision} 位"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "必填",
|
|
80
|
+
"invalid": "必须为以下其中一个值:${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "必填",
|
|
73
84
|
"invalid": "无效的 ID 格式",
|
|
@@ -75,6 +86,14 @@
|
|
|
75
86
|
"maxLength": "长度最多 ${maxLength} 个字符",
|
|
76
87
|
"numeric": "必须为数字 ID",
|
|
77
88
|
"uuid": "必须为有效的 UUID",
|
|
89
|
+
"uuidv1": "必须为有效的 UUID v1",
|
|
90
|
+
"uuidv2": "必须为有效的 UUID v2",
|
|
91
|
+
"uuidv3": "必须为有效的 UUID v3",
|
|
92
|
+
"uuidv4": "必须为有效的 UUID v4",
|
|
93
|
+
"uuidv5": "必须为有效的 UUID v5",
|
|
94
|
+
"uuidv6": "必须为有效的 UUID v6",
|
|
95
|
+
"uuidv7": "必须为有效的 UUID v7",
|
|
96
|
+
"uuidv8": "必须为有效的 UUID v8",
|
|
78
97
|
"objectId": "必须为有效的 MongoDB ObjectId",
|
|
79
98
|
"nanoid": "必须为有效的 Nano ID",
|
|
80
99
|
"snowflake": "必须为有效的 Snowflake ID",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "僅允許企業郵箱地址",
|
|
18
18
|
"noDisposable": "不允許使用臨時郵箱地址"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "必填",
|
|
22
|
+
"invalid": "必須為以下值之一:${values}",
|
|
23
|
+
"minSelect": "至少需選擇 ${min} 項",
|
|
24
|
+
"maxSelect": "最多只能選擇 ${max} 項",
|
|
25
|
+
"duplicate": "不允許重複的值"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "必填",
|
|
22
29
|
"invalid": "無效的 URL 格式",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "必須為有限數字",
|
|
69
76
|
"precision": "小數位數不可超過 ${precision} 位"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "必填",
|
|
80
|
+
"invalid": "必須為以下其中一個值:${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "必填",
|
|
73
84
|
"invalid": "無效的 ID 格式",
|
|
@@ -75,6 +86,14 @@
|
|
|
75
86
|
"maxLength": "長度最多 ${maxLength} 字元",
|
|
76
87
|
"numeric": "必須為數字 ID",
|
|
77
88
|
"uuid": "必須為有效的 UUID",
|
|
89
|
+
"uuidv1": "必須為有效的 UUID v1",
|
|
90
|
+
"uuidv2": "必須為有效的 UUID v2",
|
|
91
|
+
"uuidv3": "必須為有效的 UUID v3",
|
|
92
|
+
"uuidv4": "必須為有效的 UUID v4",
|
|
93
|
+
"uuidv5": "必須為有效的 UUID v5",
|
|
94
|
+
"uuidv6": "必須為有效的 UUID v6",
|
|
95
|
+
"uuidv7": "必須為有效的 UUID v7",
|
|
96
|
+
"uuidv8": "必須為有效的 UUID v8",
|
|
78
97
|
"objectId": "必須為有效的 MongoDB ObjectId",
|
|
79
98
|
"nanoid": "必須為有效的 Nano ID",
|
|
80
99
|
"snowflake": "必須為有效的 Snowflake ID",
|
package/src/index.ts
CHANGED
|
@@ -7,8 +7,10 @@ export * from "./validators/common/datetime"
|
|
|
7
7
|
export * from "./validators/common/email"
|
|
8
8
|
export * from "./validators/common/file"
|
|
9
9
|
export * from "./validators/common/id"
|
|
10
|
+
export * from "./validators/common/many-of"
|
|
10
11
|
export * from "./validators/common/ip"
|
|
11
12
|
export * from "./validators/common/number"
|
|
13
|
+
export * from "./validators/common/one-of"
|
|
12
14
|
export * from "./validators/common/password"
|
|
13
15
|
export * from "./validators/common/text"
|
|
14
16
|
export * from "./validators/common/time"
|
|
@@ -22,6 +22,14 @@ import { getLocale, type Locale } from "../../config"
|
|
|
22
22
|
* @property {string} [maxLength] - Message when ID is too long
|
|
23
23
|
* @property {string} [numeric] - Message when numeric ID format is invalid
|
|
24
24
|
* @property {string} [uuid] - Message when UUID format is invalid
|
|
25
|
+
* @property {string} [uuidv1] - Message when UUID v1 format is invalid
|
|
26
|
+
* @property {string} [uuidv2] - Message when UUID v2 format is invalid
|
|
27
|
+
* @property {string} [uuidv3] - Message when UUID v3 format is invalid
|
|
28
|
+
* @property {string} [uuidv4] - Message when UUID v4 format is invalid
|
|
29
|
+
* @property {string} [uuidv5] - Message when UUID v5 format is invalid
|
|
30
|
+
* @property {string} [uuidv6] - Message when UUID v6 format is invalid
|
|
31
|
+
* @property {string} [uuidv7] - Message when UUID v7 format is invalid
|
|
32
|
+
* @property {string} [uuidv8] - Message when UUID v8 format is invalid
|
|
25
33
|
* @property {string} [objectId] - Message when MongoDB ObjectId format is invalid
|
|
26
34
|
* @property {string} [nanoid] - Message when Nano ID format is invalid
|
|
27
35
|
* @property {string} [snowflake] - Message when Snowflake ID format is invalid
|
|
@@ -41,6 +49,14 @@ export type IdMessages = {
|
|
|
41
49
|
maxLength?: string
|
|
42
50
|
numeric?: string
|
|
43
51
|
uuid?: string
|
|
52
|
+
uuidv1?: string
|
|
53
|
+
uuidv2?: string
|
|
54
|
+
uuidv3?: string
|
|
55
|
+
uuidv4?: string
|
|
56
|
+
uuidv5?: string
|
|
57
|
+
uuidv6?: string
|
|
58
|
+
uuidv7?: string
|
|
59
|
+
uuidv8?: string
|
|
44
60
|
objectId?: string
|
|
45
61
|
nanoid?: string
|
|
46
62
|
snowflake?: string
|
|
@@ -61,7 +77,15 @@ export type IdMessages = {
|
|
|
61
77
|
*
|
|
62
78
|
* Available types:
|
|
63
79
|
* - numeric: Pure numeric IDs (1, 123, 999999)
|
|
64
|
-
* - uuid: UUID
|
|
80
|
+
* - uuid: UUID any version (v1–v8)
|
|
81
|
+
* - uuidv1: UUID v1 (timestamp-based)
|
|
82
|
+
* - uuidv2: UUID v2 (DCE security)
|
|
83
|
+
* - uuidv3: UUID v3 (MD5 name-based)
|
|
84
|
+
* - uuidv4: UUID v4 (random)
|
|
85
|
+
* - uuidv5: UUID v5 (SHA-1 name-based)
|
|
86
|
+
* - uuidv6: UUID v6 (reordered timestamp, RFC 9562)
|
|
87
|
+
* - uuidv7: UUID v7 (Unix timestamp, sortable, RFC 9562)
|
|
88
|
+
* - uuidv8: UUID v8 (custom, RFC 9562)
|
|
65
89
|
* - objectId: MongoDB ObjectId (24-character hexadecimal)
|
|
66
90
|
* - nanoid: Nano ID format (21-character URL-safe)
|
|
67
91
|
* - snowflake: Twitter Snowflake (19-digit number)
|
|
@@ -72,7 +96,15 @@ export type IdMessages = {
|
|
|
72
96
|
*/
|
|
73
97
|
export type IdType =
|
|
74
98
|
| "numeric" // Pure numeric IDs (1, 123, 999999)
|
|
75
|
-
| "uuid" // UUID
|
|
99
|
+
| "uuid" // UUID any version (v1–v8)
|
|
100
|
+
| "uuidv1" // UUID v1 (timestamp-based)
|
|
101
|
+
| "uuidv2" // UUID v2 (DCE security)
|
|
102
|
+
| "uuidv3" // UUID v3 (MD5 name-based)
|
|
103
|
+
| "uuidv4" // UUID v4 (random)
|
|
104
|
+
| "uuidv5" // UUID v5 (SHA-1 name-based)
|
|
105
|
+
| "uuidv6" // UUID v6 (reordered timestamp, RFC 9562)
|
|
106
|
+
| "uuidv7" // UUID v7 (Unix timestamp, sortable, RFC 9562)
|
|
107
|
+
| "uuidv8" // UUID v8 (custom, RFC 9562)
|
|
76
108
|
| "objectId" // MongoDB ObjectId (24-character hexadecimal)
|
|
77
109
|
| "nanoid" // Nano ID
|
|
78
110
|
| "snowflake" // Twitter Snowflake (19-digit number)
|
|
@@ -143,9 +175,19 @@ export type IdSchema<IsRequired extends boolean, Type extends IdType | undefined
|
|
|
143
175
|
* @constant {Record<string, RegExp>} ID_PATTERNS
|
|
144
176
|
* @description Maps each ID type to its corresponding regex pattern
|
|
145
177
|
*/
|
|
178
|
+
const UUID_BASE = (version: string) => new RegExp(`^[0-9a-f]{8}-[0-9a-f]{4}-${version}[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`, "i")
|
|
179
|
+
|
|
146
180
|
const ID_PATTERNS = {
|
|
147
181
|
numeric: /^\d+$/,
|
|
148
|
-
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-
|
|
182
|
+
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
183
|
+
uuidv1: UUID_BASE("1"),
|
|
184
|
+
uuidv2: UUID_BASE("2"),
|
|
185
|
+
uuidv3: UUID_BASE("3"),
|
|
186
|
+
uuidv4: UUID_BASE("4"),
|
|
187
|
+
uuidv5: UUID_BASE("5"),
|
|
188
|
+
uuidv6: UUID_BASE("6"),
|
|
189
|
+
uuidv7: UUID_BASE("7"),
|
|
190
|
+
uuidv8: UUID_BASE("8"),
|
|
149
191
|
objectId: /^[0-9a-f]{24}$/i,
|
|
150
192
|
nanoid: /^[A-Za-z0-9_-]{21}$/,
|
|
151
193
|
snowflake: /^\d{19}$/,
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview ManyOf validator for Zod Kit
|
|
3
|
+
*
|
|
4
|
+
* Provides multi-select validation that restricts input to an array of values
|
|
5
|
+
* from a predefined set, with min/max selection, duplicate control, and transformation.
|
|
6
|
+
*
|
|
7
|
+
* @author Ong Hoe Yuan
|
|
8
|
+
* @version 0.2.2
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z, ZodType } from "zod"
|
|
12
|
+
import { t } from "../../i18n"
|
|
13
|
+
import { getLocale, type Locale } from "../../config"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Type definition for manyOf validation error messages
|
|
17
|
+
*
|
|
18
|
+
* @interface ManyOfMessages
|
|
19
|
+
* @property {string} [required] - Message when field is required but empty
|
|
20
|
+
* @property {string} [invalid] - Message when a value is not in the allowed list
|
|
21
|
+
* @property {string} [minSelect] - Message when fewer than minimum items are selected
|
|
22
|
+
* @property {string} [maxSelect] - Message when more than maximum items are selected
|
|
23
|
+
* @property {string} [duplicate] - Message when duplicate values are found
|
|
24
|
+
*/
|
|
25
|
+
export type ManyOfMessages = {
|
|
26
|
+
required?: string
|
|
27
|
+
invalid?: string
|
|
28
|
+
minSelect?: string
|
|
29
|
+
maxSelect?: string
|
|
30
|
+
duplicate?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Configuration options for manyOf validation
|
|
35
|
+
*
|
|
36
|
+
* @template IsRequired - Whether the field is required (affects return type)
|
|
37
|
+
* @template T - The type of allowed values
|
|
38
|
+
*
|
|
39
|
+
* @interface ManyOfOptions
|
|
40
|
+
* @property {T[]} values - Array of allowed values
|
|
41
|
+
* @property {T[] | null} [defaultValue] - Default value when input is empty
|
|
42
|
+
* @property {number} [min] - Minimum number of selections
|
|
43
|
+
* @property {number} [max] - Maximum number of selections
|
|
44
|
+
* @property {boolean} [allowDuplicates=false] - Whether to allow duplicate selections
|
|
45
|
+
* @property {boolean} [caseSensitive=true] - Whether string matching is case-sensitive
|
|
46
|
+
* @property {Function} [transform] - Custom transformation function applied to each value
|
|
47
|
+
* @property {Record<Locale, ManyOfMessages>} [i18n] - Custom error messages for different locales
|
|
48
|
+
*/
|
|
49
|
+
export type ManyOfOptions<IsRequired extends boolean = true, T extends string | number = string | number> = {
|
|
50
|
+
values: T[]
|
|
51
|
+
defaultValue?: IsRequired extends true ? T[] : T[] | null
|
|
52
|
+
min?: number
|
|
53
|
+
max?: number
|
|
54
|
+
allowDuplicates?: boolean
|
|
55
|
+
caseSensitive?: boolean
|
|
56
|
+
transform?: (value: T[]) => T[]
|
|
57
|
+
i18n?: Partial<Record<Locale, Partial<ManyOfMessages>>>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Type alias for manyOf validation schema based on required flag
|
|
62
|
+
*
|
|
63
|
+
* @template IsRequired - Whether the field is required
|
|
64
|
+
* @template T - The type of allowed values
|
|
65
|
+
*/
|
|
66
|
+
export type ManyOfSchema<IsRequired extends boolean, T> = IsRequired extends true ? ZodType<T[]> : ZodType<T[] | null>
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Creates a Zod schema for multi-select validation that restricts values to a predefined set
|
|
70
|
+
*
|
|
71
|
+
* @template IsRequired - Whether the field is required (affects return type)
|
|
72
|
+
* @template T - The type of allowed values (string | number)
|
|
73
|
+
* @param {IsRequired} [required=false] - Whether the field is required
|
|
74
|
+
* @param {ManyOfOptions<IsRequired, T>} options - Configuration options (values is required)
|
|
75
|
+
* @returns {ManyOfSchema<IsRequired, T>} Zod schema for manyOf validation
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* // Basic multi-select (optional by default)
|
|
80
|
+
* const tagsSchema = manyOf(false, { values: ["js", "ts", "rust", "go"] })
|
|
81
|
+
* tagsSchema.parse(["js", "ts"]) // ✓ ["js", "ts"]
|
|
82
|
+
* tagsSchema.parse(null) // ✓ null
|
|
83
|
+
*
|
|
84
|
+
* // Required with min/max
|
|
85
|
+
* const skillsSchema = manyOf(true, {
|
|
86
|
+
* values: ["react", "vue", "angular", "svelte"],
|
|
87
|
+
* min: 1,
|
|
88
|
+
* max: 3,
|
|
89
|
+
* })
|
|
90
|
+
* skillsSchema.parse(["react"]) // ✓
|
|
91
|
+
* skillsSchema.parse([]) // ✗ minSelect
|
|
92
|
+
* skillsSchema.parse(["react", "vue", "angular", "svelte"]) // ✗ maxSelect
|
|
93
|
+
*
|
|
94
|
+
* // Case-insensitive matching
|
|
95
|
+
* const colorsSchema = manyOf(true, {
|
|
96
|
+
* values: ["red", "green", "blue"],
|
|
97
|
+
* caseSensitive: false,
|
|
98
|
+
* })
|
|
99
|
+
* colorsSchema.parse(["RED", "Green"]) // ✓ ["red", "green"]
|
|
100
|
+
*
|
|
101
|
+
* // No duplicates (default)
|
|
102
|
+
* const rolesSchema = manyOf(true, { values: ["admin", "editor", "viewer"] })
|
|
103
|
+
* rolesSchema.parse(["admin", "admin"]) // ✗ duplicate
|
|
104
|
+
*
|
|
105
|
+
* // Allow duplicates
|
|
106
|
+
* const itemsSchema = manyOf(true, {
|
|
107
|
+
* values: [1, 2, 3],
|
|
108
|
+
* allowDuplicates: true,
|
|
109
|
+
* })
|
|
110
|
+
* itemsSchema.parse([1, 1, 2]) // ✓ [1, 1, 2]
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function manyOf<IsRequired extends boolean = false, T extends string | number = string | number>(
|
|
114
|
+
required?: IsRequired,
|
|
115
|
+
options?: Omit<ManyOfOptions<IsRequired, T>, "required">,
|
|
116
|
+
): ManyOfSchema<IsRequired, T> {
|
|
117
|
+
const { values = [] as unknown as T[], defaultValue = null, min, max, allowDuplicates = false, caseSensitive = true, transform, i18n } = options ?? {}
|
|
118
|
+
|
|
119
|
+
const isRequired = required ?? (false as IsRequired)
|
|
120
|
+
|
|
121
|
+
const getMessage = (key: keyof ManyOfMessages, params?: Record<string, any>) => {
|
|
122
|
+
if (i18n) {
|
|
123
|
+
const currentLocale = getLocale()
|
|
124
|
+
const customMessages = i18n[currentLocale]
|
|
125
|
+
if (customMessages && customMessages[key]) {
|
|
126
|
+
const template = customMessages[key]!
|
|
127
|
+
return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "")
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return t(`common.manyOf.${key}`, params)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const normalizeItem = (item: unknown): unknown => {
|
|
134
|
+
// Coerce number strings to numbers when values contains numbers
|
|
135
|
+
const hasNumbers = values.some((v) => typeof v === "number")
|
|
136
|
+
if (hasNumbers && typeof item === "string" && !isNaN(Number(item)) && item.trim() !== "") {
|
|
137
|
+
const numVal = Number(item)
|
|
138
|
+
if ((values as number[]).includes(numVal)) return numVal
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Case-insensitive normalization
|
|
142
|
+
if (!caseSensitive && typeof item === "string") {
|
|
143
|
+
const match = values.find((v) => typeof v === "string" && v.toLowerCase() === item.toLowerCase())
|
|
144
|
+
if (match !== undefined) return match
|
|
145
|
+
return item
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return item
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const preprocessFn = (val: unknown) => {
|
|
152
|
+
if (val === null || val === undefined || val === "") {
|
|
153
|
+
return defaultValue
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!Array.isArray(val)) {
|
|
157
|
+
// Accept single value and wrap in array
|
|
158
|
+
return [normalizeItem(val)]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return val.map(normalizeItem)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const baseSchema = z.preprocess(preprocessFn, z.any())
|
|
165
|
+
|
|
166
|
+
const schema = baseSchema.superRefine((val, ctx) => {
|
|
167
|
+
if (val === null) {
|
|
168
|
+
if (isRequired) {
|
|
169
|
+
ctx.addIssue({ code: "custom", message: getMessage("required") })
|
|
170
|
+
}
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!Array.isArray(val)) {
|
|
175
|
+
ctx.addIssue({ code: "custom", message: getMessage("invalid", { values: values.join(", ") }) })
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check each item is in the allowed values
|
|
180
|
+
for (const item of val) {
|
|
181
|
+
if (!values.includes(item as T)) {
|
|
182
|
+
ctx.addIssue({
|
|
183
|
+
code: "custom",
|
|
184
|
+
message: getMessage("invalid", { values: values.join(", ") }),
|
|
185
|
+
})
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Duplicate check
|
|
191
|
+
if (!allowDuplicates) {
|
|
192
|
+
const seen = new Set()
|
|
193
|
+
for (const item of val) {
|
|
194
|
+
if (seen.has(item)) {
|
|
195
|
+
ctx.addIssue({ code: "custom", message: getMessage("duplicate") })
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
seen.add(item)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Min/max selection count
|
|
203
|
+
if (min !== undefined && val.length < min) {
|
|
204
|
+
ctx.addIssue({ code: "custom", message: getMessage("minSelect", { min }) })
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (max !== undefined && val.length > max) {
|
|
209
|
+
ctx.addIssue({ code: "custom", message: getMessage("maxSelect", { max }) })
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
.transform((val) => {
|
|
214
|
+
if (val === null || !Array.isArray(val) || !transform) return val
|
|
215
|
+
return transform(val as T[])
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
return schema as unknown as ManyOfSchema<IsRequired, T>
|
|
219
|
+
}
|