@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.
- 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-error.d.ts +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 +13 -5
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +4 -4
- 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 +7 -7
- 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 +26 -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 +19 -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 +13 -12
- 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 +74 -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,16 +1,17 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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 =
|
|
9
|
+
const schema = record(
|
|
9
10
|
'any',
|
|
10
11
|
'app.bsky.feed.post',
|
|
11
|
-
|
|
12
|
-
$type:
|
|
13
|
-
text:
|
|
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 =
|
|
67
|
+
const schema = record(
|
|
67
68
|
'any',
|
|
68
69
|
'app.bsky.feed.post',
|
|
69
|
-
|
|
70
|
-
|
|
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 =
|
|
127
|
+
const schema = record(
|
|
103
128
|
'any',
|
|
104
129
|
'app.bsky.feed.post',
|
|
105
|
-
|
|
106
|
-
$type:
|
|
107
|
-
text:
|
|
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 =
|
|
165
|
+
const schema = record(
|
|
138
166
|
'any',
|
|
139
167
|
'app.bsky.feed.post',
|
|
140
|
-
|
|
141
|
-
$type:
|
|
142
|
-
text:
|
|
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 =
|
|
201
|
+
const schema = record(
|
|
174
202
|
'tid',
|
|
175
203
|
'app.bsky.feed.post',
|
|
176
|
-
|
|
177
|
-
$type:
|
|
178
|
-
text:
|
|
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 =
|
|
237
|
+
const schema = record(
|
|
210
238
|
'nsid',
|
|
211
239
|
'app.bsky.feed.post',
|
|
212
|
-
|
|
213
|
-
$type:
|
|
214
|
-
text:
|
|
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 =
|
|
276
|
+
const schema = record(
|
|
249
277
|
'literal:self',
|
|
250
278
|
'app.bsky.feed.post',
|
|
251
|
-
|
|
252
|
-
$type:
|
|
253
|
-
text:
|
|
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 =
|
|
307
|
+
const schema = record(
|
|
280
308
|
'literal:customKey',
|
|
281
309
|
'app.bsky.feed.post',
|
|
282
|
-
|
|
283
|
-
$type:
|
|
284
|
-
text:
|
|
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 =
|
|
334
|
+
const schema = record(
|
|
307
335
|
'any',
|
|
308
336
|
'app.bsky.feed.post#main',
|
|
309
|
-
|
|
310
|
-
$type:
|
|
311
|
-
text:
|
|
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 =
|
|
369
|
+
const schema = record(
|
|
342
370
|
'any',
|
|
343
371
|
'app.bsky.feed.post',
|
|
344
|
-
|
|
345
|
-
$type:
|
|
346
|
-
text:
|
|
347
|
-
createdAt:
|
|
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 =
|
|
408
|
+
const schema = record(
|
|
381
409
|
'any',
|
|
382
410
|
'app.bsky.feed.post',
|
|
383
|
-
|
|
384
|
-
$type:
|
|
385
|
-
text:
|
|
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 =
|
|
473
|
+
const complexSchema = record(
|
|
446
474
|
'any',
|
|
447
475
|
'app.bsky.complex',
|
|
448
|
-
|
|
449
|
-
$type:
|
|
450
|
-
nested:
|
|
451
|
-
deep:
|
|
452
|
-
value:
|
|
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 =
|
|
499
|
+
const schema = record(
|
|
472
500
|
'any',
|
|
473
501
|
'app.bsky.feed.post',
|
|
474
|
-
|
|
475
|
-
$type:
|
|
476
|
-
text:
|
|
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 =
|
|
520
|
+
const schema = record(
|
|
493
521
|
'any',
|
|
494
522
|
'app.bsky.feed.post',
|
|
495
|
-
|
|
496
|
-
$type:
|
|
497
|
-
text:
|
|
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 =
|
|
537
|
+
const schema = record(
|
|
510
538
|
'any',
|
|
511
539
|
'app.bsky.feed.post',
|
|
512
|
-
|
|
513
|
-
$type:
|
|
514
|
-
text:
|
|
515
|
-
author:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
579
|
+
const schema = record(
|
|
560
580
|
'nsid',
|
|
561
581
|
'app.bsky.test',
|
|
562
|
-
|
|
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 =
|
|
589
|
+
const schema = record(
|
|
570
590
|
'literal:custom',
|
|
571
591
|
'app.bsky.test',
|
|
572
|
-
|
|
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 =
|
|
600
|
+
const schema = record(
|
|
581
601
|
'any',
|
|
582
602
|
'app.bsky.feed.post',
|
|
583
|
-
|
|
584
|
-
$type:
|
|
585
|
-
text:
|
|
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 =
|
|
636
|
+
const schema = record(
|
|
617
637
|
'any',
|
|
618
638
|
'app.bsky.feed.post',
|
|
619
|
-
|
|
620
|
-
$type:
|
|
621
|
-
text:
|
|
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 =
|
|
655
|
+
const schema = record(
|
|
636
656
|
'any',
|
|
637
657
|
'app.bsky.feed.post',
|
|
638
|
-
|
|
639
|
-
$type:
|
|
640
|
-
text:
|
|
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 =
|
|
672
|
+
const schema = record(
|
|
653
673
|
'any',
|
|
654
674
|
'app.bsky.feed.post#reply123',
|
|
655
|
-
|
|
656
|
-
$type:
|
|
657
|
-
text:
|
|
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 =
|
|
690
|
+
const schema = record(
|
|
671
691
|
'any',
|
|
672
692
|
'app.bsky.feed.post',
|
|
673
|
-
|
|
674
|
-
$type:
|
|
675
|
-
text:
|
|
693
|
+
object({
|
|
694
|
+
$type: string(),
|
|
695
|
+
text: string(),
|
|
676
696
|
}),
|
|
677
697
|
)
|
|
678
698
|
|
package/src/schema/record.ts
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
import {
|
|
2
2
|
$Typed,
|
|
3
|
-
|
|
3
|
+
$typed,
|
|
4
|
+
InferInput,
|
|
5
|
+
InferOutput,
|
|
4
6
|
LexiconRecordKey,
|
|
5
7
|
NsidString,
|
|
6
8
|
Schema,
|
|
7
9
|
TidString,
|
|
8
|
-
|
|
10
|
+
Unknown$TypedObject,
|
|
11
|
+
ValidationContext,
|
|
9
12
|
Validator,
|
|
10
|
-
ValidatorContext,
|
|
11
13
|
} from '../core.js'
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
> extends Schema<
|
|
29
|
-
|
|
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:
|
|
33
|
-
readonly $type:
|
|
34
|
-
readonly schema:
|
|
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
|
|
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
|
|
47
|
-
input:
|
|
48
|
-
): $Typed<
|
|
49
|
-
return input
|
|
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
|
|
59
|
-
return this.build
|
|
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 (
|
|
68
|
+
if (result.value.$type !== this.$type) {
|
|
73
69
|
return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])
|
|
74
70
|
}
|
|
75
71
|
|
|
76
|
-
return result
|
|
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 =
|
|
96
|
-
const tidSchema =
|
|
97
|
-
const nsidSchema =
|
|
98
|
-
const selfLiteralSchema =
|
|
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
|
|
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
|
+
}
|