@atproto/lex-schema 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/LICENSE.txt +1 -1
- package/dist/core/$type.d.ts +11 -0
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js +4 -0
- package/dist/core/$type.js.map +1 -1
- package/dist/core/schema.d.ts +31 -24
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +38 -8
- package/dist/core/schema.js.map +1 -1
- package/dist/core/string-format.d.ts +35 -35
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +49 -91
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-issue.js +1 -1
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +53 -32
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +18 -22
- package/dist/core/validator.js.map +1 -1
- package/dist/external.d.ts +0 -85
- package/dist/external.d.ts.map +1 -1
- package/dist/external.js +0 -164
- package/dist/external.js.map +1 -1
- package/dist/helpers.d.ts +10 -5
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +3 -3
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +9 -5
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +14 -5
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +9 -7
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +9 -5
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +3 -7
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +6 -7
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +3 -2
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +7 -3
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +10 -8
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +5 -1
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +6 -5
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +10 -4
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +8 -8
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +11 -2
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +21 -14
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +7 -0
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +7 -9
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +8 -4
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +5 -5
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +9 -5
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +4 -4
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +5 -0
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/literal.d.ts +6 -9
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +7 -4
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +3 -2
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +5 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +4 -3
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +6 -4
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +6 -5
- package/dist/schema/nullable.d.ts.map +1 -1
- package/dist/schema/nullable.js +9 -5
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.d.ts +10 -8
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +11 -3
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +7 -5
- package/dist/schema/optional.d.ts.map +1 -1
- package/dist/schema/optional.js +14 -6
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.d.ts +24 -13
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +47 -25
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +12 -9
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js +11 -0
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +1 -0
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +5 -0
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +6 -5
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js +5 -0
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +2 -1
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +5 -0
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +2 -1
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +5 -0
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +48 -30
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +12 -9
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +9 -6
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +9 -16
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +4 -4
- package/dist/schema/refine.d.ts.map +1 -1
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/regexp.d.ts +4 -3
- package/dist/schema/regexp.d.ts.map +1 -1
- package/dist/schema/regexp.js +5 -0
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts +7 -8
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +13 -19
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +2 -1
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js +5 -0
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +6 -5
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +5 -0
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +43 -26
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +6 -3
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +16 -25
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +7 -17
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +9 -21
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +15 -11
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +6 -6
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +7 -5
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown-object.d.ts +5 -4
- package/dist/schema/unknown-object.d.ts.map +1 -1
- package/dist/schema/unknown-object.js +5 -1
- package/dist/schema/unknown-object.js.map +1 -1
- package/dist/schema/unknown.d.ts +3 -2
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +5 -1
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.d.ts +9 -0
- package/dist/schema/with-default.d.ts.map +1 -0
- package/dist/schema/with-default.js +27 -0
- package/dist/schema/with-default.js.map +1 -0
- package/dist/schema.d.ts +2 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -4
- package/dist/schema.js.map +1 -1
- package/dist/util/assertion-util.d.ts +0 -6
- package/dist/util/assertion-util.d.ts.map +1 -1
- package/dist/util/assertion-util.js +0 -28
- package/dist/util/assertion-util.js.map +1 -1
- package/dist/util/memoize.d.ts +2 -2
- package/dist/util/memoize.d.ts.map +1 -1
- package/dist/util/memoize.js +23 -39
- package/dist/util/memoize.js.map +1 -1
- package/package.json +3 -3
- package/src/core/$type.test.ts +20 -0
- package/src/core/$type.ts +30 -0
- package/src/core/schema.ts +86 -38
- package/src/core/string-format.ts +119 -158
- package/src/core/validation-issue.ts +1 -1
- package/src/core/validator.ts +93 -53
- package/src/external.ts +0 -404
- package/src/helpers.test.ts +22 -21
- package/src/helpers.ts +14 -14
- package/src/schema/array.test.ts +38 -40
- package/src/schema/array.ts +35 -13
- package/src/schema/blob.test.ts +21 -21
- package/src/schema/blob.ts +19 -17
- package/src/schema/boolean.test.ts +9 -8
- package/src/schema/boolean.ts +7 -13
- package/src/schema/bytes.test.ts +13 -13
- package/src/schema/bytes.ts +13 -8
- package/src/schema/cid.test.ts +3 -3
- package/src/schema/cid.ts +15 -13
- package/src/schema/custom.test.ts +26 -26
- package/src/schema/custom.ts +20 -13
- package/src/schema/dict.test.ts +21 -39
- package/src/schema/dict.ts +28 -19
- package/src/schema/discriminated-union.test.ts +128 -128
- package/src/schema/discriminated-union.ts +45 -26
- package/src/schema/enum.test.ts +17 -16
- package/src/schema/enum.ts +16 -16
- package/src/schema/integer.test.ts +22 -21
- package/src/schema/integer.ts +12 -9
- package/src/schema/intersection.test.ts +10 -10
- package/src/schema/intersection.ts +17 -14
- package/src/schema/literal.test.ts +35 -34
- package/src/schema/literal.ts +12 -15
- package/src/schema/never.test.ts +5 -5
- package/src/schema/never.ts +7 -2
- package/src/schema/null.test.ts +3 -3
- package/src/schema/null.ts +9 -9
- package/src/schema/nullable.test.ts +31 -42
- package/src/schema/nullable.ts +17 -9
- package/src/schema/object.test.ts +10 -12
- package/src/schema/object.ts +27 -18
- package/src/schema/optional.test.ts +21 -28
- package/src/schema/optional.ts +27 -10
- package/src/schema/params.test.ts +471 -47
- package/src/schema/params.ts +72 -38
- package/src/schema/payload.test.ts +150 -156
- package/src/schema/payload.ts +35 -19
- package/src/schema/permission-set.test.ts +206 -273
- package/src/schema/permission-set.ts +8 -0
- package/src/schema/permission.test.ts +177 -177
- package/src/schema/permission.ts +13 -5
- package/src/schema/procedure.test.ts +183 -242
- package/src/schema/procedure.ts +18 -5
- package/src/schema/query.test.ts +186 -200
- package/src/schema/query.ts +16 -4
- package/src/schema/record.test.ts +121 -101
- package/src/schema/record.ts +74 -40
- package/src/schema/ref.test.ts +101 -118
- package/src/schema/ref.ts +33 -28
- package/src/schema/refine.test.ts +28 -28
- package/src/schema/refine.ts +23 -20
- package/src/schema/regexp.test.ts +29 -33
- package/src/schema/regexp.ts +11 -7
- package/src/schema/string.test.ts +35 -35
- package/src/schema/string.ts +24 -33
- package/src/schema/subscription.test.ts +259 -387
- package/src/schema/subscription.ts +16 -4
- package/src/schema/token.test.ts +47 -324
- package/src/schema/token.ts +14 -7
- package/src/schema/typed-object.test.ts +98 -81
- package/src/schema/typed-object.ts +68 -33
- package/src/schema/typed-ref.test.ts +206 -234
- package/src/schema/typed-ref.ts +40 -42
- package/src/schema/typed-union.test.ts +40 -64
- package/src/schema/typed-union.ts +36 -58
- package/src/schema/union.test.ts +17 -27
- package/src/schema/union.ts +20 -16
- package/src/schema/unknown-object.test.ts +8 -8
- package/src/schema/unknown-object.ts +9 -7
- package/src/schema/unknown.test.ts +4 -4
- package/src/schema/unknown.ts +7 -5
- package/src/schema/with-default.ts +35 -0
- package/src/schema.ts +2 -6
- package/src/util/assertion-util.ts +0 -39
- package/src/util/memoize.ts +26 -46
- package/dist/schema/_parameters.d.ts +0 -17
- package/dist/schema/_parameters.d.ts.map +0 -1
- package/dist/schema/_parameters.js +0 -20
- package/dist/schema/_parameters.js.map +0 -1
- package/src/schema/_parameters.test.ts +0 -417
- package/src/schema/_parameters.ts +0 -26
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
2
|
+
import { Infer, Unknown$Type, Unknown$TypedObject } from '../core.js'
|
|
3
|
+
import { enumSchema } from './enum.js'
|
|
4
|
+
import { integer } from './integer.js'
|
|
5
|
+
import { nullable } from './nullable.js'
|
|
6
|
+
import { object } from './object.js'
|
|
7
|
+
import { optional } from './optional.js'
|
|
8
|
+
import { string } from './string.js'
|
|
9
|
+
import { typedObject } from './typed-object.js'
|
|
9
10
|
|
|
10
11
|
describe('TypedObjectSchema', () => {
|
|
11
|
-
const schema =
|
|
12
|
-
'app.bsky.feed.post
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const schema = typedObject(
|
|
13
|
+
'app.bsky.feed.post',
|
|
14
|
+
'main',
|
|
15
|
+
object({
|
|
16
|
+
text: string(),
|
|
17
|
+
likes: optional(integer()),
|
|
16
18
|
}),
|
|
17
19
|
)
|
|
20
|
+
type Schema = Infer<typeof schema>
|
|
18
21
|
|
|
19
22
|
describe('basic validation', () => {
|
|
20
23
|
it('validates plain objects without $type', () => {
|
|
@@ -27,7 +30,7 @@ describe('TypedObjectSchema', () => {
|
|
|
27
30
|
|
|
28
31
|
it('validates plain objects with matching $type', () => {
|
|
29
32
|
const result = schema.safeParse({
|
|
30
|
-
$type: 'app.bsky.feed.post
|
|
33
|
+
$type: 'app.bsky.feed.post',
|
|
31
34
|
text: 'Hello world',
|
|
32
35
|
likes: 5,
|
|
33
36
|
})
|
|
@@ -36,7 +39,7 @@ describe('TypedObjectSchema', () => {
|
|
|
36
39
|
|
|
37
40
|
it('rejects objects with non-matching $type', () => {
|
|
38
41
|
const result = schema.safeParse({
|
|
39
|
-
$type: 'app.bsky.feed.like
|
|
42
|
+
$type: 'app.bsky.feed.like',
|
|
40
43
|
text: 'Hello world',
|
|
41
44
|
likes: 5,
|
|
42
45
|
})
|
|
@@ -142,15 +145,15 @@ describe('TypedObjectSchema', () => {
|
|
|
142
145
|
|
|
143
146
|
it('rejects object $type', () => {
|
|
144
147
|
const result = schema.safeParse({
|
|
145
|
-
$type: { type: 'app.bsky.feed.post
|
|
148
|
+
$type: { type: 'app.bsky.feed.post' },
|
|
146
149
|
text: 'Hello world',
|
|
147
150
|
})
|
|
148
151
|
expect(result.success).toBe(false)
|
|
149
152
|
})
|
|
150
153
|
|
|
151
|
-
it('rejects
|
|
154
|
+
it('rejects non-normalized $type', () => {
|
|
152
155
|
const result = schema.safeParse({
|
|
153
|
-
$type: 'app.bsky.feed.post',
|
|
156
|
+
$type: 'app.bsky.feed.post#main',
|
|
154
157
|
text: 'Hello world',
|
|
155
158
|
})
|
|
156
159
|
expect(result.success).toBe(false)
|
|
@@ -185,12 +188,12 @@ describe('TypedObjectSchema', () => {
|
|
|
185
188
|
})
|
|
186
189
|
|
|
187
190
|
it('returns true for objects with matching $type', () => {
|
|
188
|
-
const obj = { $type: 'app.bsky.feed.post
|
|
191
|
+
const obj = { $type: 'app.bsky.feed.post', text: 'Hello' }
|
|
189
192
|
expect(schema.isTypeOf(obj)).toBe(true)
|
|
190
193
|
})
|
|
191
194
|
|
|
192
195
|
it('returns false for objects with non-matching $type', () => {
|
|
193
|
-
const obj = { $type: 'app.bsky.feed.like
|
|
196
|
+
const obj = { $type: 'app.bsky.feed.like', text: 'Hello' }
|
|
194
197
|
expect(schema.isTypeOf(obj)).toBe(false)
|
|
195
198
|
})
|
|
196
199
|
|
|
@@ -203,6 +206,30 @@ describe('TypedObjectSchema', () => {
|
|
|
203
206
|
const obj = { $type: 123, text: 'Hello' }
|
|
204
207
|
expect(schema.isTypeOf(obj)).toBe(false)
|
|
205
208
|
})
|
|
209
|
+
|
|
210
|
+
it('properly discriminates Unknown$TypeObject', () => {
|
|
211
|
+
function foo(value: Unknown$TypedObject | Schema) {
|
|
212
|
+
if (schema.isTypeOf(value)) {
|
|
213
|
+
value.text
|
|
214
|
+
} else {
|
|
215
|
+
// @ts-expect-error
|
|
216
|
+
value.text
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
foo({
|
|
221
|
+
$type: 'app.bsky.feed.post',
|
|
222
|
+
text: 'aze',
|
|
223
|
+
// @ts-expect-error
|
|
224
|
+
unknownProperty: 'should not be allowed !',
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
foo({
|
|
228
|
+
$type: 'blah' as Unknown$Type,
|
|
229
|
+
// @ts-expect-error
|
|
230
|
+
unknownProperty: 'should not be allowed !',
|
|
231
|
+
})
|
|
232
|
+
})
|
|
206
233
|
})
|
|
207
234
|
|
|
208
235
|
describe('$isTypeOf method', () => {
|
|
@@ -212,19 +239,19 @@ describe('TypedObjectSchema', () => {
|
|
|
212
239
|
})
|
|
213
240
|
|
|
214
241
|
it('returns true for objects with matching $type', () => {
|
|
215
|
-
const obj = { $type: 'app.bsky.feed.post
|
|
242
|
+
const obj = { $type: 'app.bsky.feed.post', text: 'Hello' }
|
|
216
243
|
expect(schema.$isTypeOf(obj)).toBe(true)
|
|
217
244
|
})
|
|
218
245
|
|
|
219
246
|
it('returns false for objects with non-matching $type', () => {
|
|
220
|
-
const obj = { $type: 'app.bsky.feed.like
|
|
247
|
+
const obj = { $type: 'app.bsky.feed.like', text: 'Hello' }
|
|
221
248
|
expect(schema.$isTypeOf(obj)).toBe(false)
|
|
222
249
|
})
|
|
223
250
|
|
|
224
251
|
it('behaves identically to isTypeOf', () => {
|
|
225
252
|
const obj1 = { text: 'Hello' }
|
|
226
|
-
const obj2 = { $type: 'app.bsky.feed.post
|
|
227
|
-
const obj3 = { $type: 'app.bsky.feed.like
|
|
253
|
+
const obj2 = { $type: 'app.bsky.feed.post', text: 'Hello' }
|
|
254
|
+
const obj3 = { $type: 'app.bsky.feed.like', text: 'Hello' }
|
|
228
255
|
|
|
229
256
|
expect(schema.$isTypeOf(obj1)).toBe(schema.isTypeOf(obj1))
|
|
230
257
|
expect(schema.$isTypeOf(obj2)).toBe(schema.isTypeOf(obj2))
|
|
@@ -239,7 +266,7 @@ describe('TypedObjectSchema', () => {
|
|
|
239
266
|
expect(result).toEqual({
|
|
240
267
|
text: 'Hello world',
|
|
241
268
|
likes: 5,
|
|
242
|
-
$type: 'app.bsky.feed.post
|
|
269
|
+
$type: 'app.bsky.feed.post',
|
|
243
270
|
})
|
|
244
271
|
})
|
|
245
272
|
|
|
@@ -248,7 +275,7 @@ describe('TypedObjectSchema', () => {
|
|
|
248
275
|
const result = schema.build(input)
|
|
249
276
|
expect(result).toEqual({
|
|
250
277
|
text: 'Hello world',
|
|
251
|
-
$type: 'app.bsky.feed.post
|
|
278
|
+
$type: 'app.bsky.feed.post',
|
|
252
279
|
})
|
|
253
280
|
})
|
|
254
281
|
|
|
@@ -259,7 +286,7 @@ describe('TypedObjectSchema', () => {
|
|
|
259
286
|
text: 'Hello',
|
|
260
287
|
likes: 10,
|
|
261
288
|
extra: 'value',
|
|
262
|
-
$type: 'app.bsky.feed.post
|
|
289
|
+
$type: 'app.bsky.feed.post',
|
|
263
290
|
})
|
|
264
291
|
})
|
|
265
292
|
|
|
@@ -271,13 +298,10 @@ describe('TypedObjectSchema', () => {
|
|
|
271
298
|
})
|
|
272
299
|
|
|
273
300
|
it('adds $type to empty object', () => {
|
|
274
|
-
const emptySchema =
|
|
275
|
-
'app.bsky.test#main',
|
|
276
|
-
new ObjectSchema({}),
|
|
277
|
-
)
|
|
301
|
+
const emptySchema = typedObject('app.bsky.test', 'main', object({}))
|
|
278
302
|
const input = {}
|
|
279
303
|
const result = emptySchema.build(input)
|
|
280
|
-
expect(result).toEqual({ $type: 'app.bsky.test
|
|
304
|
+
expect(result).toEqual({ $type: 'app.bsky.test' })
|
|
281
305
|
})
|
|
282
306
|
})
|
|
283
307
|
|
|
@@ -288,7 +312,7 @@ describe('TypedObjectSchema', () => {
|
|
|
288
312
|
expect(result).toEqual({
|
|
289
313
|
text: 'Hello world',
|
|
290
314
|
likes: 5,
|
|
291
|
-
$type: 'app.bsky.feed.post
|
|
315
|
+
$type: 'app.bsky.feed.post',
|
|
292
316
|
})
|
|
293
317
|
})
|
|
294
318
|
|
|
@@ -309,15 +333,14 @@ describe('TypedObjectSchema', () => {
|
|
|
309
333
|
})
|
|
310
334
|
|
|
311
335
|
describe('with complex nested schemas', () => {
|
|
312
|
-
const complexSchema =
|
|
313
|
-
'app.bsky.actor.profile
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
),
|
|
336
|
+
const complexSchema = typedObject(
|
|
337
|
+
'app.bsky.actor.profile',
|
|
338
|
+
'main',
|
|
339
|
+
object({
|
|
340
|
+
displayName: string(),
|
|
341
|
+
bio: optional(string({ maxLength: 256 })),
|
|
342
|
+
followerCount: optional(integer({ minimum: 0 })),
|
|
343
|
+
verified: optional(nullable(enumSchema([true, false]))),
|
|
321
344
|
}),
|
|
322
345
|
)
|
|
323
346
|
|
|
@@ -357,7 +380,7 @@ describe('TypedObjectSchema', () => {
|
|
|
357
380
|
|
|
358
381
|
it('validates with matching $type', () => {
|
|
359
382
|
const result = complexSchema.safeParse({
|
|
360
|
-
$type: 'app.bsky.actor.profile
|
|
383
|
+
$type: 'app.bsky.actor.profile',
|
|
361
384
|
displayName: 'John Doe',
|
|
362
385
|
})
|
|
363
386
|
expect(result.success).toBe(true)
|
|
@@ -365,7 +388,7 @@ describe('TypedObjectSchema', () => {
|
|
|
365
388
|
|
|
366
389
|
it('rejects with non-matching $type', () => {
|
|
367
390
|
const result = complexSchema.safeParse({
|
|
368
|
-
$type: 'app.bsky.feed.post
|
|
391
|
+
$type: 'app.bsky.feed.post',
|
|
369
392
|
displayName: 'John Doe',
|
|
370
393
|
})
|
|
371
394
|
expect(result.success).toBe(false)
|
|
@@ -374,18 +397,20 @@ describe('TypedObjectSchema', () => {
|
|
|
374
397
|
|
|
375
398
|
describe('with different $type formats', () => {
|
|
376
399
|
it('validates with main type', () => {
|
|
377
|
-
const mainSchema =
|
|
378
|
-
'app.bsky.feed.post
|
|
379
|
-
|
|
400
|
+
const mainSchema = typedObject(
|
|
401
|
+
'app.bsky.feed.post',
|
|
402
|
+
'main',
|
|
403
|
+
object({ text: string() }),
|
|
380
404
|
)
|
|
381
405
|
const result = mainSchema.safeParse({ text: 'Hello' })
|
|
382
406
|
expect(result.success).toBe(true)
|
|
383
407
|
})
|
|
384
408
|
|
|
385
409
|
it('validates with custom fragment', () => {
|
|
386
|
-
const fragmentSchema =
|
|
387
|
-
'app.bsky.feed.post
|
|
388
|
-
|
|
410
|
+
const fragmentSchema = typedObject(
|
|
411
|
+
'app.bsky.feed.post',
|
|
412
|
+
'reply',
|
|
413
|
+
object({ text: string() }),
|
|
389
414
|
)
|
|
390
415
|
const result = fragmentSchema.safeParse({
|
|
391
416
|
$type: 'app.bsky.feed.post#reply',
|
|
@@ -395,9 +420,10 @@ describe('TypedObjectSchema', () => {
|
|
|
395
420
|
})
|
|
396
421
|
|
|
397
422
|
it('distinguishes between different fragments', () => {
|
|
398
|
-
const replySchema =
|
|
399
|
-
'app.bsky.feed.post
|
|
400
|
-
|
|
423
|
+
const replySchema = typedObject(
|
|
424
|
+
'app.bsky.feed.post',
|
|
425
|
+
'reply',
|
|
426
|
+
object({ text: string() }),
|
|
401
427
|
)
|
|
402
428
|
const result = replySchema.safeParse({
|
|
403
429
|
$type: 'app.bsky.feed.post#quote',
|
|
@@ -407,9 +433,10 @@ describe('TypedObjectSchema', () => {
|
|
|
407
433
|
})
|
|
408
434
|
|
|
409
435
|
it('validates with long NSID', () => {
|
|
410
|
-
const longSchema =
|
|
411
|
-
'com.example.app.feature.action.detail
|
|
412
|
-
|
|
436
|
+
const longSchema = typedObject(
|
|
437
|
+
'com.example.app.feature.action.detail',
|
|
438
|
+
'variant',
|
|
439
|
+
object({ value: string() }),
|
|
413
440
|
)
|
|
414
441
|
const result = longSchema.safeParse({
|
|
415
442
|
$type: 'com.example.app.feature.action.detail#variant',
|
|
@@ -421,10 +448,7 @@ describe('TypedObjectSchema', () => {
|
|
|
421
448
|
|
|
422
449
|
describe('edge cases', () => {
|
|
423
450
|
it('validates object with only extra properties', () => {
|
|
424
|
-
const minimalSchema =
|
|
425
|
-
'app.bsky.test#main',
|
|
426
|
-
new ObjectSchema({}),
|
|
427
|
-
)
|
|
451
|
+
const minimalSchema = typedObject('app.bsky.test', 'main', object({}))
|
|
428
452
|
const result = minimalSchema.safeParse({
|
|
429
453
|
extra1: 'value1',
|
|
430
454
|
extra2: 'value2',
|
|
@@ -433,21 +457,15 @@ describe('TypedObjectSchema', () => {
|
|
|
433
457
|
})
|
|
434
458
|
|
|
435
459
|
it('validates empty object with no required properties', () => {
|
|
436
|
-
const minimalSchema =
|
|
437
|
-
'app.bsky.test#main',
|
|
438
|
-
new ObjectSchema({}),
|
|
439
|
-
)
|
|
460
|
+
const minimalSchema = typedObject('app.bsky.test', 'main', object({}))
|
|
440
461
|
const result = minimalSchema.safeParse({})
|
|
441
462
|
expect(result.success).toBe(true)
|
|
442
463
|
})
|
|
443
464
|
|
|
444
465
|
it('validates with $type as only property', () => {
|
|
445
|
-
const minimalSchema =
|
|
446
|
-
'app.bsky.test#main',
|
|
447
|
-
new ObjectSchema({}),
|
|
448
|
-
)
|
|
466
|
+
const minimalSchema = typedObject('app.bsky.test', 'main', object({}))
|
|
449
467
|
const result = minimalSchema.safeParse({
|
|
450
|
-
$type: 'app.bsky.test
|
|
468
|
+
$type: 'app.bsky.test',
|
|
451
469
|
})
|
|
452
470
|
expect(result.success).toBe(true)
|
|
453
471
|
})
|
|
@@ -494,15 +512,14 @@ describe('TypedObjectSchema', () => {
|
|
|
494
512
|
})
|
|
495
513
|
|
|
496
514
|
describe('integration with all property types', () => {
|
|
497
|
-
const fullSchema =
|
|
498
|
-
'app.bsky.test
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
),
|
|
515
|
+
const fullSchema = typedObject(
|
|
516
|
+
'app.bsky.test',
|
|
517
|
+
'full',
|
|
518
|
+
object({
|
|
519
|
+
required: string(),
|
|
520
|
+
optional: optional(string()),
|
|
521
|
+
nullable: nullable(string()),
|
|
522
|
+
optionalNullable: optional(nullable(string())),
|
|
506
523
|
}),
|
|
507
524
|
)
|
|
508
525
|
|
|
@@ -576,9 +593,9 @@ describe('TypedObjectSchema', () => {
|
|
|
576
593
|
})
|
|
577
594
|
|
|
578
595
|
describe('comparison with plain ObjectSchema', () => {
|
|
579
|
-
const plainSchema =
|
|
580
|
-
text:
|
|
581
|
-
likes:
|
|
596
|
+
const plainSchema = object({
|
|
597
|
+
text: string(),
|
|
598
|
+
likes: optional(integer()),
|
|
582
599
|
})
|
|
583
600
|
|
|
584
601
|
it('typed schema accepts same input as plain schema', () => {
|
|
@@ -605,7 +622,7 @@ describe('TypedObjectSchema', () => {
|
|
|
605
622
|
})
|
|
606
623
|
|
|
607
624
|
it('typed schema accepts matching $type', () => {
|
|
608
|
-
const input = { $type: 'app.bsky.feed.post
|
|
625
|
+
const input = { $type: 'app.bsky.feed.post', text: 'Hello' }
|
|
609
626
|
const typedResult = schema.safeParse(input)
|
|
610
627
|
expect(typedResult.success).toBe(true)
|
|
611
628
|
})
|
|
@@ -1,61 +1,60 @@
|
|
|
1
1
|
import { isPlainObject } from '@atproto/lex-data'
|
|
2
2
|
import {
|
|
3
3
|
$Type,
|
|
4
|
+
$TypeOf,
|
|
4
5
|
$Typed,
|
|
5
6
|
$TypedMaybe,
|
|
6
|
-
|
|
7
|
+
$type,
|
|
8
|
+
$typed,
|
|
9
|
+
InferInput,
|
|
10
|
+
InferOutput,
|
|
11
|
+
NsidString,
|
|
7
12
|
Schema,
|
|
8
|
-
|
|
13
|
+
Unknown$TypedObject,
|
|
14
|
+
ValidationContext,
|
|
9
15
|
Validator,
|
|
10
|
-
ValidatorContext,
|
|
11
16
|
} from '../core.js'
|
|
12
|
-
import { TypedObject } from './typed-union.js'
|
|
13
|
-
|
|
14
|
-
export type TypedObjectSchemaOutput<
|
|
15
|
-
T extends $Type,
|
|
16
|
-
S extends Validator<{ [k: string]: unknown }>,
|
|
17
|
-
> = $TypedMaybe<Infer<S>, T>
|
|
18
17
|
|
|
19
18
|
export class TypedObjectSchema<
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
> extends Schema<
|
|
19
|
+
const TType extends $Type = $Type,
|
|
20
|
+
const TShape extends Validator<{ [k: string]: unknown }> = any,
|
|
21
|
+
> extends Schema<
|
|
22
|
+
$TypedMaybe<InferInput<TShape>, TType>,
|
|
23
|
+
$TypedMaybe<InferOutput<TShape>, TType>
|
|
24
|
+
> {
|
|
23
25
|
constructor(
|
|
24
|
-
readonly $type:
|
|
25
|
-
readonly schema:
|
|
26
|
+
readonly $type: TType,
|
|
27
|
+
readonly schema: TShape,
|
|
26
28
|
) {
|
|
27
29
|
super()
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
isTypeOf<X extends Record<string, unknown>>(
|
|
31
33
|
value: X,
|
|
32
|
-
): value is
|
|
33
|
-
|
|
34
|
-
TypedObject
|
|
35
|
-
> {
|
|
34
|
+
): value is X extends { $type?: TType }
|
|
35
|
+
? X
|
|
36
|
+
: $TypedMaybe<Exclude<X, Unknown$TypedObject>, TType> {
|
|
36
37
|
return value.$type === undefined || value.$type === this.$type
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
build
|
|
40
|
-
input:
|
|
41
|
-
): $Typed<
|
|
42
|
-
return input.$type
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
build(
|
|
41
|
+
input: Omit<InferInput<this>, '$type'>,
|
|
42
|
+
): $Typed<InferOutput<this>, TType> {
|
|
43
|
+
return this.parse($typed(input, this.$type)) as $Typed<
|
|
44
|
+
InferOutput<this>,
|
|
45
|
+
TType
|
|
46
|
+
>
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
$isTypeOf<X extends Record<string, unknown>>(value: X) {
|
|
48
50
|
return this.isTypeOf(value)
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
$build
|
|
52
|
-
return this.build
|
|
53
|
+
$build(input: Omit<InferInput<this>, '$type'>) {
|
|
54
|
+
return this.build(input)
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
validateInContext(
|
|
56
|
-
input: unknown,
|
|
57
|
-
ctx: ValidatorContext,
|
|
58
|
-
): ValidationResult<TypedObjectSchemaOutput<T, S>> {
|
|
57
|
+
validateInContext(input: unknown, ctx: ValidationContext) {
|
|
59
58
|
if (!isPlainObject(input)) {
|
|
60
59
|
return ctx.issueInvalidType(input, 'object')
|
|
61
60
|
}
|
|
@@ -68,8 +67,44 @@ export class TypedObjectSchema<
|
|
|
68
67
|
return ctx.issueInvalidPropertyValue(input, '$type', [this.$type])
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
return ctx.validate(input, this.schema)
|
|
72
|
-
TypedObjectSchemaOutput<T, S>
|
|
73
|
-
>
|
|
70
|
+
return ctx.validate(input, this.schema)
|
|
74
71
|
}
|
|
75
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* This function offers two overloads:
|
|
76
|
+
* - One that allows creating a {@link TypedObjectSchema}, and infer the output
|
|
77
|
+
* type from the provided arguments, without requiring to specify any of the
|
|
78
|
+
* generics. This is useful when you want to define a record without
|
|
79
|
+
* explicitly defining its interface. This version does not support circular
|
|
80
|
+
* references, as TypeScript cannot infer types in such cases.
|
|
81
|
+
* - One allows creating a {@link TypedObjectSchema} with an explicitly defined
|
|
82
|
+
* interface. This will typically be used by codegen (`lex build`) to generate
|
|
83
|
+
* schemas that work even if they contain circular references.
|
|
84
|
+
*/
|
|
85
|
+
export function typedObject<
|
|
86
|
+
const N extends NsidString,
|
|
87
|
+
const H extends string,
|
|
88
|
+
const S extends Validator<{ [k: string]: unknown }>,
|
|
89
|
+
>(nsid: N, hash: H, validator: S): TypedObjectSchema<$Type<N, H>, S>
|
|
90
|
+
export function typedObject<V extends { $type?: $Type }>(
|
|
91
|
+
nsid: V extends { $type?: infer T extends string }
|
|
92
|
+
? T extends `${infer N}#${string}`
|
|
93
|
+
? N
|
|
94
|
+
: T // (T is a "main" type, so already an NSID)
|
|
95
|
+
: never,
|
|
96
|
+
hash: V extends { $type?: infer T extends string }
|
|
97
|
+
? T extends `${string}#${infer H}`
|
|
98
|
+
? H
|
|
99
|
+
: 'main'
|
|
100
|
+
: never,
|
|
101
|
+
validator: Validator<Omit<V, '$type'>>,
|
|
102
|
+
): TypedObjectSchema<$TypeOf<V>, Validator<V>>
|
|
103
|
+
/*@__NO_SIDE_EFFECTS__*/
|
|
104
|
+
export function typedObject<
|
|
105
|
+
const N extends NsidString,
|
|
106
|
+
const H extends string,
|
|
107
|
+
const S extends Validator<{ [k: string]: unknown }>,
|
|
108
|
+
>(nsid: N, hash: H, validator: S) {
|
|
109
|
+
return new TypedObjectSchema<$Type<N, H>, S>($type(nsid, hash), validator)
|
|
110
|
+
}
|