@hy_ong/zod-kit 0.1.16 → 0.2.1

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 (225) hide show
  1. package/.github/workflows/ci.yml +24 -0
  2. package/CLAUDE.md +184 -0
  3. package/dist/chunk-36NWHESN.js +124 -0
  4. package/dist/chunk-4LYZAO3P.js +165 -0
  5. package/dist/chunk-5GAZQDVS.cjs +206 -0
  6. package/dist/chunk-5LS4DSRQ.cjs +127 -0
  7. package/dist/chunk-5OGW2ERW.js +181 -0
  8. package/dist/chunk-5ZEKWPSE.cjs +69 -0
  9. package/dist/chunk-6OGDPSWT.js +135 -0
  10. package/dist/chunk-6X22I6NQ.cjs +136 -0
  11. package/dist/chunk-77KZUPPN.cjs +177 -0
  12. package/dist/chunk-AANSHH2O.cjs +165 -0
  13. package/dist/chunk-AI72FMOF.cjs +130 -0
  14. package/dist/chunk-AWV2IT66.js +146 -0
  15. package/dist/chunk-B3U5G3AA.js +160 -0
  16. package/dist/chunk-CFFCBWYL.cjs +99 -0
  17. package/dist/chunk-DPXRMSB2.js +130 -0
  18. package/dist/chunk-DRXPGQM6.cjs +135 -0
  19. package/dist/chunk-EAU42EVH.js +161 -0
  20. package/dist/chunk-FC6VDOC7.js +206 -0
  21. package/dist/chunk-FVO4743A.cjs +134 -0
  22. package/dist/chunk-G6DV7LX7.cjs +161 -0
  23. package/dist/chunk-I2RJMDXN.js +90 -0
  24. package/dist/chunk-IJEEM3DI.js +136 -0
  25. package/dist/chunk-JBNCMS42.cjs +151 -0
  26. package/dist/chunk-JZ2SHRGZ.js +87 -0
  27. package/dist/chunk-KARFFIMP.js +696 -0
  28. package/dist/chunk-LIQSVJLS.js +177 -0
  29. package/dist/chunk-LKPXHW5N.cjs +181 -0
  30. package/dist/chunk-MAQRXYE6.js +118 -0
  31. package/dist/chunk-MCDESS3T.js +69 -0
  32. package/dist/chunk-MG25BEV4.cjs +160 -0
  33. package/dist/chunk-NKCYXBGX.js +99 -0
  34. package/dist/chunk-OEK7QSQP.js +75 -0
  35. package/dist/chunk-OMFQ7Z63.cjs +696 -0
  36. package/dist/chunk-OP4KV3BY.cjs +124 -0
  37. package/dist/chunk-P2NONIMS.js +257 -0
  38. package/dist/chunk-P364KRO5.js +61 -0
  39. package/dist/chunk-PGSDXR2I.js +71 -0
  40. package/dist/chunk-PL2GERLG.cjs +61 -0
  41. package/dist/chunk-R5G4V7C6.cjs +75 -0
  42. package/dist/chunk-RKHX3DGH.js +127 -0
  43. package/dist/chunk-TSHL7ZO2.js +134 -0
  44. package/dist/chunk-UFNVCUPQ.cjs +301 -0
  45. package/dist/chunk-VCRKYMJM.js +301 -0
  46. package/dist/chunk-VDOAPLA6.cjs +257 -0
  47. package/dist/chunk-VP5CCP5F.cjs +90 -0
  48. package/dist/chunk-W2EWMV3A.cjs +87 -0
  49. package/dist/chunk-WWRFBLCR.cjs +146 -0
  50. package/dist/chunk-YALLOVNO.cjs +118 -0
  51. package/dist/chunk-YAU6JCYL.cjs +71 -0
  52. package/dist/chunk-YWV2BBXN.cjs +2526 -0
  53. package/dist/chunk-ZBOQCXD4.js +2526 -0
  54. package/dist/chunk-ZFQQXWNB.js +151 -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 +262 -0
  89. package/dist/common/id.d.ts +262 -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 -3599
  118. package/dist/index.d.cts +28 -3150
  119. package/dist/index.d.ts +28 -3150
  120. package/dist/index.js +135 -3475
  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 +128 -2
  162. package/src/config.ts +2 -2
  163. package/src/i18n/index.ts +18 -2
  164. package/src/i18n/locales/en-GB.json +247 -0
  165. package/src/i18n/locales/{en.json → en-US.json} +43 -0
  166. package/src/i18n/locales/id-ID.json +247 -0
  167. package/src/i18n/locales/ja-JP.json +247 -0
  168. package/src/i18n/locales/ko-KR.json +247 -0
  169. package/src/i18n/locales/ms-MY.json +247 -0
  170. package/src/i18n/locales/th-TH.json +247 -0
  171. package/src/i18n/locales/vi-VN.json +247 -0
  172. package/src/i18n/locales/zh-CN.json +247 -0
  173. package/src/i18n/locales/zh-TW.json +43 -0
  174. package/src/index.ts +10 -2
  175. package/src/validators/common/boolean.ts +1 -1
  176. package/src/validators/common/color.ts +192 -0
  177. package/src/validators/common/coordinate.ts +159 -0
  178. package/src/validators/common/credit-card.ts +134 -0
  179. package/src/validators/common/date.ts +1 -1
  180. package/src/validators/common/datetime.ts +1 -1
  181. package/src/validators/common/email.ts +1 -1
  182. package/src/validators/common/file.ts +1 -1
  183. package/src/validators/common/id.ts +1 -1
  184. package/src/validators/common/ip.ts +210 -0
  185. package/src/validators/common/number.ts +1 -1
  186. package/src/validators/common/password.ts +1 -1
  187. package/src/validators/common/text.ts +1 -1
  188. package/src/validators/common/time.ts +1 -1
  189. package/src/validators/common/url.ts +1 -1
  190. package/src/validators/taiwan/bank-account.ts +176 -0
  191. package/src/validators/taiwan/business-id.ts +1 -1
  192. package/src/validators/taiwan/fax.ts +1 -1
  193. package/src/validators/taiwan/invoice.ts +84 -0
  194. package/src/validators/taiwan/license-plate.ts +110 -0
  195. package/src/validators/taiwan/mobile.ts +1 -1
  196. package/src/validators/taiwan/national-id.ts +1 -1
  197. package/src/validators/taiwan/passport.ts +103 -0
  198. package/src/validators/taiwan/postal-code.ts +1 -1
  199. package/src/validators/taiwan/tel.ts +1 -1
  200. package/tests/common/boolean.test.ts +6 -6
  201. package/tests/common/color.test.ts +587 -0
  202. package/tests/common/coordinate.test.ts +345 -0
  203. package/tests/common/credit-card.test.ts +378 -0
  204. package/tests/common/date.test.ts +8 -8
  205. package/tests/common/datetime.test.ts +7 -7
  206. package/tests/common/email.test.ts +6 -6
  207. package/tests/common/file.test.ts +6 -6
  208. package/tests/common/id.test.ts +5 -5
  209. package/tests/common/ip.test.ts +419 -0
  210. package/tests/common/number.test.ts +6 -6
  211. package/tests/common/password.test.ts +5 -5
  212. package/tests/common/text.test.ts +7 -7
  213. package/tests/common/time.test.ts +7 -7
  214. package/tests/common/url.test.ts +10 -10
  215. package/tests/taiwan/bank-account.test.ts +286 -0
  216. package/tests/taiwan/business-id.test.ts +4 -4
  217. package/tests/taiwan/fax.test.ts +7 -7
  218. package/tests/taiwan/invoice.test.ts +227 -0
  219. package/tests/taiwan/license-plate.test.ts +280 -0
  220. package/tests/taiwan/mobile.test.ts +7 -7
  221. package/tests/taiwan/national-id.test.ts +4 -4
  222. package/tests/taiwan/passport.test.ts +277 -0
  223. package/tests/taiwan/postal-code.test.ts +6 -6
  224. package/tests/taiwan/tel.test.ts +7 -7
  225. package/tsup.config.ts +36 -0
@@ -0,0 +1,24 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ node-version: [18, 20, 22]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-node@v4
17
+ with:
18
+ node-version: ${{ matrix.node-version }}
19
+ cache: npm
20
+ - run: npm ci
21
+ - run: npx eslint src/
22
+ - run: npx tsc --noEmit
23
+ - run: npm test -- --run
24
+ - run: npm run build
package/CLAUDE.md ADDED
@@ -0,0 +1,184 @@
1
+ # CLAUDE.md — zod-kit
2
+
3
+ ## Project Overview
4
+
5
+ `@hy_ong/zod-kit` is a TypeScript library providing pre-built Zod validation schemas with full i18n support (10 locales). It includes common validators (email, password, text, number, credit card, IP, color, coordinate, etc.) and Taiwan-specific validators (National ID, Business ID, mobile, tel, fax, postal code, invoice, license plate, bank account, passport).
6
+
7
+ - **Version:** 0.2.0
8
+ - **License:** MIT
9
+ - **NPM:** `@hy_ong/zod-kit`
10
+ - **Peer dependency:** Zod ^4.3.6
11
+ - **Production dependency:** dayjs ^1.11.19
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ # Run all tests (1050 test cases across 25 files)
17
+ npm test
18
+
19
+ # Run tests once (no watch mode)
20
+ npm test -- --run
21
+
22
+ # Run specific test file
23
+ npx vitest run tests/common/email.test.ts
24
+
25
+ # Build (ESM + CJS + .d.ts, 26 entry points with code splitting)
26
+ npm run build
27
+ # Uses tsup.config.ts for multi-entry build
28
+
29
+ # Lint
30
+ npx eslint src/
31
+
32
+ # Type check
33
+ npx tsc --noEmit
34
+
35
+ # Publish (runs tests + build automatically via prepublishOnly)
36
+ npm publish --access public
37
+ ```
38
+
39
+ ## Project Structure
40
+
41
+ ```
42
+ src/
43
+ ├── index.ts # Re-exports all validators + config
44
+ ├── config.ts # Locale state (setLocale, getLocale)
45
+ ├── i18n/
46
+ │ ├── index.ts # Translation function t(key, params)
47
+ │ └── locales/
48
+ │ ├── en-US.json # English (US)
49
+ │ ├── en-GB.json # English (UK)
50
+ │ ├── zh-TW.json # Traditional Chinese
51
+ │ ├── zh-CN.json # Simplified Chinese
52
+ │ ├── ja-JP.json # Japanese
53
+ │ ├── ko-KR.json # Korean
54
+ │ ├── ms-MY.json # Malay
55
+ │ ├── id-ID.json # Indonesian
56
+ │ ├── th-TH.json # Thai
57
+ │ └── vi-VN.json # Vietnamese
58
+ └── validators/
59
+ ├── common/ # 15 universal validators
60
+ │ ├── boolean.ts
61
+ │ ├── color.ts # Hex, RGB, HSL with alpha support
62
+ │ ├── coordinate.ts # Latitude, longitude, coordinate pairs
63
+ │ ├── credit-card.ts # Luhn algorithm, card type detection
64
+ │ ├── date.ts
65
+ │ ├── datetime.ts # Uses dayjs for parsing
66
+ │ ├── email.ts
67
+ │ ├── file.ts
68
+ │ ├── id.ts # UUID, nanoid, ObjectId
69
+ │ ├── ip.ts # IPv4, IPv6, CIDR support
70
+ │ ├── number.ts
71
+ │ ├── password.ts
72
+ │ ├── text.ts
73
+ │ ├── time.ts
74
+ │ └── url.ts
75
+ └── taiwan/ # 10 Taiwan-specific validators
76
+ ├── bank-account.ts # 銀行帳號 (bank code + account number)
77
+ ├── business-id.ts # 統一編號 (checksum)
78
+ ├── fax.ts
79
+ ├── invoice.ts # 統一發票 (2 letters + 8 digits)
80
+ ├── license-plate.ts # 車牌號碼 (new + legacy formats)
81
+ ├── mobile.ts # 09X-XXXX-XXXX
82
+ ├── national-id.ts # 身分證/居留證 (checksum)
83
+ ├── passport.ts # 護照號碼 (9 digits, type prefix)
84
+ ├── postal-code.ts # 3/5/6-digit with official data
85
+ └── tel.ts # Landline + toll-free (0800/0809)
86
+
87
+ tests/ # Mirrors src/validators/ structure
88
+ ├── common/ # 15 test files
89
+ └── taiwan/ # 10 test files
90
+ ```
91
+
92
+ ## Per-Module Imports
93
+
94
+ Users can import individual validators for tree-shaking:
95
+
96
+ ```typescript
97
+ // Full import
98
+ import { email, twNationalId } from "@hy_ong/zod-kit"
99
+
100
+ // Per-module import (tree-shakeable)
101
+ import { email } from "@hy_ong/zod-kit/common/email"
102
+ import { twNationalId } from "@hy_ong/zod-kit/taiwan/national-id"
103
+ ```
104
+
105
+ All 26 sub-modules are configured in `tsup.config.ts` (entry points) and `package.json` (exports map).
106
+
107
+ ## Architecture & Patterns
108
+
109
+ ### Validator Function Signature
110
+
111
+ Every validator follows this consistent pattern:
112
+
113
+ ```typescript
114
+ function validatorName<IsRequired extends boolean = false>(
115
+ required?: IsRequired,
116
+ options?: Omit<ValidatorOptions<IsRequired>, "required">
117
+ ): ValidatorSchema<IsRequired>
118
+ ```
119
+
120
+ - First param: `required` boolean (defaults to `false`)
121
+ - Second param: options object (varies per validator)
122
+ - Returns: `ZodType` — required returns non-nullable, optional returns nullable
123
+
124
+ ### Validation Pipeline
125
+
126
+ All validators use a two-step approach:
127
+ 1. `z.preprocess()` — input transformation (trim, coerce, normalize)
128
+ 2. `.superRefine()` — complex business logic and custom error messages
129
+
130
+ ### i18n System
131
+
132
+ - 10 locales: en-US, en-GB, zh-TW, zh-CN, ja-JP, ko-KR, ms-MY, id-ID, th-TH, vi-VN
133
+ - Locale state is global: `setLocale("en-US")` / `setLocale("zh-TW")`
134
+ - Translation keys are dot-separated: `common.email.invalid`
135
+ - Parameter substitution: `${paramName}` in message templates
136
+ - Each validator supports custom `i18n` overrides in options
137
+
138
+ ### Taiwan Validators
139
+
140
+ Taiwan validators implement domain-specific algorithms:
141
+ - **National ID / Business ID**: Checksum verification with weighted sums
142
+ - **Postal Code**: Validates against official Chunghwa Post data (28KB+ of mappings)
143
+ - **Tel/Fax**: Area code + digit length rules per region
144
+ - **Mobile**: Operator prefix validation (090-099)
145
+ - **Invoice**: 2-letter prefix + 8-digit format, auto-strip hyphens, auto-uppercase
146
+ - **License Plate**: New (2012+) and legacy formats for car/motorcycle
147
+ - **Bank Account**: Bank code validation against official list + account number length
148
+ - **Passport**: 9-digit format with type prefix (0=diplomatic, 1=official, 2=ordinary, 3=travel)
149
+
150
+ ## Code Style
151
+
152
+ - **Formatter:** Prettier — no semicolons, double quotes, 200 char line width, 2-space indent
153
+ - **Linter:** ESLint 9 + typescript-eslint (recommended rules, `no-explicit-any` disabled)
154
+ - **TypeScript:** Strict mode, ESNext target, declaration files generated
155
+ - **Module:** ES Module (`"type": "module"` in package.json)
156
+ - **Tests:** Vitest with globals enabled, node environment
157
+
158
+ ## Conventions
159
+
160
+ - Commit messages use **Conventional Commits**: `feat(scope):`, `fix(scope):`, `chore:`, `docs:`
161
+ - Every validator exports both a factory function and relevant type definitions
162
+ - Test files use `beforeEach(() => setLocale("en-US"))` for consistent locale
163
+ - Test structure: `describe("validatorName(required) features")` → nested `describe` per feature → `it` cases
164
+ - Source files are organized by domain: `common/` for universal, `taiwan/` for locale-specific
165
+ - CI runs on GitHub Actions (Node 18/20/22): lint, type-check, test, build
166
+
167
+ ## Build Output
168
+
169
+ `dist/` contains per-module outputs with code splitting:
170
+ - `{module}.js` — ESM bundle
171
+ - `{module}.cjs` — CommonJS bundle
172
+ - `{module}.d.ts` — TypeScript declarations (ESM)
173
+ - `{module}.d.cts` — TypeScript declarations (CJS)
174
+ - `chunk-*.js` / `chunk-*.cjs` — Shared code chunks
175
+
176
+ ## Adding a New Validator
177
+
178
+ 1. Create `src/validators/{domain}/{name}.ts` following the generic pattern above
179
+ 2. Add i18n keys to all 10 locale files in `src/i18n/locales/`
180
+ 3. Export from `src/index.ts`
181
+ 4. Add entry point to `tsup.config.ts`
182
+ 5. Add exports map entry to `package.json`
183
+ 6. Create `tests/{domain}/{name}.test.ts` with comprehensive coverage
184
+ 7. Test with `npx vitest run tests/{domain}/{name}.test.ts`
@@ -0,0 +1,124 @@
1
+ import {
2
+ getLocale,
3
+ t
4
+ } from "./chunk-ZBOQCXD4.js";
5
+
6
+ // src/validators/common/date.ts
7
+ import { z } from "zod";
8
+ import dayjs from "dayjs";
9
+ import customParseFormat from "dayjs/plugin/customParseFormat";
10
+ import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
11
+ import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
12
+ import isToday from "dayjs/plugin/isToday";
13
+ import weekday from "dayjs/plugin/weekday";
14
+ dayjs.extend(isSameOrAfter);
15
+ dayjs.extend(isSameOrBefore);
16
+ dayjs.extend(customParseFormat);
17
+ dayjs.extend(isToday);
18
+ dayjs.extend(weekday);
19
+ function date(required, options) {
20
+ const {
21
+ min,
22
+ max,
23
+ format = "YYYY-MM-DD",
24
+ includes,
25
+ excludes,
26
+ mustBePast,
27
+ mustBeFuture,
28
+ mustBeToday,
29
+ mustNotBeToday,
30
+ weekdaysOnly,
31
+ weekendsOnly,
32
+ transform,
33
+ defaultValue = null,
34
+ i18n
35
+ } = options ?? {};
36
+ const isRequired = required ?? false;
37
+ const actualDefaultValue = defaultValue ?? (isRequired ? "" : null);
38
+ const getMessage = (key, params) => {
39
+ if (i18n) {
40
+ const currentLocale = getLocale();
41
+ const customMessages = i18n[currentLocale];
42
+ if (customMessages && customMessages[key]) {
43
+ const template = customMessages[key];
44
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
45
+ }
46
+ }
47
+ return t(`common.date.${key}`, params);
48
+ };
49
+ const preprocessFn = (val) => {
50
+ if (val === "" || val === null || val === void 0) {
51
+ return actualDefaultValue;
52
+ }
53
+ let processed = String(val).trim();
54
+ if (transform) {
55
+ processed = transform(processed);
56
+ }
57
+ return processed;
58
+ };
59
+ const baseSchema = isRequired ? z.preprocess(preprocessFn, z.string()) : z.preprocess(preprocessFn, z.string().nullable());
60
+ const schema = baseSchema.superRefine((val, ctx) => {
61
+ if (val === null) return;
62
+ if (isRequired && (val === "" || val === "null" || val === "undefined")) {
63
+ ctx.addIssue({ code: "custom", message: getMessage("required") });
64
+ return;
65
+ }
66
+ if (val !== null && !dayjs(val, format, true).isValid()) {
67
+ ctx.addIssue({ code: "custom", message: getMessage("format", { format }) });
68
+ return;
69
+ }
70
+ const dateObj = dayjs(val, format);
71
+ if (val !== null && min !== void 0 && !dateObj.isSameOrAfter(dayjs(min, format))) {
72
+ ctx.addIssue({ code: "custom", message: getMessage("min", { min }) });
73
+ return;
74
+ }
75
+ if (val !== null && max !== void 0 && !dateObj.isSameOrBefore(dayjs(max, format))) {
76
+ ctx.addIssue({ code: "custom", message: getMessage("max", { max }) });
77
+ return;
78
+ }
79
+ if (val !== null && includes !== void 0 && !val.includes(includes)) {
80
+ ctx.addIssue({ code: "custom", message: getMessage("includes", { includes }) });
81
+ return;
82
+ }
83
+ if (val !== null && excludes !== void 0) {
84
+ const excludeList = Array.isArray(excludes) ? excludes : [excludes];
85
+ for (const exclude of excludeList) {
86
+ if (val.includes(exclude)) {
87
+ ctx.addIssue({ code: "custom", message: getMessage("excludes", { excludes: exclude }) });
88
+ return;
89
+ }
90
+ }
91
+ }
92
+ const today = dayjs().startOf("day");
93
+ const targetDate = dateObj.startOf("day");
94
+ if (val !== null && mustBePast && !targetDate.isBefore(today)) {
95
+ ctx.addIssue({ code: "custom", message: getMessage("past") });
96
+ return;
97
+ }
98
+ if (val !== null && mustBeFuture && !targetDate.isAfter(today)) {
99
+ ctx.addIssue({ code: "custom", message: getMessage("future") });
100
+ return;
101
+ }
102
+ if (val !== null && mustBeToday && !targetDate.isSame(today)) {
103
+ ctx.addIssue({ code: "custom", message: getMessage("today") });
104
+ return;
105
+ }
106
+ if (val !== null && mustNotBeToday && targetDate.isSame(today)) {
107
+ ctx.addIssue({ code: "custom", message: getMessage("notToday") });
108
+ return;
109
+ }
110
+ if (val !== null && weekdaysOnly && (dateObj.day() === 0 || dateObj.day() === 6)) {
111
+ ctx.addIssue({ code: "custom", message: getMessage("weekday") });
112
+ return;
113
+ }
114
+ if (val !== null && weekendsOnly && dateObj.day() !== 0 && dateObj.day() !== 6) {
115
+ ctx.addIssue({ code: "custom", message: getMessage("weekend") });
116
+ return;
117
+ }
118
+ });
119
+ return schema;
120
+ }
121
+
122
+ export {
123
+ date
124
+ };
@@ -0,0 +1,165 @@
1
+ import {
2
+ getLocale,
3
+ t
4
+ } from "./chunk-ZBOQCXD4.js";
5
+
6
+ // src/validators/common/number.ts
7
+ import { z } from "zod";
8
+ function number(required, options) {
9
+ const { min, max, defaultValue, type = "both", positive, negative, nonNegative, nonPositive, multipleOf, precision, finite = true, transform, parseCommas = false, i18n } = options ?? {};
10
+ const isRequired = required ?? false;
11
+ const getMessage = (key, params) => {
12
+ if (i18n) {
13
+ const currentLocale = getLocale();
14
+ const customMessages = i18n[currentLocale];
15
+ if (customMessages && customMessages[key]) {
16
+ const template = customMessages[key];
17
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params?.[k] ?? "");
18
+ }
19
+ }
20
+ return t(`common.number.${key}`, params);
21
+ };
22
+ const actualDefaultValue = defaultValue ?? null;
23
+ const schema = z.preprocess(
24
+ (val) => {
25
+ if (val === "" || val === void 0 || val === null) {
26
+ return actualDefaultValue;
27
+ }
28
+ if (typeof val === "string") {
29
+ let processedVal = val.trim();
30
+ if (parseCommas) {
31
+ processedVal = processedVal.replace(/,/g, "");
32
+ }
33
+ const parsed = Number(processedVal);
34
+ if (isNaN(parsed)) {
35
+ return parsed;
36
+ }
37
+ if (transform) {
38
+ return transform(parsed);
39
+ }
40
+ return parsed;
41
+ }
42
+ if (typeof val === "number") {
43
+ if (transform && Number.isFinite(val)) {
44
+ return transform(val);
45
+ }
46
+ return val;
47
+ }
48
+ return val;
49
+ },
50
+ z.union([z.number(), z.null(), z.nan(), z.custom((val) => val === Infinity || val === -Infinity)])
51
+ ).superRefine((val, ctx) => {
52
+ if (isRequired && val === null) {
53
+ ctx.addIssue({
54
+ code: "custom",
55
+ message: getMessage("required")
56
+ });
57
+ return;
58
+ }
59
+ if (val === null) return;
60
+ if (isNaN(val)) {
61
+ if (type === "integer") {
62
+ ctx.addIssue({
63
+ code: "custom",
64
+ message: getMessage("integer")
65
+ });
66
+ } else if (type === "float") {
67
+ ctx.addIssue({
68
+ code: "custom",
69
+ message: getMessage("float")
70
+ });
71
+ } else {
72
+ ctx.addIssue({
73
+ code: "custom",
74
+ message: getMessage("invalid")
75
+ });
76
+ }
77
+ return;
78
+ }
79
+ if (finite && !Number.isFinite(val)) {
80
+ ctx.addIssue({
81
+ code: "custom",
82
+ message: getMessage("finite")
83
+ });
84
+ return;
85
+ }
86
+ if (type === "integer" && !Number.isInteger(val)) {
87
+ ctx.addIssue({
88
+ code: "custom",
89
+ message: getMessage("integer")
90
+ });
91
+ return;
92
+ }
93
+ if (type === "float" && Number.isInteger(val)) {
94
+ ctx.addIssue({
95
+ code: "custom",
96
+ message: getMessage("float")
97
+ });
98
+ return;
99
+ }
100
+ if (positive && val <= 0) {
101
+ ctx.addIssue({
102
+ code: "custom",
103
+ message: getMessage("positive")
104
+ });
105
+ return;
106
+ }
107
+ if (negative && val >= 0) {
108
+ ctx.addIssue({
109
+ code: "custom",
110
+ message: getMessage("negative")
111
+ });
112
+ return;
113
+ }
114
+ if (nonNegative && val < 0) {
115
+ ctx.addIssue({
116
+ code: "custom",
117
+ message: getMessage("nonNegative")
118
+ });
119
+ return;
120
+ }
121
+ if (nonPositive && val > 0) {
122
+ ctx.addIssue({
123
+ code: "custom",
124
+ message: getMessage("nonPositive")
125
+ });
126
+ return;
127
+ }
128
+ if (min !== void 0 && val < min) {
129
+ ctx.addIssue({
130
+ code: "custom",
131
+ message: getMessage("min", { min })
132
+ });
133
+ return;
134
+ }
135
+ if (max !== void 0 && val > max) {
136
+ ctx.addIssue({
137
+ code: "custom",
138
+ message: getMessage("max", { max })
139
+ });
140
+ return;
141
+ }
142
+ if (multipleOf !== void 0 && val % multipleOf !== 0) {
143
+ ctx.addIssue({
144
+ code: "custom",
145
+ message: getMessage("multipleOf", { multipleOf })
146
+ });
147
+ return;
148
+ }
149
+ if (precision !== void 0) {
150
+ const decimalPlaces = (val.toString().split(".")[1] || "").length;
151
+ if (decimalPlaces > precision) {
152
+ ctx.addIssue({
153
+ code: "custom",
154
+ message: getMessage("precision", { precision })
155
+ });
156
+ return;
157
+ }
158
+ }
159
+ });
160
+ return schema;
161
+ }
162
+
163
+ export {
164
+ number
165
+ };