@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,345 @@
1
+ import { describe, it, expect, beforeEach } from "vitest"
2
+ import { coordinate, setLocale, validateLatitude, validateLongitude } from "../../src"
3
+
4
+ describe("coordinate(true) validator", () => {
5
+ beforeEach(() => setLocale("en-US"))
6
+
7
+ describe("pair mode", () => {
8
+ it("should validate correct coordinate pairs", () => {
9
+ const schema = coordinate(true)
10
+
11
+ expect(schema.parse("25.0330,121.5654")).toBe("25.0330,121.5654")
12
+ expect(schema.parse("0,0")).toBe("0,0")
13
+ expect(schema.parse("-90,180")).toBe("-90,180")
14
+ expect(schema.parse("90,-180")).toBe("90,-180")
15
+ })
16
+
17
+ it("should validate pairs with spaces around comma", () => {
18
+ const schema = coordinate(true)
19
+
20
+ expect(schema.parse("25.0330, 121.5654")).toBe("25.0330, 121.5654")
21
+ expect(schema.parse(" 25.0330 , 121.5654 ")).toBe("25.0330 , 121.5654")
22
+ })
23
+
24
+ it("should reject pairs with latitude out of range", () => {
25
+ const schema = coordinate(true)
26
+
27
+ expect(() => schema.parse("91,0")).toThrow("Latitude must be between -90 and 90")
28
+ expect(() => schema.parse("-91,0")).toThrow("Latitude must be between -90 and 90")
29
+ expect(() => schema.parse("100,200")).toThrow("Latitude must be between -90 and 90")
30
+ })
31
+
32
+ it("should reject pairs with longitude out of range", () => {
33
+ const schema = coordinate(true)
34
+
35
+ expect(() => schema.parse("0,181")).toThrow("Longitude must be between -180 and 180")
36
+ expect(() => schema.parse("0,-181")).toThrow("Longitude must be between -180 and 180")
37
+ })
38
+
39
+ it("should reject non-numeric coordinate pairs", () => {
40
+ const schema = coordinate(true)
41
+
42
+ expect(() => schema.parse("abc,def")).toThrow("Invalid coordinate")
43
+ })
44
+
45
+ it("should reject pairs with wrong number of parts", () => {
46
+ const schema = coordinate(true)
47
+
48
+ expect(() => schema.parse("25.0330")).toThrow("Invalid coordinate")
49
+ expect(() => schema.parse("25.0330,121.5654,0")).toThrow("Invalid coordinate")
50
+ })
51
+ })
52
+
53
+ describe("latitude mode", () => {
54
+ it("should validate correct latitudes", () => {
55
+ const schema = coordinate(true, { type: "latitude" })
56
+
57
+ expect(schema.parse("0")).toBe("0")
58
+ expect(schema.parse("90")).toBe("90")
59
+ expect(schema.parse("-90")).toBe("-90")
60
+ expect(schema.parse("45.5")).toBe("45.5")
61
+ expect(schema.parse("-45.123456")).toBe("-45.123456")
62
+ })
63
+
64
+ it("should reject latitudes out of range", () => {
65
+ const schema = coordinate(true, { type: "latitude" })
66
+
67
+ expect(() => schema.parse("91")).toThrow("Latitude must be between -90 and 90")
68
+ expect(() => schema.parse("-91")).toThrow("Latitude must be between -90 and 90")
69
+ expect(() => schema.parse("180")).toThrow("Latitude must be between -90 and 90")
70
+ })
71
+
72
+ it("should reject non-numeric latitude values", () => {
73
+ const schema = coordinate(true, { type: "latitude" })
74
+
75
+ expect(() => schema.parse("abc")).toThrow("Latitude must be between -90 and 90")
76
+ })
77
+ })
78
+
79
+ describe("longitude mode", () => {
80
+ it("should validate correct longitudes", () => {
81
+ const schema = coordinate(true, { type: "longitude" })
82
+
83
+ expect(schema.parse("0")).toBe("0")
84
+ expect(schema.parse("180")).toBe("180")
85
+ expect(schema.parse("-180")).toBe("-180")
86
+ expect(schema.parse("121.5654")).toBe("121.5654")
87
+ expect(schema.parse("-73.9857")).toBe("-73.9857")
88
+ })
89
+
90
+ it("should reject longitudes out of range", () => {
91
+ const schema = coordinate(true, { type: "longitude" })
92
+
93
+ expect(() => schema.parse("181")).toThrow("Longitude must be between -180 and 180")
94
+ expect(() => schema.parse("-181")).toThrow("Longitude must be between -180 and 180")
95
+ expect(() => schema.parse("360")).toThrow("Longitude must be between -180 and 180")
96
+ })
97
+
98
+ it("should reject non-numeric longitude values", () => {
99
+ const schema = coordinate(true, { type: "longitude" })
100
+
101
+ expect(() => schema.parse("abc")).toThrow("Longitude must be between -180 and 180")
102
+ })
103
+ })
104
+
105
+ describe("precision", () => {
106
+ it("should accept values within precision limit for pair mode", () => {
107
+ const schema = coordinate(true, { precision: 4 })
108
+
109
+ expect(schema.parse("25.0330,121.5654")).toBe("25.0330,121.5654")
110
+ expect(schema.parse("25.03,121.56")).toBe("25.03,121.56")
111
+ expect(schema.parse("25,121")).toBe("25,121")
112
+ })
113
+
114
+ it("should reject values exceeding precision limit for pair mode", () => {
115
+ const schema = coordinate(true, { precision: 2 })
116
+
117
+ expect(() => schema.parse("25.033,121.565")).toThrow("Invalid coordinate")
118
+ })
119
+
120
+ it("should enforce precision in latitude mode", () => {
121
+ const schema = coordinate(true, { type: "latitude", precision: 2 })
122
+
123
+ expect(schema.parse("45.12")).toBe("45.12")
124
+ expect(schema.parse("45")).toBe("45")
125
+ expect(() => schema.parse("45.123")).toThrow("Invalid coordinate")
126
+ })
127
+
128
+ it("should enforce precision in longitude mode", () => {
129
+ const schema = coordinate(true, { type: "longitude", precision: 3 })
130
+
131
+ expect(schema.parse("121.565")).toBe("121.565")
132
+ expect(schema.parse("121")).toBe("121")
133
+ expect(() => schema.parse("121.5654")).toThrow("Invalid coordinate")
134
+ })
135
+ })
136
+
137
+ describe("required/optional behavior", () => {
138
+ it("should require a value when required=true", () => {
139
+ const schema = coordinate(true)
140
+
141
+ expect(() => schema.parse("")).toThrow("Required")
142
+ expect(() => schema.parse(null)).toThrow()
143
+ expect(() => schema.parse(undefined)).toThrow()
144
+ })
145
+
146
+ it("should allow empty values when required=false", () => {
147
+ const schema = coordinate(false)
148
+
149
+ expect(schema.parse("")).toBe(null)
150
+ expect(schema.parse(null)).toBe(null)
151
+ expect(schema.parse(undefined)).toBe(null)
152
+ })
153
+
154
+ it("should still validate when optional and a value is provided", () => {
155
+ const schema = coordinate(false)
156
+
157
+ expect(schema.parse("25.0330,121.5654")).toBe("25.0330,121.5654")
158
+ expect(() => schema.parse("100,200")).toThrow("Latitude must be between -90 and 90")
159
+ })
160
+
161
+ it("should use default values for required schema", () => {
162
+ const schema = coordinate(true, { defaultValue: "0,0" })
163
+
164
+ expect(schema.parse("")).toBe("0,0")
165
+ })
166
+
167
+ it("should use default values for optional schema", () => {
168
+ const schema = coordinate(false, { defaultValue: "0,0" })
169
+
170
+ expect(schema.parse("")).toBe("0,0")
171
+ expect(schema.parse(null)).toBe("0,0")
172
+ })
173
+ })
174
+
175
+ describe("transform function", () => {
176
+ it("should apply custom transform before validation", () => {
177
+ const schema = coordinate(true, {
178
+ transform: (val) => val.replace(/\s+/g, ""),
179
+ })
180
+
181
+ expect(schema.parse("25.0330 , 121.5654")).toBe("25.0330,121.5654")
182
+ })
183
+
184
+ it("should apply transform that normalizes separators", () => {
185
+ const schema = coordinate(true, {
186
+ transform: (val) => val.replace(";", ","),
187
+ })
188
+
189
+ expect(schema.parse("25.0330;121.5654")).toBe("25.0330,121.5654")
190
+ })
191
+ })
192
+
193
+ describe("i18n support", () => {
194
+ it("should use English messages by default", () => {
195
+ setLocale("en-US")
196
+ const schema = coordinate(true)
197
+
198
+ expect(() => schema.parse("")).toThrow("Required")
199
+ expect(() => schema.parse("abc,def")).toThrow("Invalid coordinate")
200
+ expect(() => schema.parse("91,0")).toThrow("Latitude must be between -90 and 90")
201
+ expect(() => schema.parse("0,181")).toThrow("Longitude must be between -180 and 180")
202
+ })
203
+
204
+ it("should use Chinese messages when locale is zh-TW", () => {
205
+ setLocale("zh-TW")
206
+ const schema = coordinate(true)
207
+
208
+ expect(() => schema.parse("")).toThrow("必填")
209
+ expect(() => schema.parse("abc,def")).toThrow("無效的座標")
210
+ expect(() => schema.parse("91,0")).toThrow("緯度必須介於 -90 與 90 之間")
211
+ expect(() => schema.parse("0,181")).toThrow("經度必須介於 -180 與 180 之間")
212
+ })
213
+
214
+ it("should support custom i18n messages", () => {
215
+ const schema = coordinate(true, {
216
+ i18n: {
217
+ "en-US": {
218
+ required: "Coordinate is required",
219
+ invalid: "Coordinate is invalid",
220
+ invalidLatitude: "Bad latitude",
221
+ invalidLongitude: "Bad longitude",
222
+ },
223
+ "zh-TW": {
224
+ required: "請輸入座標",
225
+ invalid: "座標格式錯誤",
226
+ invalidLatitude: "緯度錯誤",
227
+ invalidLongitude: "經度錯誤",
228
+ },
229
+ },
230
+ })
231
+
232
+ setLocale("en-US")
233
+ expect(() => schema.parse("")).toThrow("Coordinate is required")
234
+ expect(() => schema.parse("abc,def")).toThrow("Coordinate is invalid")
235
+ expect(() => schema.parse("91,0")).toThrow("Bad latitude")
236
+ expect(() => schema.parse("0,181")).toThrow("Bad longitude")
237
+
238
+ setLocale("zh-TW")
239
+ expect(() => schema.parse("")).toThrow("請輸入座標")
240
+ expect(() => schema.parse("abc,def")).toThrow("座標格式錯誤")
241
+ expect(() => schema.parse("91,0")).toThrow("緯度錯誤")
242
+ expect(() => schema.parse("0,181")).toThrow("經度錯誤")
243
+ })
244
+ })
245
+
246
+ describe("utility functions", () => {
247
+ it("validateLatitude should accept valid latitudes", () => {
248
+ expect(validateLatitude(0)).toBe(true)
249
+ expect(validateLatitude(90)).toBe(true)
250
+ expect(validateLatitude(-90)).toBe(true)
251
+ expect(validateLatitude(45.5)).toBe(true)
252
+ expect(validateLatitude(-45.123456)).toBe(true)
253
+ })
254
+
255
+ it("validateLatitude should reject invalid latitudes", () => {
256
+ expect(validateLatitude(91)).toBe(false)
257
+ expect(validateLatitude(-91)).toBe(false)
258
+ expect(validateLatitude(180)).toBe(false)
259
+ expect(validateLatitude(NaN)).toBe(false)
260
+ expect(validateLatitude(Infinity)).toBe(false)
261
+ expect(validateLatitude(-Infinity)).toBe(false)
262
+ })
263
+
264
+ it("validateLongitude should accept valid longitudes", () => {
265
+ expect(validateLongitude(0)).toBe(true)
266
+ expect(validateLongitude(180)).toBe(true)
267
+ expect(validateLongitude(-180)).toBe(true)
268
+ expect(validateLongitude(121.5654)).toBe(true)
269
+ expect(validateLongitude(-73.9857)).toBe(true)
270
+ })
271
+
272
+ it("validateLongitude should reject invalid longitudes", () => {
273
+ expect(validateLongitude(181)).toBe(false)
274
+ expect(validateLongitude(-181)).toBe(false)
275
+ expect(validateLongitude(360)).toBe(false)
276
+ expect(validateLongitude(NaN)).toBe(false)
277
+ expect(validateLongitude(Infinity)).toBe(false)
278
+ expect(validateLongitude(-Infinity)).toBe(false)
279
+ })
280
+ })
281
+
282
+ describe("edge cases", () => {
283
+ it("should handle NaN inputs", () => {
284
+ const schema = coordinate(true)
285
+
286
+ expect(() => schema.parse("NaN,NaN")).toThrow("Invalid coordinate")
287
+ expect(() => schema.parse(NaN)).toThrow()
288
+ })
289
+
290
+ it("should handle empty and whitespace-only inputs", () => {
291
+ const schema = coordinate(true)
292
+ const optionalSchema = coordinate(false)
293
+
294
+ expect(() => schema.parse("")).toThrow("Required")
295
+ expect(() => schema.parse(" ")).toThrow("Required")
296
+ expect(() => schema.parse("\t\n")).toThrow("Required")
297
+
298
+ expect(optionalSchema.parse("")).toBe(null)
299
+ expect(optionalSchema.parse(" ")).toBe(null)
300
+ expect(optionalSchema.parse("\t\n")).toBe(null)
301
+ })
302
+
303
+ it("should handle boundary values for latitude", () => {
304
+ const latSchema = coordinate(true, { type: "latitude" })
305
+
306
+ expect(latSchema.parse("90")).toBe("90")
307
+ expect(latSchema.parse("-90")).toBe("-90")
308
+ expect(latSchema.parse("0")).toBe("0")
309
+ expect(() => latSchema.parse("90.0001")).toThrow("Latitude must be between -90 and 90")
310
+ expect(() => latSchema.parse("-90.0001")).toThrow("Latitude must be between -90 and 90")
311
+ })
312
+
313
+ it("should handle boundary values for longitude", () => {
314
+ const lngSchema = coordinate(true, { type: "longitude" })
315
+
316
+ expect(lngSchema.parse("180")).toBe("180")
317
+ expect(lngSchema.parse("-180")).toBe("-180")
318
+ expect(lngSchema.parse("0")).toBe("0")
319
+ expect(() => lngSchema.parse("180.0001")).toThrow("Longitude must be between -180 and 180")
320
+ expect(() => lngSchema.parse("-180.0001")).toThrow("Longitude must be between -180 and 180")
321
+ })
322
+
323
+ it("should handle boundary values for coordinate pairs", () => {
324
+ const schema = coordinate(true)
325
+
326
+ expect(schema.parse("90,180")).toBe("90,180")
327
+ expect(schema.parse("-90,-180")).toBe("-90,-180")
328
+ expect(schema.parse("90,-180")).toBe("90,-180")
329
+ expect(schema.parse("-90,180")).toBe("-90,180")
330
+ })
331
+
332
+ it("should handle numeric input via string coercion", () => {
333
+ const schema = coordinate(true, { type: "latitude" })
334
+
335
+ expect(schema.parse(45)).toBe("45")
336
+ })
337
+
338
+ it("should handle 'null' and 'undefined' string values when required", () => {
339
+ const schema = coordinate(true)
340
+
341
+ expect(() => schema.parse("null")).toThrow("Required")
342
+ expect(() => schema.parse("undefined")).toThrow("Required")
343
+ })
344
+ })
345
+ })