@hy_ong/zod-kit 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/.github/workflows/ci.yml +24 -0
  2. package/CLAUDE.md +64 -22
  3. package/dist/chunk-2SWEVDFZ.js +134 -0
  4. package/dist/chunk-32JI34CV.cjs +146 -0
  5. package/dist/chunk-42C5OHRK.js +71 -0
  6. package/dist/chunk-46VAH2BJ.js +160 -0
  7. package/dist/chunk-5JGTDL3Y.js +87 -0
  8. package/dist/chunk-5LEXCVLX.js +257 -0
  9. package/dist/chunk-6AAP4LPF.js +2606 -0
  10. package/dist/chunk-B4EZYZOK.cjs +215 -0
  11. package/dist/chunk-COYKBWTI.js +161 -0
  12. package/dist/chunk-DFJZ3NS2.cjs +151 -0
  13. package/dist/chunk-EDHT4LPO.js +118 -0
  14. package/dist/chunk-EGHL277K.cjs +165 -0
  15. package/dist/chunk-ERH4NIMU.cjs +69 -0
  16. package/dist/chunk-FM3EZ72O.js +165 -0
  17. package/dist/chunk-GJIRDBZJ.cjs +90 -0
  18. package/dist/chunk-H2XTEM4M.js +696 -0
  19. package/dist/chunk-HMSM6FFA.cjs +181 -0
  20. package/dist/chunk-HTEHINI7.cjs +177 -0
  21. package/dist/chunk-JOLSGZGN.cjs +696 -0
  22. package/dist/chunk-JXY7APBU.js +69 -0
  23. package/dist/chunk-K2UOY6TB.js +136 -0
  24. package/dist/chunk-KFOHKTFD.js +61 -0
  25. package/dist/chunk-L4HSIKTU.cjs +135 -0
  26. package/dist/chunk-LH7ZB4BK.js +124 -0
  27. package/dist/chunk-LL4ZWLGO.js +90 -0
  28. package/dist/chunk-M6MTP3NY.cjs +99 -0
  29. package/dist/chunk-MHJFYYGV.js +215 -0
  30. package/dist/chunk-MINMXGW3.js +135 -0
  31. package/dist/chunk-MM7IL2RG.js +181 -0
  32. package/dist/chunk-OPQJWHXN.cjs +301 -0
  33. package/dist/chunk-ORFHDJII.cjs +136 -0
  34. package/dist/chunk-ORVV4MCF.cjs +87 -0
  35. package/dist/chunk-QICQ6YEY.js +75 -0
  36. package/dist/chunk-RKUQREMW.js +127 -0
  37. package/dist/chunk-RO47DKQG.js +146 -0
  38. package/dist/chunk-RRPXIRTQ.cjs +257 -0
  39. package/dist/chunk-RYFG2GKM.cjs +118 -0
  40. package/dist/chunk-STNHTRG7.cjs +124 -0
  41. package/dist/chunk-TFGS34VD.cjs +71 -0
  42. package/dist/chunk-TQXDUMML.cjs +61 -0
  43. package/dist/chunk-UBK3VCVH.cjs +134 -0
  44. package/dist/chunk-UCOXAZJF.cjs +2606 -0
  45. package/dist/chunk-UQZKFAFX.js +130 -0
  46. package/dist/chunk-VB2KV2ZM.cjs +130 -0
  47. package/dist/chunk-WABKPFPK.js +151 -0
  48. package/dist/chunk-WDI4QJMQ.js +177 -0
  49. package/dist/chunk-YDH3L27K.cjs +127 -0
  50. package/dist/chunk-YIM3D2AD.js +99 -0
  51. package/dist/chunk-YPSEIDUR.cjs +160 -0
  52. package/dist/chunk-ZNJLWJX3.cjs +75 -0
  53. package/dist/chunk-ZTFCJCPO.cjs +161 -0
  54. package/dist/chunk-ZXUMK2RR.js +301 -0
  55. package/dist/common/boolean.cjs +7 -0
  56. package/dist/common/boolean.d.cts +119 -0
  57. package/dist/common/boolean.d.ts +119 -0
  58. package/dist/common/boolean.js +7 -0
  59. package/dist/common/color.cjs +9 -0
  60. package/dist/common/color.d.cts +26 -0
  61. package/dist/common/color.d.ts +26 -0
  62. package/dist/common/color.js +9 -0
  63. package/dist/common/coordinate.cjs +11 -0
  64. package/dist/common/coordinate.d.cts +23 -0
  65. package/dist/common/coordinate.d.ts +23 -0
  66. package/dist/common/coordinate.js +11 -0
  67. package/dist/common/credit-card.cjs +11 -0
  68. package/dist/common/credit-card.d.cts +22 -0
  69. package/dist/common/credit-card.d.ts +22 -0
  70. package/dist/common/credit-card.js +11 -0
  71. package/dist/common/date.cjs +7 -0
  72. package/dist/common/date.d.cts +174 -0
  73. package/dist/common/date.d.ts +174 -0
  74. package/dist/common/date.js +7 -0
  75. package/dist/common/datetime.cjs +15 -0
  76. package/dist/common/datetime.d.cts +301 -0
  77. package/dist/common/datetime.d.ts +301 -0
  78. package/dist/common/datetime.js +15 -0
  79. package/dist/common/email.cjs +7 -0
  80. package/dist/common/email.d.cts +149 -0
  81. package/dist/common/email.d.ts +149 -0
  82. package/dist/common/email.js +7 -0
  83. package/dist/common/file.cjs +7 -0
  84. package/dist/common/file.d.cts +178 -0
  85. package/dist/common/file.d.ts +178 -0
  86. package/dist/common/file.js +7 -0
  87. package/dist/common/id.cjs +13 -0
  88. package/dist/common/id.d.cts +288 -0
  89. package/dist/common/id.d.ts +288 -0
  90. package/dist/common/id.js +13 -0
  91. package/dist/common/ip.cjs +11 -0
  92. package/dist/common/ip.d.cts +25 -0
  93. package/dist/common/ip.d.ts +25 -0
  94. package/dist/common/ip.js +11 -0
  95. package/dist/common/number.cjs +7 -0
  96. package/dist/common/number.d.cts +167 -0
  97. package/dist/common/number.d.ts +167 -0
  98. package/dist/common/number.js +7 -0
  99. package/dist/common/password.cjs +7 -0
  100. package/dist/common/password.d.cts +192 -0
  101. package/dist/common/password.d.ts +192 -0
  102. package/dist/common/password.js +7 -0
  103. package/dist/common/text.cjs +7 -0
  104. package/dist/common/text.d.cts +156 -0
  105. package/dist/common/text.d.ts +156 -0
  106. package/dist/common/text.js +7 -0
  107. package/dist/common/time.cjs +15 -0
  108. package/dist/common/time.d.cts +268 -0
  109. package/dist/common/time.d.ts +268 -0
  110. package/dist/common/time.js +15 -0
  111. package/dist/common/url.cjs +7 -0
  112. package/dist/common/url.d.cts +196 -0
  113. package/dist/common/url.d.ts +196 -0
  114. package/dist/common/url.js +7 -0
  115. package/dist/config-CABSSvAp.d.cts +5 -0
  116. package/dist/config-CABSSvAp.d.ts +5 -0
  117. package/dist/index.cjs +180 -5255
  118. package/dist/index.d.cts +28 -3150
  119. package/dist/index.d.ts +28 -3150
  120. package/dist/index.js +135 -5131
  121. package/dist/taiwan/bank-account.cjs +11 -0
  122. package/dist/taiwan/bank-account.d.cts +22 -0
  123. package/dist/taiwan/bank-account.d.ts +22 -0
  124. package/dist/taiwan/bank-account.js +11 -0
  125. package/dist/taiwan/business-id.cjs +9 -0
  126. package/dist/taiwan/business-id.d.cts +133 -0
  127. package/dist/taiwan/business-id.d.ts +133 -0
  128. package/dist/taiwan/business-id.js +9 -0
  129. package/dist/taiwan/fax.cjs +9 -0
  130. package/dist/taiwan/fax.d.cts +157 -0
  131. package/dist/taiwan/fax.d.ts +157 -0
  132. package/dist/taiwan/fax.js +9 -0
  133. package/dist/taiwan/invoice.cjs +9 -0
  134. package/dist/taiwan/invoice.d.cts +17 -0
  135. package/dist/taiwan/invoice.d.ts +17 -0
  136. package/dist/taiwan/invoice.js +9 -0
  137. package/dist/taiwan/license-plate.cjs +9 -0
  138. package/dist/taiwan/license-plate.d.cts +19 -0
  139. package/dist/taiwan/license-plate.d.ts +19 -0
  140. package/dist/taiwan/license-plate.js +9 -0
  141. package/dist/taiwan/mobile.cjs +9 -0
  142. package/dist/taiwan/mobile.d.cts +146 -0
  143. package/dist/taiwan/mobile.d.ts +146 -0
  144. package/dist/taiwan/mobile.js +9 -0
  145. package/dist/taiwan/national-id.cjs +15 -0
  146. package/dist/taiwan/national-id.d.cts +214 -0
  147. package/dist/taiwan/national-id.d.ts +214 -0
  148. package/dist/taiwan/national-id.js +15 -0
  149. package/dist/taiwan/passport.cjs +9 -0
  150. package/dist/taiwan/passport.d.cts +19 -0
  151. package/dist/taiwan/passport.d.ts +19 -0
  152. package/dist/taiwan/passport.js +9 -0
  153. package/dist/taiwan/postal-code.cjs +17 -0
  154. package/dist/taiwan/postal-code.d.cts +237 -0
  155. package/dist/taiwan/postal-code.d.ts +237 -0
  156. package/dist/taiwan/postal-code.js +17 -0
  157. package/dist/taiwan/tel.cjs +9 -0
  158. package/dist/taiwan/tel.d.cts +162 -0
  159. package/dist/taiwan/tel.d.ts +162 -0
  160. package/dist/taiwan/tel.js +9 -0
  161. package/package.json +132 -6
  162. package/src/i18n/locales/en-GB.json +51 -0
  163. package/src/i18n/locales/en-US.json +52 -1
  164. package/src/i18n/locales/id-ID.json +51 -0
  165. package/src/i18n/locales/ja-JP.json +51 -0
  166. package/src/i18n/locales/ko-KR.json +51 -0
  167. package/src/i18n/locales/ms-MY.json +51 -0
  168. package/src/i18n/locales/th-TH.json +51 -0
  169. package/src/i18n/locales/vi-VN.json +51 -0
  170. package/src/i18n/locales/zh-CN.json +51 -0
  171. package/src/i18n/locales/zh-TW.json +51 -0
  172. package/src/index.ts +10 -2
  173. package/src/validators/common/color.ts +192 -0
  174. package/src/validators/common/coordinate.ts +159 -0
  175. package/src/validators/common/credit-card.ts +134 -0
  176. package/src/validators/common/id.ts +45 -3
  177. package/src/validators/common/ip.ts +210 -0
  178. package/src/validators/taiwan/bank-account.ts +176 -0
  179. package/src/validators/taiwan/invoice.ts +84 -0
  180. package/src/validators/taiwan/license-plate.ts +110 -0
  181. package/src/validators/taiwan/passport.ts +103 -0
  182. package/tests/common/color.test.ts +587 -0
  183. package/tests/common/coordinate.test.ts +345 -0
  184. package/tests/common/credit-card.test.ts +378 -0
  185. package/tests/common/id.test.ts +68 -3
  186. package/tests/common/ip.test.ts +419 -0
  187. package/tests/taiwan/bank-account.test.ts +286 -0
  188. package/tests/taiwan/invoice.test.ts +227 -0
  189. package/tests/taiwan/license-plate.test.ts +280 -0
  190. package/tests/taiwan/passport.test.ts +277 -0
  191. package/tsup.config.ts +36 -0
@@ -0,0 +1,160 @@
1
+ import {
2
+ getLocale,
3
+ t
4
+ } from "./chunk-6AAP4LPF.js";
5
+
6
+ // src/validators/common/password.ts
7
+ import { z } from "zod";
8
+ var COMMON_PASSWORDS = [
9
+ "password",
10
+ "123456",
11
+ "123456789",
12
+ "12345678",
13
+ "12345",
14
+ "1234567",
15
+ "admin",
16
+ "qwerty",
17
+ "abc123",
18
+ "password123",
19
+ "letmein",
20
+ "welcome",
21
+ "monkey",
22
+ "dragon",
23
+ "sunshine",
24
+ "princess"
25
+ ];
26
+ var calculatePasswordStrength = (password2) => {
27
+ let score = 0;
28
+ if (password2.length >= 8) score += 1;
29
+ if (password2.length >= 12) score += 1;
30
+ if (password2.length >= 16) score += 1;
31
+ if (/[a-z]/.test(password2)) score += 1;
32
+ if (/[A-Z]/.test(password2)) score += 1;
33
+ if (/[0-9]/.test(password2)) score += 1;
34
+ if (/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password2)) score += 1;
35
+ if (/(.)\1{2,}/.test(password2)) score -= 1;
36
+ 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;
37
+ if (score <= 2) return "weak";
38
+ if (score <= 4) return "medium";
39
+ if (score <= 6) return "strong";
40
+ return "very-strong";
41
+ };
42
+ function password(required, options) {
43
+ const {
44
+ min,
45
+ max,
46
+ uppercase,
47
+ lowercase,
48
+ digits,
49
+ special,
50
+ noRepeating,
51
+ noSequential,
52
+ noCommonWords,
53
+ minStrength,
54
+ excludes,
55
+ includes,
56
+ regex,
57
+ transform,
58
+ defaultValue,
59
+ i18n
60
+ } = options ?? {};
61
+ const isRequired = required ?? false;
62
+ const actualDefaultValue = defaultValue ?? (isRequired ? "" : null);
63
+ const getMessage = (key, params) => {
64
+ if (i18n) {
65
+ const currentLocale = getLocale();
66
+ const customMessages = i18n[currentLocale];
67
+ if (customMessages && customMessages[key]) {
68
+ const template = customMessages[key];
69
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
70
+ }
71
+ }
72
+ return t(`common.password.${key}`, params);
73
+ };
74
+ const preprocessFn = (val) => {
75
+ if (val === "" || val === null || val === void 0) {
76
+ return actualDefaultValue;
77
+ }
78
+ let processed = String(val);
79
+ if (transform) {
80
+ processed = transform(processed);
81
+ }
82
+ return processed;
83
+ };
84
+ const baseSchema = isRequired ? z.preprocess(preprocessFn, z.string()) : z.preprocess(preprocessFn, z.string().nullable());
85
+ const schema = baseSchema.superRefine((val, ctx) => {
86
+ if (val === null) return;
87
+ if (isRequired && (val === "" || val === "null" || val === "undefined")) {
88
+ ctx.addIssue({ code: "custom", message: getMessage("required") });
89
+ return;
90
+ }
91
+ if (val !== null && min !== void 0 && val.length < min) {
92
+ ctx.addIssue({ code: "custom", message: getMessage("min", { min }) });
93
+ return;
94
+ }
95
+ if (val !== null && max !== void 0 && val.length > max) {
96
+ ctx.addIssue({ code: "custom", message: getMessage("max", { max }) });
97
+ return;
98
+ }
99
+ if (val !== null && uppercase && !/[A-Z]/.test(val)) {
100
+ ctx.addIssue({ code: "custom", message: getMessage("uppercase") });
101
+ return;
102
+ }
103
+ if (val !== null && lowercase && !/[a-z]/.test(val)) {
104
+ ctx.addIssue({ code: "custom", message: getMessage("lowercase") });
105
+ return;
106
+ }
107
+ if (val !== null && digits && !/[0-9]/.test(val)) {
108
+ ctx.addIssue({ code: "custom", message: getMessage("digits") });
109
+ return;
110
+ }
111
+ if (val !== null && special && !/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(val)) {
112
+ ctx.addIssue({ code: "custom", message: getMessage("special") });
113
+ return;
114
+ }
115
+ if (val !== null && noRepeating && /(.)\1{2,}/.test(val)) {
116
+ ctx.addIssue({ code: "custom", message: getMessage("noRepeating") });
117
+ return;
118
+ }
119
+ 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)) {
120
+ ctx.addIssue({ code: "custom", message: getMessage("noSequential") });
121
+ return;
122
+ }
123
+ if (val !== null && noCommonWords && COMMON_PASSWORDS.some((common) => val.toLowerCase().includes(common.toLowerCase()))) {
124
+ ctx.addIssue({ code: "custom", message: getMessage("noCommonWords") });
125
+ return;
126
+ }
127
+ if (val !== null && minStrength) {
128
+ const strength = calculatePasswordStrength(val);
129
+ const strengthLevels = ["weak", "medium", "strong", "very-strong"];
130
+ const currentLevel = strengthLevels.indexOf(strength);
131
+ const requiredLevel = strengthLevels.indexOf(minStrength);
132
+ if (currentLevel < requiredLevel) {
133
+ ctx.addIssue({ code: "custom", message: getMessage("minStrength", { minStrength }) });
134
+ return;
135
+ }
136
+ }
137
+ if (val !== null && includes !== void 0 && !val.includes(includes)) {
138
+ ctx.addIssue({ code: "custom", message: getMessage("includes", { includes }) });
139
+ return;
140
+ }
141
+ if (val !== null && excludes !== void 0) {
142
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
143
+ for (const exclude of excludeList) {
144
+ if (val.includes(exclude)) {
145
+ ctx.addIssue({ code: "custom", message: getMessage("excludes", { excludes: exclude }) });
146
+ return;
147
+ }
148
+ }
149
+ }
150
+ if (val !== null && regex !== void 0 && !regex.test(val)) {
151
+ ctx.addIssue({ code: "custom", message: getMessage("invalid", { regex }) });
152
+ return;
153
+ }
154
+ });
155
+ return schema;
156
+ }
157
+
158
+ export {
159
+ password
160
+ };
@@ -0,0 +1,87 @@
1
+ import {
2
+ getLocale,
3
+ t
4
+ } from "./chunk-6AAP4LPF.js";
5
+
6
+ // src/validators/taiwan/business-id.ts
7
+ import { z } from "zod";
8
+ var validateTaiwanBusinessId = (value) => {
9
+ if (!/^\d{8}$/.test(value)) {
10
+ return false;
11
+ }
12
+ const digits = value.split("").map(Number);
13
+ const coefficients = [1, 2, 1, 2, 1, 2, 4];
14
+ let sum = 0;
15
+ for (let i = 0; i < 7; i++) {
16
+ const product = digits[i] * coefficients[i];
17
+ sum += Math.floor(product / 10) + product % 10;
18
+ }
19
+ sum += digits[7];
20
+ if (sum % 5 === 0) {
21
+ return true;
22
+ }
23
+ if (sum % 10 === 0) {
24
+ return true;
25
+ }
26
+ if (digits[6] === 7) {
27
+ let altSum = 0;
28
+ for (let i = 0; i < 7; i++) {
29
+ const product = digits[i] * coefficients[i];
30
+ altSum += Math.floor(product / 10) + product % 10;
31
+ }
32
+ altSum += 1 + digits[7];
33
+ if (altSum % 5 === 0 || altSum % 10 === 0) {
34
+ return true;
35
+ }
36
+ }
37
+ return false;
38
+ };
39
+ function twBusinessId(required, options) {
40
+ const { transform, defaultValue, i18n } = options ?? {};
41
+ const isRequired = required ?? false;
42
+ const actualDefaultValue = defaultValue ?? (isRequired ? "" : null);
43
+ const getMessage = (key, params) => {
44
+ if (i18n) {
45
+ const currentLocale = getLocale();
46
+ const customMessages = i18n[currentLocale];
47
+ if (customMessages && customMessages[key]) {
48
+ const template = customMessages[key];
49
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
50
+ }
51
+ }
52
+ return t(`taiwan.businessId.${key}`, params);
53
+ };
54
+ const preprocessFn = (val) => {
55
+ if (val === "" || val === null || val === void 0) {
56
+ return actualDefaultValue;
57
+ }
58
+ let processed = String(val).trim();
59
+ if (processed === "" && !required) {
60
+ return null;
61
+ }
62
+ if (transform) {
63
+ processed = transform(processed);
64
+ }
65
+ return processed;
66
+ };
67
+ const baseSchema = isRequired ? z.preprocess(preprocessFn, z.string()) : z.preprocess(preprocessFn, z.string().nullable());
68
+ const schema = baseSchema.superRefine((val, ctx) => {
69
+ if (val === null) return;
70
+ if (isRequired && (val === "" || val === "null" || val === "undefined")) {
71
+ ctx.addIssue({ code: "custom", message: getMessage("required") });
72
+ return;
73
+ }
74
+ if (val === null) return;
75
+ if (!isRequired && val === "") return;
76
+ if (!validateTaiwanBusinessId(val)) {
77
+ ctx.addIssue({ code: "custom", message: getMessage("invalid") });
78
+ return;
79
+ }
80
+ });
81
+ return schema;
82
+ }
83
+
84
+ export {
85
+ validateTaiwanBusinessId,
86
+ twBusinessId
87
+ };
@@ -0,0 +1,257 @@
1
+ import {
2
+ getLocale,
3
+ t
4
+ } from "./chunk-6AAP4LPF.js";
5
+
6
+ // src/validators/common/time.ts
7
+ import { z } from "zod";
8
+ var TIME_PATTERNS = {
9
+ "HH:mm": /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/,
10
+ "HH:mm:ss": /^([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/,
11
+ "hh:mm A": /^(0?[1-9]|1[0-2]):[0-5][0-9]\s?(AM|PM)$/i,
12
+ "hh:mm:ss A": /^(0?[1-9]|1[0-2]):[0-5][0-9]:[0-5][0-9]\s?(AM|PM)$/i,
13
+ "H:mm": /^([0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/,
14
+ "h:mm A": /^([1-9]|1[0-2]):[0-5][0-9]\s?(AM|PM)$/i
15
+ };
16
+ var parseTimeToMinutes = (timeStr, format) => {
17
+ const cleanTime = timeStr.trim();
18
+ try {
19
+ if (format.includes("A")) {
20
+ const match = cleanTime.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?\s?(AM|PM)$/i);
21
+ if (!match) return null;
22
+ const [, hourStr, minuteStr, , period] = match;
23
+ let hour = parseInt(hourStr, 10);
24
+ const minute = parseInt(minuteStr, 10);
25
+ if (hour < 1 || hour > 12 || minute < 0 || minute > 59) {
26
+ return null;
27
+ }
28
+ if (period.toUpperCase() === "PM" && hour !== 12) {
29
+ hour += 12;
30
+ } else if (period.toUpperCase() === "AM" && hour === 12) {
31
+ hour = 0;
32
+ }
33
+ return hour * 60 + minute;
34
+ } else {
35
+ const match = cleanTime.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
36
+ if (!match) return null;
37
+ const [, hourStr, minuteStr] = match;
38
+ const hour = parseInt(hourStr, 10);
39
+ const minute = parseInt(minuteStr, 10);
40
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
41
+ return null;
42
+ }
43
+ return hour * 60 + minute;
44
+ }
45
+ } catch {
46
+ return null;
47
+ }
48
+ };
49
+ var validateTimeFormat = (value, format) => {
50
+ const pattern = TIME_PATTERNS[format];
51
+ return pattern.test(value.trim());
52
+ };
53
+ var normalizeTime = (timeStr, format) => {
54
+ const cleanTime = timeStr.trim();
55
+ if (format.includes("A")) {
56
+ const match = cleanTime.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?\s?(AM|PM)$/i);
57
+ if (!match) return null;
58
+ const [, hourStr, minuteStr, secondStr = "00", period] = match;
59
+ let hour = parseInt(hourStr, 10);
60
+ const minute = parseInt(minuteStr, 10);
61
+ const second = parseInt(secondStr, 10);
62
+ if (period.toUpperCase() === "PM" && hour !== 12) {
63
+ hour += 12;
64
+ } else if (period.toUpperCase() === "AM" && hour === 12) {
65
+ hour = 0;
66
+ }
67
+ 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")}`;
68
+ }
69
+ if (format === "H:mm") {
70
+ const match = cleanTime.match(/^(\d{1,2}):(\d{2})$/);
71
+ if (!match) return null;
72
+ const [, hourStr, minuteStr] = match;
73
+ return `${hourStr.padStart(2, "0")}:${minuteStr}`;
74
+ }
75
+ return cleanTime;
76
+ };
77
+ function time(required, options) {
78
+ const {
79
+ format = "HH:mm",
80
+ min,
81
+ max,
82
+ minHour,
83
+ maxHour,
84
+ allowedHours,
85
+ minuteStep,
86
+ secondStep,
87
+ includes,
88
+ excludes,
89
+ regex,
90
+ trimMode = "trim",
91
+ casing = "none",
92
+ whitelist,
93
+ whitelistOnly = false,
94
+ transform,
95
+ defaultValue,
96
+ i18n
97
+ } = options ?? {};
98
+ const isRequired = required ?? false;
99
+ const actualDefaultValue = defaultValue ?? (isRequired ? "" : null);
100
+ const getMessage = (key, params) => {
101
+ if (i18n) {
102
+ const currentLocale = getLocale();
103
+ const customMessages = i18n[currentLocale];
104
+ if (customMessages && customMessages[key]) {
105
+ const template = customMessages[key];
106
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
107
+ }
108
+ }
109
+ return t(`common.time.${key}`, params);
110
+ };
111
+ const preprocessFn = (val) => {
112
+ if (val === null || val === void 0) {
113
+ return actualDefaultValue;
114
+ }
115
+ let processed = String(val);
116
+ switch (trimMode) {
117
+ case "trim":
118
+ processed = processed.trim();
119
+ break;
120
+ case "trimStart":
121
+ processed = processed.trimStart();
122
+ break;
123
+ case "trimEnd":
124
+ processed = processed.trimEnd();
125
+ break;
126
+ case "none":
127
+ break;
128
+ }
129
+ if (processed === "") {
130
+ if (whitelist && whitelist.includes("")) {
131
+ return "";
132
+ }
133
+ if (!isRequired) {
134
+ return actualDefaultValue;
135
+ }
136
+ return actualDefaultValue;
137
+ }
138
+ switch (casing) {
139
+ case "upper":
140
+ processed = processed.toUpperCase();
141
+ break;
142
+ case "lower":
143
+ processed = processed.toLowerCase();
144
+ break;
145
+ case "none":
146
+ break;
147
+ }
148
+ if (transform) {
149
+ processed = transform(processed);
150
+ }
151
+ return processed;
152
+ };
153
+ const baseSchema = isRequired ? z.preprocess(preprocessFn, z.string()) : z.preprocess(preprocessFn, z.string().nullable());
154
+ const schema = baseSchema.superRefine((val, ctx) => {
155
+ if (val === null) return;
156
+ if (isRequired && (val === "" || val === "null" || val === "undefined")) {
157
+ ctx.addIssue({ code: "custom", message: getMessage("required") });
158
+ return;
159
+ }
160
+ if (val === null) return;
161
+ if (!isRequired && val === "") return;
162
+ if (whitelist && whitelist.length > 0) {
163
+ if (whitelist.includes(val)) {
164
+ return;
165
+ }
166
+ if (whitelistOnly) {
167
+ ctx.addIssue({ code: "custom", message: getMessage("notInWhitelist") });
168
+ return;
169
+ }
170
+ }
171
+ if (regex) {
172
+ if (!regex.test(val)) {
173
+ ctx.addIssue({ code: "custom", message: getMessage("customRegex") });
174
+ return;
175
+ }
176
+ } else {
177
+ if (!validateTimeFormat(val, format)) {
178
+ ctx.addIssue({ code: "custom", message: getMessage("format", { format }) });
179
+ return;
180
+ }
181
+ }
182
+ if (includes && !val.includes(includes)) {
183
+ ctx.addIssue({ code: "custom", message: getMessage("includes", { includes }) });
184
+ return;
185
+ }
186
+ if (excludes) {
187
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
188
+ for (const exclude of excludeList) {
189
+ if (val.includes(exclude)) {
190
+ ctx.addIssue({ code: "custom", message: getMessage("excludes", { excludes: exclude }) });
191
+ return;
192
+ }
193
+ }
194
+ }
195
+ if (regex) {
196
+ return;
197
+ }
198
+ const timeMinutes = parseTimeToMinutes(val, format);
199
+ if (timeMinutes === null) {
200
+ ctx.addIssue({ code: "custom", message: getMessage("invalid") });
201
+ return;
202
+ }
203
+ const hour = Math.floor(timeMinutes / 60);
204
+ if (minHour !== void 0 && hour < minHour) {
205
+ ctx.addIssue({ code: "custom", message: getMessage("hour", { minHour, maxHour: maxHour ?? 23 }) });
206
+ return;
207
+ }
208
+ if (maxHour !== void 0 && hour > maxHour) {
209
+ ctx.addIssue({ code: "custom", message: getMessage("hour", { minHour: minHour ?? 0, maxHour }) });
210
+ return;
211
+ }
212
+ if (allowedHours && allowedHours.length > 0) {
213
+ if (!allowedHours.includes(hour)) {
214
+ ctx.addIssue({ code: "custom", message: getMessage("hour", { allowedHours: allowedHours.join(", ") }) });
215
+ return;
216
+ }
217
+ }
218
+ const minute = timeMinutes % 60;
219
+ if (minuteStep !== void 0 && minute % minuteStep !== 0) {
220
+ ctx.addIssue({ code: "custom", message: getMessage("minute", { minuteStep }) });
221
+ return;
222
+ }
223
+ if (secondStep !== void 0 && format.includes("ss")) {
224
+ const secondMatch = val.match(/:(\d{2})$/);
225
+ if (secondMatch) {
226
+ const seconds = parseInt(secondMatch[1], 10);
227
+ if (seconds % secondStep !== 0) {
228
+ ctx.addIssue({ code: "custom", message: getMessage("second", { secondStep }) });
229
+ return;
230
+ }
231
+ }
232
+ }
233
+ if (min) {
234
+ const minMinutes = parseTimeToMinutes(min, format);
235
+ if (minMinutes !== null && timeMinutes < minMinutes) {
236
+ ctx.addIssue({ code: "custom", message: getMessage("min", { min }) });
237
+ return;
238
+ }
239
+ }
240
+ if (max) {
241
+ const maxMinutes = parseTimeToMinutes(max, format);
242
+ if (maxMinutes !== null && timeMinutes > maxMinutes) {
243
+ ctx.addIssue({ code: "custom", message: getMessage("max", { max }) });
244
+ return;
245
+ }
246
+ }
247
+ });
248
+ return schema;
249
+ }
250
+
251
+ export {
252
+ TIME_PATTERNS,
253
+ parseTimeToMinutes,
254
+ validateTimeFormat,
255
+ normalizeTime,
256
+ time
257
+ };