@atproto/lex-schema 0.0.2 → 0.0.3

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 +68 -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,531 @@
1
+ import { LiteralSchema } from './literal.js'
2
+
3
+ describe('LiteralSchema', () => {
4
+ describe('string literals', () => {
5
+ const schema = new LiteralSchema('hello')
6
+
7
+ it('validates exact string match', () => {
8
+ const result = schema.safeParse('hello')
9
+ expect(result.success).toBe(true)
10
+ if (result.success) {
11
+ expect(result.value).toBe('hello')
12
+ }
13
+ })
14
+
15
+ it('rejects different strings', () => {
16
+ const result = schema.safeParse('world')
17
+ expect(result.success).toBe(false)
18
+ })
19
+
20
+ it('rejects similar strings with different case', () => {
21
+ const result = schema.safeParse('Hello')
22
+ expect(result.success).toBe(false)
23
+ })
24
+
25
+ it('rejects empty string when literal is non-empty', () => {
26
+ const result = schema.safeParse('')
27
+ expect(result.success).toBe(false)
28
+ })
29
+
30
+ it('rejects numbers', () => {
31
+ const result = schema.safeParse(42)
32
+ expect(result.success).toBe(false)
33
+ })
34
+
35
+ it('rejects booleans', () => {
36
+ const result = schema.safeParse(true)
37
+ expect(result.success).toBe(false)
38
+ })
39
+
40
+ it('rejects null', () => {
41
+ const result = schema.safeParse(null)
42
+ expect(result.success).toBe(false)
43
+ })
44
+
45
+ it('rejects undefined', () => {
46
+ const result = schema.safeParse(undefined)
47
+ expect(result.success).toBe(false)
48
+ })
49
+
50
+ it('rejects objects', () => {
51
+ const result = schema.safeParse({})
52
+ expect(result.success).toBe(false)
53
+ })
54
+
55
+ it('rejects arrays', () => {
56
+ const result = schema.safeParse(['hello'])
57
+ expect(result.success).toBe(false)
58
+ })
59
+ })
60
+
61
+ describe('empty string literal', () => {
62
+ const schema = new LiteralSchema('')
63
+
64
+ it('validates empty string', () => {
65
+ const result = schema.safeParse('')
66
+ expect(result.success).toBe(true)
67
+ if (result.success) {
68
+ expect(result.value).toBe('')
69
+ }
70
+ })
71
+
72
+ it('rejects non-empty strings', () => {
73
+ const result = schema.safeParse('hello')
74
+ expect(result.success).toBe(false)
75
+ })
76
+
77
+ it('rejects null', () => {
78
+ const result = schema.safeParse(null)
79
+ expect(result.success).toBe(false)
80
+ })
81
+
82
+ it('rejects undefined', () => {
83
+ const result = schema.safeParse(undefined)
84
+ expect(result.success).toBe(false)
85
+ })
86
+ })
87
+
88
+ describe('number literals', () => {
89
+ const schema = new LiteralSchema(42)
90
+
91
+ it('validates exact number match', () => {
92
+ const result = schema.safeParse(42)
93
+ expect(result.success).toBe(true)
94
+ if (result.success) {
95
+ expect(result.value).toBe(42)
96
+ }
97
+ })
98
+
99
+ it('rejects different numbers', () => {
100
+ const result = schema.safeParse(43)
101
+ expect(result.success).toBe(false)
102
+ })
103
+
104
+ it('rejects string representation of number', () => {
105
+ const result = schema.safeParse('42')
106
+ expect(result.success).toBe(false)
107
+ })
108
+
109
+ it('rejects booleans', () => {
110
+ const result = schema.safeParse(true)
111
+ expect(result.success).toBe(false)
112
+ })
113
+
114
+ it('rejects null', () => {
115
+ const result = schema.safeParse(null)
116
+ expect(result.success).toBe(false)
117
+ })
118
+
119
+ it('rejects undefined', () => {
120
+ const result = schema.safeParse(undefined)
121
+ expect(result.success).toBe(false)
122
+ })
123
+ })
124
+
125
+ describe('zero literal', () => {
126
+ const schema = new LiteralSchema(0)
127
+
128
+ it('validates zero', () => {
129
+ const result = schema.safeParse(0)
130
+ expect(result.success).toBe(true)
131
+ if (result.success) {
132
+ expect(result.value).toBe(0)
133
+ }
134
+ })
135
+
136
+ it('rejects false', () => {
137
+ const result = schema.safeParse(false)
138
+ expect(result.success).toBe(false)
139
+ })
140
+
141
+ it('rejects null', () => {
142
+ const result = schema.safeParse(null)
143
+ expect(result.success).toBe(false)
144
+ })
145
+
146
+ it('rejects empty string', () => {
147
+ const result = schema.safeParse('')
148
+ expect(result.success).toBe(false)
149
+ })
150
+
151
+ it('rejects other numbers', () => {
152
+ const result = schema.safeParse(1)
153
+ expect(result.success).toBe(false)
154
+ })
155
+ })
156
+
157
+ describe('negative number literals', () => {
158
+ const schema = new LiteralSchema(-42)
159
+
160
+ it('validates exact negative number match', () => {
161
+ const result = schema.safeParse(-42)
162
+ expect(result.success).toBe(true)
163
+ if (result.success) {
164
+ expect(result.value).toBe(-42)
165
+ }
166
+ })
167
+
168
+ it('rejects positive equivalent', () => {
169
+ const result = schema.safeParse(42)
170
+ expect(result.success).toBe(false)
171
+ })
172
+
173
+ it('rejects different negative numbers', () => {
174
+ const result = schema.safeParse(-43)
175
+ expect(result.success).toBe(false)
176
+ })
177
+ })
178
+
179
+ describe('decimal number literals', () => {
180
+ const schema = new LiteralSchema(3.14)
181
+
182
+ it('validates exact decimal match', () => {
183
+ const result = schema.safeParse(3.14)
184
+ expect(result.success).toBe(true)
185
+ if (result.success) {
186
+ expect(result.value).toBe(3.14)
187
+ }
188
+ })
189
+
190
+ it('rejects different decimals', () => {
191
+ const result = schema.safeParse(3.15)
192
+ expect(result.success).toBe(false)
193
+ })
194
+
195
+ it('rejects integer equivalent', () => {
196
+ const result = schema.safeParse(3)
197
+ expect(result.success).toBe(false)
198
+ })
199
+ })
200
+
201
+ describe('boolean literals', () => {
202
+ describe('true literal', () => {
203
+ const schema = new LiteralSchema(true)
204
+
205
+ it('validates true', () => {
206
+ const result = schema.safeParse(true)
207
+ expect(result.success).toBe(true)
208
+ if (result.success) {
209
+ expect(result.value).toBe(true)
210
+ }
211
+ })
212
+
213
+ it('rejects false', () => {
214
+ const result = schema.safeParse(false)
215
+ expect(result.success).toBe(false)
216
+ })
217
+
218
+ it('rejects truthy values', () => {
219
+ const result = schema.safeParse(1)
220
+ expect(result.success).toBe(false)
221
+ })
222
+
223
+ it('rejects string representation', () => {
224
+ const result = schema.safeParse('true')
225
+ expect(result.success).toBe(false)
226
+ })
227
+
228
+ it('rejects null', () => {
229
+ const result = schema.safeParse(null)
230
+ expect(result.success).toBe(false)
231
+ })
232
+
233
+ it('rejects undefined', () => {
234
+ const result = schema.safeParse(undefined)
235
+ expect(result.success).toBe(false)
236
+ })
237
+ })
238
+
239
+ describe('false literal', () => {
240
+ const schema = new LiteralSchema(false)
241
+
242
+ it('validates false', () => {
243
+ const result = schema.safeParse(false)
244
+ expect(result.success).toBe(true)
245
+ if (result.success) {
246
+ expect(result.value).toBe(false)
247
+ }
248
+ })
249
+
250
+ it('rejects true', () => {
251
+ const result = schema.safeParse(true)
252
+ expect(result.success).toBe(false)
253
+ })
254
+
255
+ it('rejects falsy values', () => {
256
+ const result = schema.safeParse(0)
257
+ expect(result.success).toBe(false)
258
+ })
259
+
260
+ it('rejects empty string', () => {
261
+ const result = schema.safeParse('')
262
+ expect(result.success).toBe(false)
263
+ })
264
+
265
+ it('rejects null', () => {
266
+ const result = schema.safeParse(null)
267
+ expect(result.success).toBe(false)
268
+ })
269
+ })
270
+ })
271
+
272
+ describe('null literal', () => {
273
+ const schema = new LiteralSchema(null)
274
+
275
+ it('validates null', () => {
276
+ const result = schema.safeParse(null)
277
+ expect(result.success).toBe(true)
278
+ if (result.success) {
279
+ expect(result.value).toBe(null)
280
+ }
281
+ })
282
+
283
+ it('rejects undefined', () => {
284
+ const result = schema.safeParse(undefined)
285
+ expect(result.success).toBe(false)
286
+ })
287
+
288
+ it('rejects false', () => {
289
+ const result = schema.safeParse(false)
290
+ expect(result.success).toBe(false)
291
+ })
292
+
293
+ it('rejects zero', () => {
294
+ const result = schema.safeParse(0)
295
+ expect(result.success).toBe(false)
296
+ })
297
+
298
+ it('rejects empty string', () => {
299
+ const result = schema.safeParse('')
300
+ expect(result.success).toBe(false)
301
+ })
302
+
303
+ it('rejects string "null"', () => {
304
+ const result = schema.safeParse('null')
305
+ expect(result.success).toBe(false)
306
+ })
307
+ })
308
+
309
+ describe('default values', () => {
310
+ describe('string literal with default', () => {
311
+ const schema = new LiteralSchema('hello', { default: 'hello' })
312
+
313
+ it('uses default value when undefined is provided', () => {
314
+ const result = schema.safeParse(undefined)
315
+ expect(result.success).toBe(true)
316
+ if (result.success) {
317
+ expect(result.value).toBe('hello')
318
+ }
319
+ })
320
+
321
+ it('uses explicit value over default', () => {
322
+ const result = schema.safeParse('hello')
323
+ expect(result.success).toBe(true)
324
+ if (result.success) {
325
+ expect(result.value).toBe('hello')
326
+ }
327
+ })
328
+
329
+ it('rejects non-matching value even with default', () => {
330
+ const result = schema.safeParse('world')
331
+ expect(result.success).toBe(false)
332
+ })
333
+ })
334
+
335
+ describe('number literal with default', () => {
336
+ const schema = new LiteralSchema(42, { default: 42 })
337
+
338
+ it('uses default value when undefined is provided', () => {
339
+ const result = schema.safeParse(undefined)
340
+ expect(result.success).toBe(true)
341
+ if (result.success) {
342
+ expect(result.value).toBe(42)
343
+ }
344
+ })
345
+
346
+ it('uses explicit value over default', () => {
347
+ const result = schema.safeParse(42)
348
+ expect(result.success).toBe(true)
349
+ })
350
+ })
351
+
352
+ describe('boolean literal with default', () => {
353
+ const schema = new LiteralSchema(true, { default: true })
354
+
355
+ it('uses default value when undefined is provided', () => {
356
+ const result = schema.safeParse(undefined)
357
+ expect(result.success).toBe(true)
358
+ if (result.success) {
359
+ expect(result.value).toBe(true)
360
+ }
361
+ })
362
+
363
+ it('uses explicit value over default', () => {
364
+ const result = schema.safeParse(true)
365
+ expect(result.success).toBe(true)
366
+ })
367
+ })
368
+
369
+ describe('null literal with default', () => {
370
+ const schema = new LiteralSchema(null, { default: null })
371
+
372
+ it('uses default value when undefined is provided', () => {
373
+ const result = schema.safeParse(undefined)
374
+ expect(result.success).toBe(true)
375
+ if (result.success) {
376
+ expect(result.value).toBe(null)
377
+ }
378
+ })
379
+
380
+ it('uses explicit value over default', () => {
381
+ const result = schema.safeParse(null)
382
+ expect(result.success).toBe(true)
383
+ })
384
+ })
385
+
386
+ describe('false literal with default', () => {
387
+ const schema = new LiteralSchema(false, { default: false })
388
+
389
+ it('uses default value when undefined is provided', () => {
390
+ const result = schema.safeParse(undefined)
391
+ expect(result.success).toBe(true)
392
+ if (result.success) {
393
+ expect(result.value).toBe(false)
394
+ }
395
+ })
396
+
397
+ it('does not confuse explicit false with undefined', () => {
398
+ const result = schema.safeParse(false)
399
+ expect(result.success).toBe(true)
400
+ })
401
+ })
402
+
403
+ describe('zero literal with default', () => {
404
+ const schema = new LiteralSchema(0, { default: 0 })
405
+
406
+ it('uses default value when undefined is provided', () => {
407
+ const result = schema.safeParse(undefined)
408
+ expect(result.success).toBe(true)
409
+ if (result.success) {
410
+ expect(result.value).toBe(0)
411
+ }
412
+ })
413
+
414
+ it('does not confuse explicit zero with undefined', () => {
415
+ const result = schema.safeParse(0)
416
+ expect(result.success).toBe(true)
417
+ })
418
+ })
419
+ })
420
+
421
+ describe('edge cases', () => {
422
+ it('handles special string characters', () => {
423
+ const schema = new LiteralSchema('hello\nworld')
424
+ expect(schema.safeParse('hello\nworld').success).toBe(true)
425
+ expect(schema.safeParse('hello world').success).toBe(false)
426
+ })
427
+
428
+ it('handles unicode characters in strings', () => {
429
+ const schema = new LiteralSchema('Hello 世界 🌍')
430
+ expect(schema.safeParse('Hello 世界 🌍').success).toBe(true)
431
+ expect(schema.safeParse('Hello world').success).toBe(false)
432
+ })
433
+
434
+ it('handles emoji literals', () => {
435
+ const schema = new LiteralSchema('🚀')
436
+ expect(schema.safeParse('🚀').success).toBe(true)
437
+ expect(schema.safeParse('🌟').success).toBe(false)
438
+ })
439
+
440
+ it('handles very long string literals', () => {
441
+ const longString = 'a'.repeat(1000)
442
+ const schema = new LiteralSchema(longString)
443
+ expect(schema.safeParse(longString).success).toBe(true)
444
+ expect(schema.safeParse(longString + 'b').success).toBe(false)
445
+ })
446
+
447
+ it('handles string with whitespace', () => {
448
+ const schema = new LiteralSchema(' hello ')
449
+ expect(schema.safeParse(' hello ').success).toBe(true)
450
+ expect(schema.safeParse('hello').success).toBe(false)
451
+ expect(schema.safeParse(' hello').success).toBe(false)
452
+ })
453
+
454
+ it('handles Number.MAX_SAFE_INTEGER', () => {
455
+ const schema = new LiteralSchema(Number.MAX_SAFE_INTEGER)
456
+ expect(schema.safeParse(Number.MAX_SAFE_INTEGER).success).toBe(true)
457
+ expect(schema.safeParse(Number.MAX_SAFE_INTEGER - 1).success).toBe(false)
458
+ })
459
+
460
+ it('handles Number.MIN_SAFE_INTEGER', () => {
461
+ const schema = new LiteralSchema(Number.MIN_SAFE_INTEGER)
462
+ expect(schema.safeParse(Number.MIN_SAFE_INTEGER).success).toBe(true)
463
+ expect(schema.safeParse(Number.MIN_SAFE_INTEGER + 1).success).toBe(false)
464
+ })
465
+
466
+ it('rejects NaN', () => {
467
+ const schema = new LiteralSchema(42)
468
+ expect(schema.safeParse(NaN).success).toBe(false)
469
+ })
470
+
471
+ it('rejects Infinity', () => {
472
+ const schema = new LiteralSchema(42)
473
+ expect(schema.safeParse(Infinity).success).toBe(false)
474
+ })
475
+
476
+ it('rejects -Infinity', () => {
477
+ const schema = new LiteralSchema(42)
478
+ expect(schema.safeParse(-Infinity).success).toBe(false)
479
+ })
480
+
481
+ it('rejects Boolean objects', () => {
482
+ const schema = new LiteralSchema(true)
483
+ expect(schema.safeParse(new Boolean(true)).success).toBe(false)
484
+ })
485
+
486
+ it('rejects String objects', () => {
487
+ const schema = new LiteralSchema('hello')
488
+ expect(schema.safeParse(new String('hello')).success).toBe(false)
489
+ })
490
+
491
+ it('rejects Number objects', () => {
492
+ const schema = new LiteralSchema(42)
493
+ expect(schema.safeParse(new Number(42)).success).toBe(false)
494
+ })
495
+
496
+ it('distinguishes between -0 and +0', () => {
497
+ const schemaPositive = new LiteralSchema(0)
498
+ const schemaNegative = new LiteralSchema(-0)
499
+ // In JavaScript, 0 === -0, so both should validate for both schemas
500
+ expect(schemaPositive.safeParse(0).success).toBe(true)
501
+ expect(schemaPositive.safeParse(-0).success).toBe(true)
502
+ expect(schemaNegative.safeParse(0).success).toBe(true)
503
+ expect(schemaNegative.safeParse(-0).success).toBe(true)
504
+ })
505
+
506
+ it('handles very small decimal differences', () => {
507
+ const schema = new LiteralSchema(0.1 + 0.2)
508
+ // Note: 0.1 + 0.2 !== 0.3 in JavaScript due to floating point precision
509
+ expect(schema.safeParse(0.1 + 0.2).success).toBe(true)
510
+ expect(schema.safeParse(0.3).success).toBe(false)
511
+ })
512
+ })
513
+
514
+ describe('type safety', () => {
515
+ it('accepts exact literal types in TypeScript', () => {
516
+ const schema = new LiteralSchema('specific' as const)
517
+ const result = schema.safeParse('specific')
518
+ expect(result.success).toBe(true)
519
+ })
520
+
521
+ it('preserves literal type in success result', () => {
522
+ const schema = new LiteralSchema(42)
523
+ const result = schema.safeParse(42)
524
+ expect(result.success).toBe(true)
525
+ if (result.success) {
526
+ // TypeScript should infer result.value as the literal type 42
527
+ expect(result.value).toBe(42)
528
+ }
529
+ })
530
+ })
531
+ })
@@ -1,14 +1,21 @@
1
- import { ValidationResult, Validator, ValidatorContext } from '../validation.js'
1
+ import { Schema, ValidationResult, ValidatorContext } from '../validation.js'
2
+
3
+ export type LiteralSchemaOptions<T extends null | string | number | boolean> = {
4
+ default?: T
5
+ }
2
6
 
3
7
  export class LiteralSchema<
4
8
  Output extends null | string | number | boolean = any,
5
- > extends Validator<Output> {
6
- constructor(readonly value: Output) {
9
+ > extends Schema<Output> {
10
+ constructor(
11
+ readonly value: Output,
12
+ readonly options?: LiteralSchemaOptions<Output>,
13
+ ) {
7
14
  super()
8
15
  }
9
16
 
10
- override validateInContext(
11
- input: unknown = this.value,
17
+ validateInContext(
18
+ input: unknown = this.options?.default,
12
19
  ctx: ValidatorContext,
13
20
  ): ValidationResult<Output> {
14
21
  if (input !== this.value) {