@atproto/lex-schema 0.0.9 → 0.0.11

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 (280) hide show
  1. package/CHANGELOG.md +41 -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-error.d.ts +1 -1
  16. package/dist/core/validation-issue.js +1 -1
  17. package/dist/core/validation-issue.js.map +1 -1
  18. package/dist/core/validator.d.ts +53 -32
  19. package/dist/core/validator.d.ts.map +1 -1
  20. package/dist/core/validator.js +18 -22
  21. package/dist/core/validator.js.map +1 -1
  22. package/dist/external.d.ts +0 -85
  23. package/dist/external.d.ts.map +1 -1
  24. package/dist/external.js +0 -164
  25. package/dist/external.js.map +1 -1
  26. package/dist/helpers.d.ts +13 -5
  27. package/dist/helpers.d.ts.map +1 -1
  28. package/dist/helpers.js +4 -4
  29. package/dist/helpers.js.map +1 -1
  30. package/dist/schema/array.d.ts +9 -5
  31. package/dist/schema/array.d.ts.map +1 -1
  32. package/dist/schema/array.js +14 -5
  33. package/dist/schema/array.js.map +1 -1
  34. package/dist/schema/blob.d.ts +9 -7
  35. package/dist/schema/blob.d.ts.map +1 -1
  36. package/dist/schema/blob.js +9 -5
  37. package/dist/schema/blob.js.map +1 -1
  38. package/dist/schema/boolean.d.ts +3 -7
  39. package/dist/schema/boolean.d.ts.map +1 -1
  40. package/dist/schema/boolean.js +6 -7
  41. package/dist/schema/boolean.js.map +1 -1
  42. package/dist/schema/bytes.d.ts +3 -2
  43. package/dist/schema/bytes.d.ts.map +1 -1
  44. package/dist/schema/bytes.js +7 -3
  45. package/dist/schema/bytes.js.map +1 -1
  46. package/dist/schema/cid.d.ts +7 -7
  47. package/dist/schema/cid.d.ts.map +1 -1
  48. package/dist/schema/cid.js +5 -1
  49. package/dist/schema/cid.js.map +1 -1
  50. package/dist/schema/custom.d.ts +6 -5
  51. package/dist/schema/custom.d.ts.map +1 -1
  52. package/dist/schema/custom.js +10 -4
  53. package/dist/schema/custom.js.map +1 -1
  54. package/dist/schema/dict.d.ts +8 -8
  55. package/dist/schema/dict.d.ts.map +1 -1
  56. package/dist/schema/dict.js +11 -2
  57. package/dist/schema/dict.js.map +1 -1
  58. package/dist/schema/discriminated-union.d.ts +21 -14
  59. package/dist/schema/discriminated-union.d.ts.map +1 -1
  60. package/dist/schema/discriminated-union.js +7 -0
  61. package/dist/schema/discriminated-union.js.map +1 -1
  62. package/dist/schema/enum.d.ts +7 -9
  63. package/dist/schema/enum.d.ts.map +1 -1
  64. package/dist/schema/enum.js +8 -4
  65. package/dist/schema/enum.js.map +1 -1
  66. package/dist/schema/integer.d.ts +5 -5
  67. package/dist/schema/integer.d.ts.map +1 -1
  68. package/dist/schema/integer.js +9 -5
  69. package/dist/schema/integer.js.map +1 -1
  70. package/dist/schema/intersection.d.ts +4 -4
  71. package/dist/schema/intersection.d.ts.map +1 -1
  72. package/dist/schema/intersection.js +5 -0
  73. package/dist/schema/intersection.js.map +1 -1
  74. package/dist/schema/literal.d.ts +6 -9
  75. package/dist/schema/literal.d.ts.map +1 -1
  76. package/dist/schema/literal.js +7 -4
  77. package/dist/schema/literal.js.map +1 -1
  78. package/dist/schema/never.d.ts +3 -2
  79. package/dist/schema/never.d.ts.map +1 -1
  80. package/dist/schema/never.js +5 -1
  81. package/dist/schema/never.js.map +1 -1
  82. package/dist/schema/null.d.ts +4 -3
  83. package/dist/schema/null.d.ts.map +1 -1
  84. package/dist/schema/null.js +6 -4
  85. package/dist/schema/null.js.map +1 -1
  86. package/dist/schema/nullable.d.ts +6 -5
  87. package/dist/schema/nullable.d.ts.map +1 -1
  88. package/dist/schema/nullable.js +9 -5
  89. package/dist/schema/nullable.js.map +1 -1
  90. package/dist/schema/object.d.ts +10 -8
  91. package/dist/schema/object.d.ts.map +1 -1
  92. package/dist/schema/object.js +11 -3
  93. package/dist/schema/object.js.map +1 -1
  94. package/dist/schema/optional.d.ts +7 -5
  95. package/dist/schema/optional.d.ts.map +1 -1
  96. package/dist/schema/optional.js +14 -6
  97. package/dist/schema/optional.js.map +1 -1
  98. package/dist/schema/params.d.ts +26 -13
  99. package/dist/schema/params.d.ts.map +1 -1
  100. package/dist/schema/params.js +47 -25
  101. package/dist/schema/params.js.map +1 -1
  102. package/dist/schema/payload.d.ts +12 -9
  103. package/dist/schema/payload.d.ts.map +1 -1
  104. package/dist/schema/payload.js +11 -0
  105. package/dist/schema/payload.js.map +1 -1
  106. package/dist/schema/permission-set.d.ts +1 -0
  107. package/dist/schema/permission-set.d.ts.map +1 -1
  108. package/dist/schema/permission-set.js +5 -0
  109. package/dist/schema/permission-set.js.map +1 -1
  110. package/dist/schema/permission.d.ts +6 -5
  111. package/dist/schema/permission.d.ts.map +1 -1
  112. package/dist/schema/permission.js +5 -0
  113. package/dist/schema/permission.js.map +1 -1
  114. package/dist/schema/procedure.d.ts +2 -1
  115. package/dist/schema/procedure.d.ts.map +1 -1
  116. package/dist/schema/procedure.js +5 -0
  117. package/dist/schema/procedure.js.map +1 -1
  118. package/dist/schema/query.d.ts +2 -1
  119. package/dist/schema/query.d.ts.map +1 -1
  120. package/dist/schema/query.js +5 -0
  121. package/dist/schema/query.js.map +1 -1
  122. package/dist/schema/record.d.ts +48 -30
  123. package/dist/schema/record.d.ts.map +1 -1
  124. package/dist/schema/record.js +12 -9
  125. package/dist/schema/record.js.map +1 -1
  126. package/dist/schema/ref.d.ts +9 -6
  127. package/dist/schema/ref.d.ts.map +1 -1
  128. package/dist/schema/ref.js +9 -16
  129. package/dist/schema/ref.js.map +1 -1
  130. package/dist/schema/refine.d.ts +4 -4
  131. package/dist/schema/refine.d.ts.map +1 -1
  132. package/dist/schema/refine.js.map +1 -1
  133. package/dist/schema/regexp.d.ts +4 -3
  134. package/dist/schema/regexp.d.ts.map +1 -1
  135. package/dist/schema/regexp.js +5 -0
  136. package/dist/schema/regexp.js.map +1 -1
  137. package/dist/schema/string.d.ts +7 -8
  138. package/dist/schema/string.d.ts.map +1 -1
  139. package/dist/schema/string.js +13 -19
  140. package/dist/schema/string.js.map +1 -1
  141. package/dist/schema/subscription.d.ts +2 -1
  142. package/dist/schema/subscription.d.ts.map +1 -1
  143. package/dist/schema/subscription.js +5 -0
  144. package/dist/schema/subscription.js.map +1 -1
  145. package/dist/schema/token.d.ts +6 -5
  146. package/dist/schema/token.d.ts.map +1 -1
  147. package/dist/schema/token.js +5 -0
  148. package/dist/schema/token.js.map +1 -1
  149. package/dist/schema/typed-object.d.ts +43 -26
  150. package/dist/schema/typed-object.d.ts.map +1 -1
  151. package/dist/schema/typed-object.js +6 -3
  152. package/dist/schema/typed-object.js.map +1 -1
  153. package/dist/schema/typed-ref.d.ts +16 -25
  154. package/dist/schema/typed-ref.d.ts.map +1 -1
  155. package/dist/schema/typed-ref.js +7 -17
  156. package/dist/schema/typed-ref.js.map +1 -1
  157. package/dist/schema/typed-union.d.ts +9 -21
  158. package/dist/schema/typed-union.d.ts.map +1 -1
  159. package/dist/schema/typed-union.js +15 -11
  160. package/dist/schema/typed-union.js.map +1 -1
  161. package/dist/schema/union.d.ts +6 -6
  162. package/dist/schema/union.d.ts.map +1 -1
  163. package/dist/schema/union.js +7 -5
  164. package/dist/schema/union.js.map +1 -1
  165. package/dist/schema/unknown-object.d.ts +5 -4
  166. package/dist/schema/unknown-object.d.ts.map +1 -1
  167. package/dist/schema/unknown-object.js +5 -1
  168. package/dist/schema/unknown-object.js.map +1 -1
  169. package/dist/schema/unknown.d.ts +3 -2
  170. package/dist/schema/unknown.d.ts.map +1 -1
  171. package/dist/schema/unknown.js +5 -1
  172. package/dist/schema/unknown.js.map +1 -1
  173. package/dist/schema/with-default.d.ts +9 -0
  174. package/dist/schema/with-default.d.ts.map +1 -0
  175. package/dist/schema/with-default.js +27 -0
  176. package/dist/schema/with-default.js.map +1 -0
  177. package/dist/schema.d.ts +2 -2
  178. package/dist/schema.d.ts.map +1 -1
  179. package/dist/schema.js +2 -4
  180. package/dist/schema.js.map +1 -1
  181. package/dist/util/assertion-util.d.ts +0 -6
  182. package/dist/util/assertion-util.d.ts.map +1 -1
  183. package/dist/util/assertion-util.js +0 -28
  184. package/dist/util/assertion-util.js.map +1 -1
  185. package/dist/util/memoize.d.ts +2 -2
  186. package/dist/util/memoize.d.ts.map +1 -1
  187. package/dist/util/memoize.js +23 -39
  188. package/dist/util/memoize.js.map +1 -1
  189. package/package.json +3 -3
  190. package/src/core/$type.test.ts +20 -0
  191. package/src/core/$type.ts +30 -0
  192. package/src/core/schema.ts +86 -38
  193. package/src/core/string-format.ts +119 -158
  194. package/src/core/validation-issue.ts +1 -1
  195. package/src/core/validator.ts +93 -53
  196. package/src/external.ts +0 -404
  197. package/src/helpers.test.ts +22 -21
  198. package/src/helpers.ts +19 -14
  199. package/src/schema/array.test.ts +38 -40
  200. package/src/schema/array.ts +35 -13
  201. package/src/schema/blob.test.ts +21 -21
  202. package/src/schema/blob.ts +19 -17
  203. package/src/schema/boolean.test.ts +9 -8
  204. package/src/schema/boolean.ts +7 -13
  205. package/src/schema/bytes.test.ts +13 -13
  206. package/src/schema/bytes.ts +13 -8
  207. package/src/schema/cid.test.ts +3 -3
  208. package/src/schema/cid.ts +13 -12
  209. package/src/schema/custom.test.ts +26 -26
  210. package/src/schema/custom.ts +20 -13
  211. package/src/schema/dict.test.ts +21 -39
  212. package/src/schema/dict.ts +28 -19
  213. package/src/schema/discriminated-union.test.ts +128 -128
  214. package/src/schema/discriminated-union.ts +45 -26
  215. package/src/schema/enum.test.ts +17 -16
  216. package/src/schema/enum.ts +16 -16
  217. package/src/schema/integer.test.ts +22 -21
  218. package/src/schema/integer.ts +12 -9
  219. package/src/schema/intersection.test.ts +10 -10
  220. package/src/schema/intersection.ts +17 -14
  221. package/src/schema/literal.test.ts +35 -34
  222. package/src/schema/literal.ts +12 -15
  223. package/src/schema/never.test.ts +5 -5
  224. package/src/schema/never.ts +7 -2
  225. package/src/schema/null.test.ts +3 -3
  226. package/src/schema/null.ts +9 -9
  227. package/src/schema/nullable.test.ts +31 -42
  228. package/src/schema/nullable.ts +17 -9
  229. package/src/schema/object.test.ts +10 -12
  230. package/src/schema/object.ts +27 -18
  231. package/src/schema/optional.test.ts +21 -28
  232. package/src/schema/optional.ts +27 -10
  233. package/src/schema/params.test.ts +471 -47
  234. package/src/schema/params.ts +74 -38
  235. package/src/schema/payload.test.ts +150 -156
  236. package/src/schema/payload.ts +35 -19
  237. package/src/schema/permission-set.test.ts +206 -273
  238. package/src/schema/permission-set.ts +8 -0
  239. package/src/schema/permission.test.ts +177 -177
  240. package/src/schema/permission.ts +13 -5
  241. package/src/schema/procedure.test.ts +183 -242
  242. package/src/schema/procedure.ts +18 -5
  243. package/src/schema/query.test.ts +186 -200
  244. package/src/schema/query.ts +16 -4
  245. package/src/schema/record.test.ts +121 -101
  246. package/src/schema/record.ts +74 -40
  247. package/src/schema/ref.test.ts +101 -118
  248. package/src/schema/ref.ts +33 -28
  249. package/src/schema/refine.test.ts +28 -28
  250. package/src/schema/refine.ts +23 -20
  251. package/src/schema/regexp.test.ts +29 -33
  252. package/src/schema/regexp.ts +11 -7
  253. package/src/schema/string.test.ts +35 -35
  254. package/src/schema/string.ts +24 -33
  255. package/src/schema/subscription.test.ts +259 -387
  256. package/src/schema/subscription.ts +16 -4
  257. package/src/schema/token.test.ts +47 -324
  258. package/src/schema/token.ts +14 -7
  259. package/src/schema/typed-object.test.ts +98 -81
  260. package/src/schema/typed-object.ts +68 -33
  261. package/src/schema/typed-ref.test.ts +206 -234
  262. package/src/schema/typed-ref.ts +40 -42
  263. package/src/schema/typed-union.test.ts +40 -64
  264. package/src/schema/typed-union.ts +36 -58
  265. package/src/schema/union.test.ts +17 -27
  266. package/src/schema/union.ts +20 -16
  267. package/src/schema/unknown-object.test.ts +8 -8
  268. package/src/schema/unknown-object.ts +9 -7
  269. package/src/schema/unknown.test.ts +4 -4
  270. package/src/schema/unknown.ts +7 -5
  271. package/src/schema/with-default.ts +35 -0
  272. package/src/schema.ts +2 -6
  273. package/src/util/assertion-util.ts +0 -39
  274. package/src/util/memoize.ts +26 -46
  275. package/dist/schema/_parameters.d.ts +0 -17
  276. package/dist/schema/_parameters.d.ts.map +0 -1
  277. package/dist/schema/_parameters.js +0 -20
  278. package/dist/schema/_parameters.js.map +0 -1
  279. package/src/schema/_parameters.test.ts +0 -417
  280. package/src/schema/_parameters.ts +0 -26
@@ -1,9 +1,9 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { NullSchema } from './null.js'
2
+ import { nullSchema } from './null.js'
3
3
 
4
4
  describe('NullSchema', () => {
5
5
  describe('basic validation', () => {
6
- const schema = new NullSchema()
6
+ const schema = nullSchema()
7
7
 
8
8
  it('validates null', () => {
9
9
  const result = schema.safeParse(null)
@@ -50,7 +50,7 @@ describe('NullSchema', () => {
50
50
  })
51
51
 
52
52
  describe('edge cases', () => {
53
- const schema = new NullSchema()
53
+ const schema = nullSchema()
54
54
 
55
55
  it('rejects falsy values', () => {
56
56
  const result = schema.safeParse(0)
@@ -1,14 +1,8 @@
1
- import { Schema, ValidationResult, ValidatorContext } from '../core.js'
1
+ import { Schema, ValidationContext } from '../core.js'
2
+ import { memoizedOptions } from '../util/memoize.js'
2
3
 
3
4
  export class NullSchema extends Schema<null> {
4
- constructor() {
5
- super()
6
- }
7
-
8
- validateInContext(
9
- input: unknown,
10
- ctx: ValidatorContext,
11
- ): ValidationResult<null> {
5
+ validateInContext(input: unknown, ctx: ValidationContext) {
12
6
  if (input !== null) {
13
7
  return ctx.issueInvalidType(input, 'null')
14
8
  }
@@ -16,3 +10,9 @@ export class NullSchema extends Schema<null> {
16
10
  return ctx.success(null)
17
11
  }
18
12
  }
13
+
14
+ export const nullSchema = /*#__PURE__*/ memoizedOptions(function () {
15
+ return new NullSchema()
16
+ })
17
+
18
+ export { nullSchema as null }
@@ -1,13 +1,14 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { EnumSchema } from './enum.js'
3
- import { IntegerSchema } from './integer.js'
4
- import { NullableSchema } from './nullable.js'
5
- import { ObjectSchema } from './object.js'
6
- import { StringSchema } from './string.js'
2
+ import { enumSchema } from './enum.js'
3
+ import { integer } from './integer.js'
4
+ import { nullable } from './nullable.js'
5
+ import { object } from './object.js'
6
+ import { string } from './string.js'
7
+ import { withDefault } from './with-default.js'
7
8
 
8
9
  describe('NullableSchema', () => {
9
10
  describe('with StringSchema', () => {
10
- const schema = new NullableSchema(new StringSchema({}))
11
+ const schema = nullable(string())
11
12
 
12
13
  it('validates null', () => {
13
14
  const result = schema.safeParse(null)
@@ -57,7 +58,7 @@ describe('NullableSchema', () => {
57
58
  })
58
59
 
59
60
  describe('with IntegerSchema', () => {
60
- const schema = new NullableSchema(new IntegerSchema({}))
61
+ const schema = nullable(integer())
61
62
 
62
63
  it('validates null', () => {
63
64
  const result = schema.safeParse(null)
@@ -102,7 +103,7 @@ describe('NullableSchema', () => {
102
103
  })
103
104
 
104
105
  describe('with EnumSchema', () => {
105
- const schema = new NullableSchema(new EnumSchema(['red', 'green', 'blue']))
106
+ const schema = nullable(enumSchema(['red', 'green', 'blue']))
106
107
 
107
108
  it('validates null', () => {
108
109
  const result = schema.safeParse(null)
@@ -135,9 +136,7 @@ describe('NullableSchema', () => {
135
136
  })
136
137
 
137
138
  describe('with constrained StringSchema', () => {
138
- const schema = new NullableSchema(
139
- new StringSchema({ minLength: 3, maxLength: 10 }),
140
- )
139
+ const schema = nullable(string({ minLength: 3, maxLength: 10 }))
141
140
 
142
141
  it('validates null', () => {
143
142
  const result = schema.safeParse(null)
@@ -171,9 +170,7 @@ describe('NullableSchema', () => {
171
170
  })
172
171
 
173
172
  describe('with constrained IntegerSchema', () => {
174
- const schema = new NullableSchema(
175
- new IntegerSchema({ minimum: 0, maximum: 100 }),
176
- )
173
+ const schema = nullable(integer({ minimum: 0, maximum: 100 }))
177
174
 
178
175
  it('validates null', () => {
179
176
  const result = schema.safeParse(null)
@@ -207,7 +204,7 @@ describe('NullableSchema', () => {
207
204
  })
208
205
 
209
206
  describe('with StringSchema having default value', () => {
210
- const schema = new NullableSchema(new StringSchema({ default: 'default' }))
207
+ const schema = nullable(withDefault(string(), 'default'))
211
208
 
212
209
  it('validates null explicitly', () => {
213
210
  const result = schema.safeParse(null)
@@ -235,10 +232,10 @@ describe('NullableSchema', () => {
235
232
  })
236
233
 
237
234
  describe('with ObjectSchema', () => {
238
- const schema = new NullableSchema(
239
- new ObjectSchema({
240
- name: new StringSchema({}),
241
- age: new IntegerSchema({}),
235
+ const schema = nullable(
236
+ object({
237
+ name: string(),
238
+ age: integer(),
242
239
  }),
243
240
  )
244
241
 
@@ -277,7 +274,7 @@ describe('NullableSchema', () => {
277
274
  })
278
275
 
279
276
  describe('nested nullable schemas', () => {
280
- const schema = new NullableSchema(new NullableSchema(new StringSchema({})))
277
+ const schema = nullable(nullable(string()))
281
278
 
282
279
  it('validates null at outer level', () => {
283
280
  const result = schema.safeParse(null)
@@ -307,7 +304,7 @@ describe('NullableSchema', () => {
307
304
  })
308
305
 
309
306
  describe('with StringSchema format constraints', () => {
310
- const schema = new NullableSchema(new StringSchema({ format: 'uri' }))
307
+ const schema = nullable(string({ format: 'uri' }))
311
308
 
312
309
  it('validates null', () => {
313
310
  const result = schema.safeParse(null)
@@ -331,7 +328,7 @@ describe('NullableSchema', () => {
331
328
  })
332
329
 
333
330
  describe('edge cases', () => {
334
- const stringSchema = new NullableSchema(new StringSchema({}))
331
+ const stringSchema = nullable(string())
335
332
 
336
333
  it('handles null correctly without coercion', () => {
337
334
  const result = stringSchema.safeParse(null)
@@ -366,7 +363,7 @@ describe('NullableSchema', () => {
366
363
 
367
364
  describe('type preservation', () => {
368
365
  it('preserves string type for valid strings', () => {
369
- const schema = new NullableSchema(new StringSchema({}))
366
+ const schema = nullable(string())
370
367
  const result = schema.safeParse('test')
371
368
  expect(result.success).toBe(true)
372
369
  if (result.success) {
@@ -376,7 +373,7 @@ describe('NullableSchema', () => {
376
373
  })
377
374
 
378
375
  it('preserves number type for valid integers', () => {
379
- const schema = new NullableSchema(new IntegerSchema({}))
376
+ const schema = nullable(integer())
380
377
  const result = schema.safeParse(42)
381
378
  expect(result.success).toBe(true)
382
379
  if (result.success) {
@@ -386,9 +383,7 @@ describe('NullableSchema', () => {
386
383
  })
387
384
 
388
385
  it('preserves object type for valid objects', () => {
389
- const schema = new NullableSchema(
390
- new ObjectSchema({ key: new StringSchema({}) }),
391
- )
386
+ const schema = nullable(object({ key: string() }))
392
387
  const input = { key: 'value' }
393
388
  const result = schema.safeParse(input)
394
389
  expect(result.success).toBe(true)
@@ -399,7 +394,7 @@ describe('NullableSchema', () => {
399
394
  })
400
395
 
401
396
  it('preserves null type exactly', () => {
402
- const schema = new NullableSchema(new StringSchema({}))
397
+ const schema = nullable(string())
403
398
  const result = schema.safeParse(null)
404
399
  expect(result.success).toBe(true)
405
400
  if (result.success) {
@@ -412,8 +407,8 @@ describe('NullableSchema', () => {
412
407
 
413
408
  describe('with complex wrapped schemas', () => {
414
409
  it('validates nullable enum with default', () => {
415
- const schema = new NullableSchema(
416
- new EnumSchema(['option1', 'option2'], { default: 'option1' }),
410
+ const schema = nullable(
411
+ withDefault(enumSchema(['option1', 'option2']), 'option1'),
417
412
  )
418
413
 
419
414
  expect(schema.safeParse(null).success).toBe(true)
@@ -424,9 +419,7 @@ describe('NullableSchema', () => {
424
419
  })
425
420
 
426
421
  it('handles nullable schema with grapheme constraints', () => {
427
- const schema = new NullableSchema(
428
- new StringSchema({ minGraphemes: 2, maxGraphemes: 5 }),
429
- )
422
+ const schema = nullable(string({ minGraphemes: 2, maxGraphemes: 5 }))
430
423
 
431
424
  expect(schema.safeParse(null).success).toBe(true)
432
425
  expect(schema.safeParse('ab').success).toBe(true)
@@ -436,9 +429,7 @@ describe('NullableSchema', () => {
436
429
  })
437
430
 
438
431
  it('handles nullable integer with negative range', () => {
439
- const schema = new NullableSchema(
440
- new IntegerSchema({ minimum: -100, maximum: -10 }),
441
- )
432
+ const schema = nullable(integer({ minimum: -100, maximum: -10 }))
442
433
 
443
434
  expect(schema.safeParse(null).success).toBe(true)
444
435
  expect(schema.safeParse(-50).success).toBe(true)
@@ -452,27 +443,25 @@ describe('NullableSchema', () => {
452
443
 
453
444
  describe('validation error behavior', () => {
454
445
  it('returns failure for wrapped schema validation errors', () => {
455
- const schema = new NullableSchema(new IntegerSchema({ minimum: 10 }))
446
+ const schema = nullable(integer({ minimum: 10 }))
456
447
  const result = schema.safeParse(5)
457
448
  expect(result.success).toBe(false)
458
449
  })
459
450
 
460
451
  it('returns failure for type mismatches', () => {
461
- const schema = new NullableSchema(new StringSchema({}))
452
+ const schema = nullable(string())
462
453
  const result = schema.safeParse(123)
463
454
  expect(result.success).toBe(false)
464
455
  })
465
456
 
466
457
  it('returns success for null regardless of wrapped constraints', () => {
467
- const schema = new NullableSchema(
468
- new StringSchema({ minLength: 100, format: 'uri' }),
469
- )
458
+ const schema = nullable(string({ minLength: 100, format: 'uri' }))
470
459
  const result = schema.safeParse(null)
471
460
  expect(result.success).toBe(true)
472
461
  })
473
462
 
474
463
  it('wrapped schema validation applies when value is not null', () => {
475
- const schema = new NullableSchema(new StringSchema({ minLength: 5 }))
464
+ const schema = nullable(string({ minLength: 5 }))
476
465
  expect(schema.safeParse(null).success).toBe(true)
477
466
  expect(schema.safeParse('hello').success).toBe(true)
478
467
  expect(schema.safeParse('hi').success).toBe(false)
@@ -1,23 +1,31 @@
1
1
  import {
2
+ InferInput,
3
+ InferOutput,
2
4
  Schema,
3
- ValidationResult,
5
+ ValidationContext,
4
6
  Validator,
5
- ValidatorContext,
6
7
  } from '../core.js'
8
+ import { memoizedTransformer } from '../util/memoize.js'
7
9
 
8
- export class NullableSchema<V> extends Schema<V | null> {
9
- constructor(readonly schema: Validator<V>) {
10
+ export class NullableSchema<const TValidator extends Validator> extends Schema<
11
+ InferInput<TValidator> | null,
12
+ InferOutput<TValidator> | null
13
+ > {
14
+ constructor(readonly validator: TValidator) {
10
15
  super()
11
16
  }
12
17
 
13
- validateInContext(
14
- input: unknown,
15
- ctx: ValidatorContext,
16
- ): ValidationResult<V | null> {
18
+ validateInContext(input: unknown, ctx: ValidationContext) {
17
19
  if (input === null) {
18
20
  return ctx.success(null)
19
21
  }
20
22
 
21
- return ctx.validate(input, this.schema)
23
+ return ctx.validate(input, this.validator)
22
24
  }
23
25
  }
26
+
27
+ export const nullable = /*#__PURE__*/ memoizedTransformer(function <
28
+ const TValidator extends Validator,
29
+ >(validator: TValidator) {
30
+ return new NullableSchema<TValidator>(validator)
31
+ })
@@ -1,18 +1,16 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { EnumSchema } from './enum.js'
3
- import { IntegerSchema } from './integer.js'
4
- import { NullableSchema } from './nullable.js'
5
- import { ObjectSchema } from './object.js'
6
- import { OptionalSchema } from './optional.js'
7
- import { StringSchema } from './string.js'
2
+ import { enumSchema } from './enum.js'
3
+ import { integer } from './integer.js'
4
+ import { nullable } from './nullable.js'
5
+ import { object } from './object.js'
6
+ import { optional } from './optional.js'
7
+ import { string } from './string.js'
8
8
 
9
9
  describe('ObjectSchema', () => {
10
- const schema = new ObjectSchema({
11
- name: new StringSchema({}),
12
- age: new OptionalSchema(new IntegerSchema({})),
13
- gender: new OptionalSchema(
14
- new NullableSchema(new EnumSchema(['male', 'female'])),
15
- ),
10
+ const schema = object({
11
+ name: string(),
12
+ age: optional(integer()),
13
+ gender: optional(nullable(enumSchema(['male', 'female']))),
16
14
  })
17
15
 
18
16
  it('validates plain objects', () => {
@@ -1,25 +1,27 @@
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
  WithOptionalProperties,
9
9
  } from '../core.js'
10
10
  import { lazyProperty } from '../util/lazy-property.js'
11
11
 
12
12
  export type ObjectSchemaShape = Record<string, Validator>
13
13
 
14
- export type ObjectSchemaOutput<Shape extends ObjectSchemaShape> =
14
+ export class ObjectSchema<
15
+ const TShape extends ObjectSchemaShape = any,
16
+ > extends Schema<
17
+ WithOptionalProperties<{
18
+ [K in keyof TShape]: InferInput<TShape[K]>
19
+ }>,
15
20
  WithOptionalProperties<{
16
- [K in keyof Shape]: Infer<Shape[K]>
21
+ [K in keyof TShape]: InferOutput<TShape[K]>
17
22
  }>
18
-
19
- export class ObjectSchema<
20
- const Shape extends ObjectSchemaShape = any,
21
- > extends Schema<ObjectSchemaOutput<Shape>> {
22
- constructor(readonly shape: Shape) {
23
+ > {
24
+ constructor(readonly shape: TShape) {
23
25
  super()
24
26
  }
25
27
 
@@ -29,10 +31,7 @@ export class ObjectSchema<
29
31
  return lazyProperty(this, 'validatorsMap', map)
30
32
  }
31
33
 
32
- validateInContext(
33
- input: unknown,
34
- ctx: ValidatorContext,
35
- ): ValidationResult<ObjectSchemaOutput<Shape>> {
34
+ validateInContext(input: unknown, ctx: ValidationContext) {
36
35
  if (!isPlainObject(input)) {
37
36
  return ctx.issueInvalidType(input, 'object')
38
37
  }
@@ -56,14 +55,24 @@ export class ObjectSchema<
56
55
  continue
57
56
  }
58
57
 
59
- if (result.value !== input[key]) {
58
+ if (!Object.is(result.value, input[key])) {
59
+ if (ctx.options.mode === 'validate') {
60
+ // In "validate" mode, we can't modify the input, so we issue an error
61
+ return ctx.issueInvalidPropertyValue(input, key, [result.value])
62
+ }
63
+
60
64
  copy ??= { ...input }
61
65
  copy[key] = result.value
62
66
  }
63
67
  }
64
68
 
65
- const output = (copy ?? input) as ObjectSchemaOutput<Shape>
66
-
67
- return ctx.success(output)
69
+ return ctx.success(copy ?? input)
68
70
  }
69
71
  }
72
+
73
+ /*@__NO_SIDE_EFFECTS__*/
74
+ export function object<const TShape extends ObjectSchemaShape>(
75
+ properties: TShape,
76
+ ) {
77
+ return new ObjectSchema<TShape>(properties)
78
+ }
@@ -1,12 +1,13 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { BooleanSchema } from './boolean.js'
3
- import { IntegerSchema } from './integer.js'
4
- import { OptionalSchema } from './optional.js'
5
- import { StringSchema } from './string.js'
2
+ import { boolean } from './boolean.js'
3
+ import { integer } from './integer.js'
4
+ import { optional } from './optional.js'
5
+ import { string } from './string.js'
6
+ import { withDefault } from './with-default.js'
6
7
 
7
8
  describe('OptionalSchema', () => {
8
9
  describe('basic validation with string schema', () => {
9
- const schema = new OptionalSchema(new StringSchema({}))
10
+ const schema = optional(string())
10
11
 
11
12
  it('validates defined string values', () => {
12
13
  const result = schema.safeParse('hello')
@@ -59,7 +60,7 @@ describe('OptionalSchema', () => {
59
60
  })
60
61
 
61
62
  describe('basic validation with integer schema', () => {
62
- const schema = new OptionalSchema(new IntegerSchema({}))
63
+ const schema = optional(integer())
63
64
 
64
65
  it('validates defined integer values', () => {
65
66
  const result = schema.safeParse(42)
@@ -110,7 +111,7 @@ describe('OptionalSchema', () => {
110
111
  })
111
112
 
112
113
  describe('basic validation with boolean schema', () => {
113
- const schema = new OptionalSchema(new BooleanSchema({}))
114
+ const schema = optional(boolean())
114
115
 
115
116
  it('validates true', () => {
116
117
  const result = schema.safeParse(true)
@@ -153,9 +154,7 @@ describe('OptionalSchema', () => {
153
154
  })
154
155
 
155
156
  describe('inner schema with constraints', () => {
156
- const schema = new OptionalSchema(
157
- new StringSchema({ minLength: 5, maxLength: 10 }),
158
- )
157
+ const schema = optional(string({ minLength: 5, maxLength: 10 }))
159
158
 
160
159
  it('validates values meeting inner schema constraints', () => {
161
160
  const result = schema.safeParse('hello')
@@ -194,7 +193,7 @@ describe('OptionalSchema', () => {
194
193
  })
195
194
 
196
195
  describe('inner schema with default value', () => {
197
- const schema = new OptionalSchema(new StringSchema({ default: 'default' }))
196
+ const schema = optional(withDefault(string(), 'default'))
198
197
 
199
198
  it('applies default value when undefined is provided', () => {
200
199
  const result = schema.safeParse(undefined)
@@ -222,9 +221,7 @@ describe('OptionalSchema', () => {
222
221
  })
223
222
 
224
223
  describe('inner schema with default value and constraints', () => {
225
- const schema = new OptionalSchema(
226
- new StringSchema({ default: 'default', minLength: 5 }),
227
- )
224
+ const schema = optional(withDefault(string({ minLength: 5 }), 'default'))
228
225
 
229
226
  it('applies default value when undefined is provided', () => {
230
227
  const result = schema.safeParse(undefined)
@@ -246,9 +243,7 @@ describe('OptionalSchema', () => {
246
243
  })
247
244
 
248
245
  describe('inner schema with invalid default value', () => {
249
- const schema = new OptionalSchema(
250
- new StringSchema({ default: 'bad', minLength: 5 }),
251
- )
246
+ const schema = optional(string({ default: 'bad', minLength: 5 }))
252
247
 
253
248
  it('returns undefined when default value violates constraints', () => {
254
249
  const result = schema.safeParse(undefined)
@@ -265,7 +260,7 @@ describe('OptionalSchema', () => {
265
260
  })
266
261
 
267
262
  describe('inner schema with integer default', () => {
268
- const schema = new OptionalSchema(new IntegerSchema({ default: 42 }))
263
+ const schema = optional(withDefault(integer(), 42))
269
264
 
270
265
  it('applies default value when undefined is provided', () => {
271
266
  const result = schema.safeParse(undefined)
@@ -293,7 +288,7 @@ describe('OptionalSchema', () => {
293
288
  })
294
289
 
295
290
  describe('inner schema with boolean default', () => {
296
- const schema = new OptionalSchema(new BooleanSchema({ default: true }))
291
+ const schema = optional(withDefault(boolean(), true))
297
292
 
298
293
  it('applies default value when undefined is provided', () => {
299
294
  const result = schema.safeParse(undefined)
@@ -321,7 +316,7 @@ describe('OptionalSchema', () => {
321
316
  })
322
317
 
323
318
  describe('edge cases', () => {
324
- const schema = new OptionalSchema(new StringSchema({}))
319
+ const schema = optional(string())
325
320
 
326
321
  it('handles very long strings', () => {
327
322
  const longString = 'a'.repeat(10000)
@@ -356,7 +351,7 @@ describe('OptionalSchema', () => {
356
351
 
357
352
  describe('type distinctions', () => {
358
353
  it('distinguishes between zero and undefined for integers', () => {
359
- const schema = new OptionalSchema(new IntegerSchema({}))
354
+ const schema = optional(integer())
360
355
 
361
356
  const zeroResult = schema.safeParse(0)
362
357
  expect(zeroResult.success).toBe(true)
@@ -372,7 +367,7 @@ describe('OptionalSchema', () => {
372
367
  })
373
368
 
374
369
  it('distinguishes between false and undefined for booleans', () => {
375
- const schema = new OptionalSchema(new BooleanSchema({}))
370
+ const schema = optional(boolean())
376
371
 
377
372
  const falseResult = schema.safeParse(false)
378
373
  expect(falseResult.success).toBe(true)
@@ -388,7 +383,7 @@ describe('OptionalSchema', () => {
388
383
  })
389
384
 
390
385
  it('distinguishes between empty string and undefined for strings', () => {
391
- const schema = new OptionalSchema(new StringSchema({}))
386
+ const schema = optional(string())
392
387
 
393
388
  const emptyResult = schema.safeParse('')
394
389
  expect(emptyResult.success).toBe(true)
@@ -405,7 +400,7 @@ describe('OptionalSchema', () => {
405
400
  })
406
401
 
407
402
  describe('nested optional schemas', () => {
408
- const schema = new OptionalSchema(new OptionalSchema(new StringSchema({})))
403
+ const schema = optional(optional(string()))
409
404
 
410
405
  it('validates defined values through nested optionals', () => {
411
406
  const result = schema.safeParse('hello')
@@ -430,7 +425,7 @@ describe('OptionalSchema', () => {
430
425
  })
431
426
 
432
427
  describe('inner schema format constraints', () => {
433
- const schema = new OptionalSchema(new StringSchema({ format: 'uri' }))
428
+ const schema = optional(string({ format: 'uri' }))
434
429
 
435
430
  it('validates values meeting format constraint', () => {
436
431
  const result = schema.safeParse('https://example.com')
@@ -449,9 +444,7 @@ describe('OptionalSchema', () => {
449
444
  })
450
445
 
451
446
  describe('integer constraint validation', () => {
452
- const schema = new OptionalSchema(
453
- new IntegerSchema({ minimum: 0, maximum: 100 }),
454
- )
447
+ const schema = optional(integer({ minimum: 0, maximum: 100 }))
455
448
 
456
449
  it('validates values within range', () => {
457
450
  const result = schema.safeParse(50)
@@ -1,22 +1,33 @@
1
1
  import {
2
+ InferInput,
3
+ InferOutput,
2
4
  Schema,
3
- ValidationResult,
5
+ UnwrapValidator,
6
+ ValidationContext,
4
7
  Validator,
5
- ValidatorContext,
6
8
  } from '../core.js'
9
+ import { memoizedTransformer } from '../util/memoize.js'
10
+ import { WithDefaultSchema } from './with-default.js'
7
11
 
8
- export class OptionalSchema<V> extends Schema<V | undefined> {
9
- constructor(readonly schema: Validator<V>) {
12
+ export class OptionalSchema<TValidator extends Validator> extends Schema<
13
+ InferInput<TValidator> | undefined,
14
+ UnwrapValidator<TValidator> extends WithDefaultSchema<infer TValidator>
15
+ ? InferOutput<TValidator>
16
+ : InferOutput<TValidator> | undefined
17
+ > {
18
+ constructor(readonly validator: TValidator) {
10
19
  super()
11
20
  }
12
21
 
13
- validateInContext(
14
- input: unknown,
15
- ctx: ValidatorContext,
16
- ): ValidationResult<V | undefined> {
22
+ validateInContext(input: unknown, ctx: ValidationContext) {
23
+ // Optimization: No need to apply child schema defaults in validation mode
24
+ if (input === undefined && ctx.options.mode === 'validate') {
25
+ return ctx.success(input)
26
+ }
27
+
17
28
  // @NOTE The inner schema might apply a default value so we need to run it
18
- // first, even if input is undefined.
19
- const result = ctx.validate(input, this.schema)
29
+ // even if input is undefined.
30
+ const result = ctx.validate(input, this.validator)
20
31
 
21
32
  if (result.success) {
22
33
  return result
@@ -29,3 +40,9 @@ export class OptionalSchema<V> extends Schema<V | undefined> {
29
40
  return result
30
41
  }
31
42
  }
43
+
44
+ export const optional = /*#__PURE__*/ memoizedTransformer(function <
45
+ const TValidator extends Validator,
46
+ >(validator: TValidator) {
47
+ return new OptionalSchema<TValidator>(validator)
48
+ })