@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,312 @@
1
+ import { UnknownSchema } from './unknown.js'
2
+
3
+ describe('UnknownSchema', () => {
4
+ describe('basic validation', () => {
5
+ const schema = new UnknownSchema()
6
+
7
+ it('accepts strings', () => {
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('accepts numbers', () => {
16
+ const result = schema.safeParse(42)
17
+ expect(result.success).toBe(true)
18
+ if (result.success) {
19
+ expect(result.value).toBe(42)
20
+ }
21
+ })
22
+
23
+ it('accepts booleans', () => {
24
+ const result = schema.safeParse(true)
25
+ expect(result.success).toBe(true)
26
+ if (result.success) {
27
+ expect(result.value).toBe(true)
28
+ }
29
+ })
30
+
31
+ it('accepts null', () => {
32
+ const result = schema.safeParse(null)
33
+ expect(result.success).toBe(true)
34
+ if (result.success) {
35
+ expect(result.value).toBe(null)
36
+ }
37
+ })
38
+
39
+ it('accepts undefined', () => {
40
+ const result = schema.safeParse(undefined)
41
+ expect(result.success).toBe(true)
42
+ if (result.success) {
43
+ expect(result.value).toBe(undefined)
44
+ }
45
+ })
46
+
47
+ it('accepts plain objects', () => {
48
+ const obj = { key: 'value', nested: { prop: 123 } }
49
+ const result = schema.safeParse(obj)
50
+ expect(result.success).toBe(true)
51
+ if (result.success) {
52
+ expect(result.value).toEqual(obj)
53
+ }
54
+ })
55
+
56
+ it('accepts arrays', () => {
57
+ const arr = [1, 2, 3, 'four', { five: 5 }]
58
+ const result = schema.safeParse(arr)
59
+ expect(result.success).toBe(true)
60
+ if (result.success) {
61
+ expect(result.value).toEqual(arr)
62
+ }
63
+ })
64
+
65
+ it('accepts empty arrays', () => {
66
+ const result = schema.safeParse([])
67
+ expect(result.success).toBe(true)
68
+ if (result.success) {
69
+ expect(result.value).toEqual([])
70
+ }
71
+ })
72
+
73
+ it('accepts empty objects', () => {
74
+ const result = schema.safeParse({})
75
+ expect(result.success).toBe(true)
76
+ if (result.success) {
77
+ expect(result.value).toEqual({})
78
+ }
79
+ })
80
+ })
81
+
82
+ describe('edge cases', () => {
83
+ const schema = new UnknownSchema()
84
+
85
+ it('accepts zero', () => {
86
+ const result = schema.safeParse(0)
87
+ expect(result.success).toBe(true)
88
+ if (result.success) {
89
+ expect(result.value).toBe(0)
90
+ }
91
+ })
92
+
93
+ it('accepts empty string', () => {
94
+ const result = schema.safeParse('')
95
+ expect(result.success).toBe(true)
96
+ if (result.success) {
97
+ expect(result.value).toBe('')
98
+ }
99
+ })
100
+
101
+ it('accepts negative numbers', () => {
102
+ const result = schema.safeParse(-123)
103
+ expect(result.success).toBe(true)
104
+ if (result.success) {
105
+ expect(result.value).toBe(-123)
106
+ }
107
+ })
108
+
109
+ it('accepts floating point numbers', () => {
110
+ const result = schema.safeParse(3.14159)
111
+ expect(result.success).toBe(true)
112
+ if (result.success) {
113
+ expect(result.value).toBe(3.14159)
114
+ }
115
+ })
116
+
117
+ it('accepts NaN', () => {
118
+ const result = schema.safeParse(NaN)
119
+ expect(result.success).toBe(true)
120
+ if (result.success) {
121
+ expect(result.value).toBeNaN()
122
+ }
123
+ })
124
+
125
+ it('accepts Infinity', () => {
126
+ const result = schema.safeParse(Infinity)
127
+ expect(result.success).toBe(true)
128
+ if (result.success) {
129
+ expect(result.value).toBe(Infinity)
130
+ }
131
+ })
132
+
133
+ it('accepts -Infinity', () => {
134
+ const result = schema.safeParse(-Infinity)
135
+ expect(result.success).toBe(true)
136
+ if (result.success) {
137
+ expect(result.value).toBe(-Infinity)
138
+ }
139
+ })
140
+
141
+ it('accepts BigInt', () => {
142
+ const bigIntValue = BigInt(9007199254740991)
143
+ const result = schema.safeParse(bigIntValue)
144
+ expect(result.success).toBe(true)
145
+ if (result.success) {
146
+ expect(result.value).toBe(bigIntValue)
147
+ }
148
+ })
149
+
150
+ it('accepts functions', () => {
151
+ const fn = () => 'test'
152
+ const result = schema.safeParse(fn)
153
+ expect(result.success).toBe(true)
154
+ if (result.success) {
155
+ expect(result.value).toBe(fn)
156
+ }
157
+ })
158
+
159
+ it('accepts Date objects', () => {
160
+ const date = new Date('2023-01-01')
161
+ const result = schema.safeParse(date)
162
+ expect(result.success).toBe(true)
163
+ if (result.success) {
164
+ expect(result.value).toBe(date)
165
+ }
166
+ })
167
+
168
+ it('accepts RegExp objects', () => {
169
+ const regex = /test/gi
170
+ const result = schema.safeParse(regex)
171
+ expect(result.success).toBe(true)
172
+ if (result.success) {
173
+ expect(result.value).toBe(regex)
174
+ }
175
+ })
176
+
177
+ it('accepts Symbol', () => {
178
+ const sym = Symbol('test')
179
+ const result = schema.safeParse(sym)
180
+ expect(result.success).toBe(true)
181
+ if (result.success) {
182
+ expect(result.value).toBe(sym)
183
+ }
184
+ })
185
+
186
+ it('accepts Map objects', () => {
187
+ const map = new Map([['key', 'value']])
188
+ const result = schema.safeParse(map)
189
+ expect(result.success).toBe(true)
190
+ if (result.success) {
191
+ expect(result.value).toBe(map)
192
+ }
193
+ })
194
+
195
+ it('accepts Set objects', () => {
196
+ const set = new Set([1, 2, 3])
197
+ const result = schema.safeParse(set)
198
+ expect(result.success).toBe(true)
199
+ if (result.success) {
200
+ expect(result.value).toBe(set)
201
+ }
202
+ })
203
+
204
+ it('accepts WeakMap objects', () => {
205
+ const weakMap = new WeakMap()
206
+ const result = schema.safeParse(weakMap)
207
+ expect(result.success).toBe(true)
208
+ if (result.success) {
209
+ expect(result.value).toBe(weakMap)
210
+ }
211
+ })
212
+
213
+ it('accepts WeakSet objects', () => {
214
+ const weakSet = new WeakSet()
215
+ const result = schema.safeParse(weakSet)
216
+ expect(result.success).toBe(true)
217
+ if (result.success) {
218
+ expect(result.value).toBe(weakSet)
219
+ }
220
+ })
221
+
222
+ it('accepts class instances', () => {
223
+ class TestClass {
224
+ constructor(public value: string) {}
225
+ }
226
+ const instance = new TestClass('test')
227
+ const result = schema.safeParse(instance)
228
+ expect(result.success).toBe(true)
229
+ if (result.success) {
230
+ expect(result.value).toBe(instance)
231
+ }
232
+ })
233
+
234
+ it('accepts nested complex structures', () => {
235
+ const complex = {
236
+ array: [1, 'two', { three: 3 }],
237
+ nested: {
238
+ deep: {
239
+ value: [null, undefined, true],
240
+ },
241
+ },
242
+ fn: () => 'test',
243
+ date: new Date(),
244
+ }
245
+ const result = schema.safeParse(complex)
246
+ expect(result.success).toBe(true)
247
+ if (result.success) {
248
+ expect(result.value).toEqual(complex)
249
+ }
250
+ })
251
+ })
252
+
253
+ describe('special JavaScript values', () => {
254
+ const schema = new UnknownSchema()
255
+
256
+ it('accepts objects with null prototype', () => {
257
+ const obj = Object.create(null)
258
+ obj.key = 'value'
259
+ const result = schema.safeParse(obj)
260
+ expect(result.success).toBe(true)
261
+ if (result.success) {
262
+ expect(result.value).toBe(obj)
263
+ }
264
+ })
265
+
266
+ it('accepts Proxy objects', () => {
267
+ const target = { value: 42 }
268
+ const proxy = new Proxy(target, {})
269
+ const result = schema.safeParse(proxy)
270
+ expect(result.success).toBe(true)
271
+ if (result.success) {
272
+ expect(result.value).toBe(proxy)
273
+ }
274
+ })
275
+
276
+ it('accepts typed arrays', () => {
277
+ const uint8Array = new Uint8Array([1, 2, 3])
278
+ const result = schema.safeParse(uint8Array)
279
+ expect(result.success).toBe(true)
280
+ if (result.success) {
281
+ expect(result.value).toBe(uint8Array)
282
+ }
283
+ })
284
+
285
+ it('accepts ArrayBuffer', () => {
286
+ const buffer = new ArrayBuffer(8)
287
+ const result = schema.safeParse(buffer)
288
+ expect(result.success).toBe(true)
289
+ if (result.success) {
290
+ expect(result.value).toBe(buffer)
291
+ }
292
+ })
293
+
294
+ it('accepts Promise objects', () => {
295
+ const promise = Promise.resolve(42)
296
+ const result = schema.safeParse(promise)
297
+ expect(result.success).toBe(true)
298
+ if (result.success) {
299
+ expect(result.value).toBe(promise)
300
+ }
301
+ })
302
+
303
+ it('accepts Error objects', () => {
304
+ const error = new Error('test error')
305
+ const result = schema.safeParse(error)
306
+ expect(result.success).toBe(true)
307
+ if (result.success) {
308
+ expect(result.value).toBe(error)
309
+ }
310
+ })
311
+ })
312
+ })
@@ -1,7 +1,7 @@
1
- import { ValidationResult, Validator, ValidatorContext } from '../validation'
1
+ import { Schema, ValidationResult, ValidatorContext } from '../validation'
2
2
 
3
- export class UnknownSchema extends Validator<unknown> {
4
- override validateInContext(
3
+ export class UnknownSchema extends Schema<unknown> {
4
+ validateInContext(
5
5
  input: unknown,
6
6
  ctx: ValidatorContext,
7
7
  ): ValidationResult<unknown> {
package/src/schema.ts CHANGED
@@ -1,6 +1,9 @@
1
- // Utilities (that depend on *and* are used by schemas)
1
+ // Global schemas
2
2
  export * from './schema/_parameters.js'
3
3
 
4
+ // Utilities
5
+ export * from './schema/refine.js'
6
+
4
7
  // Concrete Types
5
8
  export * from './schema/array.js'
6
9
  export * from './schema/blob.js'
@@ -14,6 +17,7 @@ export * from './schema/literal.js'
14
17
  export * from './schema/never.js'
15
18
  export * from './schema/null.js'
16
19
  export * from './schema/object.js'
20
+ export * from './schema/regexp.js'
17
21
  export * from './schema/string.js'
18
22
  export * from './schema/unknown-object.js'
19
23
  export * from './schema/unknown.js'
@@ -22,6 +26,8 @@ export * from './schema/unknown.js'
22
26
  export * from './schema/custom.js'
23
27
  export * from './schema/discriminated-union.js'
24
28
  export * from './schema/intersection.js'
29
+ export * from './schema/nullable.js'
30
+ export * from './schema/optional.js'
25
31
  export * from './schema/ref.js'
26
32
  export * from './schema/union.js'
27
33
 
@@ -16,6 +16,7 @@
16
16
  * // result is [{ value: 1, sum: 2 }, { value: 2, sum: 4 }, { value: 3, sum: 6 }]
17
17
  * ```
18
18
  */
19
+ /*@__NO_SIDE_EFFECTS__*/
19
20
  export function arrayAgg<T, O>(
20
21
  arr: readonly T[],
21
22
  cmp: (a: T, b: T) => boolean,
@@ -0,0 +1,14 @@
1
+ /*@__NO_SIDE_EFFECTS__*/
2
+ export function lazyProperty<
3
+ O extends object,
4
+ const K extends keyof O,
5
+ const V extends O[K],
6
+ >(obj: O, key: K, value: V): V {
7
+ Object.defineProperty(obj, key, {
8
+ value,
9
+ writable: false,
10
+ enumerable: false,
11
+ configurable: true,
12
+ })
13
+ return value
14
+ }
@@ -0,0 +1,92 @@
1
+ import {
2
+ ValidationOptions,
3
+ ValidationResult,
4
+ Validator,
5
+ ValidatorContext,
6
+ } from './validator.js'
7
+
8
+ export abstract class Schema<Output> implements Validator<Output> {
9
+ declare readonly ['_lex']: { output: Output }
10
+
11
+ abstract validateInContext(
12
+ input: unknown,
13
+ ctx: ValidatorContext,
14
+ ): ValidationResult<Output>
15
+
16
+ assert(input: unknown): asserts input is Output {
17
+ const result = this.safeParse(input, { allowTransform: false })
18
+ if (!result.success) throw result.error
19
+ }
20
+
21
+ matches(input: unknown): input is Output {
22
+ const result = this.safeParse(input, { allowTransform: false })
23
+ return result.success
24
+ }
25
+
26
+ ifMatches<I>(input: I): (I & Output) | undefined {
27
+ return this.matches(input) ? input : undefined
28
+ }
29
+
30
+ parse<I>(
31
+ input: I,
32
+ options: ValidationOptions & { allowTransform: false },
33
+ ): I & Output
34
+ parse(input: unknown, options?: ValidationOptions): Output
35
+ parse(input: unknown, options?: ValidationOptions): Output {
36
+ const result = this.safeParse(input, options)
37
+ if (!result.success) throw result.error
38
+ return result.value
39
+ }
40
+
41
+ safeParse<I>(
42
+ input: I,
43
+ options: ValidationOptions & { allowTransform: false },
44
+ ): ValidationResult<I & Output>
45
+ safeParse(
46
+ input: unknown,
47
+ options?: ValidationOptions,
48
+ ): ValidationResult<Output>
49
+ safeParse(
50
+ input: unknown,
51
+ options?: ValidationOptions,
52
+ ): ValidationResult<Output> {
53
+ return ValidatorContext.validate(input, this, options)
54
+ }
55
+
56
+ // @NOTE The built lexicons namespaces will export utility functions that
57
+ // allow accessing the schema's methods without the need to specify ".main."
58
+ // as part of the namespace. This way, a utility for a particular record type
59
+ // can be called like "app.bsky.feed.post.<utility>()" instead of
60
+ // "app.bsky.feed.post.main.<utility>()". Because those utilities could
61
+ // conflict with other schemas (e.g. if there is a lexicon definition at
62
+ // "#<utility>"), those exported utilities will be prefixed with "$". In order
63
+ // to be able to consistently call the utilities, when using the "main" and
64
+ // non "main" definitions, we also expose the same methods with a "$" prefix.
65
+ // Thanks to this, both of the following call will be possible:
66
+ //
67
+ // - "app.bsky.feed.post.$parse(...)" // calls a utility function created by "lex build"
68
+ // - "app.bsky.feed.defs.postView.$parse(...)" // uses the alias defined below on the schema instance
69
+
70
+ $assert(input: unknown): asserts input is Output {
71
+ return this.assert(input)
72
+ }
73
+
74
+ $matches(input: unknown): input is Output {
75
+ return this.matches(input)
76
+ }
77
+
78
+ $ifMatches<I>(input: I): (I & Output) | undefined {
79
+ return this.ifMatches(input)
80
+ }
81
+
82
+ $parse(input: unknown, options?: ValidationOptions): Output {
83
+ return this.parse(input, options)
84
+ }
85
+
86
+ $safeParse(
87
+ input: unknown,
88
+ options?: ValidationOptions,
89
+ ): ValidationResult<Output> {
90
+ return this.safeParse(input, options)
91
+ }
92
+ }
@@ -1,18 +1,20 @@
1
1
  import { ResultFailure, failureError } from '../core.js'
2
+ import { arrayAgg } from '../util/array-agg.js'
2
3
  import {
3
- ValidationIssue,
4
- aggregateIssues,
5
- stringifyIssue,
4
+ Issue,
5
+ IssueInvalidType,
6
+ IssueInvalidValue,
6
7
  } from './validation-issue.js'
7
8
 
8
9
  export class ValidationError extends Error {
9
10
  name = 'ValidationError'
10
11
 
11
- constructor(
12
- readonly issues: ValidationIssue[],
13
- options?: ErrorOptions,
14
- ) {
15
- super(issues.map(stringifyIssue).join(', '), options)
12
+ readonly issues: Issue[]
13
+
14
+ constructor(issues: Issue[], options?: ErrorOptions) {
15
+ const issuesAgg = aggregateIssues(issues)
16
+ super(issuesAgg.join(', '), options)
17
+ this.issues = issuesAgg
16
18
  }
17
19
 
18
20
  static fromFailures(
@@ -20,7 +22,7 @@ export class ValidationError extends Error {
20
22
  ): ValidationError {
21
23
  if (failures.length === 1) return failures[0].error
22
24
  const issues = failures.flatMap(extractFailureIssues)
23
- return new ValidationError(aggregateIssues(issues), {
25
+ return new ValidationError(issues, {
24
26
  // Keep the original errors as the cause chain
25
27
  cause: failures.map(failureError),
26
28
  })
@@ -30,3 +32,52 @@ export class ValidationError extends Error {
30
32
  function extractFailureIssues(result: ResultFailure<ValidationError>) {
31
33
  return result.error.issues
32
34
  }
35
+
36
+ function aggregateIssues(issues: Issue[]): Issue[] {
37
+ // Quick path for common cases
38
+ if (issues.length <= 1) return issues
39
+ if (issues.length === 2 && issues[0].code !== issues[1].code) return issues
40
+
41
+ return [
42
+ // Aggregate invalid_type with identical paths
43
+ ...arrayAgg(
44
+ issues.filter((issue) => issue instanceof IssueInvalidType),
45
+ (a, b) => comparePropertyPaths(a.path, b.path),
46
+ (issues) =>
47
+ new IssueInvalidType(
48
+ issues[0].path,
49
+ issues[0].input,
50
+ Array.from(new Set(issues.flatMap((iss) => iss.expected))),
51
+ ),
52
+ ),
53
+ // Aggregate invalid_value with identical paths
54
+ ...arrayAgg(
55
+ issues.filter((issue) => issue instanceof IssueInvalidValue),
56
+ (a, b) => comparePropertyPaths(a.path, b.path),
57
+ (issues) =>
58
+ new IssueInvalidValue(
59
+ issues[0].path,
60
+ issues[0].input,
61
+ Array.from(new Set(issues.flatMap((iss) => iss.values))),
62
+ ),
63
+ ),
64
+ // Pass through other issues
65
+ ...issues.filter(
66
+ (issue) =>
67
+ !(issue instanceof IssueInvalidType) &&
68
+ !(issue instanceof IssueInvalidValue),
69
+ ),
70
+ ]
71
+ }
72
+
73
+ /*@__NO_SIDE_EFFECTS__*/
74
+ function comparePropertyPaths(
75
+ a: readonly PropertyKey[],
76
+ b: readonly PropertyKey[],
77
+ ) {
78
+ if (a.length !== b.length) return false
79
+ for (let i = 0; i < a.length; i++) {
80
+ if (a[i] !== b[i]) return false
81
+ }
82
+ return true
83
+ }