@atproto/lex-schema 0.1.4 → 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 +24 -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,676 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { discriminatedUnion } from './discriminated-union.js'
3
- import { enumSchema } from './enum.js'
4
- import { integer } from './integer.js'
5
- import { literal } from './literal.js'
6
- import { object } from './object.js'
7
- import { string } from './string.js'
8
-
9
- describe('DiscriminatedUnionSchema', () => {
10
- describe('with literal discriminators', () => {
11
- const schema = discriminatedUnion('type', [
12
- object({
13
- type: literal('cat'),
14
- meow: string(),
15
- }),
16
- object({
17
- type: literal('dog'),
18
- bark: string(),
19
- }),
20
- ])
21
-
22
- it('validates first variant', () => {
23
- const result = schema.safeParse({
24
- type: 'cat',
25
- meow: 'meow',
26
- })
27
- expect(result.success).toBe(true)
28
- })
29
-
30
- it('validates second variant', () => {
31
- const result = schema.safeParse({
32
- type: 'dog',
33
- bark: 'woof',
34
- })
35
- expect(result.success).toBe(true)
36
- })
37
-
38
- it('rejects non-objects', () => {
39
- const result = schema.safeParse('not an object')
40
- expect(result.success).toBe(false)
41
- })
42
-
43
- it('rejects null', () => {
44
- const result = schema.safeParse(null)
45
- expect(result.success).toBe(false)
46
- })
47
-
48
- it('rejects undefined', () => {
49
- const result = schema.safeParse(undefined)
50
- expect(result.success).toBe(false)
51
- })
52
-
53
- it('rejects arrays', () => {
54
- const result = schema.safeParse([{ type: 'cat', meow: 'meow' }])
55
- expect(result.success).toBe(false)
56
- })
57
-
58
- it('rejects objects missing discriminator', () => {
59
- const result = schema.safeParse({
60
- meow: 'meow',
61
- })
62
- expect(result.success).toBe(false)
63
- })
64
-
65
- it('rejects objects with invalid discriminator value', () => {
66
- const result = schema.safeParse({
67
- type: 'bird',
68
- chirp: 'tweet',
69
- })
70
- expect(result.success).toBe(false)
71
- })
72
-
73
- it('rejects objects with valid discriminator but invalid properties', () => {
74
- const result = schema.safeParse({
75
- type: 'cat',
76
- meow: 123,
77
- })
78
- expect(result.success).toBe(false)
79
- })
80
-
81
- it('rejects objects with valid discriminator but missing required properties', () => {
82
- const result = schema.safeParse({
83
- type: 'cat',
84
- })
85
- expect(result.success).toBe(false)
86
- })
87
-
88
- it('allows extra properties', () => {
89
- const result = schema.safeParse({
90
- type: 'cat',
91
- meow: 'meow',
92
- extra: 'property',
93
- })
94
- expect(result.success).toBe(true)
95
- })
96
- })
97
-
98
- describe('with enum discriminators', () => {
99
- const schema = discriminatedUnion('status', [
100
- object({
101
- status: enumSchema(['pending', 'processing']),
102
- progress: integer(),
103
- }),
104
- object({
105
- status: enumSchema(['complete', 'failed']),
106
- result: string(),
107
- }),
108
- ])
109
-
110
- it('validates first variant with first enum value', () => {
111
- const result = schema.safeParse({
112
- status: 'pending',
113
- progress: 0,
114
- })
115
- expect(result.success).toBe(true)
116
- })
117
-
118
- it('validates first variant with second enum value', () => {
119
- const result = schema.safeParse({
120
- status: 'processing',
121
- progress: 50,
122
- })
123
- expect(result.success).toBe(true)
124
- })
125
-
126
- it('validates second variant with first enum value', () => {
127
- const result = schema.safeParse({
128
- status: 'complete',
129
- result: 'success',
130
- })
131
- expect(result.success).toBe(true)
132
- })
133
-
134
- it('validates second variant with second enum value', () => {
135
- const result = schema.safeParse({
136
- status: 'failed',
137
- result: 'error',
138
- })
139
- expect(result.success).toBe(true)
140
- })
141
-
142
- it('rejects invalid discriminator value', () => {
143
- const result = schema.safeParse({
144
- status: 'unknown',
145
- progress: 0,
146
- })
147
- expect(result.success).toBe(false)
148
- })
149
-
150
- it('rejects object missing discriminator', () => {
151
- const result = schema.safeParse({
152
- progress: 0,
153
- })
154
- expect(result.success).toBe(false)
155
- })
156
-
157
- it('rejects object with valid discriminator but wrong properties', () => {
158
- const result = schema.safeParse({
159
- status: 'pending',
160
- result: 'success',
161
- })
162
- expect(result.success).toBe(false)
163
- })
164
- })
165
-
166
- describe('with mixed literal and enum discriminators', () => {
167
- const schema = discriminatedUnion('kind', [
168
- object({
169
- kind: literal('simple'),
170
- value: string(),
171
- }),
172
- object({
173
- kind: enumSchema(['complex', 'advanced']),
174
- value: integer(),
175
- }),
176
- ])
177
-
178
- it('validates literal discriminator variant', () => {
179
- const result = schema.safeParse({
180
- kind: 'simple',
181
- value: 'text',
182
- })
183
- expect(result.success).toBe(true)
184
- })
185
-
186
- it('validates enum discriminator variant with first value', () => {
187
- const result = schema.safeParse({
188
- kind: 'complex',
189
- value: 42,
190
- })
191
- expect(result.success).toBe(true)
192
- })
193
-
194
- it('validates enum discriminator variant with second value', () => {
195
- const result = schema.safeParse({
196
- kind: 'advanced',
197
- value: 100,
198
- })
199
- expect(result.success).toBe(true)
200
- })
201
-
202
- it('rejects invalid discriminator value', () => {
203
- const result = schema.safeParse({
204
- kind: 'unknown',
205
- value: 'test',
206
- })
207
- expect(result.success).toBe(false)
208
- })
209
- })
210
-
211
- describe('with single variant', () => {
212
- const schema = discriminatedUnion('type', [
213
- object({
214
- type: literal('only'),
215
- value: string(),
216
- }),
217
- ])
218
-
219
- it('validates the single variant', () => {
220
- const result = schema.safeParse({
221
- type: 'only',
222
- value: 'test',
223
- })
224
- expect(result.success).toBe(true)
225
- })
226
-
227
- it('rejects other discriminator values', () => {
228
- const result = schema.safeParse({
229
- type: 'other',
230
- value: 'test',
231
- })
232
- expect(result.success).toBe(false)
233
- })
234
- })
235
-
236
- describe('with three variants', () => {
237
- const schema = discriminatedUnion('shape', [
238
- object({
239
- shape: literal('circle'),
240
- radius: integer(),
241
- }),
242
- object({
243
- shape: literal('square'),
244
- side: integer(),
245
- }),
246
- object({
247
- shape: literal('rectangle'),
248
- width: integer(),
249
- height: integer(),
250
- }),
251
- ])
252
-
253
- it('validates first variant', () => {
254
- const result = schema.safeParse({
255
- shape: 'circle',
256
- radius: 10,
257
- })
258
- expect(result.success).toBe(true)
259
- })
260
-
261
- it('validates second variant', () => {
262
- const result = schema.safeParse({
263
- shape: 'square',
264
- side: 5,
265
- })
266
- expect(result.success).toBe(true)
267
- })
268
-
269
- it('validates third variant', () => {
270
- const result = schema.safeParse({
271
- shape: 'rectangle',
272
- width: 10,
273
- height: 20,
274
- })
275
- expect(result.success).toBe(true)
276
- })
277
-
278
- it('rejects invalid discriminator', () => {
279
- const result = schema.safeParse({
280
- shape: 'triangle',
281
- sides: 3,
282
- })
283
- expect(result.success).toBe(false)
284
- })
285
- })
286
-
287
- describe('with number discriminators', () => {
288
- const schema = discriminatedUnion('version', [
289
- object({
290
- version: literal(1),
291
- oldFormat: string(),
292
- }),
293
- object({
294
- version: literal(2),
295
- newFormat: string(),
296
- }),
297
- ])
298
-
299
- it('validates first version', () => {
300
- const result = schema.safeParse({
301
- version: 1,
302
- oldFormat: 'data',
303
- })
304
- expect(result.success).toBe(true)
305
- })
306
-
307
- it('validates second version', () => {
308
- const result = schema.safeParse({
309
- version: 2,
310
- newFormat: 'data',
311
- })
312
- expect(result.success).toBe(true)
313
- })
314
-
315
- it('rejects invalid version number', () => {
316
- const result = schema.safeParse({
317
- version: 3,
318
- format: 'data',
319
- })
320
- expect(result.success).toBe(false)
321
- })
322
-
323
- it('rejects string version', () => {
324
- const result = schema.safeParse({
325
- version: '1',
326
- oldFormat: 'data',
327
- })
328
- expect(result.success).toBe(false)
329
- })
330
- })
331
-
332
- describe('with boolean discriminators', () => {
333
- const schema = discriminatedUnion('enabled', [
334
- object({
335
- enabled: literal(true),
336
- config: string(),
337
- }),
338
- object({
339
- enabled: literal(false),
340
- reason: string(),
341
- }),
342
- ])
343
-
344
- it('validates true variant', () => {
345
- const result = schema.safeParse({
346
- enabled: true,
347
- config: 'settings',
348
- })
349
- expect(result.success).toBe(true)
350
- })
351
-
352
- it('validates false variant', () => {
353
- const result = schema.safeParse({
354
- enabled: false,
355
- reason: 'disabled',
356
- })
357
- expect(result.success).toBe(true)
358
- })
359
-
360
- it('rejects string boolean', () => {
361
- const result = schema.safeParse({
362
- enabled: 'true',
363
- config: 'settings',
364
- })
365
- expect(result.success).toBe(false)
366
- })
367
- })
368
-
369
- describe('with null discriminator', () => {
370
- const schema = discriminatedUnion('value', [
371
- object({
372
- value: literal(null),
373
- empty: string(),
374
- }),
375
- object({
376
- value: literal('present'),
377
- data: string(),
378
- }),
379
- ])
380
-
381
- it('validates null discriminator variant', () => {
382
- const result = schema.safeParse({
383
- value: null,
384
- empty: 'nothing',
385
- })
386
- expect(result.success).toBe(true)
387
- })
388
-
389
- it('validates non-null discriminator variant', () => {
390
- const result = schema.safeParse({
391
- value: 'present',
392
- data: 'something',
393
- })
394
- expect(result.success).toBe(true)
395
- })
396
-
397
- it('rejects undefined discriminator', () => {
398
- const result = schema.safeParse({
399
- value: undefined,
400
- empty: 'nothing',
401
- })
402
- expect(result.success).toBe(false)
403
- })
404
- })
405
-
406
- describe('constructor validation', () => {
407
- it('throws on overlapping literal discriminator values', () => {
408
- expect(() => {
409
- discriminatedUnion('type', [
410
- object({
411
- type: literal('duplicate'),
412
- a: string(),
413
- }),
414
- object({
415
- type: literal('duplicate'),
416
- b: string(),
417
- }),
418
- ])
419
- }).toThrow('Overlapping discriminator value: duplicate')
420
- })
421
-
422
- it('throws on overlapping enum discriminator values', () => {
423
- expect(() => {
424
- discriminatedUnion('status', [
425
- object({
426
- status: enumSchema(['active', 'pending']),
427
- a: string(),
428
- }),
429
- object({
430
- status: enumSchema(['pending', 'complete']),
431
- b: string(),
432
- }),
433
- ])
434
- }).toThrow('Overlapping discriminator value: pending')
435
- })
436
-
437
- it('throws on overlapping literal and enum discriminator values', () => {
438
- expect(() => {
439
- discriminatedUnion('kind', [
440
- object({
441
- kind: literal('test'),
442
- a: string(),
443
- }),
444
- object({
445
- kind: enumSchema(['test', 'other']),
446
- b: string(),
447
- }),
448
- ])
449
- }).toThrow('Overlapping discriminator value: test')
450
- })
451
- })
452
-
453
- describe('edge cases', () => {
454
- const schema = discriminatedUnion('type', [
455
- object({
456
- type: literal('a'),
457
- value: string(),
458
- }),
459
- object({
460
- type: literal('b'),
461
- value: integer(),
462
- }),
463
- ])
464
-
465
- it('rejects empty object', () => {
466
- const result = schema.safeParse({})
467
- expect(result.success).toBe(false)
468
- })
469
-
470
- it('rejects object with null discriminator', () => {
471
- const result = schema.safeParse({
472
- type: null,
473
- value: 'test',
474
- })
475
- expect(result.success).toBe(false)
476
- })
477
-
478
- it('rejects object with undefined discriminator', () => {
479
- const result = schema.safeParse({
480
- type: undefined,
481
- value: 'test',
482
- })
483
- expect(result.success).toBe(false)
484
- })
485
-
486
- it('rejects primitive values', () => {
487
- expect(schema.safeParse(42).success).toBe(false)
488
- expect(schema.safeParse('string').success).toBe(false)
489
- expect(schema.safeParse(true).success).toBe(false)
490
- })
491
-
492
- it('handles empty string discriminator', () => {
493
- const emptySchema = discriminatedUnion('key', [
494
- object({
495
- key: literal(''),
496
- value: string(),
497
- }),
498
- ])
499
-
500
- const result = emptySchema.safeParse({
501
- key: '',
502
- value: 'test',
503
- })
504
- expect(result.success).toBe(true)
505
- })
506
-
507
- it('handles zero discriminator', () => {
508
- const zeroSchema = discriminatedUnion('count', [
509
- object({
510
- count: literal(0),
511
- value: string(),
512
- }),
513
- ])
514
-
515
- const result = zeroSchema.safeParse({
516
- count: 0,
517
- value: 'test',
518
- })
519
- expect(result.success).toBe(true)
520
- })
521
-
522
- it('handles false discriminator', () => {
523
- const falseSchema = discriminatedUnion('flag', [
524
- object({
525
- flag: literal(false),
526
- value: string(),
527
- }),
528
- ])
529
-
530
- const result = falseSchema.safeParse({
531
- flag: false,
532
- value: 'test',
533
- })
534
- expect(result.success).toBe(true)
535
- })
536
-
537
- it('rejects class instances', () => {
538
- class CustomClass {
539
- type = 'a'
540
- value = 'test'
541
- }
542
- const result = schema.safeParse(new CustomClass())
543
- expect(result.success).toBe(false)
544
- })
545
-
546
- it('handles discriminator with special characters', () => {
547
- const specialSchema = discriminatedUnion('$type', [
548
- object({
549
- $type: literal('test'),
550
- value: string(),
551
- }),
552
- ])
553
-
554
- const result = specialSchema.safeParse({
555
- $type: 'test',
556
- value: 'data',
557
- })
558
- expect(result.success).toBe(true)
559
- })
560
-
561
- it('handles objects with prototype properties', () => {
562
- const obj = Object.create({ type: 'a' })
563
- obj.value = 'test'
564
- // Should fail because discriminator is not an own property
565
- const result = schema.safeParse(obj)
566
- expect(result.success).toBe(false)
567
- })
568
-
569
- it('validates object with discriminator as own property', () => {
570
- const obj = Object.create(null)
571
- obj.type = 'a'
572
- obj.value = 'test'
573
- const result = schema.safeParse(obj)
574
- expect(result.success).toBe(true)
575
- })
576
- })
577
-
578
- describe('complex nested structures', () => {
579
- const schema = discriminatedUnion('type', [
580
- object({
581
- type: literal('user'),
582
- name: string(),
583
- age: integer(),
584
- }),
585
- object({
586
- type: literal('post'),
587
- title: string(),
588
- content: string(),
589
- }),
590
- ])
591
-
592
- it('validates complex user object', () => {
593
- const result = schema.safeParse({
594
- type: 'user',
595
- name: 'Alice',
596
- age: 30,
597
- })
598
- expect(result.success).toBe(true)
599
- })
600
-
601
- it('validates complex post object', () => {
602
- const result = schema.safeParse({
603
- type: 'post',
604
- title: 'Hello World',
605
- content: 'This is a test post',
606
- })
607
- expect(result.success).toBe(true)
608
- })
609
-
610
- it('rejects user with missing age', () => {
611
- const result = schema.safeParse({
612
- type: 'user',
613
- name: 'Alice',
614
- })
615
- expect(result.success).toBe(false)
616
- })
617
-
618
- it('rejects post with invalid content type', () => {
619
- const result = schema.safeParse({
620
- type: 'post',
621
- title: 'Hello World',
622
- content: 123,
623
- })
624
- expect(result.success).toBe(false)
625
- })
626
-
627
- it('rejects mixed properties from different variants', () => {
628
- const result = schema.safeParse({
629
- type: 'user',
630
- name: 'Alice',
631
- title: 'Hello World',
632
- })
633
- expect(result.success).toBe(false)
634
- })
635
- })
636
-
637
- describe('discriminator key variations', () => {
638
- it('works with different discriminator key names', () => {
639
- const kindSchema = discriminatedUnion('kind', [
640
- object({
641
- kind: literal('a'),
642
- value: string(),
643
- }),
644
- ])
645
-
646
- const tagSchema = discriminatedUnion('tag', [
647
- object({
648
- tag: literal('a'),
649
- value: string(),
650
- }),
651
- ])
652
-
653
- expect(kindSchema.safeParse({ kind: 'a', value: 'test' }).success).toBe(
654
- true,
655
- )
656
- expect(tagSchema.safeParse({ tag: 'a', value: 'test' }).success).toBe(
657
- true,
658
- )
659
- })
660
-
661
- it('rejects when discriminator key does not match schema', () => {
662
- const schema = discriminatedUnion('type', [
663
- object({
664
- type: literal('a'),
665
- value: string(),
666
- }),
667
- ])
668
-
669
- const result = schema.safeParse({
670
- kind: 'a',
671
- value: 'test',
672
- })
673
- expect(result.success).toBe(false)
674
- })
675
- })
676
- })