@atproto/lex-schema 0.1.5 → 0.1.6
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 +14 -0
- package/dist/core/$type.d.ts +2 -2
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js.map +1 -1
- package/dist/core/record-key.d.ts +1 -1
- package/dist/core/record-key.d.ts.map +1 -1
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/schema.d.ts +3 -2
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +1 -1
- package/dist/core/schema.js.map +1 -1
- package/dist/core/standard-schema.d.ts +2 -2
- package/dist/core/standard-schema.d.ts.map +1 -1
- package/dist/core/standard-schema.js.map +1 -1
- package/dist/core/string-format.d.ts +2 -2
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-error.d.ts +1 -1
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +1 -1
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validator.d.ts +1 -1
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +1 -1
- package/dist/core/validator.js.map +1 -1
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +2 -2
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +1 -1
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +1 -1
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +1 -1
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +2 -2
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.js +1 -1
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.js +1 -1
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +1 -1
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +3 -3
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.js +1 -1
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +1 -1
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +1 -1
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +1 -1
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +2 -1
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.js +1 -1
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.js +1 -1
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +1 -1
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +3 -1
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/lex-map.d.ts +1 -1
- package/dist/schema/lex-map.d.ts.map +1 -1
- package/dist/schema/lex-map.js +1 -1
- package/dist/schema/lex-map.js.map +1 -1
- package/dist/schema/lex-value.d.ts +1 -1
- package/dist/schema/lex-value.d.ts.map +1 -1
- package/dist/schema/lex-value.js +1 -1
- package/dist/schema/lex-value.js.map +1 -1
- package/dist/schema/literal.js +1 -1
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.js +1 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.js +1 -1
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +1 -1
- package/dist/schema/nullable.d.ts.map +1 -1
- package/dist/schema/nullable.js +1 -1
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.d.ts +2 -1
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +1 -1
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +2 -1
- package/dist/schema/optional.d.ts.map +1 -1
- package/dist/schema/optional.js +2 -1
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.d.ts +1 -1
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +1 -1
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +3 -2
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js +2 -1
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +1 -1
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +1 -0
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +1 -1
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +1 -1
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +2 -0
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +1 -1
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +2 -0
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +2 -2
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +1 -1
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +1 -1
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +1 -1
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +2 -2
- package/dist/schema/refine.d.ts.map +1 -1
- package/dist/schema/refine.js +1 -1
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/regexp.js +1 -1
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts +2 -2
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +1 -1
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +3 -2
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js +2 -0
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +1 -1
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +1 -1
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +2 -2
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +1 -1
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +1 -1
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +1 -1
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +1 -1
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +3 -1
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +1 -1
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +1 -1
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown.js +1 -1
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.d.ts +1 -1
- package/dist/schema/with-default.d.ts.map +1 -1
- package/dist/schema/with-default.js +1 -1
- package/dist/schema/with-default.js.map +1 -1
- package/package.json +6 -10
- package/src/core/$type.test.ts +0 -24
- package/src/core/$type.ts +0 -199
- package/src/core/record-key.ts +0 -85
- package/src/core/result.ts +0 -15
- package/src/core/schema.ts +0 -412
- package/src/core/standard-schema.test.ts +0 -124
- package/src/core/standard-schema.ts +0 -31
- package/src/core/string-format.ts +0 -411
- package/src/core/types.ts +0 -120
- package/src/core/validation-error.ts +0 -134
- package/src/core/validation-issue.ts +0 -340
- package/src/core/validator.ts +0 -636
- package/src/core.ts +0 -9
- package/src/external.ts +0 -3
- package/src/helpers.test.ts +0 -694
- package/src/helpers.ts +0 -222
- package/src/index.ts +0 -3
- package/src/schema/array.test.ts +0 -251
- package/src/schema/array.ts +0 -126
- package/src/schema/blob.test.ts +0 -733
- package/src/schema/blob.ts +0 -150
- package/src/schema/boolean.test.ts +0 -118
- package/src/schema/boolean.ts +0 -46
- package/src/schema/bytes.test.ts +0 -227
- package/src/schema/bytes.ts +0 -81
- package/src/schema/cid.test.ts +0 -125
- package/src/schema/cid.ts +0 -69
- package/src/schema/custom.test.ts +0 -414
- package/src/schema/custom.ts +0 -106
- package/src/schema/dict.test.ts +0 -181
- package/src/schema/dict.ts +0 -122
- package/src/schema/discriminated-union.test.ts +0 -676
- package/src/schema/discriminated-union.ts +0 -196
- package/src/schema/enum.test.ts +0 -398
- package/src/schema/enum.ts +0 -77
- package/src/schema/integer.test.ts +0 -314
- package/src/schema/integer.ts +0 -86
- package/src/schema/intersection.test.ts +0 -33
- package/src/schema/intersection.ts +0 -113
- package/src/schema/lex-map.test.ts +0 -593
- package/src/schema/lex-map.ts +0 -63
- package/src/schema/lex-value.test.ts +0 -81
- package/src/schema/lex-value.ts +0 -86
- package/src/schema/literal.test.ts +0 -533
- package/src/schema/literal.ts +0 -70
- package/src/schema/never.test.ts +0 -175
- package/src/schema/never.ts +0 -56
- package/src/schema/null.test.ts +0 -80
- package/src/schema/null.ts +0 -49
- package/src/schema/nullable.test.ts +0 -470
- package/src/schema/nullable.ts +0 -74
- package/src/schema/object.test.ts +0 -69
- package/src/schema/object.ts +0 -136
- package/src/schema/optional.test.ts +0 -479
- package/src/schema/optional.ts +0 -92
- package/src/schema/params.test.ts +0 -1118
- package/src/schema/params.ts +0 -371
- package/src/schema/payload.test.ts +0 -340
- package/src/schema/payload.ts +0 -204
- package/src/schema/permission-set.test.ts +0 -613
- package/src/schema/permission-set.ts +0 -86
- package/src/schema/permission.test.ts +0 -537
- package/src/schema/permission.ts +0 -63
- package/src/schema/procedure.test.ts +0 -324
- package/src/schema/procedure.ts +0 -98
- package/src/schema/query.test.ts +0 -348
- package/src/schema/query.ts +0 -86
- package/src/schema/record.test.ts +0 -812
- package/src/schema/record.ts +0 -217
- package/src/schema/ref.test.ts +0 -349
- package/src/schema/ref.ts +0 -103
- package/src/schema/refine.test.ts +0 -579
- package/src/schema/refine.ts +0 -153
- package/src/schema/regexp.test.ts +0 -577
- package/src/schema/regexp.ts +0 -82
- package/src/schema/string.test.ts +0 -773
- package/src/schema/string.ts +0 -229
- package/src/schema/subscription.test.ts +0 -499
- package/src/schema/subscription.ts +0 -108
- package/src/schema/token.test.ts +0 -152
- package/src/schema/token.ts +0 -103
- package/src/schema/typed-object.test.ts +0 -745
- package/src/schema/typed-object.ts +0 -181
- package/src/schema/typed-ref.test.ts +0 -796
- package/src/schema/typed-ref.ts +0 -126
- package/src/schema/typed-union.test.ts +0 -355
- package/src/schema/typed-union.ts +0 -130
- package/src/schema/union.test.ts +0 -191
- package/src/schema/union.ts +0 -89
- package/src/schema/unknown.test.ts +0 -313
- package/src/schema/unknown.ts +0 -47
- package/src/schema/with-default.ts +0 -81
- package/src/schema.ts +0 -43
- package/src/util/array-agg.test.ts +0 -42
- package/src/util/array-agg.ts +0 -44
- package/src/util/assertion-util.ts +0 -1
- package/src/util/if-any.ts +0 -3
- package/src/util/lazy-property.ts +0 -14
- package/src/util/memoize.ts +0 -37
- package/tsconfig.build.json +0 -12
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
|
@@ -1,745 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
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'
|
|
10
|
-
|
|
11
|
-
describe('TypedObjectSchema', () => {
|
|
12
|
-
const schema = typedObject(
|
|
13
|
-
'app.bsky.feed.post',
|
|
14
|
-
'main',
|
|
15
|
-
object({
|
|
16
|
-
text: string(),
|
|
17
|
-
likes: optional(integer()),
|
|
18
|
-
}),
|
|
19
|
-
)
|
|
20
|
-
type Schema = Infer<typeof schema>
|
|
21
|
-
|
|
22
|
-
describe('basic validation', () => {
|
|
23
|
-
it('validates plain objects without $type', () => {
|
|
24
|
-
const result = schema.safeParse({
|
|
25
|
-
text: 'Hello world',
|
|
26
|
-
likes: 5,
|
|
27
|
-
})
|
|
28
|
-
expect(result.success).toBe(true)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('validates plain objects with matching $type', () => {
|
|
32
|
-
const result = schema.safeParse({
|
|
33
|
-
$type: 'app.bsky.feed.post',
|
|
34
|
-
text: 'Hello world',
|
|
35
|
-
likes: 5,
|
|
36
|
-
})
|
|
37
|
-
expect(result.success).toBe(true)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('rejects objects with non-matching $type', () => {
|
|
41
|
-
const result = schema.safeParse({
|
|
42
|
-
$type: 'app.bsky.feed.like',
|
|
43
|
-
text: 'Hello world',
|
|
44
|
-
likes: 5,
|
|
45
|
-
})
|
|
46
|
-
expect(result.success).toBe(false)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('rejects non-objects', () => {
|
|
50
|
-
const result = schema.safeParse('not an object')
|
|
51
|
-
expect(result.success).toBe(false)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('rejects null', () => {
|
|
55
|
-
const result = schema.safeParse(null)
|
|
56
|
-
expect(result.success).toBe(false)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('rejects undefined', () => {
|
|
60
|
-
const result = schema.safeParse(undefined)
|
|
61
|
-
expect(result.success).toBe(false)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('rejects arrays', () => {
|
|
65
|
-
const result = schema.safeParse(['text', 5])
|
|
66
|
-
expect(result.success).toBe(false)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('rejects numbers', () => {
|
|
70
|
-
const result = schema.safeParse(123)
|
|
71
|
-
expect(result.success).toBe(false)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('rejects booleans', () => {
|
|
75
|
-
const result = schema.safeParse(true)
|
|
76
|
-
expect(result.success).toBe(false)
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
describe('property validation', () => {
|
|
81
|
-
it('rejects missing required properties', () => {
|
|
82
|
-
const result = schema.safeParse({
|
|
83
|
-
likes: 5,
|
|
84
|
-
})
|
|
85
|
-
expect(result.success).toBe(false)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('validates optional properties', () => {
|
|
89
|
-
const result = schema.safeParse({
|
|
90
|
-
text: 'Hello world',
|
|
91
|
-
})
|
|
92
|
-
expect(result.success).toBe(true)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('rejects invalid property types', () => {
|
|
96
|
-
const result = schema.safeParse({
|
|
97
|
-
text: 'Hello world',
|
|
98
|
-
likes: 'five',
|
|
99
|
-
})
|
|
100
|
-
expect(result.success).toBe(false)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('rejects invalid required property types', () => {
|
|
104
|
-
const result = schema.safeParse({
|
|
105
|
-
text: 123,
|
|
106
|
-
likes: 5,
|
|
107
|
-
})
|
|
108
|
-
expect(result.success).toBe(false)
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('ignores extra properties', () => {
|
|
112
|
-
const result = schema.safeParse({
|
|
113
|
-
text: 'Hello world',
|
|
114
|
-
likes: 5,
|
|
115
|
-
extra: 'value',
|
|
116
|
-
})
|
|
117
|
-
expect(result.success).toBe(true)
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
describe('$type validation', () => {
|
|
122
|
-
it('treats undefined $type as valid', () => {
|
|
123
|
-
const result = schema.safeParse({
|
|
124
|
-
$type: undefined,
|
|
125
|
-
text: 'Hello world',
|
|
126
|
-
})
|
|
127
|
-
expect(result.success).toBe(true)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('rejects empty string $type', () => {
|
|
131
|
-
const result = schema.safeParse({
|
|
132
|
-
$type: '',
|
|
133
|
-
text: 'Hello world',
|
|
134
|
-
})
|
|
135
|
-
expect(result.success).toBe(false)
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it('rejects numeric $type', () => {
|
|
139
|
-
const result = schema.safeParse({
|
|
140
|
-
$type: 123,
|
|
141
|
-
text: 'Hello world',
|
|
142
|
-
})
|
|
143
|
-
expect(result.success).toBe(false)
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('rejects object $type', () => {
|
|
147
|
-
const result = schema.safeParse({
|
|
148
|
-
$type: { type: 'app.bsky.feed.post' },
|
|
149
|
-
text: 'Hello world',
|
|
150
|
-
})
|
|
151
|
-
expect(result.success).toBe(false)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('rejects non-normalized $type', () => {
|
|
155
|
-
const result = schema.safeParse({
|
|
156
|
-
$type: 'app.bsky.feed.post#main',
|
|
157
|
-
text: 'Hello world',
|
|
158
|
-
})
|
|
159
|
-
expect(result.success).toBe(false)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it('rejects $type with extra characters', () => {
|
|
163
|
-
const result = schema.safeParse({
|
|
164
|
-
$type: 'app.bsky.feed.post#main-extra',
|
|
165
|
-
text: 'Hello world',
|
|
166
|
-
})
|
|
167
|
-
expect(result.success).toBe(false)
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
it('rejects case-mismatched $type', () => {
|
|
171
|
-
const result = schema.safeParse({
|
|
172
|
-
$type: 'APP.BSKY.FEED.POST#MAIN',
|
|
173
|
-
text: 'Hello world',
|
|
174
|
-
})
|
|
175
|
-
expect(result.success).toBe(false)
|
|
176
|
-
})
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
describe('isTypeOf method', () => {
|
|
180
|
-
it('returns true for objects without $type', () => {
|
|
181
|
-
const obj = { text: 'Hello' }
|
|
182
|
-
expect(schema.isTypeOf(obj)).toBe(true)
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
it('returns true for objects with undefined $type', () => {
|
|
186
|
-
const obj = { $type: undefined, text: 'Hello' }
|
|
187
|
-
expect(schema.isTypeOf(obj)).toBe(true)
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('returns true for objects with matching $type', () => {
|
|
191
|
-
const obj = { $type: 'app.bsky.feed.post', text: 'Hello' }
|
|
192
|
-
expect(schema.isTypeOf(obj)).toBe(true)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('returns false for objects with non-matching $type', () => {
|
|
196
|
-
const obj = { $type: 'app.bsky.feed.like', text: 'Hello' }
|
|
197
|
-
expect(schema.isTypeOf(obj)).toBe(false)
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it('returns false for objects with empty $type', () => {
|
|
201
|
-
const obj = { $type: '', text: 'Hello' }
|
|
202
|
-
expect(schema.isTypeOf(obj)).toBe(false)
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
it('returns false for objects with numeric $type', () => {
|
|
206
|
-
const obj = { $type: 123, text: 'Hello' }
|
|
207
|
-
expect(schema.isTypeOf(obj)).toBe(false)
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
it('properly discriminates Unknown$TypeObject', () => {
|
|
211
|
-
function foo(value: Unknown$TypedObject | Schema) {
|
|
212
|
-
if (schema.isTypeOf(value)) {
|
|
213
|
-
void value.text
|
|
214
|
-
} else {
|
|
215
|
-
// @ts-expect-error
|
|
216
|
-
void 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
|
-
})
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
describe('$isTypeOf method', () => {
|
|
236
|
-
it('returns true for objects without $type', () => {
|
|
237
|
-
const obj = { text: 'Hello' }
|
|
238
|
-
expect(schema.$isTypeOf(obj)).toBe(true)
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
it('returns true for objects with matching $type', () => {
|
|
242
|
-
const obj = { $type: 'app.bsky.feed.post', text: 'Hello' }
|
|
243
|
-
expect(schema.$isTypeOf(obj)).toBe(true)
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
it('returns false for objects with non-matching $type', () => {
|
|
247
|
-
const obj = { $type: 'app.bsky.feed.like', text: 'Hello' }
|
|
248
|
-
expect(schema.$isTypeOf(obj)).toBe(false)
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
it('behaves identically to isTypeOf', () => {
|
|
252
|
-
const obj1 = { text: 'Hello' }
|
|
253
|
-
const obj2 = { $type: 'app.bsky.feed.post', text: 'Hello' }
|
|
254
|
-
const obj3 = { $type: 'app.bsky.feed.like', text: 'Hello' }
|
|
255
|
-
|
|
256
|
-
expect(schema.$isTypeOf(obj1)).toBe(schema.isTypeOf(obj1))
|
|
257
|
-
expect(schema.$isTypeOf(obj2)).toBe(schema.isTypeOf(obj2))
|
|
258
|
-
expect(schema.$isTypeOf(obj3)).toBe(schema.isTypeOf(obj3))
|
|
259
|
-
})
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
describe('build method', () => {
|
|
263
|
-
it('adds $type to object without $type', () => {
|
|
264
|
-
const input = { text: 'Hello world', likes: 5 }
|
|
265
|
-
const result = schema.build(input)
|
|
266
|
-
expect(result).toEqual({
|
|
267
|
-
text: 'Hello world',
|
|
268
|
-
likes: 5,
|
|
269
|
-
$type: 'app.bsky.feed.post',
|
|
270
|
-
})
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
it('adds $type to object with only required properties', () => {
|
|
274
|
-
const input = { text: 'Hello world' }
|
|
275
|
-
const result = schema.build(input)
|
|
276
|
-
expect(result).toEqual({
|
|
277
|
-
text: 'Hello world',
|
|
278
|
-
$type: 'app.bsky.feed.post',
|
|
279
|
-
})
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
it('preserves existing properties', () => {
|
|
283
|
-
const input = { text: 'Hello', likes: 10, extra: 'value' } as any
|
|
284
|
-
const result = schema.build(input)
|
|
285
|
-
expect(result).toEqual({
|
|
286
|
-
text: 'Hello',
|
|
287
|
-
likes: 10,
|
|
288
|
-
extra: 'value',
|
|
289
|
-
$type: 'app.bsky.feed.post',
|
|
290
|
-
})
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
it('does not mutate the input object', () => {
|
|
294
|
-
const input = { text: 'Hello world', likes: 5 }
|
|
295
|
-
const inputCopy = { ...input }
|
|
296
|
-
schema.build(input)
|
|
297
|
-
expect(input).toEqual(inputCopy)
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
it('adds $type to empty object', () => {
|
|
301
|
-
const emptySchema = typedObject('app.bsky.test', 'main', object({}))
|
|
302
|
-
const input = {}
|
|
303
|
-
const result = emptySchema.build(input)
|
|
304
|
-
expect(result).toEqual({ $type: 'app.bsky.test' })
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
describe('build() does not validate', () => {
|
|
308
|
-
const validationSchema = typedObject(
|
|
309
|
-
'app.bsky.test',
|
|
310
|
-
'validation',
|
|
311
|
-
object({
|
|
312
|
-
actor: string({ format: 'did' }),
|
|
313
|
-
count: integer(),
|
|
314
|
-
}),
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
it('does not throw for invalid data', () => {
|
|
318
|
-
const result = validationSchema.build({
|
|
319
|
-
// @ts-expect-error
|
|
320
|
-
actor: 'not-a-did',
|
|
321
|
-
count: 123,
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
expect(result.$type).toBe('app.bsky.test#validation')
|
|
325
|
-
expect(result.actor).toBe('not-a-did')
|
|
326
|
-
expect(result.count).toBe(123)
|
|
327
|
-
})
|
|
328
|
-
|
|
329
|
-
it('does not throw for invalid types', () => {
|
|
330
|
-
const result = validationSchema.build({
|
|
331
|
-
actor: 'did:plc:abc123',
|
|
332
|
-
// @ts-expect-error
|
|
333
|
-
count: 'not-a-number',
|
|
334
|
-
})
|
|
335
|
-
|
|
336
|
-
expect(result.$type).toBe('app.bsky.test#validation')
|
|
337
|
-
expect(result.count).toBe('not-a-number')
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
it('does not throw for missing required fields', () => {
|
|
341
|
-
// @ts-expect-error
|
|
342
|
-
const result = validationSchema.build({})
|
|
343
|
-
|
|
344
|
-
expect(result.$type).toBe('app.bsky.test#validation')
|
|
345
|
-
expect(result.actor).toBeUndefined()
|
|
346
|
-
expect(result.count).toBeUndefined()
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
it('does not throw for extra fields', () => {
|
|
350
|
-
const result = validationSchema.build({
|
|
351
|
-
actor: 'did:plc:abc123',
|
|
352
|
-
count: 42,
|
|
353
|
-
// @ts-expect-error
|
|
354
|
-
extra: 'unexpected',
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
expect(result.$type).toBe('app.bsky.test#validation')
|
|
358
|
-
// @ts-expect-error
|
|
359
|
-
expect(result.extra).toBe('unexpected')
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
it('parse() still validates after build()', () => {
|
|
363
|
-
const built = validationSchema.build({
|
|
364
|
-
// @ts-expect-error
|
|
365
|
-
actor: 'not-a-did',
|
|
366
|
-
count: 123,
|
|
367
|
-
})
|
|
368
|
-
|
|
369
|
-
expect(() => validationSchema.parse(built)).toThrow('Invalid DID')
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
it('safeParse() can detect validation errors after build()', () => {
|
|
373
|
-
const built = validationSchema.build({
|
|
374
|
-
// @ts-expect-error
|
|
375
|
-
actor: 'not-a-did',
|
|
376
|
-
count: 123,
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
const result = validationSchema.safeParse(built)
|
|
380
|
-
expect(result.success).toBe(false)
|
|
381
|
-
})
|
|
382
|
-
})
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
describe('$build method', () => {
|
|
386
|
-
it('adds $type to object without $type', () => {
|
|
387
|
-
const input = { text: 'Hello world', likes: 5 }
|
|
388
|
-
const result = schema.$build(input)
|
|
389
|
-
expect(result).toEqual({
|
|
390
|
-
text: 'Hello world',
|
|
391
|
-
likes: 5,
|
|
392
|
-
$type: 'app.bsky.feed.post',
|
|
393
|
-
})
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
it('behaves identically to build', () => {
|
|
397
|
-
const input1 = { text: 'Hello world', likes: 5 }
|
|
398
|
-
const input2 = { text: 'Another post' }
|
|
399
|
-
|
|
400
|
-
expect(schema.$build(input1)).toEqual(schema.build(input1))
|
|
401
|
-
expect(schema.$build(input2)).toEqual(schema.build(input2))
|
|
402
|
-
})
|
|
403
|
-
|
|
404
|
-
it('does not mutate the input object', () => {
|
|
405
|
-
const input = { text: 'Hello world' }
|
|
406
|
-
const inputCopy = { ...input }
|
|
407
|
-
schema.$build(input)
|
|
408
|
-
expect(input).toEqual(inputCopy)
|
|
409
|
-
})
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
describe('bound $ methods', () => {
|
|
413
|
-
it('$build can be used as a detached function', () => {
|
|
414
|
-
const { $build } = schema
|
|
415
|
-
const result = $build({ text: 'Hello' })
|
|
416
|
-
expect(result).toEqual({
|
|
417
|
-
text: 'Hello',
|
|
418
|
-
$type: 'app.bsky.feed.post',
|
|
419
|
-
})
|
|
420
|
-
})
|
|
421
|
-
|
|
422
|
-
it('$isTypeOf can be used as a detached function', () => {
|
|
423
|
-
const { $isTypeOf } = schema
|
|
424
|
-
expect($isTypeOf({ text: 'Hello' })).toBe(true)
|
|
425
|
-
expect($isTypeOf({ $type: 'app.bsky.feed.post', text: 'Hello' })).toBe(
|
|
426
|
-
true,
|
|
427
|
-
)
|
|
428
|
-
expect($isTypeOf({ $type: 'other.type', text: 'Hello' })).toBe(false)
|
|
429
|
-
})
|
|
430
|
-
|
|
431
|
-
it('$parse can be used as a detached function', () => {
|
|
432
|
-
const { $parse } = schema
|
|
433
|
-
const result = $parse({ text: 'Hello' })
|
|
434
|
-
expect(result).toEqual({ text: 'Hello' })
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
it('$matches can be used as a detached function', () => {
|
|
438
|
-
const { $matches } = schema
|
|
439
|
-
expect($matches({ text: 'Hello' })).toBe(true)
|
|
440
|
-
expect($matches(42)).toBe(false)
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
it('lazy property returns the same function on repeated access', () => {
|
|
444
|
-
const fn1 = schema.$build
|
|
445
|
-
const fn2 = schema.$build
|
|
446
|
-
expect(fn1).toBe(fn2)
|
|
447
|
-
})
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
describe('with complex nested schemas', () => {
|
|
451
|
-
const complexSchema = typedObject(
|
|
452
|
-
'app.bsky.actor.profile',
|
|
453
|
-
'main',
|
|
454
|
-
object({
|
|
455
|
-
displayName: string(),
|
|
456
|
-
bio: optional(string({ maxLength: 256 })),
|
|
457
|
-
followerCount: optional(integer({ minimum: 0 })),
|
|
458
|
-
verified: optional(nullable(enumSchema([true, false]))),
|
|
459
|
-
}),
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
it('validates complex nested structure', () => {
|
|
463
|
-
const result = complexSchema.safeParse({
|
|
464
|
-
displayName: 'John Doe',
|
|
465
|
-
bio: 'Software developer',
|
|
466
|
-
followerCount: 1000,
|
|
467
|
-
verified: true,
|
|
468
|
-
})
|
|
469
|
-
expect(result.success).toBe(true)
|
|
470
|
-
})
|
|
471
|
-
|
|
472
|
-
it('validates with nullable property set to null', () => {
|
|
473
|
-
const result = complexSchema.safeParse({
|
|
474
|
-
displayName: 'John Doe',
|
|
475
|
-
verified: null,
|
|
476
|
-
})
|
|
477
|
-
expect(result.success).toBe(true)
|
|
478
|
-
})
|
|
479
|
-
|
|
480
|
-
it('rejects when nested constraint is violated', () => {
|
|
481
|
-
const result = complexSchema.safeParse({
|
|
482
|
-
displayName: 'John Doe',
|
|
483
|
-
followerCount: -1,
|
|
484
|
-
})
|
|
485
|
-
expect(result.success).toBe(false)
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
it('rejects when string exceeds maxLength', () => {
|
|
489
|
-
const result = complexSchema.safeParse({
|
|
490
|
-
displayName: 'John Doe',
|
|
491
|
-
bio: 'x'.repeat(257),
|
|
492
|
-
})
|
|
493
|
-
expect(result.success).toBe(false)
|
|
494
|
-
})
|
|
495
|
-
|
|
496
|
-
it('validates with matching $type', () => {
|
|
497
|
-
const result = complexSchema.safeParse({
|
|
498
|
-
$type: 'app.bsky.actor.profile',
|
|
499
|
-
displayName: 'John Doe',
|
|
500
|
-
})
|
|
501
|
-
expect(result.success).toBe(true)
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
it('rejects with non-matching $type', () => {
|
|
505
|
-
const result = complexSchema.safeParse({
|
|
506
|
-
$type: 'app.bsky.feed.post',
|
|
507
|
-
displayName: 'John Doe',
|
|
508
|
-
})
|
|
509
|
-
expect(result.success).toBe(false)
|
|
510
|
-
})
|
|
511
|
-
})
|
|
512
|
-
|
|
513
|
-
describe('with different $type formats', () => {
|
|
514
|
-
it('validates with main type', () => {
|
|
515
|
-
const mainSchema = typedObject(
|
|
516
|
-
'app.bsky.feed.post',
|
|
517
|
-
'main',
|
|
518
|
-
object({ text: string() }),
|
|
519
|
-
)
|
|
520
|
-
const result = mainSchema.safeParse({ text: 'Hello' })
|
|
521
|
-
expect(result.success).toBe(true)
|
|
522
|
-
})
|
|
523
|
-
|
|
524
|
-
it('validates with custom fragment', () => {
|
|
525
|
-
const fragmentSchema = typedObject(
|
|
526
|
-
'app.bsky.feed.post',
|
|
527
|
-
'reply',
|
|
528
|
-
object({ text: string() }),
|
|
529
|
-
)
|
|
530
|
-
const result = fragmentSchema.safeParse({
|
|
531
|
-
$type: 'app.bsky.feed.post#reply',
|
|
532
|
-
text: 'Hello',
|
|
533
|
-
})
|
|
534
|
-
expect(result.success).toBe(true)
|
|
535
|
-
})
|
|
536
|
-
|
|
537
|
-
it('distinguishes between different fragments', () => {
|
|
538
|
-
const replySchema = typedObject(
|
|
539
|
-
'app.bsky.feed.post',
|
|
540
|
-
'reply',
|
|
541
|
-
object({ text: string() }),
|
|
542
|
-
)
|
|
543
|
-
const result = replySchema.safeParse({
|
|
544
|
-
$type: 'app.bsky.feed.post#quote',
|
|
545
|
-
text: 'Hello',
|
|
546
|
-
})
|
|
547
|
-
expect(result.success).toBe(false)
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
it('validates with long NSID', () => {
|
|
551
|
-
const longSchema = typedObject(
|
|
552
|
-
'com.example.app.feature.action.detail',
|
|
553
|
-
'variant',
|
|
554
|
-
object({ value: string() }),
|
|
555
|
-
)
|
|
556
|
-
const result = longSchema.safeParse({
|
|
557
|
-
$type: 'com.example.app.feature.action.detail#variant',
|
|
558
|
-
value: 'test',
|
|
559
|
-
})
|
|
560
|
-
expect(result.success).toBe(true)
|
|
561
|
-
})
|
|
562
|
-
})
|
|
563
|
-
|
|
564
|
-
describe('edge cases', () => {
|
|
565
|
-
it('validates object with only extra properties', () => {
|
|
566
|
-
const minimalSchema = typedObject('app.bsky.test', 'main', object({}))
|
|
567
|
-
const result = minimalSchema.safeParse({
|
|
568
|
-
extra1: 'value1',
|
|
569
|
-
extra2: 'value2',
|
|
570
|
-
})
|
|
571
|
-
expect(result.success).toBe(true)
|
|
572
|
-
})
|
|
573
|
-
|
|
574
|
-
it('validates empty object with no required properties', () => {
|
|
575
|
-
const minimalSchema = typedObject('app.bsky.test', 'main', object({}))
|
|
576
|
-
const result = minimalSchema.safeParse({})
|
|
577
|
-
expect(result.success).toBe(true)
|
|
578
|
-
})
|
|
579
|
-
|
|
580
|
-
it('validates with $type as only property', () => {
|
|
581
|
-
const minimalSchema = typedObject('app.bsky.test', 'main', object({}))
|
|
582
|
-
const result = minimalSchema.safeParse({
|
|
583
|
-
$type: 'app.bsky.test',
|
|
584
|
-
})
|
|
585
|
-
expect(result.success).toBe(true)
|
|
586
|
-
})
|
|
587
|
-
|
|
588
|
-
it('rejects object with prototype properties', () => {
|
|
589
|
-
const obj = Object.create({ inherited: 'value' })
|
|
590
|
-
obj.text = 'Hello world'
|
|
591
|
-
const result = schema.safeParse(obj)
|
|
592
|
-
expect(result.success).toBe(false)
|
|
593
|
-
})
|
|
594
|
-
|
|
595
|
-
it('rejects Date objects', () => {
|
|
596
|
-
const result = schema.safeParse(new Date())
|
|
597
|
-
expect(result.success).toBe(false)
|
|
598
|
-
})
|
|
599
|
-
|
|
600
|
-
it('rejects RegExp objects', () => {
|
|
601
|
-
const result = schema.safeParse(/pattern/)
|
|
602
|
-
expect(result.success).toBe(false)
|
|
603
|
-
})
|
|
604
|
-
|
|
605
|
-
it('rejects Error objects', () => {
|
|
606
|
-
const result = schema.safeParse(new Error('test'))
|
|
607
|
-
expect(result.success).toBe(false)
|
|
608
|
-
})
|
|
609
|
-
|
|
610
|
-
it('rejects Map objects', () => {
|
|
611
|
-
const result = schema.safeParse(new Map())
|
|
612
|
-
expect(result.success).toBe(false)
|
|
613
|
-
})
|
|
614
|
-
|
|
615
|
-
it('rejects Set objects', () => {
|
|
616
|
-
const result = schema.safeParse(new Set())
|
|
617
|
-
expect(result.success).toBe(false)
|
|
618
|
-
})
|
|
619
|
-
|
|
620
|
-
it('rejects class instances', () => {
|
|
621
|
-
class CustomClass {
|
|
622
|
-
text = 'Hello'
|
|
623
|
-
}
|
|
624
|
-
const result = schema.safeParse(new CustomClass())
|
|
625
|
-
expect(result.success).toBe(false)
|
|
626
|
-
})
|
|
627
|
-
})
|
|
628
|
-
|
|
629
|
-
describe('integration with all property types', () => {
|
|
630
|
-
const fullSchema = typedObject(
|
|
631
|
-
'app.bsky.test',
|
|
632
|
-
'full',
|
|
633
|
-
object({
|
|
634
|
-
required: string(),
|
|
635
|
-
optional: optional(string()),
|
|
636
|
-
nullable: nullable(string()),
|
|
637
|
-
optionalNullable: optional(nullable(string())),
|
|
638
|
-
}),
|
|
639
|
-
)
|
|
640
|
-
|
|
641
|
-
it('validates with all properties present', () => {
|
|
642
|
-
const result = fullSchema.safeParse({
|
|
643
|
-
required: 'value',
|
|
644
|
-
optional: 'value',
|
|
645
|
-
nullable: 'value',
|
|
646
|
-
optionalNullable: 'value',
|
|
647
|
-
})
|
|
648
|
-
expect(result.success).toBe(true)
|
|
649
|
-
})
|
|
650
|
-
|
|
651
|
-
it('validates with only required property and nullable', () => {
|
|
652
|
-
const result = fullSchema.safeParse({
|
|
653
|
-
required: 'value',
|
|
654
|
-
nullable: 'value',
|
|
655
|
-
})
|
|
656
|
-
expect(result.success).toBe(true)
|
|
657
|
-
})
|
|
658
|
-
|
|
659
|
-
it('validates with nullable property set to null', () => {
|
|
660
|
-
const result = fullSchema.safeParse({
|
|
661
|
-
required: 'value',
|
|
662
|
-
nullable: null,
|
|
663
|
-
})
|
|
664
|
-
expect(result.success).toBe(true)
|
|
665
|
-
})
|
|
666
|
-
|
|
667
|
-
it('validates with required nullable and optional nullable set to null', () => {
|
|
668
|
-
const result = fullSchema.safeParse({
|
|
669
|
-
required: 'value',
|
|
670
|
-
nullable: 'value',
|
|
671
|
-
optionalNullable: null,
|
|
672
|
-
})
|
|
673
|
-
expect(result.success).toBe(true)
|
|
674
|
-
})
|
|
675
|
-
|
|
676
|
-
it('rejects when required property is missing', () => {
|
|
677
|
-
const result = fullSchema.safeParse({
|
|
678
|
-
optional: 'value',
|
|
679
|
-
nullable: 'value',
|
|
680
|
-
})
|
|
681
|
-
expect(result.success).toBe(false)
|
|
682
|
-
})
|
|
683
|
-
|
|
684
|
-
it('rejects when required property is null', () => {
|
|
685
|
-
const result = fullSchema.safeParse({
|
|
686
|
-
required: null,
|
|
687
|
-
})
|
|
688
|
-
expect(result.success).toBe(false)
|
|
689
|
-
})
|
|
690
|
-
|
|
691
|
-
it('rejects when required property is undefined', () => {
|
|
692
|
-
const result = fullSchema.safeParse({
|
|
693
|
-
required: undefined,
|
|
694
|
-
})
|
|
695
|
-
expect(result.success).toBe(false)
|
|
696
|
-
})
|
|
697
|
-
|
|
698
|
-
it('validates with $type and all properties', () => {
|
|
699
|
-
const result = fullSchema.safeParse({
|
|
700
|
-
$type: 'app.bsky.test#full',
|
|
701
|
-
required: 'value',
|
|
702
|
-
optional: 'value',
|
|
703
|
-
nullable: null,
|
|
704
|
-
optionalNullable: 'value',
|
|
705
|
-
})
|
|
706
|
-
expect(result.success).toBe(true)
|
|
707
|
-
})
|
|
708
|
-
})
|
|
709
|
-
|
|
710
|
-
describe('comparison with plain ObjectSchema', () => {
|
|
711
|
-
const plainSchema = object({
|
|
712
|
-
text: string(),
|
|
713
|
-
likes: optional(integer()),
|
|
714
|
-
})
|
|
715
|
-
|
|
716
|
-
it('typed schema accepts same input as plain schema', () => {
|
|
717
|
-
const input = { text: 'Hello', likes: 5 }
|
|
718
|
-
const typedResult = schema.safeParse(input)
|
|
719
|
-
const plainResult = plainSchema.safeParse(input)
|
|
720
|
-
expect(typedResult.success).toBe(plainResult.success)
|
|
721
|
-
})
|
|
722
|
-
|
|
723
|
-
it('typed schema adds $type enforcement', () => {
|
|
724
|
-
const input = { $type: 'wrong.type', text: 'Hello' }
|
|
725
|
-
const typedResult = schema.safeParse(input)
|
|
726
|
-
const plainResult = plainSchema.safeParse(input)
|
|
727
|
-
expect(typedResult.success).toBe(false)
|
|
728
|
-
expect(plainResult.success).toBe(true)
|
|
729
|
-
})
|
|
730
|
-
|
|
731
|
-
it('both schemas reject invalid types', () => {
|
|
732
|
-
const input = { text: 123 }
|
|
733
|
-
const typedResult = schema.safeParse(input)
|
|
734
|
-
const plainResult = plainSchema.safeParse(input)
|
|
735
|
-
expect(typedResult.success).toBe(false)
|
|
736
|
-
expect(plainResult.success).toBe(false)
|
|
737
|
-
})
|
|
738
|
-
|
|
739
|
-
it('typed schema accepts matching $type', () => {
|
|
740
|
-
const input = { $type: 'app.bsky.feed.post', text: 'Hello' }
|
|
741
|
-
const typedResult = schema.safeParse(input)
|
|
742
|
-
expect(typedResult.success).toBe(true)
|
|
743
|
-
})
|
|
744
|
-
})
|
|
745
|
-
})
|