@atproto/lex-schema 0.0.9 → 0.0.10

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 (279) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/LICENSE.txt +1 -1
  3. package/dist/core/$type.d.ts +11 -0
  4. package/dist/core/$type.d.ts.map +1 -1
  5. package/dist/core/$type.js +4 -0
  6. package/dist/core/$type.js.map +1 -1
  7. package/dist/core/schema.d.ts +31 -24
  8. package/dist/core/schema.d.ts.map +1 -1
  9. package/dist/core/schema.js +38 -8
  10. package/dist/core/schema.js.map +1 -1
  11. package/dist/core/string-format.d.ts +35 -35
  12. package/dist/core/string-format.d.ts.map +1 -1
  13. package/dist/core/string-format.js +49 -91
  14. package/dist/core/string-format.js.map +1 -1
  15. package/dist/core/validation-issue.js +1 -1
  16. package/dist/core/validation-issue.js.map +1 -1
  17. package/dist/core/validator.d.ts +53 -32
  18. package/dist/core/validator.d.ts.map +1 -1
  19. package/dist/core/validator.js +18 -22
  20. package/dist/core/validator.js.map +1 -1
  21. package/dist/external.d.ts +0 -85
  22. package/dist/external.d.ts.map +1 -1
  23. package/dist/external.js +0 -164
  24. package/dist/external.js.map +1 -1
  25. package/dist/helpers.d.ts +10 -5
  26. package/dist/helpers.d.ts.map +1 -1
  27. package/dist/helpers.js +3 -3
  28. package/dist/helpers.js.map +1 -1
  29. package/dist/schema/array.d.ts +9 -5
  30. package/dist/schema/array.d.ts.map +1 -1
  31. package/dist/schema/array.js +14 -5
  32. package/dist/schema/array.js.map +1 -1
  33. package/dist/schema/blob.d.ts +9 -7
  34. package/dist/schema/blob.d.ts.map +1 -1
  35. package/dist/schema/blob.js +9 -5
  36. package/dist/schema/blob.js.map +1 -1
  37. package/dist/schema/boolean.d.ts +3 -7
  38. package/dist/schema/boolean.d.ts.map +1 -1
  39. package/dist/schema/boolean.js +6 -7
  40. package/dist/schema/boolean.js.map +1 -1
  41. package/dist/schema/bytes.d.ts +3 -2
  42. package/dist/schema/bytes.d.ts.map +1 -1
  43. package/dist/schema/bytes.js +7 -3
  44. package/dist/schema/bytes.js.map +1 -1
  45. package/dist/schema/cid.d.ts +7 -7
  46. package/dist/schema/cid.d.ts.map +1 -1
  47. package/dist/schema/cid.js +5 -1
  48. package/dist/schema/cid.js.map +1 -1
  49. package/dist/schema/custom.d.ts +6 -5
  50. package/dist/schema/custom.d.ts.map +1 -1
  51. package/dist/schema/custom.js +10 -4
  52. package/dist/schema/custom.js.map +1 -1
  53. package/dist/schema/dict.d.ts +8 -8
  54. package/dist/schema/dict.d.ts.map +1 -1
  55. package/dist/schema/dict.js +11 -2
  56. package/dist/schema/dict.js.map +1 -1
  57. package/dist/schema/discriminated-union.d.ts +21 -14
  58. package/dist/schema/discriminated-union.d.ts.map +1 -1
  59. package/dist/schema/discriminated-union.js +7 -0
  60. package/dist/schema/discriminated-union.js.map +1 -1
  61. package/dist/schema/enum.d.ts +7 -9
  62. package/dist/schema/enum.d.ts.map +1 -1
  63. package/dist/schema/enum.js +8 -4
  64. package/dist/schema/enum.js.map +1 -1
  65. package/dist/schema/integer.d.ts +5 -5
  66. package/dist/schema/integer.d.ts.map +1 -1
  67. package/dist/schema/integer.js +9 -5
  68. package/dist/schema/integer.js.map +1 -1
  69. package/dist/schema/intersection.d.ts +4 -4
  70. package/dist/schema/intersection.d.ts.map +1 -1
  71. package/dist/schema/intersection.js +5 -0
  72. package/dist/schema/intersection.js.map +1 -1
  73. package/dist/schema/literal.d.ts +6 -9
  74. package/dist/schema/literal.d.ts.map +1 -1
  75. package/dist/schema/literal.js +7 -4
  76. package/dist/schema/literal.js.map +1 -1
  77. package/dist/schema/never.d.ts +3 -2
  78. package/dist/schema/never.d.ts.map +1 -1
  79. package/dist/schema/never.js +5 -1
  80. package/dist/schema/never.js.map +1 -1
  81. package/dist/schema/null.d.ts +4 -3
  82. package/dist/schema/null.d.ts.map +1 -1
  83. package/dist/schema/null.js +6 -4
  84. package/dist/schema/null.js.map +1 -1
  85. package/dist/schema/nullable.d.ts +6 -5
  86. package/dist/schema/nullable.d.ts.map +1 -1
  87. package/dist/schema/nullable.js +9 -5
  88. package/dist/schema/nullable.js.map +1 -1
  89. package/dist/schema/object.d.ts +10 -8
  90. package/dist/schema/object.d.ts.map +1 -1
  91. package/dist/schema/object.js +11 -3
  92. package/dist/schema/object.js.map +1 -1
  93. package/dist/schema/optional.d.ts +7 -5
  94. package/dist/schema/optional.d.ts.map +1 -1
  95. package/dist/schema/optional.js +14 -6
  96. package/dist/schema/optional.js.map +1 -1
  97. package/dist/schema/params.d.ts +24 -13
  98. package/dist/schema/params.d.ts.map +1 -1
  99. package/dist/schema/params.js +47 -25
  100. package/dist/schema/params.js.map +1 -1
  101. package/dist/schema/payload.d.ts +12 -9
  102. package/dist/schema/payload.d.ts.map +1 -1
  103. package/dist/schema/payload.js +11 -0
  104. package/dist/schema/payload.js.map +1 -1
  105. package/dist/schema/permission-set.d.ts +1 -0
  106. package/dist/schema/permission-set.d.ts.map +1 -1
  107. package/dist/schema/permission-set.js +5 -0
  108. package/dist/schema/permission-set.js.map +1 -1
  109. package/dist/schema/permission.d.ts +6 -5
  110. package/dist/schema/permission.d.ts.map +1 -1
  111. package/dist/schema/permission.js +5 -0
  112. package/dist/schema/permission.js.map +1 -1
  113. package/dist/schema/procedure.d.ts +2 -1
  114. package/dist/schema/procedure.d.ts.map +1 -1
  115. package/dist/schema/procedure.js +5 -0
  116. package/dist/schema/procedure.js.map +1 -1
  117. package/dist/schema/query.d.ts +2 -1
  118. package/dist/schema/query.d.ts.map +1 -1
  119. package/dist/schema/query.js +5 -0
  120. package/dist/schema/query.js.map +1 -1
  121. package/dist/schema/record.d.ts +48 -30
  122. package/dist/schema/record.d.ts.map +1 -1
  123. package/dist/schema/record.js +12 -9
  124. package/dist/schema/record.js.map +1 -1
  125. package/dist/schema/ref.d.ts +9 -6
  126. package/dist/schema/ref.d.ts.map +1 -1
  127. package/dist/schema/ref.js +9 -16
  128. package/dist/schema/ref.js.map +1 -1
  129. package/dist/schema/refine.d.ts +4 -4
  130. package/dist/schema/refine.d.ts.map +1 -1
  131. package/dist/schema/refine.js.map +1 -1
  132. package/dist/schema/regexp.d.ts +4 -3
  133. package/dist/schema/regexp.d.ts.map +1 -1
  134. package/dist/schema/regexp.js +5 -0
  135. package/dist/schema/regexp.js.map +1 -1
  136. package/dist/schema/string.d.ts +7 -8
  137. package/dist/schema/string.d.ts.map +1 -1
  138. package/dist/schema/string.js +13 -19
  139. package/dist/schema/string.js.map +1 -1
  140. package/dist/schema/subscription.d.ts +2 -1
  141. package/dist/schema/subscription.d.ts.map +1 -1
  142. package/dist/schema/subscription.js +5 -0
  143. package/dist/schema/subscription.js.map +1 -1
  144. package/dist/schema/token.d.ts +6 -5
  145. package/dist/schema/token.d.ts.map +1 -1
  146. package/dist/schema/token.js +5 -0
  147. package/dist/schema/token.js.map +1 -1
  148. package/dist/schema/typed-object.d.ts +43 -26
  149. package/dist/schema/typed-object.d.ts.map +1 -1
  150. package/dist/schema/typed-object.js +6 -3
  151. package/dist/schema/typed-object.js.map +1 -1
  152. package/dist/schema/typed-ref.d.ts +16 -25
  153. package/dist/schema/typed-ref.d.ts.map +1 -1
  154. package/dist/schema/typed-ref.js +7 -17
  155. package/dist/schema/typed-ref.js.map +1 -1
  156. package/dist/schema/typed-union.d.ts +9 -21
  157. package/dist/schema/typed-union.d.ts.map +1 -1
  158. package/dist/schema/typed-union.js +15 -11
  159. package/dist/schema/typed-union.js.map +1 -1
  160. package/dist/schema/union.d.ts +6 -6
  161. package/dist/schema/union.d.ts.map +1 -1
  162. package/dist/schema/union.js +7 -5
  163. package/dist/schema/union.js.map +1 -1
  164. package/dist/schema/unknown-object.d.ts +5 -4
  165. package/dist/schema/unknown-object.d.ts.map +1 -1
  166. package/dist/schema/unknown-object.js +5 -1
  167. package/dist/schema/unknown-object.js.map +1 -1
  168. package/dist/schema/unknown.d.ts +3 -2
  169. package/dist/schema/unknown.d.ts.map +1 -1
  170. package/dist/schema/unknown.js +5 -1
  171. package/dist/schema/unknown.js.map +1 -1
  172. package/dist/schema/with-default.d.ts +9 -0
  173. package/dist/schema/with-default.d.ts.map +1 -0
  174. package/dist/schema/with-default.js +27 -0
  175. package/dist/schema/with-default.js.map +1 -0
  176. package/dist/schema.d.ts +2 -2
  177. package/dist/schema.d.ts.map +1 -1
  178. package/dist/schema.js +2 -4
  179. package/dist/schema.js.map +1 -1
  180. package/dist/util/assertion-util.d.ts +0 -6
  181. package/dist/util/assertion-util.d.ts.map +1 -1
  182. package/dist/util/assertion-util.js +0 -28
  183. package/dist/util/assertion-util.js.map +1 -1
  184. package/dist/util/memoize.d.ts +2 -2
  185. package/dist/util/memoize.d.ts.map +1 -1
  186. package/dist/util/memoize.js +23 -39
  187. package/dist/util/memoize.js.map +1 -1
  188. package/package.json +3 -3
  189. package/src/core/$type.test.ts +20 -0
  190. package/src/core/$type.ts +30 -0
  191. package/src/core/schema.ts +86 -38
  192. package/src/core/string-format.ts +119 -158
  193. package/src/core/validation-issue.ts +1 -1
  194. package/src/core/validator.ts +93 -53
  195. package/src/external.ts +0 -404
  196. package/src/helpers.test.ts +22 -21
  197. package/src/helpers.ts +14 -14
  198. package/src/schema/array.test.ts +38 -40
  199. package/src/schema/array.ts +35 -13
  200. package/src/schema/blob.test.ts +21 -21
  201. package/src/schema/blob.ts +19 -17
  202. package/src/schema/boolean.test.ts +9 -8
  203. package/src/schema/boolean.ts +7 -13
  204. package/src/schema/bytes.test.ts +13 -13
  205. package/src/schema/bytes.ts +13 -8
  206. package/src/schema/cid.test.ts +3 -3
  207. package/src/schema/cid.ts +13 -12
  208. package/src/schema/custom.test.ts +26 -26
  209. package/src/schema/custom.ts +20 -13
  210. package/src/schema/dict.test.ts +21 -39
  211. package/src/schema/dict.ts +28 -19
  212. package/src/schema/discriminated-union.test.ts +128 -128
  213. package/src/schema/discriminated-union.ts +45 -26
  214. package/src/schema/enum.test.ts +17 -16
  215. package/src/schema/enum.ts +16 -16
  216. package/src/schema/integer.test.ts +22 -21
  217. package/src/schema/integer.ts +12 -9
  218. package/src/schema/intersection.test.ts +10 -10
  219. package/src/schema/intersection.ts +17 -14
  220. package/src/schema/literal.test.ts +35 -34
  221. package/src/schema/literal.ts +12 -15
  222. package/src/schema/never.test.ts +5 -5
  223. package/src/schema/never.ts +7 -2
  224. package/src/schema/null.test.ts +3 -3
  225. package/src/schema/null.ts +9 -9
  226. package/src/schema/nullable.test.ts +31 -42
  227. package/src/schema/nullable.ts +17 -9
  228. package/src/schema/object.test.ts +10 -12
  229. package/src/schema/object.ts +27 -18
  230. package/src/schema/optional.test.ts +21 -28
  231. package/src/schema/optional.ts +27 -10
  232. package/src/schema/params.test.ts +471 -47
  233. package/src/schema/params.ts +72 -38
  234. package/src/schema/payload.test.ts +150 -156
  235. package/src/schema/payload.ts +35 -19
  236. package/src/schema/permission-set.test.ts +206 -273
  237. package/src/schema/permission-set.ts +8 -0
  238. package/src/schema/permission.test.ts +177 -177
  239. package/src/schema/permission.ts +13 -5
  240. package/src/schema/procedure.test.ts +183 -242
  241. package/src/schema/procedure.ts +18 -5
  242. package/src/schema/query.test.ts +186 -200
  243. package/src/schema/query.ts +16 -4
  244. package/src/schema/record.test.ts +121 -101
  245. package/src/schema/record.ts +74 -40
  246. package/src/schema/ref.test.ts +101 -118
  247. package/src/schema/ref.ts +33 -28
  248. package/src/schema/refine.test.ts +28 -28
  249. package/src/schema/refine.ts +23 -20
  250. package/src/schema/regexp.test.ts +29 -33
  251. package/src/schema/regexp.ts +11 -7
  252. package/src/schema/string.test.ts +35 -35
  253. package/src/schema/string.ts +24 -33
  254. package/src/schema/subscription.test.ts +259 -387
  255. package/src/schema/subscription.ts +16 -4
  256. package/src/schema/token.test.ts +47 -324
  257. package/src/schema/token.ts +14 -7
  258. package/src/schema/typed-object.test.ts +98 -81
  259. package/src/schema/typed-object.ts +68 -33
  260. package/src/schema/typed-ref.test.ts +206 -234
  261. package/src/schema/typed-ref.ts +40 -42
  262. package/src/schema/typed-union.test.ts +40 -64
  263. package/src/schema/typed-union.ts +36 -58
  264. package/src/schema/union.test.ts +17 -27
  265. package/src/schema/union.ts +20 -16
  266. package/src/schema/unknown-object.test.ts +8 -8
  267. package/src/schema/unknown-object.ts +9 -7
  268. package/src/schema/unknown.test.ts +4 -4
  269. package/src/schema/unknown.ts +7 -5
  270. package/src/schema/with-default.ts +35 -0
  271. package/src/schema.ts +2 -6
  272. package/src/util/assertion-util.ts +0 -39
  273. package/src/util/memoize.ts +26 -46
  274. package/dist/schema/_parameters.d.ts +0 -17
  275. package/dist/schema/_parameters.d.ts.map +0 -1
  276. package/dist/schema/_parameters.js +0 -20
  277. package/dist/schema/_parameters.js.map +0 -1
  278. package/src/schema/_parameters.test.ts +0 -417
  279. package/src/schema/_parameters.ts +0 -26
@@ -1,11 +1,11 @@
1
1
  import { describe, expect, it, vi } from 'vitest'
2
2
  import { IssueCustom } from '../core.js'
3
- import { CustomSchema } from './custom.js'
3
+ import { custom } from './custom.js'
4
4
 
5
5
  describe('CustomSchema', () => {
6
6
  describe('basic validation', () => {
7
7
  it('validates input that passes custom assertion', () => {
8
- const schema = new CustomSchema(
8
+ const schema = custom(
9
9
  (input): input is string => typeof input === 'string',
10
10
  'Must be a string',
11
11
  )
@@ -17,7 +17,7 @@ describe('CustomSchema', () => {
17
17
  })
18
18
 
19
19
  it('rejects input that fails custom assertion', () => {
20
- const schema = new CustomSchema(
20
+ const schema = custom(
21
21
  (input): input is string => typeof input === 'string',
22
22
  'Must be a string',
23
23
  )
@@ -26,7 +26,7 @@ describe('CustomSchema', () => {
26
26
  })
27
27
 
28
28
  it('includes custom message in error', () => {
29
- const schema = new CustomSchema(
29
+ const schema = custom(
30
30
  (input): input is string => typeof input === 'string',
31
31
  'Custom error message',
32
32
  )
@@ -45,7 +45,7 @@ describe('CustomSchema', () => {
45
45
  age: number
46
46
  }
47
47
 
48
- const schema = new CustomSchema((input): input is User => {
48
+ const schema = custom((input): input is User => {
49
49
  return (
50
50
  typeof input === 'object' &&
51
51
  input !== null &&
@@ -65,7 +65,7 @@ describe('CustomSchema', () => {
65
65
  age: number
66
66
  }
67
67
 
68
- const schema = new CustomSchema((input): input is User => {
68
+ const schema = custom((input): input is User => {
69
69
  return (
70
70
  typeof input === 'object' &&
71
71
  input !== null &&
@@ -80,7 +80,7 @@ describe('CustomSchema', () => {
80
80
  })
81
81
 
82
82
  it('validates arrays with specific element types', () => {
83
- const schema = new CustomSchema((input): input is number[] => {
83
+ const schema = custom((input): input is number[] => {
84
84
  return (
85
85
  Array.isArray(input) &&
86
86
  input.every((item) => typeof item === 'number')
@@ -92,7 +92,7 @@ describe('CustomSchema', () => {
92
92
  })
93
93
 
94
94
  it('rejects arrays with mixed types', () => {
95
- const schema = new CustomSchema((input): input is number[] => {
95
+ const schema = custom((input): input is number[] => {
96
96
  return (
97
97
  Array.isArray(input) &&
98
98
  input.every((item) => typeof item === 'number')
@@ -106,7 +106,7 @@ describe('CustomSchema', () => {
106
106
 
107
107
  describe('custom context usage', () => {
108
108
  it('can add custom issues through context', () => {
109
- const schema = new CustomSchema((input, ctx): input is string => {
109
+ const schema = custom((input, ctx): input is string => {
110
110
  if (typeof input !== 'string') {
111
111
  ctx.addIssue({
112
112
  code: 'invalid_type',
@@ -125,7 +125,7 @@ describe('CustomSchema', () => {
125
125
 
126
126
  it('accesses path from context', () => {
127
127
  let capturedPath: any[] = []
128
- const schema = new CustomSchema((input, ctx): input is string => {
128
+ const schema = custom((input, ctx): input is string => {
129
129
  capturedPath = [...ctx.path]
130
130
  return typeof input === 'string'
131
131
  }, 'Must be a string')
@@ -135,7 +135,7 @@ describe('CustomSchema', () => {
135
135
  })
136
136
 
137
137
  it('validates with custom path', () => {
138
- const schema = new CustomSchema(
138
+ const schema = custom(
139
139
  (input): input is string => typeof input === 'string',
140
140
  'Must be a string',
141
141
  'customField',
@@ -149,7 +149,7 @@ describe('CustomSchema', () => {
149
149
  })
150
150
 
151
151
  it('validates with array of paths', () => {
152
- const schema = new CustomSchema(
152
+ const schema = custom(
153
153
  (input): input is string => typeof input === 'string',
154
154
  'Must be a string',
155
155
  ['nested', 'field'],
@@ -166,7 +166,7 @@ describe('CustomSchema', () => {
166
166
 
167
167
  describe('business logic validation', () => {
168
168
  it('validates email format', () => {
169
- const schema = new CustomSchema((input): input is string => {
169
+ const schema = custom((input): input is string => {
170
170
  return (
171
171
  typeof input === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input)
172
172
  )
@@ -180,7 +180,7 @@ describe('CustomSchema', () => {
180
180
  })
181
181
 
182
182
  it('validates password strength', () => {
183
- const schema = new CustomSchema((input): input is string => {
183
+ const schema = custom((input): input is string => {
184
184
  if (typeof input !== 'string') return false
185
185
  return (
186
186
  input.length >= 8 &&
@@ -198,7 +198,7 @@ describe('CustomSchema', () => {
198
198
  })
199
199
 
200
200
  it('validates age range', () => {
201
- const schema = new CustomSchema((input): input is number => {
201
+ const schema = custom((input): input is number => {
202
202
  return typeof input === 'number' && input >= 18 && input <= 120
203
203
  }, 'Age must be between 18 and 120')
204
204
 
@@ -213,7 +213,7 @@ describe('CustomSchema', () => {
213
213
  })
214
214
 
215
215
  it('validates positive numbers', () => {
216
- const schema = new CustomSchema((input): input is number => {
216
+ const schema = custom((input): input is number => {
217
217
  return typeof input === 'number' && input > 0
218
218
  }, 'Must be a positive number')
219
219
 
@@ -230,7 +230,7 @@ describe('CustomSchema', () => {
230
230
 
231
231
  describe('edge cases', () => {
232
232
  it('handles null input', () => {
233
- const schema = new CustomSchema(
233
+ const schema = custom(
234
234
  (input): input is null => input === null,
235
235
  'Must be null',
236
236
  )
@@ -243,7 +243,7 @@ describe('CustomSchema', () => {
243
243
  })
244
244
 
245
245
  it('handles undefined input', () => {
246
- const schema = new CustomSchema(
246
+ const schema = custom(
247
247
  (input): input is undefined => input === undefined,
248
248
  'Must be undefined',
249
249
  )
@@ -256,7 +256,7 @@ describe('CustomSchema', () => {
256
256
  })
257
257
 
258
258
  it('handles empty string', () => {
259
- const schema = new CustomSchema(
259
+ const schema = custom(
260
260
  (input): input is string =>
261
261
  typeof input === 'string' && input.length > 0,
262
262
  'Must be a non-empty string',
@@ -270,7 +270,7 @@ describe('CustomSchema', () => {
270
270
  })
271
271
 
272
272
  it('handles empty array', () => {
273
- const schema = new CustomSchema(
273
+ const schema = custom(
274
274
  (input): input is any[] => Array.isArray(input) && input.length > 0,
275
275
  'Must be a non-empty array',
276
276
  )
@@ -288,7 +288,7 @@ describe('CustomSchema', () => {
288
288
  metadata: { count: number }
289
289
  }
290
290
 
291
- const schema = new CustomSchema((input): input is ComplexType => {
291
+ const schema = custom((input): input is ComplexType => {
292
292
  if (typeof input !== 'object' || input === null) return false
293
293
  const obj = input as any
294
294
  return (
@@ -325,7 +325,7 @@ describe('CustomSchema', () => {
325
325
  it('correctly narrows union types', () => {
326
326
  type StringOrNumber = string | number
327
327
 
328
- const schema = new CustomSchema(
328
+ const schema = custom(
329
329
  (input): input is string => typeof input === 'string',
330
330
  'Must be a string',
331
331
  )
@@ -347,7 +347,7 @@ describe('CustomSchema', () => {
347
347
  | { type: 'circle'; radius: number }
348
348
  | { type: 'rectangle'; width: number; height: number }
349
349
 
350
- const circleSchema = new CustomSchema((input): input is Shape => {
350
+ const circleSchema = custom((input): input is Shape => {
351
351
  return (
352
352
  typeof input === 'object' &&
353
353
  input !== null &&
@@ -380,13 +380,13 @@ describe('CustomSchema', () => {
380
380
  return typeof input === 'string'
381
381
  })
382
382
 
383
- new CustomSchema(assertion as any, 'Must be a string').safeParse('test')
383
+ custom(assertion as any, 'Must be a string').safeParse('test')
384
384
 
385
385
  expect(assertion).toHaveBeenCalledTimes(1)
386
386
  })
387
387
 
388
388
  it('provides addIssue method in context', () => {
389
- const schema = new CustomSchema((input, ctx): input is string => {
389
+ const schema = custom((input, ctx): input is string => {
390
390
  ctx.addIssue(new IssueCustom(ctx.path, input, 'This is a custom issue'))
391
391
  return false
392
392
  }, 'Must be a string')
@@ -403,7 +403,7 @@ describe('CustomSchema', () => {
403
403
  })
404
404
 
405
405
  it('provides path array in context', () => {
406
- const schema = new CustomSchema((input, ctx): input is string => {
406
+ const schema = custom((input, ctx): input is string => {
407
407
  expect(Array.isArray(ctx.path)).toBe(true)
408
408
  return typeof input === 'string'
409
409
  }, 'Must be a string')
@@ -3,8 +3,7 @@ import {
3
3
  IssueCustom,
4
4
  PropertyKey,
5
5
  Schema,
6
- ValidationResult,
7
- ValidatorContext,
6
+ ValidationContext,
8
7
  } from '../core.js'
9
8
 
10
9
  export type CustomAssertionContext = {
@@ -12,27 +11,35 @@ export type CustomAssertionContext = {
12
11
  addIssue(issue: Issue): void
13
12
  }
14
13
 
15
- export type CustomAssertion<T = any> = (
14
+ export type CustomAssertion<TValue> = (
16
15
  this: null,
17
16
  input: unknown,
18
17
  ctx: CustomAssertionContext,
19
- ) => input is T
18
+ ) => input is TValue
20
19
 
21
- export class CustomSchema<T = unknown> extends Schema<T> {
20
+ export class CustomSchema<const TValue = unknown> extends Schema<TValue> {
22
21
  constructor(
23
- private readonly assertion: CustomAssertion<T>,
22
+ private readonly assertion: CustomAssertion<TValue>,
24
23
  private readonly message: string,
25
24
  private readonly path?: PropertyKey | readonly PropertyKey[],
26
25
  ) {
27
26
  super()
28
27
  }
29
28
 
30
- validateInContext(
31
- input: unknown,
32
- ctx: ValidatorContext,
33
- ): ValidationResult<T> {
34
- if (this.assertion.call(null, input, ctx)) return ctx.success(input as T)
35
- const path = ctx.concatPath(this.path)
36
- return ctx.issue(new IssueCustom(path, input, this.message))
29
+ validateInContext(input: unknown, ctx: ValidationContext) {
30
+ if (!this.assertion.call(null, input, ctx)) {
31
+ const path = ctx.concatPath(this.path)
32
+ return ctx.issue(new IssueCustom(path, input, this.message))
33
+ }
34
+ return ctx.success(input as TValue)
37
35
  }
38
36
  }
37
+
38
+ /*@__NO_SIDE_EFFECTS__*/
39
+ export function custom<TValue>(
40
+ assertion: CustomAssertion<TValue>,
41
+ message: string,
42
+ path?: PropertyKey | readonly PropertyKey[],
43
+ ) {
44
+ return new CustomSchema<TValue>(assertion, message, path)
45
+ }
@@ -1,12 +1,12 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { BooleanSchema } from './boolean.js'
3
- import { DictSchema } from './dict.js'
4
- import { EnumSchema } from './enum.js'
5
- import { IntegerSchema } from './integer.js'
6
- import { StringSchema } from './string.js'
2
+ import { boolean } from './boolean.js'
3
+ import { dict } from './dict.js'
4
+ import { enumSchema } from './enum.js'
5
+ import { integer } from './integer.js'
6
+ import { string } from './string.js'
7
7
 
8
8
  describe('DictSchema', () => {
9
- const schema = new DictSchema(new StringSchema({}), new IntegerSchema({}))
9
+ const schema = dict(string(), integer())
10
10
 
11
11
  it('validates plain objects with valid keys and values', () => {
12
12
  const result = schema.safeParse({
@@ -59,10 +59,7 @@ describe('DictSchema', () => {
59
59
  })
60
60
 
61
61
  it('validates with enum key schema', () => {
62
- const enumKeySchema = new DictSchema(
63
- new EnumSchema(['tag1', 'tag2', 'tag3']),
64
- new BooleanSchema({}),
65
- )
62
+ const enumKeySchema = dict(enumSchema(['tag1', 'tag2', 'tag3']), boolean())
66
63
 
67
64
  const result = enumKeySchema.safeParse({
68
65
  tag1: true,
@@ -73,10 +70,7 @@ describe('DictSchema', () => {
73
70
  })
74
71
 
75
72
  it('rejects invalid keys with enum key schema', () => {
76
- const enumKeySchema = new DictSchema(
77
- new EnumSchema(['tag1', 'tag2']),
78
- new BooleanSchema({}),
79
- )
73
+ const enumKeySchema = dict(enumSchema(['tag1', 'tag2']), boolean())
80
74
 
81
75
  const result = enumKeySchema.safeParse({
82
76
  tag1: true,
@@ -86,10 +80,7 @@ describe('DictSchema', () => {
86
80
  })
87
81
 
88
82
  it('validates nested dict schemas', () => {
89
- const nestedSchema = new DictSchema(
90
- new StringSchema({}),
91
- new DictSchema(new StringSchema({}), new IntegerSchema({})),
92
- )
83
+ const nestedSchema = dict(string(), dict(string(), integer()))
93
84
 
94
85
  const result = nestedSchema.safeParse({
95
86
  group1: { count: 10, total: 20 },
@@ -99,9 +90,9 @@ describe('DictSchema', () => {
99
90
  })
100
91
 
101
92
  it('validates with string key schema constraints', () => {
102
- const constrainedKeySchema = new DictSchema(
103
- new StringSchema({ minLength: 3, maxLength: 10 }),
104
- new IntegerSchema({}),
93
+ const constrainedKeySchema = dict(
94
+ string({ minLength: 3, maxLength: 10 }),
95
+ integer(),
105
96
  )
106
97
 
107
98
  const result = constrainedKeySchema.safeParse({
@@ -112,10 +103,7 @@ describe('DictSchema', () => {
112
103
  })
113
104
 
114
105
  it('rejects keys that do not meet key schema constraints', () => {
115
- const constrainedKeySchema = new DictSchema(
116
- new StringSchema({ minLength: 3 }),
117
- new IntegerSchema({}),
118
- )
106
+ const constrainedKeySchema = dict(string({ minLength: 3 }), integer())
119
107
 
120
108
  const result = constrainedKeySchema.safeParse({
121
109
  ab: 1, // too short
@@ -124,9 +112,9 @@ describe('DictSchema', () => {
124
112
  })
125
113
 
126
114
  it('validates with value schema constraints', () => {
127
- const constrainedValueSchema = new DictSchema(
128
- new StringSchema({}),
129
- new IntegerSchema({ minimum: 0, maximum: 100 }),
115
+ const constrainedValueSchema = dict(
116
+ string(),
117
+ integer({ minimum: 0, maximum: 100 }),
130
118
  )
131
119
 
132
120
  const result = constrainedValueSchema.safeParse({
@@ -137,9 +125,9 @@ describe('DictSchema', () => {
137
125
  })
138
126
 
139
127
  it('rejects values that do not meet value schema constraints', () => {
140
- const constrainedValueSchema = new DictSchema(
141
- new StringSchema({}),
142
- new IntegerSchema({ minimum: 0, maximum: 100 }),
128
+ const constrainedValueSchema = dict(
129
+ string(),
130
+ integer({ minimum: 0, maximum: 100 }),
143
131
  )
144
132
 
145
133
  const result = constrainedValueSchema.safeParse({
@@ -149,10 +137,7 @@ describe('DictSchema', () => {
149
137
  })
150
138
 
151
139
  it('validates dict with string values', () => {
152
- const stringValueSchema = new DictSchema(
153
- new StringSchema({}),
154
- new StringSchema({}),
155
- )
140
+ const stringValueSchema = dict(string(), string())
156
141
 
157
142
  const result = stringValueSchema.safeParse({
158
143
  name: 'Alice',
@@ -163,10 +148,7 @@ describe('DictSchema', () => {
163
148
  })
164
149
 
165
150
  it('validates dict with boolean values', () => {
166
- const booleanValueSchema = new DictSchema(
167
- new StringSchema({}),
168
- new BooleanSchema({}),
169
- )
151
+ const booleanValueSchema = dict(string(), boolean())
170
152
 
171
153
  const result = booleanValueSchema.safeParse({
172
154
  enabled: true,
@@ -1,38 +1,36 @@
1
1
  import { isPlainObject } from '@atproto/lex-data'
2
2
  import {
3
- Infer,
3
+ InferInput,
4
+ InferOutput,
4
5
  Schema,
5
- ValidationResult,
6
+ ValidationContext,
6
7
  Validator,
7
- ValidatorContext,
8
8
  } from '../core.js'
9
9
 
10
- export type DictSchemaOutput<
11
- KeySchema extends Validator<string>,
12
- ValueSchema extends Validator,
13
- > = Record<Infer<KeySchema>, Infer<ValueSchema>>
14
-
15
10
  /**
16
11
  * @note There is no dictionary in Lexicon schemas. This is a custom extension
17
12
  * to allow map-like objects when using the lex library programmatically (i.e.
18
13
  * not code generated from a lexicon schema).
19
14
  */
20
15
  export class DictSchema<
21
- const KeySchema extends Validator<string> = any,
22
- const ValueSchema extends Validator = any,
23
- > extends Schema<DictSchemaOutput<KeySchema, ValueSchema>> {
16
+ const TKey extends Validator<string> = any,
17
+ const TValue extends Validator = any,
18
+ > extends Schema<
19
+ Record<InferInput<TKey>, InferInput<TValue>>,
20
+ Record<InferInput<TKey>, InferOutput<TValue>>
21
+ > {
24
22
  constructor(
25
- readonly keySchema: KeySchema,
26
- readonly valueSchema: ValueSchema,
23
+ readonly keySchema: TKey,
24
+ readonly valueSchema: TValue,
27
25
  ) {
28
26
  super()
29
27
  }
30
28
 
31
29
  validateInContext(
32
30
  input: unknown,
33
- ctx: ValidatorContext,
31
+ ctx: ValidationContext,
34
32
  options?: { ignoredKeys?: { has(k: string): boolean } },
35
- ): ValidationResult<DictSchemaOutput<KeySchema, ValueSchema>> {
33
+ ) {
36
34
  if (!isPlainObject(input)) {
37
35
  return ctx.issueInvalidType(input, 'dict')
38
36
  }
@@ -54,14 +52,25 @@ export class DictSchema<
54
52
  const valueResult = ctx.validateChild(input, key, this.valueSchema)
55
53
  if (!valueResult.success) return valueResult
56
54
 
57
- if (valueResult.value !== input[key]) {
55
+ if (!Object.is(valueResult.value, input[key])) {
56
+ if (ctx.options.mode === 'validate') {
57
+ // In "validate" mode, we can't modify the input, so we issue an error
58
+ return ctx.issueInvalidPropertyValue(input, key, [valueResult.value])
59
+ }
60
+
58
61
  copy ??= { ...input }
59
62
  copy[key] = valueResult.value
60
63
  }
61
64
  }
62
65
 
63
- return ctx.success(
64
- (copy ?? input) as DictSchemaOutput<KeySchema, ValueSchema>,
65
- )
66
+ return ctx.success(copy ?? input)
66
67
  }
67
68
  }
69
+
70
+ /*@__NO_SIDE_EFFECTS__*/
71
+ export function dict<
72
+ const TKey extends Validator<string>,
73
+ const TValue extends Validator,
74
+ >(key: TKey, value: TValue) {
75
+ return new DictSchema<TKey, TValue>(key, value)
76
+ }