@hy_ong/zod-kit 0.2.2 → 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-JXY7APBU.js → chunk-2JGRV3JO.js} +1 -1
- package/dist/{chunk-FM3EZ72O.js → chunk-3QLUXIY2.js} +1 -1
- package/dist/chunk-3ZF5JO3F.js +61 -0
- package/dist/{chunk-STNHTRG7.cjs → chunk-4AQB4RSU.cjs} +3 -3
- package/dist/{chunk-OPQJWHXN.cjs → chunk-53EEWALQ.cjs} +3 -3
- package/dist/{chunk-ZNJLWJX3.cjs → chunk-5ZMTAI4G.cjs} +3 -3
- package/dist/{chunk-RKUQREMW.js → chunk-6DFP7XY2.js} +1 -1
- package/dist/{chunk-5LEXCVLX.js → chunk-6IAPM7BP.js} +1 -1
- package/dist/{chunk-RO47DKQG.js → chunk-7SKH66CM.js} +1 -1
- package/dist/chunk-7X3XPK6A.js +92 -0
- package/dist/{chunk-UBK3VCVH.cjs → chunk-A2GAEU4O.cjs} +3 -3
- package/dist/{chunk-KFOHKTFD.js → chunk-AI4U42JV.js} +1 -1
- package/dist/{chunk-ERH4NIMU.cjs → chunk-AYCXAJRA.cjs} +3 -3
- package/dist/{chunk-YDH3L27K.cjs → chunk-BZSPJJYT.cjs} +3 -3
- package/dist/chunk-BZWQPSHO.cjs +92 -0
- package/dist/{chunk-H2XTEM4M.js → chunk-D55YFP5R.js} +1 -1
- package/dist/{chunk-LL4ZWLGO.js → chunk-E52GRXYY.js} +1 -1
- package/dist/{chunk-RRPXIRTQ.cjs → chunk-EDTNS2XL.cjs} +3 -3
- package/dist/{chunk-RYFG2GKM.cjs → chunk-FEL432I2.cjs} +3 -3
- package/dist/chunk-FMPHW7ID.cjs +61 -0
- package/dist/{chunk-JOLSGZGN.cjs → chunk-FP4O2ICM.cjs} +3 -3
- package/dist/{chunk-EGHL277K.cjs → chunk-G747FHUZ.cjs} +3 -3
- package/dist/{chunk-5JGTDL3Y.js → chunk-G7FLGJYD.js} +1 -1
- package/dist/{chunk-HMSM6FFA.cjs → chunk-H25N5GP6.cjs} +3 -3
- package/dist/{chunk-MHJFYYGV.js → chunk-H6STFX4I.js} +1 -1
- package/dist/{chunk-EDHT4LPO.js → chunk-HQ4RAMSD.js} +1 -1
- package/dist/{chunk-ORFHDJII.cjs → chunk-HZ2WESSL.cjs} +3 -3
- package/dist/{chunk-ORVV4MCF.cjs → chunk-IWR3H7IH.cjs} +3 -3
- package/dist/{chunk-B4EZYZOK.cjs → chunk-JZEF5Q3W.cjs} +3 -3
- package/dist/{chunk-HTEHINI7.cjs → chunk-KIUO2HIR.cjs} +3 -3
- package/dist/{chunk-QICQ6YEY.js → chunk-LBH5U2DZ.js} +1 -1
- package/dist/{chunk-42C5OHRK.js → chunk-LC4RNKBM.js} +1 -1
- package/dist/{chunk-TFGS34VD.cjs → chunk-LNWEJED7.cjs} +3 -3
- package/dist/{chunk-32JI34CV.cjs → chunk-LXFRQLH4.cjs} +3 -3
- package/dist/{chunk-46VAH2BJ.js → chunk-NWQSOSNF.js} +1 -1
- package/dist/{chunk-WDI4QJMQ.js → chunk-OGU7AIZF.js} +1 -1
- package/dist/{chunk-YIM3D2AD.js → chunk-OKO6WO6M.js} +1 -1
- package/dist/{chunk-MINMXGW3.js → chunk-OSUPJCBA.js} +1 -1
- package/dist/{chunk-6AAP4LPF.js → chunk-POIDES2L.js} +110 -0
- package/dist/{chunk-YPSEIDUR.cjs → chunk-Q24GYUTO.cjs} +3 -3
- package/dist/{chunk-UCOXAZJF.cjs → chunk-Q7TUNJD4.cjs} +110 -0
- package/dist/{chunk-L4HSIKTU.cjs → chunk-QQWX3ICK.cjs} +3 -3
- package/dist/{chunk-COYKBWTI.js → chunk-RFWCYULE.js} +1 -1
- package/dist/{chunk-ZXUMK2RR.js → chunk-RHKBT3M2.js} +1 -1
- package/dist/{chunk-LH7ZB4BK.js → chunk-RVGCMQ4J.js} +1 -1
- package/dist/{chunk-K2UOY6TB.js → chunk-T7PG4JDW.js} +1 -1
- package/dist/{chunk-DFJZ3NS2.cjs → chunk-TDEXEIHH.cjs} +3 -3
- package/dist/{chunk-GJIRDBZJ.cjs → chunk-TPXRQT2H.cjs} +3 -3
- package/dist/{chunk-UQZKFAFX.js → chunk-TRQMRHFM.js} +1 -1
- package/dist/{chunk-WABKPFPK.js → chunk-U2PB6XEO.js} +1 -1
- package/dist/{chunk-ZTFCJCPO.cjs → chunk-UCPKW43K.cjs} +3 -3
- package/dist/{chunk-TQXDUMML.cjs → chunk-V2KKGSKQ.cjs} +3 -3
- package/dist/{chunk-VB2KV2ZM.cjs → chunk-VKBNKPFO.cjs} +3 -3
- package/dist/{chunk-MM7IL2RG.js → chunk-XAN4CAVH.js} +1 -1
- package/dist/{chunk-M6MTP3NY.cjs → chunk-ZCX22PY4.cjs} +3 -3
- package/dist/{chunk-2SWEVDFZ.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.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 +11 -1
- package/src/i18n/locales/en-GB.json +11 -0
- package/src/i18n/locales/en-US.json +11 -0
- package/src/i18n/locales/id-ID.json +11 -0
- package/src/i18n/locales/ja-JP.json +11 -0
- package/src/i18n/locales/ko-KR.json +11 -0
- package/src/i18n/locales/ms-MY.json +11 -0
- package/src/i18n/locales/th-TH.json +11 -0
- package/src/i18n/locales/vi-VN.json +11 -0
- package/src/i18n/locales/zh-CN.json +11 -0
- package/src/i18n/locales/zh-TW.json +11 -0
- package/src/index.ts +2 -0
- package/src/validators/common/many-of.ts +219 -0
- package/src/validators/common/one-of.ts +172 -0
- package/tests/common/many-of.test.ts +198 -0
- package/tests/common/one-of.test.ts +136 -0
- package/tsup.config.ts +2 -0
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
validate5DigitPostalCode,
|
|
6
6
|
validate6DigitPostalCode,
|
|
7
7
|
validateTaiwanPostalCode
|
|
8
|
-
} from "../chunk-
|
|
9
|
-
import "../chunk-
|
|
8
|
+
} from "../chunk-D55YFP5R.js";
|
|
9
|
+
import "../chunk-POIDES2L.js";
|
|
10
10
|
export {
|
|
11
11
|
VALID_3_DIGIT_PREFIXES,
|
|
12
12
|
twPostalCode,
|
package/dist/taiwan/tel.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
require('../chunk-
|
|
4
|
+
var _chunkVKBNKPFOcjs = require('../chunk-VKBNKPFO.cjs');
|
|
5
|
+
require('../chunk-Q7TUNJD4.cjs');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
exports.twTel =
|
|
9
|
+
exports.twTel = _chunkVKBNKPFOcjs.twTel; exports.validateTaiwanTel = _chunkVKBNKPFOcjs.validateTaiwanTel;
|
package/dist/taiwan/tel.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hy_ong/zod-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "A comprehensive TypeScript library providing pre-built Zod validation schemas with full internationalization support for common data types and Taiwan-specific formats",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"zod",
|
|
@@ -91,11 +91,21 @@
|
|
|
91
91
|
"import": "./dist/common/ip.js",
|
|
92
92
|
"require": "./dist/common/ip.cjs"
|
|
93
93
|
},
|
|
94
|
+
"./common/many-of": {
|
|
95
|
+
"types": "./dist/common/many-of.d.ts",
|
|
96
|
+
"import": "./dist/common/many-of.js",
|
|
97
|
+
"require": "./dist/common/many-of.cjs"
|
|
98
|
+
},
|
|
94
99
|
"./common/number": {
|
|
95
100
|
"types": "./dist/common/number.d.ts",
|
|
96
101
|
"import": "./dist/common/number.js",
|
|
97
102
|
"require": "./dist/common/number.cjs"
|
|
98
103
|
},
|
|
104
|
+
"./common/one-of": {
|
|
105
|
+
"types": "./dist/common/one-of.d.ts",
|
|
106
|
+
"import": "./dist/common/one-of.js",
|
|
107
|
+
"require": "./dist/common/one-of.cjs"
|
|
108
|
+
},
|
|
99
109
|
"./common/password": {
|
|
100
110
|
"types": "./dist/common/password.d.ts",
|
|
101
111
|
"import": "./dist/common/password.js",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "Only business email addresses are allowed",
|
|
18
18
|
"noDisposable": "Disposable email addresses are not allowed"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "Required",
|
|
22
|
+
"invalid": "Must be from: ${values}",
|
|
23
|
+
"minSelect": "Must select at least ${min} item(s)",
|
|
24
|
+
"maxSelect": "Must select at most ${max} item(s)",
|
|
25
|
+
"duplicate": "Duplicate values are not allowed"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "Required",
|
|
22
29
|
"invalid": "Invalid URL format",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "Must be a finite number",
|
|
69
76
|
"precision": "Must have at most ${precision} decimal places"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "Required",
|
|
80
|
+
"invalid": "Must be one of: ${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "Required",
|
|
73
84
|
"invalid": "Invalid ID format",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "Only business email addresses are allowed",
|
|
18
18
|
"noDisposable": "Disposable email addresses are not allowed"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "Required",
|
|
22
|
+
"invalid": "Must be from: ${values}",
|
|
23
|
+
"minSelect": "Must select at least ${min} item(s)",
|
|
24
|
+
"maxSelect": "Must select at most ${max} item(s)",
|
|
25
|
+
"duplicate": "Duplicate values are not allowed"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "Required",
|
|
22
29
|
"invalid": "Invalid URL format",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "Must be a finite number",
|
|
69
76
|
"precision": "Must have at most ${precision} decimal places"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "Required",
|
|
80
|
+
"invalid": "Must be one of: ${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "Required",
|
|
73
84
|
"invalid": "Invalid ID format",
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
"businessOnly": "Hanya alamat email bisnis yang diizinkan",
|
|
18
18
|
"noDisposable": "Alamat email sekali pakai tidak diizinkan"
|
|
19
19
|
},
|
|
20
|
+
"manyOf": {
|
|
21
|
+
"required": "Wajib diisi",
|
|
22
|
+
"invalid": "Harus dari: ${values}",
|
|
23
|
+
"minSelect": "Harus memilih minimal ${min} item",
|
|
24
|
+
"maxSelect": "Harus memilih maksimal ${max} item",
|
|
25
|
+
"duplicate": "Nilai duplikat tidak diperbolehkan"
|
|
26
|
+
},
|
|
20
27
|
"url": {
|
|
21
28
|
"required": "Wajib diisi",
|
|
22
29
|
"invalid": "Format URL tidak valid",
|
|
@@ -68,6 +75,10 @@
|
|
|
68
75
|
"finite": "Harus berupa angka terhingga",
|
|
69
76
|
"precision": "Harus memiliki maksimal ${precision} tempat desimal"
|
|
70
77
|
},
|
|
78
|
+
"oneOf": {
|
|
79
|
+
"required": "Wajib diisi",
|
|
80
|
+
"invalid": "Harus salah satu dari: ${values}"
|
|
81
|
+
},
|
|
71
82
|
"id": {
|
|
72
83
|
"required": "Wajib diisi",
|
|
73
84
|
"invalid": "Format ID tidak valid",
|
|
@@ -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形式です",
|
|
@@ -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 형식입니다",
|
|
@@ -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",
|
|
@@ -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 ไม่ถูกต้อง",
|
|
@@ -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ệ",
|
|
@@ -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 格式",
|
|
@@ -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 格式",
|
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"
|
|
@@ -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
|
+
}
|