@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,39 +1,44 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { Schema } from '../core.js'
3
- import { IntegerSchema } from './integer.js'
4
- import { ObjectSchema } from './object.js'
5
- import { OptionalSchema } from './optional.js'
6
- import { RefSchema } from './ref.js'
7
- import { StringSchema } from './string.js'
2
+ import { Schema, Validator } from '../core.js'
3
+ import { integer } from './integer.js'
4
+ import { object } from './object.js'
5
+ import { optional } from './optional.js'
6
+ import { ref } from './ref.js'
7
+ import { string } from './string.js'
8
8
 
9
9
  describe('RefSchema', () => {
10
10
  describe('basic validation', () => {
11
11
  it('validates through a simple string reference', () => {
12
- const schema = new RefSchema(() => new StringSchema({}))
12
+ const schema = ref(() => innerSchema)
13
+ const innerSchema = string()
13
14
  const result = schema.safeParse('hello')
14
15
  expect(result.success).toBe(true)
15
16
  })
16
17
 
17
18
  it('validates through an integer reference', () => {
18
- const schema = new RefSchema(() => new IntegerSchema({}))
19
+ const schema = ref(() => innerSchema)
20
+ const innerSchema = integer()
19
21
  const result = schema.safeParse(42)
20
22
  expect(result.success).toBe(true)
21
23
  })
22
24
 
23
25
  it('rejects invalid input through reference', () => {
24
- const schema = new RefSchema(() => new StringSchema({}))
26
+ const schema = ref(() => innerSchema)
27
+ const innerSchema = string()
25
28
  const result = schema.safeParse(123)
26
29
  expect(result.success).toBe(false)
27
30
  })
28
31
 
29
32
  it('validates null rejection through reference', () => {
30
- const schema = new RefSchema(() => new StringSchema({}))
33
+ const schema = ref(() => innerSchema)
34
+ const innerSchema = string()
31
35
  const result = schema.safeParse(null)
32
36
  expect(result.success).toBe(false)
33
37
  })
34
38
 
35
39
  it('validates undefined rejection through reference', () => {
36
- const schema = new RefSchema(() => new IntegerSchema({}))
40
+ const schema = ref(() => innerSchema)
41
+ const innerSchema = integer()
37
42
  const result = schema.safeParse(undefined)
38
43
  expect(result.success).toBe(false)
39
44
  })
@@ -42,9 +47,9 @@ describe('RefSchema', () => {
42
47
  describe('lazy schema resolution', () => {
43
48
  it('does not call getter until first validation', () => {
44
49
  let getterCalled = false
45
- const schema = new RefSchema(() => {
50
+ const schema = ref(() => {
46
51
  getterCalled = true
47
- return new StringSchema({})
52
+ return string()
48
53
  })
49
54
  expect(getterCalled).toBe(false)
50
55
 
@@ -52,31 +57,18 @@ describe('RefSchema', () => {
52
57
  expect(getterCalled).toBe(true)
53
58
  })
54
59
 
55
- it('caches the resolved schema', () => {
56
- let callCount = 0
57
- const schema = new RefSchema(() => {
58
- callCount++
59
- return new StringSchema({})
60
- })
61
-
62
- schema.safeParse('first')
63
- schema.safeParse('second')
64
- schema.safeParse('third')
65
-
66
- expect(callCount).toBe(1)
67
- })
68
-
69
60
  it('throws error if getter is called multiple times', () => {
70
- const schema = new RefSchema(() => new StringSchema({}))
61
+ const schema = ref(() => innerSchema)
62
+ const innerSchema = string()
71
63
 
72
64
  // Access schema property to resolve it
73
- schema.schema
65
+ schema.validator
74
66
 
75
67
  // Try to access the original getter again (which should throw)
76
68
  // This is internal behavior, but we're testing the protection mechanism
77
69
  expect(() => {
78
70
  // Force access to the cached schema property
79
- const schemaValue = schema.schema
71
+ const schemaValue = schema.validator
80
72
  // This should work fine as it's now cached
81
73
  expect(schemaValue).toBeDefined()
82
74
  }).not.toThrow()
@@ -85,28 +77,28 @@ describe('RefSchema', () => {
85
77
 
86
78
  describe('with object schemas', () => {
87
79
  it('validates objects through reference', () => {
88
- const schema = new RefSchema(
89
- () =>
90
- new ObjectSchema({
91
- name: new StringSchema({}),
92
- age: new IntegerSchema({}),
93
- }),
94
- )
95
- const result = schema.safeParse({
96
- name: 'Alice',
97
- age: 30,
80
+ const schema = ref(() => innerSchema)
81
+ const innerSchema = object({
82
+ name: string(),
83
+ age: integer(),
84
+ })
85
+
86
+ expect(
87
+ schema.safeValidate({
88
+ name: 'Alice',
89
+ age: 30,
90
+ }),
91
+ ).toMatchObject({
92
+ success: true,
98
93
  })
99
- expect(result.success).toBe(true)
100
94
  })
101
95
 
102
96
  it('rejects invalid objects through reference', () => {
103
- const schema = new RefSchema(
104
- () =>
105
- new ObjectSchema({
106
- name: new StringSchema({}),
107
- age: new IntegerSchema({}),
108
- }),
109
- )
97
+ const schema = ref(() => innerSchema)
98
+ const innerSchema = object({
99
+ name: string(),
100
+ age: integer(),
101
+ })
110
102
  const result = schema.safeParse({
111
103
  name: 'Alice',
112
104
  age: 'thirty',
@@ -115,13 +107,11 @@ describe('RefSchema', () => {
115
107
  })
116
108
 
117
109
  it('rejects objects with missing properties through reference', () => {
118
- const schema = new RefSchema(
119
- () =>
120
- new ObjectSchema({
121
- name: new StringSchema({}),
122
- age: new IntegerSchema({}),
123
- }),
124
- )
110
+ const schema = ref(() => innerSchema)
111
+ const innerSchema = object({
112
+ name: string(),
113
+ age: integer(),
114
+ })
125
115
  const result = schema.safeParse({
126
116
  name: 'Alice',
127
117
  })
@@ -131,52 +121,35 @@ describe('RefSchema', () => {
131
121
 
132
122
  describe('with constrained schemas', () => {
133
123
  it('validates string with minLength constraint through reference', () => {
134
- const schema = new RefSchema(() => new StringSchema({ minLength: 5 }))
124
+ const schema = ref(() => innerSchema)
125
+ const innerSchema = string({ minLength: 5 })
135
126
  const result = schema.safeParse('hello')
136
127
  expect(result.success).toBe(true)
137
128
  })
138
129
 
139
130
  it('rejects string violating minLength through reference', () => {
140
- const schema = new RefSchema(() => new StringSchema({ minLength: 5 }))
131
+ const schema = ref(() => innerSchema)
132
+ const innerSchema = string({ minLength: 5 })
141
133
  const result = schema.safeParse('hi')
142
134
  expect(result.success).toBe(false)
143
135
  })
144
136
 
145
137
  it('validates integer with range constraints through reference', () => {
146
- const schema = new RefSchema(
147
- () => new IntegerSchema({ minimum: 0, maximum: 100 }),
148
- )
138
+ const schema = ref(() => innerSchema)
139
+ const innerSchema = integer({ minimum: 0, maximum: 100 })
149
140
  const result = schema.safeParse(50)
150
141
  expect(result.success).toBe(true)
151
142
  })
152
143
 
153
144
  it('rejects integer violating constraints through reference', () => {
154
- const schema = new RefSchema(
155
- () => new IntegerSchema({ minimum: 0, maximum: 100 }),
156
- )
145
+ const schema = ref(() => innerSchema)
146
+ const innerSchema = integer({ minimum: 0, maximum: 100 })
157
147
  const result = schema.safeParse(150)
158
148
  expect(result.success).toBe(false)
159
149
  })
160
150
  })
161
151
 
162
152
  describe('circular references', () => {
163
- it('prevents recursive getter calls', () => {
164
- // Create a schema that would cause infinite recursion if not protected
165
- let schema: RefSchema<any>
166
-
167
- // eslint-disable-next-line prefer-const
168
- schema = new RefSchema(() => {
169
- // This would normally cause infinite recursion
170
- // but the getter protection should prevent it
171
- return schema.schema
172
- })
173
-
174
- // The first access causes stack overflow before the protection can kick in
175
- expect(() => {
176
- schema.safeParse('test')
177
- }).toThrow()
178
- })
179
-
180
153
  it('supports indirect circular references', () => {
181
154
  // Create two schemas that reference each other
182
155
  // This demonstrates forward references are possible
@@ -184,14 +157,14 @@ describe('RefSchema', () => {
184
157
  type A = { value: string; ref?: B }
185
158
  type B = { value: number; ref?: A }
186
159
 
187
- const schemaA: Schema<A> = new ObjectSchema({
188
- value: new StringSchema({}),
189
- ref: new OptionalSchema(new RefSchema<B>((() => schemaB) as any)),
160
+ const schemaA: Schema<A> = object({
161
+ value: string(),
162
+ ref: optional(ref<Validator<B>>((() => schemaB) as any)),
190
163
  })
191
164
 
192
- const schemaB: Schema<B> = new ObjectSchema({
193
- value: new IntegerSchema({}),
194
- ref: new OptionalSchema(new RefSchema<A>((() => schemaA) as any)),
165
+ const schemaB: Schema<B> = object({
166
+ value: integer(),
167
+ ref: optional(ref<Validator<A>>((() => schemaA) as any)),
195
168
  })
196
169
 
197
170
  expect(
@@ -222,7 +195,8 @@ describe('RefSchema', () => {
222
195
 
223
196
  describe('multiple validations', () => {
224
197
  it('validates multiple inputs correctly', () => {
225
- const schema = new RefSchema(() => new StringSchema({ minLength: 3 }))
198
+ const schema = ref(() => innerSchema)
199
+ const innerSchema = string({ minLength: 3 })
226
200
 
227
201
  const result1 = schema.safeParse('hello')
228
202
  expect(result1.success).toBe(true)
@@ -238,12 +212,11 @@ describe('RefSchema', () => {
238
212
  })
239
213
 
240
214
  it('handles different types of validation failures', () => {
241
- const schema = new RefSchema(
242
- () =>
243
- new ObjectSchema({
244
- name: new StringSchema({ minLength: 2 }),
245
- age: new IntegerSchema({ minimum: 0 }),
246
- }),
215
+ const schema = ref(() =>
216
+ object({
217
+ name: string({ minLength: 2 }),
218
+ age: integer({ minimum: 0 }),
219
+ }),
247
220
  )
248
221
 
249
222
  const result1 = schema.safeParse({ name: 'A', age: 25 })
@@ -259,43 +232,50 @@ describe('RefSchema', () => {
259
232
 
260
233
  describe('edge cases', () => {
261
234
  it('handles empty string validation', () => {
262
- const schema = new RefSchema(() => new StringSchema({}))
235
+ const schema = ref(() => innerSchema)
236
+ const innerSchema = string()
263
237
  const result = schema.safeParse('')
264
238
  expect(result.success).toBe(true)
265
239
  })
266
240
 
267
241
  it('handles zero validation', () => {
268
- const schema = new RefSchema(() => new IntegerSchema({}))
242
+ const schema = ref(() => innerSchema)
243
+ const innerSchema = integer()
269
244
  const result = schema.safeParse(0)
270
245
  expect(result.success).toBe(true)
271
246
  })
272
247
 
273
248
  it('rejects NaN through integer reference', () => {
274
- const schema = new RefSchema(() => new IntegerSchema({}))
249
+ const schema = ref(() => innerSchema)
250
+ const innerSchema = integer()
275
251
  const result = schema.safeParse(NaN)
276
252
  expect(result.success).toBe(false)
277
253
  })
278
254
 
279
255
  it('rejects Infinity through integer reference', () => {
280
- const schema = new RefSchema(() => new IntegerSchema({}))
256
+ const schema = ref(() => innerSchema)
257
+ const innerSchema = integer()
281
258
  const result = schema.safeParse(Infinity)
282
259
  expect(result.success).toBe(false)
283
260
  })
284
261
 
285
262
  it('rejects arrays when expecting string', () => {
286
- const schema = new RefSchema(() => new StringSchema({}))
263
+ const schema = ref(() => innerSchema)
264
+ const innerSchema = string()
287
265
  const result = schema.safeParse(['array'])
288
266
  expect(result.success).toBe(false)
289
267
  })
290
268
 
291
269
  it('rejects objects when expecting string', () => {
292
- const schema = new RefSchema(() => new StringSchema({}))
270
+ const schema = ref(() => innerSchema)
271
+ const innerSchema = string()
293
272
  const result = schema.safeParse({ key: 'value' })
294
273
  expect(result.success).toBe(false)
295
274
  })
296
275
 
297
276
  it('rejects booleans when expecting string', () => {
298
- const schema = new RefSchema(() => new StringSchema({}))
277
+ const schema = ref(() => innerSchema)
278
+ const innerSchema = string()
299
279
  const result = schema.safeParse(true)
300
280
  expect(result.success).toBe(false)
301
281
  })
@@ -303,25 +283,28 @@ describe('RefSchema', () => {
303
283
 
304
284
  describe('nested references', () => {
305
285
  it('validates through nested RefSchema', () => {
306
- const innerRef = new RefSchema(() => new StringSchema({ minLength: 3 }))
307
- const outerRef = new RefSchema(() => innerRef)
286
+ const innerRef = ref(() => innerSchema)
287
+ const innerSchema = string({ minLength: 3 })
288
+ const outerRef = ref(() => innerRef)
308
289
 
309
290
  const result = outerRef.safeParse('hello')
310
291
  expect(result.success).toBe(true)
311
292
  })
312
293
 
313
294
  it('rejects invalid input through nested RefSchema', () => {
314
- const innerRef = new RefSchema(() => new StringSchema({ minLength: 3 }))
315
- const outerRef = new RefSchema(() => innerRef)
295
+ const innerRef = ref(() => innerSchema)
296
+ const innerSchema = string({ minLength: 3 })
297
+ const outerRef = ref(() => innerRef)
316
298
 
317
299
  const result = outerRef.safeParse('hi')
318
300
  expect(result.success).toBe(false)
319
301
  })
320
302
 
321
303
  it('validates with deeply nested references', () => {
322
- const level3 = new RefSchema(() => new IntegerSchema({ minimum: 0 }))
323
- const level2 = new RefSchema(() => level3)
324
- const level1 = new RefSchema(() => level2)
304
+ const level3 = ref(() => innerSchema)
305
+ const innerSchema = integer({ minimum: 0 })
306
+ const level2 = ref(() => level3)
307
+ const level1 = ref(() => level2)
325
308
 
326
309
  const result = level1.safeParse(42)
327
310
  expect(result.success).toBe(true)
@@ -330,20 +313,20 @@ describe('RefSchema', () => {
330
313
 
331
314
  describe('schema property access', () => {
332
315
  it('allows direct access to resolved schema', () => {
333
- const innerSchema = new StringSchema({ minLength: 5 })
334
- const refSchema = new RefSchema(() => innerSchema)
316
+ const innerSchema = string({ minLength: 5 })
317
+ const refSchema = ref(() => innerSchema)
335
318
 
336
- const resolved = refSchema.schema
319
+ const resolved = refSchema.validator
337
320
  expect(resolved).toBe(innerSchema)
338
321
  })
339
322
 
340
323
  it('returns same instance on multiple schema property accesses', () => {
341
- const innerSchema = new StringSchema({})
342
- const refSchema = new RefSchema(() => innerSchema)
324
+ const innerSchema = string()
325
+ const refSchema = ref(() => innerSchema)
343
326
 
344
- const first = refSchema.schema
345
- const second = refSchema.schema
346
- const third = refSchema.schema
327
+ const first = refSchema.validator
328
+ const second = refSchema.validator
329
+ const third = refSchema.validator
347
330
 
348
331
  expect(first).toBe(second)
349
332
  expect(second).toBe(third)
@@ -351,14 +334,14 @@ describe('RefSchema', () => {
351
334
 
352
335
  it('resolves schema before validation', () => {
353
336
  let resolved = false
354
- const refSchema = new RefSchema(() => {
337
+ const refSchema = ref(() => {
355
338
  resolved = true
356
- return new StringSchema({})
339
+ return string()
357
340
  })
358
341
 
359
342
  expect(resolved).toBe(false)
360
343
 
361
- const schemaValue = refSchema.schema
344
+ const schemaValue = refSchema.validator
362
345
  expect(resolved).toBe(true)
363
346
  expect(schemaValue).toBeDefined()
364
347
  })
package/src/schema/ref.ts CHANGED
@@ -1,16 +1,25 @@
1
1
  import {
2
+ InferInput,
3
+ InferOutput,
2
4
  Schema,
3
- ValidationResult,
5
+ ValidationContext,
4
6
  Validator,
5
- ValidatorContext,
7
+ WrappedValidator,
6
8
  } from '../core.js'
7
9
 
8
- export type RefSchemaGetter<V> = () => Validator<V>
10
+ export type RefSchemaGetter<out TValidator extends Validator> = () => TValidator
9
11
 
10
- export class RefSchema<V = any> extends Schema<V> {
11
- #getter: RefSchemaGetter<V>
12
+ export class RefSchema<const TValidator extends Validator>
13
+ extends Schema<
14
+ InferInput<TValidator>,
15
+ InferOutput<TValidator>,
16
+ TValidator['__lex']
17
+ >
18
+ implements WrappedValidator<TValidator>
19
+ {
20
+ #getter: RefSchemaGetter<TValidator>
12
21
 
13
- constructor(getter: RefSchemaGetter<V>) {
22
+ constructor(getter: RefSchemaGetter<TValidator>) {
14
23
  // @NOTE In order to avoid circular dependency issues, we don't resolve
15
24
  // the schema here. Instead, we resolve it lazily when first accessed.
16
25
 
@@ -19,32 +28,28 @@ export class RefSchema<V = any> extends Schema<V> {
19
28
  this.#getter = getter
20
29
  }
21
30
 
22
- get schema(): Validator<V> {
23
- const value = this.#getter.call(null)
24
-
25
- // Prevents a getter from depending on itself recursively, also allows GC to
26
- // clean up the getter function.
27
- this.#getter = throwAlreadyCalled
28
-
29
- // Disable the getter and cache the resolved schema on the instance
30
- Object.defineProperty(this, 'schema', {
31
- value,
32
- writable: false,
33
- enumerable: false,
34
- configurable: true,
35
- })
31
+ get validator(): TValidator {
32
+ return this.#getter.call(null)
33
+ }
36
34
 
37
- return value
35
+ unwrap(): TValidator {
36
+ return this.validator
38
37
  }
39
38
 
40
- validateInContext(
41
- input: unknown,
42
- ctx: ValidatorContext,
43
- ): ValidationResult<V> {
44
- return ctx.validate(input, this.schema)
39
+ validateInContext(input: unknown, ctx: ValidationContext) {
40
+ return ctx.validate(input, this.validator)
45
41
  }
46
42
  }
47
43
 
48
- function throwAlreadyCalled(): never {
49
- throw new Error('RefSchema getter called multiple times')
44
+ /*@__NO_SIDE_EFFECTS__*/
45
+ export function ref<const TValidator extends Validator>(
46
+ get: RefSchemaGetter<TValidator>,
47
+ ): RefSchema<TValidator>
48
+ export function ref<TInput, TOutput extends TInput = TInput>(
49
+ get: RefSchemaGetter<Validator<TInput, TOutput>>,
50
+ ): RefSchema<Validator<TInput, TOutput>>
51
+ export function ref<const TValidator extends Validator>(
52
+ get: RefSchemaGetter<TValidator>,
53
+ ) {
54
+ return new RefSchema<TValidator>(get)
50
55
  }