@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,16 +1,17 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { ObjectSchema } from './object.js'
3
- import { RecordSchema } from './record.js'
4
- import { StringSchema } from './string.js'
2
+ import { Infer, Unknown$Type, Unknown$TypedObject } from '../core.js'
3
+ import { object } from './object.js'
4
+ import { record } from './record.js'
5
+ import { string } from './string.js'
5
6
 
6
7
  describe('RecordSchema', () => {
7
8
  describe('basic validation', () => {
8
- const schema = new RecordSchema(
9
+ const schema = record(
9
10
  'any',
10
11
  'app.bsky.feed.post',
11
- new ObjectSchema({
12
- $type: new StringSchema({}),
13
- text: new StringSchema({}),
12
+ object({
13
+ $type: string(),
14
+ text: string(),
14
15
  }),
15
16
  )
16
17
 
@@ -63,14 +64,14 @@ describe('RecordSchema', () => {
63
64
  })
64
65
 
65
66
  describe('isTypeOf method', () => {
66
- const schema = new RecordSchema(
67
+ const schema = record(
67
68
  'any',
68
69
  'app.bsky.feed.post',
69
- new ObjectSchema({
70
- $type: new StringSchema({}),
71
- text: new StringSchema({}),
70
+ object({
71
+ text: string(),
72
72
  }),
73
73
  )
74
+ type Schema = Infer<typeof schema>
74
75
 
75
76
  it('returns true for matching $type', () => {
76
77
  const result = schema.isTypeOf({ $type: 'app.bsky.feed.post' })
@@ -96,15 +97,39 @@ describe('RecordSchema', () => {
96
97
  const result = schema.isTypeOf({ $type: null })
97
98
  expect(result).toBe(false)
98
99
  })
100
+
101
+ it('properly discriminates Unknown$TypeObject', () => {
102
+ function foo(value: Unknown$TypedObject | Schema) {
103
+ if (schema.isTypeOf(value)) {
104
+ value.text
105
+ } else {
106
+ // @ts-expect-error
107
+ value.text
108
+ }
109
+ }
110
+
111
+ foo({
112
+ $type: 'app.bsky.feed.post',
113
+ text: 'aze',
114
+ // @ts-expect-error
115
+ unknownProperty: 'should not be allowed !',
116
+ })
117
+
118
+ foo({
119
+ $type: 'blah' as Unknown$Type,
120
+ // @ts-expect-error
121
+ unknownProperty: 'should not be allowed !',
122
+ })
123
+ })
99
124
  })
100
125
 
101
126
  describe('build method', () => {
102
- const schema = new RecordSchema(
127
+ const schema = record(
103
128
  'any',
104
129
  'app.bsky.feed.post',
105
- new ObjectSchema({
106
- $type: new StringSchema({}),
107
- text: new StringSchema({}),
130
+ object({
131
+ $type: string(),
132
+ text: string(),
108
133
  }),
109
134
  )
110
135
 
@@ -117,15 +142,18 @@ describe('RecordSchema', () => {
117
142
  it('preserves existing properties', () => {
118
143
  const result = schema.build({
119
144
  text: 'Hello world',
145
+ // @ts-expect-error
120
146
  extra: 'value',
121
147
  })
122
148
  expect(result.$type).toBe('app.bsky.feed.post')
123
149
  expect(result.text).toBe('Hello world')
150
+ // @ts-expect-error
124
151
  expect(result.extra).toBe('value')
125
152
  })
126
153
 
127
154
  it('overwrites existing $type', () => {
128
155
  const result = schema.build({
156
+ // @ts-expect-error
129
157
  $type: 'wrong.type',
130
158
  text: 'Hello world',
131
159
  })
@@ -134,12 +162,12 @@ describe('RecordSchema', () => {
134
162
  })
135
163
 
136
164
  describe('key type: any', () => {
137
- const schema = new RecordSchema(
165
+ const schema = record(
138
166
  'any',
139
167
  'app.bsky.feed.post',
140
- new ObjectSchema({
141
- $type: new StringSchema({}),
142
- text: new StringSchema({}),
168
+ object({
169
+ $type: string(),
170
+ text: string(),
143
171
  }),
144
172
  )
145
173
 
@@ -170,12 +198,12 @@ describe('RecordSchema', () => {
170
198
  })
171
199
 
172
200
  describe('key type: tid', () => {
173
- const schema = new RecordSchema(
201
+ const schema = record(
174
202
  'tid',
175
203
  'app.bsky.feed.post',
176
- new ObjectSchema({
177
- $type: new StringSchema({}),
178
- text: new StringSchema({}),
204
+ object({
205
+ $type: string(),
206
+ text: string(),
179
207
  }),
180
208
  )
181
209
 
@@ -206,12 +234,12 @@ describe('RecordSchema', () => {
206
234
  })
207
235
 
208
236
  describe('key type: nsid', () => {
209
- const schema = new RecordSchema(
237
+ const schema = record(
210
238
  'nsid',
211
239
  'app.bsky.feed.post',
212
- new ObjectSchema({
213
- $type: new StringSchema({}),
214
- text: new StringSchema({}),
240
+ object({
241
+ $type: string(),
242
+ text: string(),
215
243
  }),
216
244
  )
217
245
 
@@ -245,12 +273,12 @@ describe('RecordSchema', () => {
245
273
 
246
274
  describe('key type: literal', () => {
247
275
  describe('literal:self', () => {
248
- const schema = new RecordSchema(
276
+ const schema = record(
249
277
  'literal:self',
250
278
  'app.bsky.feed.post',
251
- new ObjectSchema({
252
- $type: new StringSchema({}),
253
- text: new StringSchema({}),
279
+ object({
280
+ $type: string(),
281
+ text: string(),
254
282
  }),
255
283
  )
256
284
 
@@ -276,12 +304,12 @@ describe('RecordSchema', () => {
276
304
  })
277
305
 
278
306
  describe('literal:customKey', () => {
279
- const schema = new RecordSchema(
307
+ const schema = record(
280
308
  'literal:customKey',
281
309
  'app.bsky.feed.post',
282
- new ObjectSchema({
283
- $type: new StringSchema({}),
284
- text: new StringSchema({}),
310
+ object({
311
+ $type: string(),
312
+ text: string(),
285
313
  }),
286
314
  )
287
315
 
@@ -303,12 +331,12 @@ describe('RecordSchema', () => {
303
331
  })
304
332
 
305
333
  describe('$type with hash fragment', () => {
306
- const schema = new RecordSchema(
334
+ const schema = record(
307
335
  'any',
308
336
  'app.bsky.feed.post#main',
309
- new ObjectSchema({
310
- $type: new StringSchema({}),
311
- text: new StringSchema({}),
337
+ object({
338
+ $type: string(),
339
+ text: string(),
312
340
  }),
313
341
  )
314
342
 
@@ -338,13 +366,13 @@ describe('RecordSchema', () => {
338
366
  })
339
367
 
340
368
  describe('complex nested schema', () => {
341
- const schema = new RecordSchema(
369
+ const schema = record(
342
370
  'any',
343
371
  'app.bsky.feed.post',
344
- new ObjectSchema({
345
- $type: new StringSchema({}),
346
- text: new StringSchema({ maxLength: 300 }),
347
- createdAt: new StringSchema({ format: 'datetime' }),
372
+ object({
373
+ $type: string(),
374
+ text: string({ maxLength: 300 }),
375
+ createdAt: string({ format: 'datetime' }),
348
376
  }),
349
377
  )
350
378
 
@@ -377,12 +405,12 @@ describe('RecordSchema', () => {
377
405
  })
378
406
 
379
407
  describe('edge cases', () => {
380
- const schema = new RecordSchema(
408
+ const schema = record(
381
409
  'any',
382
410
  'app.bsky.feed.post',
383
- new ObjectSchema({
384
- $type: new StringSchema({}),
385
- text: new StringSchema({}),
411
+ object({
412
+ $type: string(),
413
+ text: string(),
386
414
  }),
387
415
  )
388
416
 
@@ -442,14 +470,14 @@ describe('RecordSchema', () => {
442
470
  })
443
471
 
444
472
  it('handles deeply nested structures', () => {
445
- const complexSchema = new RecordSchema(
473
+ const complexSchema = record(
446
474
  'any',
447
475
  'app.bsky.complex',
448
- new ObjectSchema({
449
- $type: new StringSchema({}),
450
- nested: new ObjectSchema({
451
- deep: new ObjectSchema({
452
- value: new StringSchema({}),
476
+ object({
477
+ $type: string(),
478
+ nested: object({
479
+ deep: object({
480
+ value: string(),
453
481
  }),
454
482
  }),
455
483
  }),
@@ -468,12 +496,12 @@ describe('RecordSchema', () => {
468
496
  })
469
497
 
470
498
  describe('$isTypeOf method', () => {
471
- const schema = new RecordSchema(
499
+ const schema = record(
472
500
  'any',
473
501
  'app.bsky.feed.post',
474
- new ObjectSchema({
475
- $type: new StringSchema({}),
476
- text: new StringSchema({}),
502
+ object({
503
+ $type: string(),
504
+ text: string(),
477
505
  }),
478
506
  )
479
507
 
@@ -489,12 +517,12 @@ describe('RecordSchema', () => {
489
517
  })
490
518
 
491
519
  describe('$build method', () => {
492
- const schema = new RecordSchema(
520
+ const schema = record(
493
521
  'any',
494
522
  'app.bsky.feed.post',
495
- new ObjectSchema({
496
- $type: new StringSchema({}),
497
- text: new StringSchema({}),
523
+ object({
524
+ $type: string(),
525
+ text: string(),
498
526
  }),
499
527
  )
500
528
 
@@ -506,13 +534,13 @@ describe('RecordSchema', () => {
506
534
  })
507
535
 
508
536
  describe('validation with missing required fields', () => {
509
- const schema = new RecordSchema(
537
+ const schema = record(
510
538
  'any',
511
539
  'app.bsky.feed.post',
512
- new ObjectSchema({
513
- $type: new StringSchema({}),
514
- text: new StringSchema({}),
515
- author: new StringSchema({}),
540
+ object({
541
+ $type: string(),
542
+ text: string(),
543
+ author: string(),
516
544
  }),
517
545
  )
518
546
 
@@ -536,40 +564,32 @@ describe('RecordSchema', () => {
536
564
 
537
565
  describe('different record key types', () => {
538
566
  it('constructs with key type "any"', () => {
539
- const schema = new RecordSchema(
540
- 'any',
541
- 'app.bsky.test',
542
- new ObjectSchema({ $type: new StringSchema({}) }),
543
- )
567
+ const schema = record('any', 'app.bsky.test', object({ $type: string() }))
544
568
  expect(schema.key).toBe('any')
545
569
  expect(schema.keySchema).toBeDefined()
546
570
  })
547
571
 
548
572
  it('constructs with key type "tid"', () => {
549
- const schema = new RecordSchema(
550
- 'tid',
551
- 'app.bsky.test',
552
- new ObjectSchema({ $type: new StringSchema({}) }),
553
- )
573
+ const schema = record('tid', 'app.bsky.test', object({ $type: string() }))
554
574
  expect(schema.key).toBe('tid')
555
575
  expect(schema.keySchema).toBeDefined()
556
576
  })
557
577
 
558
578
  it('constructs with key type "nsid"', () => {
559
- const schema = new RecordSchema(
579
+ const schema = record(
560
580
  'nsid',
561
581
  'app.bsky.test',
562
- new ObjectSchema({ $type: new StringSchema({}) }),
582
+ object({ $type: string() }),
563
583
  )
564
584
  expect(schema.key).toBe('nsid')
565
585
  expect(schema.keySchema).toBeDefined()
566
586
  })
567
587
 
568
588
  it('constructs with literal key type', () => {
569
- const schema = new RecordSchema(
589
+ const schema = record(
570
590
  'literal:custom',
571
591
  'app.bsky.test',
572
- new ObjectSchema({ $type: new StringSchema({}) }),
592
+ object({ $type: string() }),
573
593
  )
574
594
  expect(schema.key).toBe('literal:custom')
575
595
  expect(schema.keySchema).toBeDefined()
@@ -577,12 +597,12 @@ describe('RecordSchema', () => {
577
597
  })
578
598
 
579
599
  describe('validation with undefined vs missing fields', () => {
580
- const schema = new RecordSchema(
600
+ const schema = record(
581
601
  'any',
582
602
  'app.bsky.feed.post',
583
- new ObjectSchema({
584
- $type: new StringSchema({}),
585
- text: new StringSchema({}),
603
+ object({
604
+ $type: string(),
605
+ text: string(),
586
606
  }),
587
607
  )
588
608
 
@@ -613,12 +633,12 @@ describe('RecordSchema', () => {
613
633
 
614
634
  describe('record with empty $type string', () => {
615
635
  it('rejects empty $type string', () => {
616
- const schema = new RecordSchema(
636
+ const schema = record(
617
637
  'any',
618
638
  'app.bsky.feed.post',
619
- new ObjectSchema({
620
- $type: new StringSchema({}),
621
- text: new StringSchema({}),
639
+ object({
640
+ $type: string(),
641
+ text: string(),
622
642
  }),
623
643
  )
624
644
 
@@ -632,12 +652,12 @@ describe('RecordSchema', () => {
632
652
 
633
653
  describe('special characters in $type', () => {
634
654
  it('validates $type with dots', () => {
635
- const schema = new RecordSchema(
655
+ const schema = record(
636
656
  'any',
637
657
  'app.bsky.feed.post',
638
- new ObjectSchema({
639
- $type: new StringSchema({}),
640
- text: new StringSchema({}),
658
+ object({
659
+ $type: string(),
660
+ text: string(),
641
661
  }),
642
662
  )
643
663
 
@@ -649,12 +669,12 @@ describe('RecordSchema', () => {
649
669
  })
650
670
 
651
671
  it('validates $type with hash and alphanumeric fragment', () => {
652
- const schema = new RecordSchema(
672
+ const schema = record(
653
673
  'any',
654
674
  'app.bsky.feed.post#reply123',
655
- new ObjectSchema({
656
- $type: new StringSchema({}),
657
- text: new StringSchema({}),
675
+ object({
676
+ $type: string(),
677
+ text: string(),
658
678
  }),
659
679
  )
660
680
 
@@ -667,12 +687,12 @@ describe('RecordSchema', () => {
667
687
  })
668
688
 
669
689
  describe('case sensitivity', () => {
670
- const schema = new RecordSchema(
690
+ const schema = record(
671
691
  'any',
672
692
  'app.bsky.feed.post',
673
- new ObjectSchema({
674
- $type: new StringSchema({}),
675
- text: new StringSchema({}),
693
+ object({
694
+ $type: string(),
695
+ text: string(),
676
696
  }),
677
697
  )
678
698
 
@@ -1,37 +1,36 @@
1
1
  import {
2
2
  $Typed,
3
- Infer,
3
+ $typed,
4
+ InferInput,
5
+ InferOutput,
4
6
  LexiconRecordKey,
5
7
  NsidString,
6
8
  Schema,
7
9
  TidString,
8
- ValidationResult,
10
+ Unknown$TypedObject,
11
+ ValidationContext,
9
12
  Validator,
10
- ValidatorContext,
11
13
  } from '../core.js'
12
- import { LiteralSchema } from './literal.js'
13
- import { StringSchema } from './string.js'
14
- import { TypedObject } from './typed-union.js'
14
+ import { literal } from './literal.js'
15
+ import { string } from './string.js'
15
16
 
16
17
  export type InferRecordKey<R extends RecordSchema> =
17
- R extends RecordSchema<infer K> ? RecordKeySchemaOutput<K> : never
18
-
19
- export type RecordSchemaOutput<
20
- T extends NsidString,
21
- S extends Validator<{ [k: string]: unknown }>,
22
- > = $Typed<Infer<S>, T>
18
+ R extends RecordSchema<infer TKey> ? RecordKeySchemaOutput<TKey> : never
23
19
 
24
20
  export class RecordSchema<
25
- K extends LexiconRecordKey = any,
26
- T extends NsidString = any,
27
- S extends Validator<{ [k: string]: unknown }> = any,
28
- > extends Schema<RecordSchemaOutput<T, S>> {
29
- keySchema: RecordKeySchema<K>
21
+ const TKey extends LexiconRecordKey = any,
22
+ const TType extends NsidString = any,
23
+ const TShape extends Validator<{ [k: string]: unknown }> = any,
24
+ > extends Schema<
25
+ $Typed<InferInput<TShape>, TType>,
26
+ $Typed<InferOutput<TShape>, TType>
27
+ > {
28
+ keySchema: RecordKeySchema<TKey>
30
29
 
31
30
  constructor(
32
- readonly key: K,
33
- readonly $type: T,
34
- readonly schema: S,
31
+ readonly key: TKey,
32
+ readonly $type: TType,
33
+ readonly schema: TShape,
35
34
  ) {
36
35
  super()
37
36
  this.keySchema = recordKey(key)
@@ -39,41 +38,38 @@ export class RecordSchema<
39
38
 
40
39
  isTypeOf<X extends { $type?: unknown }>(
41
40
  value: X,
42
- ): value is Exclude<X extends { $type: T } ? X : $Typed<X, T>, TypedObject> {
41
+ ): value is X extends { $type: TType }
42
+ ? X
43
+ : $Typed<Exclude<X, Unknown$TypedObject>, TType> {
43
44
  return value.$type === this.$type
44
45
  }
45
46
 
46
- build<X extends Omit<Infer<S>, '$type'>>(
47
- input: X,
48
- ): $Typed<Omit<X, '$type'>, T> {
49
- return input.$type === this.$type
50
- ? (input as $Typed<X, T>)
51
- : { ...input, $type: this.$type }
47
+ build(
48
+ input: Omit<InferInput<this>, '$type'>,
49
+ ): $Typed<InferOutput<this>, TType> {
50
+ return this.parse($typed(input, this.$type))
52
51
  }
53
52
 
54
53
  $isTypeOf<X extends { $type?: unknown }>(value: X) {
55
54
  return this.isTypeOf<X>(value)
56
55
  }
57
56
 
58
- $build<X extends Omit<Infer<S>, '$type'>>(input: X) {
59
- return this.build<X>(input)
57
+ $build(input: Omit<InferInput<this>, '$type'>) {
58
+ return this.build(input)
60
59
  }
61
60
 
62
- validateInContext(
63
- input: unknown,
64
- ctx: ValidatorContext,
65
- ): ValidationResult<RecordSchemaOutput<T, S>> {
61
+ validateInContext(input: unknown, ctx: ValidationContext) {
66
62
  const result = ctx.validate(input, this.schema)
67
63
 
68
64
  if (!result.success) {
69
65
  return result
70
66
  }
71
67
 
72
- if (this.$type !== result.value.$type) {
68
+ if (result.value.$type !== this.$type) {
73
69
  return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])
74
70
  }
75
71
 
76
- return result as ValidationResult<RecordSchemaOutput<T, S>>
72
+ return result
77
73
  }
78
74
  }
79
75
 
@@ -92,10 +88,10 @@ export type RecordKeySchema<Key extends LexiconRecordKey> = Schema<
92
88
  RecordKeySchemaOutput<Key>
93
89
  >
94
90
 
95
- const keySchema = new StringSchema({ minLength: 1 })
96
- const tidSchema = new StringSchema({ format: 'tid' })
97
- const nsidSchema = new StringSchema({ format: 'nsid' })
98
- const selfLiteralSchema = new LiteralSchema('self')
91
+ const keySchema = string({ minLength: 1 })
92
+ const tidSchema = string({ format: 'tid' })
93
+ const nsidSchema = string({ format: 'nsid' })
94
+ const selfLiteralSchema = literal('self')
99
95
 
100
96
  function recordKey<Key extends LexiconRecordKey>(
101
97
  key: Key,
@@ -107,8 +103,46 @@ function recordKey<Key extends LexiconRecordKey>(
107
103
  if (key.startsWith('literal:')) {
108
104
  const value = key.slice(8) as RecordKeySchemaOutput<Key>
109
105
  if (value === 'self') return selfLiteralSchema as any
110
- return new LiteralSchema(value)
106
+ return literal(value)
111
107
  }
112
108
 
113
109
  throw new Error(`Unsupported record key type: ${key}`)
114
110
  }
111
+
112
+ /**
113
+ * Ensures that a `$type` used in a record is a valid NSID (i.e. no fragment).
114
+ */
115
+ type AsNsid<T> = T extends `${string}#${string}` ? never : T
116
+
117
+ /**
118
+ * This function offers two overloads:
119
+ * - One that allows creating a {@link RecordSchema}, and infer the output type
120
+ * from the provided arguments, without requiring to specify any of the
121
+ * generics. This is useful when you want to define a record without
122
+ * explicitly defining its interface. This version does not support circular
123
+ * references, as TypeScript cannot infer types in such cases.
124
+ * - One allows creating a {@link RecordSchema} with an explicitly defined
125
+ * interface. This will typically be used by codegen (`lex build`) to generate
126
+ * schemas that work even if they contain circular references.
127
+ */
128
+ export function record<
129
+ const K extends LexiconRecordKey,
130
+ const T extends NsidString,
131
+ const S extends Validator<{ [k: string]: unknown }>,
132
+ >(key: K, type: AsNsid<T>, validator: S): RecordSchema<K, T, S>
133
+ export function record<
134
+ const K extends LexiconRecordKey,
135
+ const V extends { $type: NsidString },
136
+ >(
137
+ key: K,
138
+ type: AsNsid<V['$type']>,
139
+ validator: Validator<Omit<V, '$type'>>,
140
+ ): RecordSchema<K, V['$type'], Validator<Omit<V, '$type'>>>
141
+ /*@__NO_SIDE_EFFECTS__*/
142
+ export function record<
143
+ const K extends LexiconRecordKey,
144
+ const T extends NsidString,
145
+ const S extends Validator<{ [k: string]: unknown }>,
146
+ >(key: K, type: T, validator: S) {
147
+ return new RecordSchema<K, T, S>(key, type, validator)
148
+ }