@atproto/lex-schema 0.1.5 → 0.1.6

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 (263) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/core/$type.d.ts +2 -2
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js.map +1 -1
  5. package/dist/core/record-key.d.ts +1 -1
  6. package/dist/core/record-key.d.ts.map +1 -1
  7. package/dist/core/record-key.js.map +1 -1
  8. package/dist/core/schema.d.ts +3 -2
  9. package/dist/core/schema.d.ts.map +1 -1
  10. package/dist/core/schema.js +1 -1
  11. package/dist/core/schema.js.map +1 -1
  12. package/dist/core/standard-schema.d.ts +2 -2
  13. package/dist/core/standard-schema.d.ts.map +1 -1
  14. package/dist/core/standard-schema.js.map +1 -1
  15. package/dist/core/string-format.d.ts +2 -2
  16. package/dist/core/string-format.d.ts.map +1 -1
  17. package/dist/core/string-format.js.map +1 -1
  18. package/dist/core/validation-error.d.ts +1 -1
  19. package/dist/core/validation-error.d.ts.map +1 -1
  20. package/dist/core/validation-error.js +1 -1
  21. package/dist/core/validation-error.js.map +1 -1
  22. package/dist/core/validator.d.ts +1 -1
  23. package/dist/core/validator.d.ts.map +1 -1
  24. package/dist/core/validator.js +1 -1
  25. package/dist/core/validator.js.map +1 -1
  26. package/dist/helpers.d.ts +2 -2
  27. package/dist/helpers.d.ts.map +1 -1
  28. package/dist/helpers.js +2 -2
  29. package/dist/helpers.js.map +1 -1
  30. package/dist/schema/array.d.ts +1 -1
  31. package/dist/schema/array.d.ts.map +1 -1
  32. package/dist/schema/array.js +1 -1
  33. package/dist/schema/array.js.map +1 -1
  34. package/dist/schema/blob.d.ts +1 -1
  35. package/dist/schema/blob.d.ts.map +1 -1
  36. package/dist/schema/blob.js +2 -2
  37. package/dist/schema/blob.js.map +1 -1
  38. package/dist/schema/boolean.js +1 -1
  39. package/dist/schema/boolean.js.map +1 -1
  40. package/dist/schema/bytes.js +1 -1
  41. package/dist/schema/bytes.js.map +1 -1
  42. package/dist/schema/cid.d.ts +1 -1
  43. package/dist/schema/cid.d.ts.map +1 -1
  44. package/dist/schema/cid.js +3 -3
  45. package/dist/schema/cid.js.map +1 -1
  46. package/dist/schema/custom.js +1 -1
  47. package/dist/schema/custom.js.map +1 -1
  48. package/dist/schema/dict.d.ts +1 -1
  49. package/dist/schema/dict.d.ts.map +1 -1
  50. package/dist/schema/dict.js +1 -1
  51. package/dist/schema/dict.js.map +1 -1
  52. package/dist/schema/discriminated-union.d.ts +1 -1
  53. package/dist/schema/discriminated-union.d.ts.map +1 -1
  54. package/dist/schema/discriminated-union.js +2 -1
  55. package/dist/schema/discriminated-union.js.map +1 -1
  56. package/dist/schema/enum.js +1 -1
  57. package/dist/schema/enum.js.map +1 -1
  58. package/dist/schema/integer.js +1 -1
  59. package/dist/schema/integer.js.map +1 -1
  60. package/dist/schema/intersection.d.ts +1 -1
  61. package/dist/schema/intersection.d.ts.map +1 -1
  62. package/dist/schema/intersection.js +3 -1
  63. package/dist/schema/intersection.js.map +1 -1
  64. package/dist/schema/lex-map.d.ts +1 -1
  65. package/dist/schema/lex-map.d.ts.map +1 -1
  66. package/dist/schema/lex-map.js +1 -1
  67. package/dist/schema/lex-map.js.map +1 -1
  68. package/dist/schema/lex-value.d.ts +1 -1
  69. package/dist/schema/lex-value.d.ts.map +1 -1
  70. package/dist/schema/lex-value.js +1 -1
  71. package/dist/schema/lex-value.js.map +1 -1
  72. package/dist/schema/literal.js +1 -1
  73. package/dist/schema/literal.js.map +1 -1
  74. package/dist/schema/never.js +1 -1
  75. package/dist/schema/never.js.map +1 -1
  76. package/dist/schema/null.js +1 -1
  77. package/dist/schema/null.js.map +1 -1
  78. package/dist/schema/nullable.d.ts +1 -1
  79. package/dist/schema/nullable.d.ts.map +1 -1
  80. package/dist/schema/nullable.js +1 -1
  81. package/dist/schema/nullable.js.map +1 -1
  82. package/dist/schema/object.d.ts +2 -1
  83. package/dist/schema/object.d.ts.map +1 -1
  84. package/dist/schema/object.js +1 -1
  85. package/dist/schema/object.js.map +1 -1
  86. package/dist/schema/optional.d.ts +2 -1
  87. package/dist/schema/optional.d.ts.map +1 -1
  88. package/dist/schema/optional.js +2 -1
  89. package/dist/schema/optional.js.map +1 -1
  90. package/dist/schema/params.d.ts +1 -1
  91. package/dist/schema/params.d.ts.map +1 -1
  92. package/dist/schema/params.js +1 -1
  93. package/dist/schema/params.js.map +1 -1
  94. package/dist/schema/payload.d.ts +3 -2
  95. package/dist/schema/payload.d.ts.map +1 -1
  96. package/dist/schema/payload.js +2 -1
  97. package/dist/schema/payload.js.map +1 -1
  98. package/dist/schema/permission-set.d.ts +1 -1
  99. package/dist/schema/permission-set.d.ts.map +1 -1
  100. package/dist/schema/permission-set.js +1 -0
  101. package/dist/schema/permission-set.js.map +1 -1
  102. package/dist/schema/permission.d.ts +1 -1
  103. package/dist/schema/permission.d.ts.map +1 -1
  104. package/dist/schema/permission.js.map +1 -1
  105. package/dist/schema/procedure.d.ts +1 -1
  106. package/dist/schema/procedure.d.ts.map +1 -1
  107. package/dist/schema/procedure.js +2 -0
  108. package/dist/schema/procedure.js.map +1 -1
  109. package/dist/schema/query.d.ts +1 -1
  110. package/dist/schema/query.d.ts.map +1 -1
  111. package/dist/schema/query.js +2 -0
  112. package/dist/schema/query.js.map +1 -1
  113. package/dist/schema/record.d.ts +2 -2
  114. package/dist/schema/record.d.ts.map +1 -1
  115. package/dist/schema/record.js +1 -1
  116. package/dist/schema/record.js.map +1 -1
  117. package/dist/schema/ref.d.ts +1 -1
  118. package/dist/schema/ref.d.ts.map +1 -1
  119. package/dist/schema/ref.js +1 -1
  120. package/dist/schema/ref.js.map +1 -1
  121. package/dist/schema/refine.d.ts +2 -2
  122. package/dist/schema/refine.d.ts.map +1 -1
  123. package/dist/schema/refine.js +1 -1
  124. package/dist/schema/refine.js.map +1 -1
  125. package/dist/schema/regexp.js +1 -1
  126. package/dist/schema/regexp.js.map +1 -1
  127. package/dist/schema/string.d.ts +2 -2
  128. package/dist/schema/string.d.ts.map +1 -1
  129. package/dist/schema/string.js +1 -1
  130. package/dist/schema/string.js.map +1 -1
  131. package/dist/schema/subscription.d.ts +3 -2
  132. package/dist/schema/subscription.d.ts.map +1 -1
  133. package/dist/schema/subscription.js +2 -0
  134. package/dist/schema/subscription.js.map +1 -1
  135. package/dist/schema/token.d.ts +1 -1
  136. package/dist/schema/token.d.ts.map +1 -1
  137. package/dist/schema/token.js +1 -1
  138. package/dist/schema/token.js.map +1 -1
  139. package/dist/schema/typed-object.d.ts +2 -2
  140. package/dist/schema/typed-object.d.ts.map +1 -1
  141. package/dist/schema/typed-object.js +1 -1
  142. package/dist/schema/typed-object.js.map +1 -1
  143. package/dist/schema/typed-ref.d.ts +1 -1
  144. package/dist/schema/typed-ref.d.ts.map +1 -1
  145. package/dist/schema/typed-ref.js +1 -1
  146. package/dist/schema/typed-ref.js.map +1 -1
  147. package/dist/schema/typed-union.d.ts +1 -1
  148. package/dist/schema/typed-union.d.ts.map +1 -1
  149. package/dist/schema/typed-union.js +3 -1
  150. package/dist/schema/typed-union.js.map +1 -1
  151. package/dist/schema/union.d.ts +1 -1
  152. package/dist/schema/union.d.ts.map +1 -1
  153. package/dist/schema/union.js +1 -1
  154. package/dist/schema/union.js.map +1 -1
  155. package/dist/schema/unknown.js +1 -1
  156. package/dist/schema/unknown.js.map +1 -1
  157. package/dist/schema/with-default.d.ts +1 -1
  158. package/dist/schema/with-default.d.ts.map +1 -1
  159. package/dist/schema/with-default.js +1 -1
  160. package/dist/schema/with-default.js.map +1 -1
  161. package/package.json +6 -10
  162. package/src/core/$type.test.ts +0 -24
  163. package/src/core/$type.ts +0 -199
  164. package/src/core/record-key.ts +0 -85
  165. package/src/core/result.ts +0 -15
  166. package/src/core/schema.ts +0 -412
  167. package/src/core/standard-schema.test.ts +0 -124
  168. package/src/core/standard-schema.ts +0 -31
  169. package/src/core/string-format.ts +0 -411
  170. package/src/core/types.ts +0 -120
  171. package/src/core/validation-error.ts +0 -134
  172. package/src/core/validation-issue.ts +0 -340
  173. package/src/core/validator.ts +0 -636
  174. package/src/core.ts +0 -9
  175. package/src/external.ts +0 -3
  176. package/src/helpers.test.ts +0 -694
  177. package/src/helpers.ts +0 -222
  178. package/src/index.ts +0 -3
  179. package/src/schema/array.test.ts +0 -251
  180. package/src/schema/array.ts +0 -126
  181. package/src/schema/blob.test.ts +0 -733
  182. package/src/schema/blob.ts +0 -150
  183. package/src/schema/boolean.test.ts +0 -118
  184. package/src/schema/boolean.ts +0 -46
  185. package/src/schema/bytes.test.ts +0 -227
  186. package/src/schema/bytes.ts +0 -81
  187. package/src/schema/cid.test.ts +0 -125
  188. package/src/schema/cid.ts +0 -69
  189. package/src/schema/custom.test.ts +0 -414
  190. package/src/schema/custom.ts +0 -106
  191. package/src/schema/dict.test.ts +0 -181
  192. package/src/schema/dict.ts +0 -122
  193. package/src/schema/discriminated-union.test.ts +0 -676
  194. package/src/schema/discriminated-union.ts +0 -196
  195. package/src/schema/enum.test.ts +0 -398
  196. package/src/schema/enum.ts +0 -77
  197. package/src/schema/integer.test.ts +0 -314
  198. package/src/schema/integer.ts +0 -86
  199. package/src/schema/intersection.test.ts +0 -33
  200. package/src/schema/intersection.ts +0 -113
  201. package/src/schema/lex-map.test.ts +0 -593
  202. package/src/schema/lex-map.ts +0 -63
  203. package/src/schema/lex-value.test.ts +0 -81
  204. package/src/schema/lex-value.ts +0 -86
  205. package/src/schema/literal.test.ts +0 -533
  206. package/src/schema/literal.ts +0 -70
  207. package/src/schema/never.test.ts +0 -175
  208. package/src/schema/never.ts +0 -56
  209. package/src/schema/null.test.ts +0 -80
  210. package/src/schema/null.ts +0 -49
  211. package/src/schema/nullable.test.ts +0 -470
  212. package/src/schema/nullable.ts +0 -74
  213. package/src/schema/object.test.ts +0 -69
  214. package/src/schema/object.ts +0 -136
  215. package/src/schema/optional.test.ts +0 -479
  216. package/src/schema/optional.ts +0 -92
  217. package/src/schema/params.test.ts +0 -1118
  218. package/src/schema/params.ts +0 -371
  219. package/src/schema/payload.test.ts +0 -340
  220. package/src/schema/payload.ts +0 -204
  221. package/src/schema/permission-set.test.ts +0 -613
  222. package/src/schema/permission-set.ts +0 -86
  223. package/src/schema/permission.test.ts +0 -537
  224. package/src/schema/permission.ts +0 -63
  225. package/src/schema/procedure.test.ts +0 -324
  226. package/src/schema/procedure.ts +0 -98
  227. package/src/schema/query.test.ts +0 -348
  228. package/src/schema/query.ts +0 -86
  229. package/src/schema/record.test.ts +0 -812
  230. package/src/schema/record.ts +0 -217
  231. package/src/schema/ref.test.ts +0 -349
  232. package/src/schema/ref.ts +0 -103
  233. package/src/schema/refine.test.ts +0 -579
  234. package/src/schema/refine.ts +0 -153
  235. package/src/schema/regexp.test.ts +0 -577
  236. package/src/schema/regexp.ts +0 -82
  237. package/src/schema/string.test.ts +0 -773
  238. package/src/schema/string.ts +0 -229
  239. package/src/schema/subscription.test.ts +0 -499
  240. package/src/schema/subscription.ts +0 -108
  241. package/src/schema/token.test.ts +0 -152
  242. package/src/schema/token.ts +0 -103
  243. package/src/schema/typed-object.test.ts +0 -745
  244. package/src/schema/typed-object.ts +0 -181
  245. package/src/schema/typed-ref.test.ts +0 -796
  246. package/src/schema/typed-ref.ts +0 -126
  247. package/src/schema/typed-union.test.ts +0 -355
  248. package/src/schema/typed-union.ts +0 -130
  249. package/src/schema/union.test.ts +0 -191
  250. package/src/schema/union.ts +0 -89
  251. package/src/schema/unknown.test.ts +0 -313
  252. package/src/schema/unknown.ts +0 -47
  253. package/src/schema/with-default.ts +0 -81
  254. package/src/schema.ts +0 -43
  255. package/src/util/array-agg.test.ts +0 -42
  256. package/src/util/array-agg.ts +0 -44
  257. package/src/util/assertion-util.ts +0 -1
  258. package/src/util/if-any.ts +0 -3
  259. package/src/util/lazy-property.ts +0 -14
  260. package/src/util/memoize.ts +0 -37
  261. package/tsconfig.build.json +0 -12
  262. package/tsconfig.json +0 -7
  263. package/tsconfig.tests.json +0 -8
@@ -1,470 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { enumSchema } from './enum.js'
3
- import { integer } from './integer.js'
4
- import { nullable } from './nullable.js'
5
- import { object } from './object.js'
6
- import { string } from './string.js'
7
- import { withDefault } from './with-default.js'
8
-
9
- describe('NullableSchema', () => {
10
- describe('with StringSchema', () => {
11
- const schema = nullable(string())
12
-
13
- it('validates null', () => {
14
- const result = schema.safeParse(null)
15
- expect(result.success).toBe(true)
16
- if (result.success) {
17
- expect(result.value).toBe(null)
18
- }
19
- })
20
-
21
- it('validates valid string values', () => {
22
- const result = schema.safeParse('hello')
23
- expect(result.success).toBe(true)
24
- if (result.success) {
25
- expect(result.value).toBe('hello')
26
- }
27
- })
28
-
29
- it('validates empty strings', () => {
30
- const result = schema.safeParse('')
31
- expect(result.success).toBe(true)
32
- })
33
-
34
- it('rejects undefined', () => {
35
- const result = schema.safeParse(undefined)
36
- expect(result.success).toBe(false)
37
- })
38
-
39
- it('rejects numbers', () => {
40
- const result = schema.safeParse(123)
41
- expect(result.success).toBe(false)
42
- })
43
-
44
- it('rejects booleans', () => {
45
- const result = schema.safeParse(true)
46
- expect(result.success).toBe(false)
47
- })
48
-
49
- it('rejects objects', () => {
50
- const result = schema.safeParse({ value: 'test' })
51
- expect(result.success).toBe(false)
52
- })
53
-
54
- it('rejects arrays', () => {
55
- const result = schema.safeParse(['hello'])
56
- expect(result.success).toBe(false)
57
- })
58
- })
59
-
60
- describe('with IntegerSchema', () => {
61
- const schema = nullable(integer())
62
-
63
- it('validates null', () => {
64
- const result = schema.safeParse(null)
65
- expect(result.success).toBe(true)
66
- if (result.success) {
67
- expect(result.value).toBe(null)
68
- }
69
- })
70
-
71
- it('validates valid integer values', () => {
72
- const result = schema.safeParse(42)
73
- expect(result.success).toBe(true)
74
- if (result.success) {
75
- expect(result.value).toBe(42)
76
- }
77
- })
78
-
79
- it('validates zero', () => {
80
- const result = schema.safeParse(0)
81
- expect(result.success).toBe(true)
82
- })
83
-
84
- it('validates negative integers', () => {
85
- const result = schema.safeParse(-42)
86
- expect(result.success).toBe(true)
87
- })
88
-
89
- it('rejects floats', () => {
90
- const result = schema.safeParse(3.14)
91
- expect(result.success).toBe(false)
92
- })
93
-
94
- it('rejects strings', () => {
95
- const result = schema.safeParse('42')
96
- expect(result.success).toBe(false)
97
- })
98
-
99
- it('rejects undefined', () => {
100
- const result = schema.safeParse(undefined)
101
- expect(result.success).toBe(false)
102
- })
103
- })
104
-
105
- describe('with EnumSchema', () => {
106
- const schema = nullable(enumSchema(['red', 'green', 'blue']))
107
-
108
- it('validates null', () => {
109
- const result = schema.safeParse(null)
110
- expect(result.success).toBe(true)
111
- if (result.success) {
112
- expect(result.value).toBe(null)
113
- }
114
- })
115
-
116
- it('validates valid enum values', () => {
117
- expect(schema.safeParse('red').success).toBe(true)
118
- expect(schema.safeParse('green').success).toBe(true)
119
- expect(schema.safeParse('blue').success).toBe(true)
120
- })
121
-
122
- it('rejects invalid enum values', () => {
123
- const result = schema.safeParse('yellow')
124
- expect(result.success).toBe(false)
125
- })
126
-
127
- it('rejects undefined', () => {
128
- const result = schema.safeParse(undefined)
129
- expect(result.success).toBe(false)
130
- })
131
-
132
- it('rejects empty string when not in enum', () => {
133
- const result = schema.safeParse('')
134
- expect(result.success).toBe(false)
135
- })
136
- })
137
-
138
- describe('with constrained StringSchema', () => {
139
- const schema = nullable(string({ minLength: 3, maxLength: 10 }))
140
-
141
- it('validates null', () => {
142
- const result = schema.safeParse(null)
143
- expect(result.success).toBe(true)
144
- })
145
-
146
- it('validates strings within constraints', () => {
147
- const result = schema.safeParse('hello')
148
- expect(result.success).toBe(true)
149
- })
150
-
151
- it('rejects strings below minimum length', () => {
152
- const result = schema.safeParse('hi')
153
- expect(result.success).toBe(false)
154
- })
155
-
156
- it('rejects strings above maximum length', () => {
157
- const result = schema.safeParse('hello world!')
158
- expect(result.success).toBe(false)
159
- })
160
-
161
- it('accepts strings at minimum boundary', () => {
162
- const result = schema.safeParse('abc')
163
- expect(result.success).toBe(true)
164
- })
165
-
166
- it('accepts strings at maximum boundary', () => {
167
- const result = schema.safeParse('1234567890')
168
- expect(result.success).toBe(true)
169
- })
170
- })
171
-
172
- describe('with constrained IntegerSchema', () => {
173
- const schema = nullable(integer({ minimum: 0, maximum: 100 }))
174
-
175
- it('validates null', () => {
176
- const result = schema.safeParse(null)
177
- expect(result.success).toBe(true)
178
- })
179
-
180
- it('validates integers within constraints', () => {
181
- const result = schema.safeParse(50)
182
- expect(result.success).toBe(true)
183
- })
184
-
185
- it('validates minimum value', () => {
186
- const result = schema.safeParse(0)
187
- expect(result.success).toBe(true)
188
- })
189
-
190
- it('validates maximum value', () => {
191
- const result = schema.safeParse(100)
192
- expect(result.success).toBe(true)
193
- })
194
-
195
- it('rejects values below minimum', () => {
196
- const result = schema.safeParse(-1)
197
- expect(result.success).toBe(false)
198
- })
199
-
200
- it('rejects values above maximum', () => {
201
- const result = schema.safeParse(101)
202
- expect(result.success).toBe(false)
203
- })
204
- })
205
-
206
- describe('with StringSchema having default value', () => {
207
- const schema = nullable(withDefault(string(), 'default'))
208
-
209
- it('validates null explicitly', () => {
210
- const result = schema.safeParse(null)
211
- expect(result.success).toBe(true)
212
- if (result.success) {
213
- expect(result.value).toBe(null)
214
- }
215
- })
216
-
217
- it('uses default value when undefined is provided', () => {
218
- const result = schema.safeParse(undefined)
219
- expect(result.success).toBe(true)
220
- if (result.success) {
221
- expect(result.value).toBe('default')
222
- }
223
- })
224
-
225
- it('validates explicit string values', () => {
226
- const result = schema.safeParse('custom')
227
- expect(result.success).toBe(true)
228
- if (result.success) {
229
- expect(result.value).toBe('custom')
230
- }
231
- })
232
- })
233
-
234
- describe('with ObjectSchema', () => {
235
- const schema = nullable(
236
- object({
237
- name: string(),
238
- age: integer(),
239
- }),
240
- )
241
-
242
- it('validates null', () => {
243
- const result = schema.safeParse(null)
244
- expect(result.success).toBe(true)
245
- if (result.success) {
246
- expect(result.value).toBe(null)
247
- }
248
- })
249
-
250
- it('validates valid objects', () => {
251
- const result = schema.safeParse({ name: 'Alice', age: 30 })
252
- expect(result.success).toBe(true)
253
- })
254
-
255
- it('rejects invalid objects with missing properties', () => {
256
- const result = schema.safeParse({ name: 'Alice' })
257
- expect(result.success).toBe(false)
258
- })
259
-
260
- it('rejects invalid objects with wrong types', () => {
261
- const result = schema.safeParse({ name: 'Alice', age: 'thirty' })
262
- expect(result.success).toBe(false)
263
- })
264
-
265
- it('rejects empty objects', () => {
266
- const result = schema.safeParse({})
267
- expect(result.success).toBe(false)
268
- })
269
-
270
- it('rejects primitive values', () => {
271
- const result = schema.safeParse('not an object')
272
- expect(result.success).toBe(false)
273
- })
274
- })
275
-
276
- describe('nested nullable schemas', () => {
277
- const schema = nullable(nullable(string()))
278
-
279
- it('validates null at outer level', () => {
280
- const result = schema.safeParse(null)
281
- expect(result.success).toBe(true)
282
- if (result.success) {
283
- expect(result.value).toBe(null)
284
- }
285
- })
286
-
287
- it('validates valid string values', () => {
288
- const result = schema.safeParse('hello')
289
- expect(result.success).toBe(true)
290
- if (result.success) {
291
- expect(result.value).toBe('hello')
292
- }
293
- })
294
-
295
- it('rejects undefined', () => {
296
- const result = schema.safeParse(undefined)
297
- expect(result.success).toBe(false)
298
- })
299
-
300
- it('rejects invalid types', () => {
301
- const result = schema.safeParse(123)
302
- expect(result.success).toBe(false)
303
- })
304
- })
305
-
306
- describe('with StringSchema format constraints', () => {
307
- const schema = nullable(string({ format: 'uri' }))
308
-
309
- it('validates null', () => {
310
- const result = schema.safeParse(null)
311
- expect(result.success).toBe(true)
312
- })
313
-
314
- it('validates valid URIs', () => {
315
- const result = schema.safeParse('https://example.com')
316
- expect(result.success).toBe(true)
317
- })
318
-
319
- it('rejects invalid URIs', () => {
320
- const result = schema.safeParse('not a uri')
321
- expect(result.success).toBe(false)
322
- })
323
-
324
- it('rejects invalid format even with valid string', () => {
325
- const result = schema.safeParse('just a string')
326
- expect(result.success).toBe(false)
327
- })
328
- })
329
-
330
- describe('edge cases', () => {
331
- const stringSchema = nullable(string())
332
-
333
- it('handles null correctly without coercion', () => {
334
- const result = stringSchema.safeParse(null)
335
- expect(result.success).toBe(true)
336
- if (result.success) {
337
- expect(result.value).toBe(null)
338
- expect(result.value).not.toBe(undefined)
339
- expect(result.value).not.toBe('')
340
- expect(result.value).not.toBe(0)
341
- expect(result.value).not.toBe(false)
342
- }
343
- })
344
-
345
- it('distinguishes null from falsy values', () => {
346
- expect(stringSchema.safeParse(null).success).toBe(true)
347
- expect(stringSchema.safeParse(undefined).success).toBe(false)
348
- expect(stringSchema.safeParse('').success).toBe(true)
349
- expect(stringSchema.safeParse(0).success).toBe(false)
350
- expect(stringSchema.safeParse(false).success).toBe(false)
351
- })
352
-
353
- it('handles NaN correctly', () => {
354
- const result = stringSchema.safeParse(NaN)
355
- expect(result.success).toBe(false)
356
- })
357
-
358
- it('handles Symbol correctly', () => {
359
- const result = stringSchema.safeParse(Symbol('test'))
360
- expect(result.success).toBe(false)
361
- })
362
- })
363
-
364
- describe('type preservation', () => {
365
- it('preserves string type for valid strings', () => {
366
- const schema = nullable(string())
367
- const result = schema.safeParse('test')
368
- expect(result.success).toBe(true)
369
- if (result.success) {
370
- expect(typeof result.value).toBe('string')
371
- expect(result.value).toBe('test')
372
- }
373
- })
374
-
375
- it('preserves number type for valid integers', () => {
376
- const schema = nullable(integer())
377
- const result = schema.safeParse(42)
378
- expect(result.success).toBe(true)
379
- if (result.success) {
380
- expect(typeof result.value).toBe('number')
381
- expect(result.value).toBe(42)
382
- }
383
- })
384
-
385
- it('preserves object type for valid objects', () => {
386
- const schema = nullable(object({ key: string() }))
387
- const input = { key: 'value' }
388
- const result = schema.safeParse(input)
389
- expect(result.success).toBe(true)
390
- if (result.success) {
391
- expect(typeof result.value).toBe('object')
392
- expect(result.value).toEqual({ key: 'value' })
393
- }
394
- })
395
-
396
- it('preserves null type exactly', () => {
397
- const schema = nullable(string())
398
- const result = schema.safeParse(null)
399
- expect(result.success).toBe(true)
400
- if (result.success) {
401
- expect(result.value).toBe(null)
402
- expect(result.value === null).toBe(true)
403
- expect(typeof result.value).toBe('object')
404
- }
405
- })
406
- })
407
-
408
- describe('with complex wrapped schemas', () => {
409
- it('validates nullable enum with default', () => {
410
- const schema = nullable(
411
- withDefault(enumSchema(['option1', 'option2']), 'option1'),
412
- )
413
-
414
- expect(schema.safeParse(null).success).toBe(true)
415
- expect(schema.safeParse('option1').success).toBe(true)
416
- expect(schema.safeParse('option2').success).toBe(true)
417
- expect(schema.safeParse(undefined).success).toBe(true)
418
- expect(schema.safeParse('invalid').success).toBe(false)
419
- })
420
-
421
- it('handles nullable schema with grapheme constraints', () => {
422
- const schema = nullable(string({ minGraphemes: 2, maxGraphemes: 5 }))
423
-
424
- expect(schema.safeParse(null).success).toBe(true)
425
- expect(schema.safeParse('ab').success).toBe(true)
426
- expect(schema.safeParse('hello').success).toBe(true)
427
- expect(schema.safeParse('a').success).toBe(false)
428
- expect(schema.safeParse('hello!').success).toBe(false)
429
- })
430
-
431
- it('handles nullable integer with negative range', () => {
432
- const schema = nullable(integer({ minimum: -100, maximum: -10 }))
433
-
434
- expect(schema.safeParse(null).success).toBe(true)
435
- expect(schema.safeParse(-50).success).toBe(true)
436
- expect(schema.safeParse(-100).success).toBe(true)
437
- expect(schema.safeParse(-10).success).toBe(true)
438
- expect(schema.safeParse(0).success).toBe(false)
439
- expect(schema.safeParse(-5).success).toBe(false)
440
- expect(schema.safeParse(-150).success).toBe(false)
441
- })
442
- })
443
-
444
- describe('validation error behavior', () => {
445
- it('returns failure for wrapped schema validation errors', () => {
446
- const schema = nullable(integer({ minimum: 10 }))
447
- const result = schema.safeParse(5)
448
- expect(result.success).toBe(false)
449
- })
450
-
451
- it('returns failure for type mismatches', () => {
452
- const schema = nullable(string())
453
- const result = schema.safeParse(123)
454
- expect(result.success).toBe(false)
455
- })
456
-
457
- it('returns success for null regardless of wrapped constraints', () => {
458
- const schema = nullable(string({ minLength: 100, format: 'uri' }))
459
- const result = schema.safeParse(null)
460
- expect(result.success).toBe(true)
461
- })
462
-
463
- it('wrapped schema validation applies when value is not null', () => {
464
- const schema = nullable(string({ minLength: 5 }))
465
- expect(schema.safeParse(null).success).toBe(true)
466
- expect(schema.safeParse('hello').success).toBe(true)
467
- expect(schema.safeParse('hi').success).toBe(false)
468
- })
469
- })
470
- })
@@ -1,74 +0,0 @@
1
- import {
2
- InferInput,
3
- InferOutput,
4
- Schema,
5
- ValidationContext,
6
- Validator,
7
- } from '../core.js'
8
- import { memoizedTransformer } from '../util/memoize.js'
9
-
10
- /**
11
- * Schema wrapper that allows null values in addition to the wrapped schema.
12
- *
13
- * When the input is `null`, validation succeeds immediately. Otherwise,
14
- * the input is validated against the wrapped schema.
15
- *
16
- * @template TValidator - The wrapped validator type
17
- *
18
- * @example
19
- * ```ts
20
- * const schema = new NullableSchema(l.string())
21
- * schema.validate(null) // success
22
- * schema.validate('hello') // success
23
- * ```
24
- */
25
- export class NullableSchema<const TValidator extends Validator> extends Schema<
26
- InferInput<TValidator> | null,
27
- InferOutput<TValidator> | null
28
- > {
29
- readonly type = 'nullable' as const
30
-
31
- constructor(readonly validator: TValidator) {
32
- super()
33
- }
34
-
35
- validateInContext(input: unknown, ctx: ValidationContext) {
36
- if (input === null) {
37
- return ctx.success(null)
38
- }
39
-
40
- return ctx.validate(input, this.validator)
41
- }
42
- }
43
-
44
- /**
45
- * Creates a nullable schema that accepts null in addition to the wrapped type.
46
- *
47
- * Wraps another schema to allow null values. Different from `optional()` which
48
- * allows undefined.
49
- *
50
- * @param validator - The validator to make nullable
51
- * @returns A new {@link NullableSchema} instance
52
- *
53
- * @example
54
- * ```ts
55
- * // Nullable string
56
- * const nullableString = l.nullable(l.string())
57
- * nullableString.parse(null) // null
58
- * nullableString.parse('hello') // 'hello'
59
- *
60
- * // In an object
61
- * const userSchema = l.object({
62
- * name: l.string(),
63
- * deletedAt: l.nullable(l.string({ format: 'datetime' })),
64
- * })
65
- *
66
- * // Combine with optional for null or undefined
67
- * const maybeString = l.optional(l.nullable(l.string()))
68
- * ```
69
- */
70
- export const nullable = /*#__PURE__*/ memoizedTransformer(function <
71
- const TValidator extends Validator,
72
- >(validator: TValidator) {
73
- return new NullableSchema<TValidator>(validator)
74
- })
@@ -1,69 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { enumSchema } from './enum.js'
3
- import { integer } from './integer.js'
4
- import { nullable } from './nullable.js'
5
- import { object } from './object.js'
6
- import { optional } from './optional.js'
7
- import { string } from './string.js'
8
-
9
- describe('ObjectSchema', () => {
10
- const schema = object({
11
- name: string(),
12
- age: optional(integer()),
13
- gender: optional(nullable(enumSchema(['male', 'female']))),
14
- })
15
-
16
- it('validates plain objects', () => {
17
- const result = schema.safeParse({
18
- name: 'Alice',
19
- age: 30,
20
- gender: 'female',
21
- })
22
- expect(result.success).toBe(true)
23
- })
24
-
25
- it('rejects non-objects', () => {
26
- const result = schema.safeParse('not an object')
27
- expect(result.success).toBe(false)
28
- })
29
-
30
- it('rejects missing properties', () => {
31
- const result = schema.safeParse({
32
- age: 30,
33
- gender: 'female',
34
- })
35
- expect(result.success).toBe(false)
36
- })
37
-
38
- it('validates optional properties', () => {
39
- const result = schema.safeParse({
40
- name: 'Alice',
41
- })
42
- expect(result.success).toBe(true)
43
- })
44
-
45
- it('validates nullable properties', () => {
46
- const result = schema.safeParse({
47
- name: 'Alice',
48
- gender: null,
49
- })
50
- expect(result.success).toBe(true)
51
- })
52
-
53
- it('rejects invalid property types', () => {
54
- const result = schema.safeParse({
55
- name: 'Alice',
56
- age: 'thirty',
57
- })
58
- expect(result.success).toBe(false)
59
- })
60
-
61
- it('ignores extra properties', () => {
62
- const result = schema.safeParse({
63
- name: 'Alice',
64
- age: 30,
65
- extra: 'value',
66
- })
67
- expect(result.success).toBe(true)
68
- })
69
- })