@atproto/lex-schema 0.0.2 → 0.0.4

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 (289) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/dist/core/$type.d.ts +6 -3
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js +1 -0
  5. package/dist/core/$type.js.map +1 -1
  6. package/dist/core/record-key.d.ts +3 -3
  7. package/dist/core/record-key.d.ts.map +1 -1
  8. package/dist/core/record-key.js +12 -6
  9. package/dist/core/record-key.js.map +1 -1
  10. package/dist/core/result.d.ts.map +1 -1
  11. package/dist/core/result.js +6 -0
  12. package/dist/core/result.js.map +1 -1
  13. package/dist/core/string-format.d.ts +30 -27
  14. package/dist/core/string-format.d.ts.map +1 -1
  15. package/dist/core/string-format.js +56 -42
  16. package/dist/core/string-format.js.map +1 -1
  17. package/dist/core/types.d.ts +9 -1
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/core/types.js.map +1 -1
  20. package/dist/external.d.ts +31 -28
  21. package/dist/external.d.ts.map +1 -1
  22. package/dist/external.js +33 -17
  23. package/dist/external.js.map +1 -1
  24. package/dist/schema/_parameters.d.ts +2 -2
  25. package/dist/schema/_parameters.d.ts.map +1 -1
  26. package/dist/schema/array.d.ts +5 -6
  27. package/dist/schema/array.d.ts.map +1 -1
  28. package/dist/schema/array.js +5 -6
  29. package/dist/schema/array.js.map +1 -1
  30. package/dist/schema/blob.d.ts +2 -3
  31. package/dist/schema/blob.d.ts.map +1 -1
  32. package/dist/schema/blob.js +1 -2
  33. package/dist/schema/blob.js.map +1 -1
  34. package/dist/schema/boolean.d.ts +4 -5
  35. package/dist/schema/boolean.d.ts.map +1 -1
  36. package/dist/schema/boolean.js +2 -3
  37. package/dist/schema/boolean.js.map +1 -1
  38. package/dist/schema/bytes.d.ts +3 -4
  39. package/dist/schema/bytes.d.ts.map +1 -1
  40. package/dist/schema/bytes.js +2 -3
  41. package/dist/schema/bytes.js.map +1 -1
  42. package/dist/schema/cid.d.ts +13 -6
  43. package/dist/schema/cid.d.ts.map +1 -1
  44. package/dist/schema/cid.js +2 -4
  45. package/dist/schema/cid.js.map +1 -1
  46. package/dist/schema/custom.d.ts +3 -4
  47. package/dist/schema/custom.d.ts.map +1 -1
  48. package/dist/schema/custom.js +4 -3
  49. package/dist/schema/custom.js.map +1 -1
  50. package/dist/schema/dict.d.ts +3 -3
  51. package/dist/schema/dict.d.ts.map +1 -1
  52. package/dist/schema/dict.js +1 -1
  53. package/dist/schema/dict.js.map +1 -1
  54. package/dist/schema/discriminated-union.d.ts +15 -24
  55. package/dist/schema/discriminated-union.d.ts.map +1 -1
  56. package/dist/schema/discriminated-union.js +40 -64
  57. package/dist/schema/discriminated-union.js.map +1 -1
  58. package/dist/schema/enum.d.ts +8 -4
  59. package/dist/schema/enum.d.ts.map +1 -1
  60. package/dist/schema/enum.js +5 -3
  61. package/dist/schema/enum.js.map +1 -1
  62. package/dist/schema/integer.d.ts +3 -4
  63. package/dist/schema/integer.d.ts.map +1 -1
  64. package/dist/schema/integer.js +3 -4
  65. package/dist/schema/integer.js.map +1 -1
  66. package/dist/schema/intersection.d.ts +22 -14
  67. package/dist/schema/intersection.d.ts.map +1 -1
  68. package/dist/schema/intersection.js +12 -22
  69. package/dist/schema/intersection.js.map +1 -1
  70. package/dist/schema/literal.d.ts +7 -3
  71. package/dist/schema/literal.d.ts.map +1 -1
  72. package/dist/schema/literal.js +5 -3
  73. package/dist/schema/literal.js.map +1 -1
  74. package/dist/schema/never.d.ts +2 -2
  75. package/dist/schema/never.d.ts.map +1 -1
  76. package/dist/schema/never.js +1 -1
  77. package/dist/schema/never.js.map +1 -1
  78. package/dist/schema/null.d.ts +2 -3
  79. package/dist/schema/null.d.ts.map +1 -1
  80. package/dist/schema/null.js +1 -2
  81. package/dist/schema/null.js.map +1 -1
  82. package/dist/schema/nullable.d.ts +7 -0
  83. package/dist/schema/nullable.d.ts.map +1 -0
  84. package/dist/schema/nullable.js +19 -0
  85. package/dist/schema/nullable.js.map +1 -0
  86. package/dist/schema/object.d.ts +10 -44
  87. package/dist/schema/object.d.ts.map +1 -1
  88. package/dist/schema/object.js +10 -46
  89. package/dist/schema/object.js.map +1 -1
  90. package/dist/schema/optional.d.ts +7 -0
  91. package/dist/schema/optional.d.ts.map +1 -0
  92. package/dist/schema/optional.js +25 -0
  93. package/dist/schema/optional.js.map +1 -0
  94. package/dist/schema/params.d.ts +14 -19
  95. package/dist/schema/params.d.ts.map +1 -1
  96. package/dist/schema/params.js +10 -24
  97. package/dist/schema/params.js.map +1 -1
  98. package/dist/schema/payload.d.ts +4 -4
  99. package/dist/schema/payload.d.ts.map +1 -1
  100. package/dist/schema/payload.js.map +1 -1
  101. package/dist/schema/permission-set.d.ts +6 -6
  102. package/dist/schema/permission-set.d.ts.map +1 -1
  103. package/dist/schema/permission-set.js +1 -2
  104. package/dist/schema/permission-set.js.map +1 -1
  105. package/dist/schema/permission.d.ts +0 -1
  106. package/dist/schema/permission.d.ts.map +1 -1
  107. package/dist/schema/permission.js +0 -1
  108. package/dist/schema/permission.js.map +1 -1
  109. package/dist/schema/procedure.d.ts +8 -9
  110. package/dist/schema/procedure.d.ts.map +1 -1
  111. package/dist/schema/procedure.js +0 -1
  112. package/dist/schema/procedure.js.map +1 -1
  113. package/dist/schema/query.d.ts +7 -8
  114. package/dist/schema/query.d.ts.map +1 -1
  115. package/dist/schema/query.js +0 -1
  116. package/dist/schema/query.js.map +1 -1
  117. package/dist/schema/record.d.ts +34 -28
  118. package/dist/schema/record.d.ts.map +1 -1
  119. package/dist/schema/record.js +1 -2
  120. package/dist/schema/record.js.map +1 -1
  121. package/dist/schema/ref.d.ts +2 -3
  122. package/dist/schema/ref.d.ts.map +1 -1
  123. package/dist/schema/ref.js +1 -2
  124. package/dist/schema/ref.js.map +1 -1
  125. package/dist/schema/refine.d.ts +18 -0
  126. package/dist/schema/refine.d.ts.map +1 -0
  127. package/dist/schema/refine.js +33 -0
  128. package/dist/schema/refine.js.map +1 -0
  129. package/dist/schema/regexp.d.ts +7 -0
  130. package/dist/schema/regexp.d.ts.map +1 -0
  131. package/dist/schema/regexp.js +22 -0
  132. package/dist/schema/regexp.js.map +1 -0
  133. package/dist/schema/string.d.ts +4 -8
  134. package/dist/schema/string.d.ts.map +1 -1
  135. package/dist/schema/string.js +6 -3
  136. package/dist/schema/string.js.map +1 -1
  137. package/dist/schema/subscription.d.ts +7 -6
  138. package/dist/schema/subscription.d.ts.map +1 -1
  139. package/dist/schema/subscription.js.map +1 -1
  140. package/dist/schema/token.d.ts +2 -3
  141. package/dist/schema/token.d.ts.map +1 -1
  142. package/dist/schema/token.js +1 -2
  143. package/dist/schema/token.js.map +1 -1
  144. package/dist/schema/typed-object.d.ts +29 -27
  145. package/dist/schema/typed-object.d.ts.map +1 -1
  146. package/dist/schema/typed-object.js +1 -2
  147. package/dist/schema/typed-object.js.map +1 -1
  148. package/dist/schema/typed-ref.d.ts +2 -2
  149. package/dist/schema/typed-ref.d.ts.map +1 -1
  150. package/dist/schema/typed-ref.js +1 -1
  151. package/dist/schema/typed-ref.js.map +1 -1
  152. package/dist/schema/typed-union.d.ts +3 -4
  153. package/dist/schema/typed-union.d.ts.map +1 -1
  154. package/dist/schema/typed-union.js +3 -10
  155. package/dist/schema/typed-union.js.map +1 -1
  156. package/dist/schema/union.d.ts +2 -2
  157. package/dist/schema/union.d.ts.map +1 -1
  158. package/dist/schema/union.js +1 -1
  159. package/dist/schema/union.js.map +1 -1
  160. package/dist/schema/unknown-object.d.ts +2 -3
  161. package/dist/schema/unknown-object.d.ts.map +1 -1
  162. package/dist/schema/unknown-object.js +1 -2
  163. package/dist/schema/unknown-object.js.map +1 -1
  164. package/dist/schema/unknown.d.ts +2 -2
  165. package/dist/schema/unknown.d.ts.map +1 -1
  166. package/dist/schema/unknown.js +1 -1
  167. package/dist/schema/unknown.js.map +1 -1
  168. package/dist/schema.d.ts +4 -0
  169. package/dist/schema.d.ts.map +1 -1
  170. package/dist/schema.js +6 -1
  171. package/dist/schema.js.map +1 -1
  172. package/dist/util/array-agg.d.ts.map +1 -1
  173. package/dist/util/array-agg.js +1 -0
  174. package/dist/util/array-agg.js.map +1 -1
  175. package/dist/util/lazy-property.d.ts +2 -0
  176. package/dist/util/lazy-property.d.ts.map +1 -0
  177. package/dist/util/lazy-property.js +14 -0
  178. package/dist/util/lazy-property.js.map +1 -0
  179. package/dist/validation/schema.d.ts +24 -0
  180. package/dist/validation/schema.d.ts.map +1 -0
  181. package/dist/validation/schema.js +57 -0
  182. package/dist/validation/schema.js.map +1 -0
  183. package/dist/validation/validation-error.d.ts +3 -3
  184. package/dist/validation/validation-error.d.ts.map +1 -1
  185. package/dist/validation/validation-error.js +32 -4
  186. package/dist/validation/validation-error.js.map +1 -1
  187. package/dist/validation/validation-issue.d.ts +32 -24
  188. package/dist/validation/validation-issue.d.ts.map +1 -1
  189. package/dist/validation/validation-issue.js +136 -92
  190. package/dist/validation/validation-issue.js.map +1 -1
  191. package/dist/validation/validator.d.ts +20 -50
  192. package/dist/validation/validator.d.ts.map +1 -1
  193. package/dist/validation/validator.js +40 -134
  194. package/dist/validation/validator.js.map +1 -1
  195. package/dist/validation.d.ts +1 -0
  196. package/dist/validation.d.ts.map +1 -1
  197. package/dist/validation.js +1 -0
  198. package/dist/validation.js.map +1 -1
  199. package/package.json +8 -4
  200. package/src/core/$type.ts +7 -4
  201. package/src/core/record-key.ts +12 -5
  202. package/src/core/result.ts +6 -0
  203. package/src/core/string-format.ts +97 -61
  204. package/src/core/types.ts +12 -6
  205. package/src/external.ts +92 -70
  206. package/src/schema/_parameters.test.ts +416 -0
  207. package/src/schema/array.test.ts +237 -0
  208. package/src/schema/array.ts +17 -11
  209. package/src/schema/blob.test.ts +506 -0
  210. package/src/schema/blob.ts +3 -5
  211. package/src/schema/boolean.test.ts +116 -0
  212. package/src/schema/boolean.ts +5 -7
  213. package/src/schema/bytes.test.ts +226 -0
  214. package/src/schema/bytes.ts +4 -6
  215. package/src/schema/cid.test.ts +155 -0
  216. package/src/schema/cid.ts +14 -8
  217. package/src/schema/custom.test.ts +413 -0
  218. package/src/schema/custom.ts +10 -8
  219. package/src/schema/dict.test.ts +198 -0
  220. package/src/schema/dict.ts +6 -8
  221. package/src/schema/discriminated-union.test.ts +675 -0
  222. package/src/schema/discriminated-union.ts +68 -95
  223. package/src/schema/enum.test.ts +396 -0
  224. package/src/schema/enum.ts +12 -5
  225. package/src/schema/integer.test.ts +312 -0
  226. package/src/schema/integer.ts +5 -7
  227. package/src/schema/intersection.test.ts +32 -0
  228. package/src/schema/intersection.ts +37 -40
  229. package/src/schema/literal.test.ts +531 -0
  230. package/src/schema/literal.ts +12 -5
  231. package/src/schema/never.test.ts +174 -0
  232. package/src/schema/never.ts +3 -10
  233. package/src/schema/null.test.ts +79 -0
  234. package/src/schema/null.ts +3 -5
  235. package/src/schema/nullable.test.ts +480 -0
  236. package/src/schema/nullable.ts +23 -0
  237. package/src/schema/object.test.ts +47 -115
  238. package/src/schema/object.ts +19 -123
  239. package/src/schema/optional.test.ts +485 -0
  240. package/src/schema/optional.ts +31 -0
  241. package/src/schema/params.test.ts +582 -0
  242. package/src/schema/params.ts +37 -55
  243. package/src/schema/payload.test.ts +345 -0
  244. package/src/schema/payload.ts +5 -5
  245. package/src/schema/permission-set.test.ts +679 -0
  246. package/src/schema/permission-set.ts +6 -8
  247. package/src/schema/permission.test.ts +536 -0
  248. package/src/schema/permission.ts +0 -2
  249. package/src/schema/procedure.test.ts +443 -0
  250. package/src/schema/procedure.ts +11 -13
  251. package/src/schema/query.test.ts +408 -0
  252. package/src/schema/query.ts +9 -11
  253. package/src/schema/record.test.ts +694 -0
  254. package/src/schema/record.ts +38 -36
  255. package/src/schema/ref.test.ts +365 -0
  256. package/src/schema/ref.ts +8 -5
  257. package/src/schema/refine.test.ts +578 -0
  258. package/src/schema/refine.ts +85 -0
  259. package/src/schema/regexp.test.ts +580 -0
  260. package/src/schema/regexp.ts +22 -0
  261. package/src/schema/string.test.ts +612 -0
  262. package/src/schema/string.ts +11 -17
  263. package/src/schema/subscription.test.ts +689 -0
  264. package/src/schema/subscription.ts +13 -8
  265. package/src/schema/token.test.ts +428 -0
  266. package/src/schema/token.ts +3 -5
  267. package/src/schema/typed-object.test.ts +612 -0
  268. package/src/schema/typed-object.ts +23 -20
  269. package/src/schema/typed-ref.test.ts +823 -0
  270. package/src/schema/typed-ref.ts +10 -5
  271. package/src/schema/typed-union.test.ts +378 -0
  272. package/src/schema/typed-union.ts +6 -15
  273. package/src/schema/union.test.ts +200 -0
  274. package/src/schema/union.ts +5 -4
  275. package/src/schema/unknown-object.test.ts +592 -0
  276. package/src/schema/unknown-object.ts +3 -5
  277. package/src/schema/unknown.test.ts +312 -0
  278. package/src/schema/unknown.ts +3 -3
  279. package/src/schema.ts +7 -1
  280. package/src/util/array-agg.ts +1 -0
  281. package/src/util/lazy-property.ts +14 -0
  282. package/src/validation/schema.ts +92 -0
  283. package/src/validation/validation-error.ts +60 -9
  284. package/src/validation/validation-issue.ts +141 -144
  285. package/src/validation/validator.ts +67 -206
  286. package/src/validation.ts +1 -0
  287. package/tsconfig.build.json +12 -0
  288. package/tsconfig.json +7 -0
  289. package/tsconfig.tests.json +9 -0
@@ -0,0 +1,312 @@
1
+ import { IntegerSchema } from './integer.js'
2
+
3
+ describe('IntegerSchema', () => {
4
+ describe('basic validation', () => {
5
+ const schema = new IntegerSchema({})
6
+
7
+ it('validates integers', () => {
8
+ const result = schema.safeParse(42)
9
+ expect(result.success).toBe(true)
10
+ })
11
+
12
+ it('validates zero', () => {
13
+ const result = schema.safeParse(0)
14
+ expect(result.success).toBe(true)
15
+ })
16
+
17
+ it('validates negative integers', () => {
18
+ const result = schema.safeParse(-42)
19
+ expect(result.success).toBe(true)
20
+ })
21
+
22
+ it('validates large integers', () => {
23
+ const result = schema.safeParse(Number.MAX_SAFE_INTEGER)
24
+ expect(result.success).toBe(true)
25
+ })
26
+
27
+ it('validates small integers', () => {
28
+ const result = schema.safeParse(Number.MIN_SAFE_INTEGER)
29
+ expect(result.success).toBe(true)
30
+ })
31
+
32
+ it('rejects floats', () => {
33
+ const result = schema.safeParse(3.14)
34
+ expect(result.success).toBe(false)
35
+ })
36
+
37
+ it('rejects strings', () => {
38
+ const result = schema.safeParse('42')
39
+ expect(result.success).toBe(false)
40
+ })
41
+
42
+ it('rejects booleans', () => {
43
+ const result = schema.safeParse(true)
44
+ expect(result.success).toBe(false)
45
+ })
46
+
47
+ it('rejects null', () => {
48
+ const result = schema.safeParse(null)
49
+ expect(result.success).toBe(false)
50
+ })
51
+
52
+ it('rejects undefined', () => {
53
+ const result = schema.safeParse(undefined)
54
+ expect(result.success).toBe(false)
55
+ })
56
+
57
+ it('rejects objects', () => {
58
+ const result = schema.safeParse({})
59
+ expect(result.success).toBe(false)
60
+ })
61
+
62
+ it('rejects arrays', () => {
63
+ const result = schema.safeParse([42])
64
+ expect(result.success).toBe(false)
65
+ })
66
+
67
+ it('rejects NaN', () => {
68
+ const result = schema.safeParse(NaN)
69
+ expect(result.success).toBe(false)
70
+ })
71
+
72
+ it('rejects Infinity', () => {
73
+ const result = schema.safeParse(Infinity)
74
+ expect(result.success).toBe(false)
75
+ })
76
+
77
+ it('rejects -Infinity', () => {
78
+ const result = schema.safeParse(-Infinity)
79
+ expect(result.success).toBe(false)
80
+ })
81
+ })
82
+
83
+ describe('default value', () => {
84
+ const schema = new IntegerSchema({ default: 10 })
85
+
86
+ it('uses default when undefined is provided', () => {
87
+ const result = schema.safeParse(undefined)
88
+ expect(result.success).toBe(true)
89
+ if (result.success) {
90
+ expect(result.value).toBe(10)
91
+ }
92
+ })
93
+
94
+ it('does not use default when explicit value is provided', () => {
95
+ const result = schema.safeParse(20)
96
+ expect(result.success).toBe(true)
97
+ if (result.success) {
98
+ expect(result.value).toBe(20)
99
+ }
100
+ })
101
+
102
+ it('does not use default when zero is provided', () => {
103
+ const result = schema.safeParse(0)
104
+ expect(result.success).toBe(true)
105
+ if (result.success) {
106
+ expect(result.value).toBe(0)
107
+ }
108
+ })
109
+ })
110
+
111
+ describe('minimum constraint', () => {
112
+ const schema = new IntegerSchema({ minimum: 10 })
113
+
114
+ it('accepts values equal to minimum', () => {
115
+ const result = schema.safeParse(10)
116
+ expect(result.success).toBe(true)
117
+ })
118
+
119
+ it('accepts values greater than minimum', () => {
120
+ const result = schema.safeParse(20)
121
+ expect(result.success).toBe(true)
122
+ })
123
+
124
+ it('rejects values less than minimum', () => {
125
+ const result = schema.safeParse(5)
126
+ expect(result.success).toBe(false)
127
+ })
128
+
129
+ it('rejects zero when minimum is positive', () => {
130
+ const result = schema.safeParse(0)
131
+ expect(result.success).toBe(false)
132
+ })
133
+
134
+ it('rejects negative values when minimum is positive', () => {
135
+ const result = schema.safeParse(-10)
136
+ expect(result.success).toBe(false)
137
+ })
138
+ })
139
+
140
+ describe('maximum constraint', () => {
141
+ const schema = new IntegerSchema({ maximum: 100 })
142
+
143
+ it('accepts values equal to maximum', () => {
144
+ const result = schema.safeParse(100)
145
+ expect(result.success).toBe(true)
146
+ })
147
+
148
+ it('accepts values less than maximum', () => {
149
+ const result = schema.safeParse(50)
150
+ expect(result.success).toBe(true)
151
+ })
152
+
153
+ it('rejects values greater than maximum', () => {
154
+ const result = schema.safeParse(150)
155
+ expect(result.success).toBe(false)
156
+ })
157
+
158
+ it('accepts zero when maximum is positive', () => {
159
+ const result = schema.safeParse(0)
160
+ expect(result.success).toBe(true)
161
+ })
162
+
163
+ it('accepts negative values when maximum is positive', () => {
164
+ const result = schema.safeParse(-10)
165
+ expect(result.success).toBe(true)
166
+ })
167
+ })
168
+
169
+ describe('minimum and maximum constraints', () => {
170
+ const schema = new IntegerSchema({ minimum: 10, maximum: 100 })
171
+
172
+ it('accepts values within range', () => {
173
+ const result = schema.safeParse(50)
174
+ expect(result.success).toBe(true)
175
+ })
176
+
177
+ it('accepts minimum value', () => {
178
+ const result = schema.safeParse(10)
179
+ expect(result.success).toBe(true)
180
+ })
181
+
182
+ it('accepts maximum value', () => {
183
+ const result = schema.safeParse(100)
184
+ expect(result.success).toBe(true)
185
+ })
186
+
187
+ it('rejects values below minimum', () => {
188
+ const result = schema.safeParse(5)
189
+ expect(result.success).toBe(false)
190
+ })
191
+
192
+ it('rejects values above maximum', () => {
193
+ const result = schema.safeParse(150)
194
+ expect(result.success).toBe(false)
195
+ })
196
+ })
197
+
198
+ describe('negative range constraints', () => {
199
+ const schema = new IntegerSchema({ minimum: -100, maximum: -10 })
200
+
201
+ it('accepts negative values within range', () => {
202
+ const result = schema.safeParse(-50)
203
+ expect(result.success).toBe(true)
204
+ })
205
+
206
+ it('accepts minimum negative value', () => {
207
+ const result = schema.safeParse(-100)
208
+ expect(result.success).toBe(true)
209
+ })
210
+
211
+ it('accepts maximum negative value', () => {
212
+ const result = schema.safeParse(-10)
213
+ expect(result.success).toBe(true)
214
+ })
215
+
216
+ it('rejects values below minimum', () => {
217
+ const result = schema.safeParse(-150)
218
+ expect(result.success).toBe(false)
219
+ })
220
+
221
+ it('rejects values above maximum', () => {
222
+ const result = schema.safeParse(-5)
223
+ expect(result.success).toBe(false)
224
+ })
225
+
226
+ it('rejects zero', () => {
227
+ const result = schema.safeParse(0)
228
+ expect(result.success).toBe(false)
229
+ })
230
+
231
+ it('rejects positive values', () => {
232
+ const result = schema.safeParse(10)
233
+ expect(result.success).toBe(false)
234
+ })
235
+ })
236
+
237
+ describe('zero constraints', () => {
238
+ const schema = new IntegerSchema({ minimum: 0, maximum: 0 })
239
+
240
+ it('accepts zero', () => {
241
+ const result = schema.safeParse(0)
242
+ expect(result.success).toBe(true)
243
+ })
244
+
245
+ it('rejects positive values', () => {
246
+ const result = schema.safeParse(1)
247
+ expect(result.success).toBe(false)
248
+ })
249
+
250
+ it('rejects negative values', () => {
251
+ const result = schema.safeParse(-1)
252
+ expect(result.success).toBe(false)
253
+ })
254
+ })
255
+
256
+ describe('combined with default value', () => {
257
+ const schema = new IntegerSchema({ default: 50, minimum: 10, maximum: 100 })
258
+
259
+ it('uses default when undefined is provided', () => {
260
+ const result = schema.safeParse(undefined)
261
+ expect(result.success).toBe(true)
262
+ if (result.success) {
263
+ expect(result.value).toBe(50)
264
+ }
265
+ })
266
+
267
+ it('validates explicit values with constraints', () => {
268
+ const result = schema.safeParse(75)
269
+ expect(result.success).toBe(true)
270
+ })
271
+
272
+ it('rejects explicit values outside constraints', () => {
273
+ const result = schema.safeParse(5)
274
+ expect(result.success).toBe(false)
275
+ })
276
+ })
277
+
278
+ describe('edge cases', () => {
279
+ it('handles minimum of 0', () => {
280
+ const schema = new IntegerSchema({ minimum: 0 })
281
+ expect(schema.safeParse(0).success).toBe(true)
282
+ expect(schema.safeParse(-1).success).toBe(false)
283
+ expect(schema.safeParse(1).success).toBe(true)
284
+ })
285
+
286
+ it('handles maximum of 0', () => {
287
+ const schema = new IntegerSchema({ maximum: 0 })
288
+ expect(schema.safeParse(0).success).toBe(true)
289
+ expect(schema.safeParse(1).success).toBe(false)
290
+ expect(schema.safeParse(-1).success).toBe(true)
291
+ })
292
+
293
+ it('handles very large ranges', () => {
294
+ const schema = new IntegerSchema({
295
+ minimum: Number.MIN_SAFE_INTEGER,
296
+ maximum: Number.MAX_SAFE_INTEGER,
297
+ })
298
+ expect(schema.safeParse(Number.MIN_SAFE_INTEGER).success).toBe(true)
299
+ expect(schema.safeParse(Number.MAX_SAFE_INTEGER).success).toBe(true)
300
+ expect(schema.safeParse(0).success).toBe(true)
301
+ })
302
+
303
+ it('allows unconstrained schema', () => {
304
+ const schema = new IntegerSchema({})
305
+ expect(schema.safeParse(Number.MIN_SAFE_INTEGER).success).toBe(true)
306
+ expect(schema.safeParse(Number.MAX_SAFE_INTEGER).success).toBe(true)
307
+ expect(schema.safeParse(0).success).toBe(true)
308
+ expect(schema.safeParse(-999999).success).toBe(true)
309
+ expect(schema.safeParse(999999).success).toBe(true)
310
+ })
311
+ })
312
+ })
@@ -1,4 +1,4 @@
1
- import { ValidationResult, Validator, ValidatorContext } from '../validation.js'
1
+ import { Schema, ValidationResult, ValidatorContext } from '../validation.js'
2
2
 
3
3
  export type IntegerSchemaOptions = {
4
4
  default?: number
@@ -6,15 +6,13 @@ export type IntegerSchemaOptions = {
6
6
  maximum?: number
7
7
  }
8
8
 
9
- export class IntegerSchema extends Validator<number> {
10
- readonly lexiconType = 'integer' as const
11
-
12
- constructor(readonly options: IntegerSchemaOptions) {
9
+ export class IntegerSchema extends Schema<number> {
10
+ constructor(readonly options: IntegerSchemaOptions = {}) {
13
11
  super()
14
12
  }
15
13
 
16
- override validateInContext(
17
- input: unknown = this.options.default,
14
+ validateInContext(
15
+ input: unknown = this.options?.default,
18
16
  ctx: ValidatorContext,
19
17
  ): ValidationResult<number> {
20
18
  if (!isInteger(input)) {
@@ -0,0 +1,32 @@
1
+ import { BooleanSchema } from './boolean.js'
2
+ import { DictSchema } from './dict.js'
3
+ import { EnumSchema } from './enum.js'
4
+ import { IntersectionSchema } from './intersection.js'
5
+ import { ObjectSchema } from './object.js'
6
+ import { StringSchema } from './string.js'
7
+
8
+ describe('IntersectionSchema', () => {
9
+ const schema = new IntersectionSchema(
10
+ new ObjectSchema({
11
+ title: new StringSchema({}),
12
+ }),
13
+ new DictSchema(new EnumSchema(['tag1', 'tag2']), new BooleanSchema({})),
14
+ )
15
+
16
+ it('validates extra properties with the provided validator', () => {
17
+ const result = schema.safeParse({
18
+ title: 'My Post',
19
+ tag1: true,
20
+ tag2: false,
21
+ })
22
+ expect(result.success).toBe(true)
23
+ })
24
+
25
+ it('rejects extra properties that fail the provided validator', () => {
26
+ const result = schema.safeParse({
27
+ title: 'My Post',
28
+ tag1: 'not a boolean',
29
+ })
30
+ expect(result.success).toBe(false)
31
+ })
32
+ })
@@ -1,57 +1,54 @@
1
+ import { Simplify } from '../core.js'
1
2
  import {
2
3
  Infer,
4
+ Schema,
3
5
  ValidationResult,
4
- Validator,
5
6
  ValidatorContext,
6
7
  } from '../validation.js'
8
+ import { DictSchema } from './dict.js'
9
+ import { ObjectSchema } from './object.js'
10
+
11
+ /**
12
+ * Allows to more accurately represent the intersection of two object types
13
+ * where both types may share some keys, and one of them uses an index
14
+ * signature.
15
+ *
16
+ * @see {@link https://www.typescriptlang.org/play/?#code/C4TwDgpgBAglC8UDeUBmB7dAuKByARgIYBOuUAvlAGTJQDaA+lAJYB2UAzsMWwOYC6OVgFcAtvgjEKAKGkATCAGMANiWiL0rLlEI4YsjVuBQA1hBA4uPVrwRQARBnT2Dm7QDdCy4dESE6ZiD8UAD0IVAi4pJQABQcABbowspyUBIORMT2AJSyEAAeYOjExqCQUACSrMCSHErAzJoAPNJQsFAFNaxyHFAASkrFck1WfAA0UMKsJqzoAO6sAHxjrVAAQh35XT39g8TDozYTUzPzSyuLdqtwVKttMYHoqO00j88bnRDdvawQ7pJ3NpQAD860BbRwSHBQLadAA0ix2G91oJ1vDggAfWABcxPF5QOH8aFtci5aRlaAwVDMfIQVKIKo1Yh1RQNZq0Jw4AgkMjkCYoRiIzjcPioyISKTkRayBQqNRQQzaQgAMRpdL01NpclcRignm8EFVWrsKrVchxQVC4XF0SxmSAA Playground link}
17
+ */
18
+ export type Intersect<A, B> = B[keyof B] extends never
19
+ ? A
20
+ : keyof A & keyof B extends never
21
+ ? // If A and B don't overlap, just return A & B
22
+ A & B
23
+ : // Otherwise, properly represent the fact that accessing using an
24
+ // index signature could return a value from either A or B
25
+ A & { [K in keyof B]: B[K] | A[keyof A & K] }
7
26
 
8
- export type IntersectionSchemaValidators = readonly [
9
- Validator,
10
- Validator,
11
- ...Validator[],
12
- ]
13
27
  export type IntersectionSchemaOutput<
14
- V extends readonly Validator[],
15
- Base = unknown,
16
- > = V extends readonly [
17
- infer First extends Validator,
18
- ...infer Rest extends Validator[],
19
- ]
20
- ? IntersectionSchemaOutput<Rest, Base & Infer<First>>
21
- : Base
28
+ Left extends ObjectSchema,
29
+ Right extends DictSchema,
30
+ > = Simplify<Intersect<Infer<Left>, Infer<Right>>>
22
31
 
23
32
  export class IntersectionSchema<
24
- V extends IntersectionSchemaValidators = any,
25
- > extends Validator<IntersectionSchemaOutput<V>> {
26
- constructor(protected readonly validators: V) {
33
+ const Left extends ObjectSchema = any,
34
+ const Right extends DictSchema = any,
35
+ > extends Schema<IntersectionSchemaOutput<Left, Right>> {
36
+ constructor(
37
+ protected readonly left: Left,
38
+ protected readonly right: Right,
39
+ ) {
27
40
  super()
28
41
  }
29
42
 
30
- override validateInContext(
43
+ validateInContext(
31
44
  input: unknown,
32
45
  ctx: ValidatorContext,
33
- ): ValidationResult<IntersectionSchemaOutput<V>> {
34
- for (let i = 0; i < this.validators.length; i++) {
35
- const result = ctx.validate(input, this.validators[i])
36
-
37
- if (!result.success) {
38
- return result
39
- }
40
-
41
- // @NOTE because transforming the value could make it invalid for previous
42
- // validators, we need to ensure the input remains unchanged only gets
43
- // transformed by the first validator.
44
- if (i !== 0 && input !== result.value) {
45
- // The alternative would be to allow transforms on a first pass
46
- // (ignoring errors) and then re-validate the final value against all
47
- // validators (without allowing further transforms). This would be way
48
- // less efficient (we could make this optional).
49
- return ctx.issueInvalidValue(input, [result.value])
50
- }
51
-
52
- input = result.value
53
- }
46
+ ): ValidationResult<IntersectionSchemaOutput<Left, Right>> {
47
+ const leftResult = ctx.validate(input, this.left)
48
+ if (!leftResult.success) return leftResult
54
49
 
55
- return ctx.success(input as IntersectionSchemaOutput<V>)
50
+ return this.right.validateInContext(leftResult.value, ctx, {
51
+ ignoredKeys: this.left.validatorsMap,
52
+ }) as ValidationResult<IntersectionSchemaOutput<Left, Right>>
56
53
  }
57
54
  }