@atproto/lex-schema 0.0.4 → 0.0.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.
Files changed (299) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/core/$type.d.ts +7 -0
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js.map +1 -1
  5. package/dist/core/property-key.d.ts.map +1 -0
  6. package/dist/core/property-key.js.map +1 -0
  7. package/dist/core/result.d.ts +7 -6
  8. package/dist/core/result.d.ts.map +1 -1
  9. package/dist/core/result.js +9 -8
  10. package/dist/core/result.js.map +1 -1
  11. package/dist/{validation → core}/schema.d.ts +21 -2
  12. package/dist/core/schema.d.ts.map +1 -0
  13. package/dist/{validation → core}/schema.js +25 -2
  14. package/dist/core/schema.js.map +1 -0
  15. package/dist/core/string-format.d.ts +37 -26
  16. package/dist/core/string-format.d.ts.map +1 -1
  17. package/dist/core/string-format.js +66 -59
  18. package/dist/core/string-format.js.map +1 -1
  19. package/dist/core/types.d.ts +3 -0
  20. package/dist/core/types.d.ts.map +1 -1
  21. package/dist/core/types.js.map +1 -1
  22. package/dist/core/validation-error.d.ts +19 -0
  23. package/dist/core/validation-error.d.ts.map +1 -0
  24. package/dist/{validation → core}/validation-error.js +13 -6
  25. package/dist/core/validation-error.js.map +1 -0
  26. package/dist/{validation → core}/validation-issue.d.ts +43 -0
  27. package/dist/core/validation-issue.d.ts.map +1 -0
  28. package/dist/{validation → core}/validation-issue.js +55 -2
  29. package/dist/core/validation-issue.js.map +1 -0
  30. package/dist/{validation → core}/validator.d.ts +5 -4
  31. package/dist/core/validator.d.ts.map +1 -0
  32. package/dist/{validation → core}/validator.js +16 -13
  33. package/dist/core/validator.js.map +1 -0
  34. package/dist/core.d.ts +5 -0
  35. package/dist/core.d.ts.map +1 -1
  36. package/dist/core.js +5 -0
  37. package/dist/core.js.map +1 -1
  38. package/dist/external.d.ts +21 -21
  39. package/dist/external.d.ts.map +1 -1
  40. package/dist/external.js +39 -55
  41. package/dist/external.js.map +1 -1
  42. package/dist/helpers.d.ts +41 -0
  43. package/dist/helpers.d.ts.map +1 -0
  44. package/dist/helpers.js +13 -0
  45. package/dist/helpers.js.map +1 -0
  46. package/dist/schema/_parameters.d.ts +1 -1
  47. package/dist/schema/_parameters.d.ts.map +1 -1
  48. package/dist/schema/_parameters.js.map +1 -1
  49. package/dist/schema/array.d.ts +1 -1
  50. package/dist/schema/array.d.ts.map +1 -1
  51. package/dist/schema/array.js +2 -2
  52. package/dist/schema/array.js.map +1 -1
  53. package/dist/schema/blob.d.ts +2 -1
  54. package/dist/schema/blob.d.ts.map +1 -1
  55. package/dist/schema/blob.js +34 -20
  56. package/dist/schema/blob.js.map +1 -1
  57. package/dist/schema/boolean.d.ts +1 -1
  58. package/dist/schema/boolean.d.ts.map +1 -1
  59. package/dist/schema/boolean.js +2 -2
  60. package/dist/schema/boolean.js.map +1 -1
  61. package/dist/schema/bytes.d.ts +1 -1
  62. package/dist/schema/bytes.d.ts.map +1 -1
  63. package/dist/schema/bytes.js +2 -2
  64. package/dist/schema/bytes.js.map +1 -1
  65. package/dist/schema/cid.d.ts +1 -1
  66. package/dist/schema/cid.d.ts.map +1 -1
  67. package/dist/schema/cid.js +2 -2
  68. package/dist/schema/cid.js.map +1 -1
  69. package/dist/schema/custom.d.ts +1 -1
  70. package/dist/schema/custom.d.ts.map +1 -1
  71. package/dist/schema/custom.js +3 -3
  72. package/dist/schema/custom.js.map +1 -1
  73. package/dist/schema/dict.d.ts +1 -1
  74. package/dist/schema/dict.d.ts.map +1 -1
  75. package/dist/schema/dict.js +2 -2
  76. package/dist/schema/dict.js.map +1 -1
  77. package/dist/schema/discriminated-union.d.ts +1 -1
  78. package/dist/schema/discriminated-union.d.ts.map +1 -1
  79. package/dist/schema/discriminated-union.js +2 -2
  80. package/dist/schema/discriminated-union.js.map +1 -1
  81. package/dist/schema/enum.d.ts +1 -1
  82. package/dist/schema/enum.d.ts.map +1 -1
  83. package/dist/schema/enum.js +2 -2
  84. package/dist/schema/enum.js.map +1 -1
  85. package/dist/schema/integer.d.ts +1 -1
  86. package/dist/schema/integer.d.ts.map +1 -1
  87. package/dist/schema/integer.js +4 -4
  88. package/dist/schema/integer.js.map +1 -1
  89. package/dist/schema/intersection.d.ts +1 -2
  90. package/dist/schema/intersection.d.ts.map +1 -1
  91. package/dist/schema/intersection.js +2 -2
  92. package/dist/schema/intersection.js.map +1 -1
  93. package/dist/schema/literal.d.ts +1 -1
  94. package/dist/schema/literal.d.ts.map +1 -1
  95. package/dist/schema/literal.js +2 -2
  96. package/dist/schema/literal.js.map +1 -1
  97. package/dist/schema/never.d.ts +1 -1
  98. package/dist/schema/never.d.ts.map +1 -1
  99. package/dist/schema/never.js +2 -2
  100. package/dist/schema/never.js.map +1 -1
  101. package/dist/schema/null.d.ts +1 -1
  102. package/dist/schema/null.d.ts.map +1 -1
  103. package/dist/schema/null.js +2 -2
  104. package/dist/schema/null.js.map +1 -1
  105. package/dist/schema/nullable.d.ts +1 -1
  106. package/dist/schema/nullable.d.ts.map +1 -1
  107. package/dist/schema/nullable.js +2 -2
  108. package/dist/schema/nullable.js.map +1 -1
  109. package/dist/schema/object.d.ts +1 -2
  110. package/dist/schema/object.d.ts.map +1 -1
  111. package/dist/schema/object.js +2 -2
  112. package/dist/schema/object.js.map +1 -1
  113. package/dist/schema/optional.d.ts +1 -1
  114. package/dist/schema/optional.d.ts.map +1 -1
  115. package/dist/schema/optional.js +2 -2
  116. package/dist/schema/optional.js.map +1 -1
  117. package/dist/schema/params.d.ts +1 -3
  118. package/dist/schema/params.d.ts.map +1 -1
  119. package/dist/schema/params.js +2 -2
  120. package/dist/schema/params.js.map +1 -1
  121. package/dist/schema/payload.d.ts +17 -15
  122. package/dist/schema/payload.d.ts.map +1 -1
  123. package/dist/schema/payload.js +28 -0
  124. package/dist/schema/payload.js.map +1 -1
  125. package/dist/schema/procedure.d.ts +3 -6
  126. package/dist/schema/procedure.d.ts.map +1 -1
  127. package/dist/schema/procedure.js +1 -0
  128. package/dist/schema/procedure.js.map +1 -1
  129. package/dist/schema/query.d.ts +3 -5
  130. package/dist/schema/query.d.ts.map +1 -1
  131. package/dist/schema/query.js +1 -0
  132. package/dist/schema/query.js.map +1 -1
  133. package/dist/schema/record.d.ts +14 -14
  134. package/dist/schema/record.d.ts.map +1 -1
  135. package/dist/schema/record.js +2 -2
  136. package/dist/schema/record.js.map +1 -1
  137. package/dist/schema/ref.d.ts +1 -1
  138. package/dist/schema/ref.d.ts.map +1 -1
  139. package/dist/schema/ref.js +2 -2
  140. package/dist/schema/ref.js.map +1 -1
  141. package/dist/schema/refine.d.ts +18 -1
  142. package/dist/schema/refine.d.ts.map +1 -1
  143. package/dist/schema/refine.js +2 -2
  144. package/dist/schema/refine.js.map +1 -1
  145. package/dist/schema/regexp.d.ts +1 -1
  146. package/dist/schema/regexp.d.ts.map +1 -1
  147. package/dist/schema/regexp.js +2 -2
  148. package/dist/schema/regexp.js.map +1 -1
  149. package/dist/schema/string.d.ts +1 -2
  150. package/dist/schema/string.d.ts.map +1 -1
  151. package/dist/schema/string.js +1 -2
  152. package/dist/schema/string.js.map +1 -1
  153. package/dist/schema/subscription.d.ts +4 -8
  154. package/dist/schema/subscription.d.ts.map +1 -1
  155. package/dist/schema/subscription.js.map +1 -1
  156. package/dist/schema/token.d.ts +1 -1
  157. package/dist/schema/token.d.ts.map +1 -1
  158. package/dist/schema/token.js +2 -2
  159. package/dist/schema/token.js.map +1 -1
  160. package/dist/schema/typed-object.d.ts +8 -8
  161. package/dist/schema/typed-object.d.ts.map +1 -1
  162. package/dist/schema/typed-object.js +2 -2
  163. package/dist/schema/typed-object.js.map +1 -1
  164. package/dist/schema/typed-ref.d.ts +1 -1
  165. package/dist/schema/typed-ref.d.ts.map +1 -1
  166. package/dist/schema/typed-ref.js +2 -2
  167. package/dist/schema/typed-ref.js.map +1 -1
  168. package/dist/schema/typed-union.d.ts +2 -3
  169. package/dist/schema/typed-union.d.ts.map +1 -1
  170. package/dist/schema/typed-union.js +2 -2
  171. package/dist/schema/typed-union.js.map +1 -1
  172. package/dist/schema/union.d.ts +1 -1
  173. package/dist/schema/union.d.ts.map +1 -1
  174. package/dist/schema/union.js +3 -6
  175. package/dist/schema/union.js.map +1 -1
  176. package/dist/schema/unknown-object.d.ts +1 -2
  177. package/dist/schema/unknown-object.d.ts.map +1 -1
  178. package/dist/schema/unknown-object.js +2 -2
  179. package/dist/schema/unknown-object.js.map +1 -1
  180. package/dist/schema/unknown.d.ts +1 -1
  181. package/dist/schema/unknown.d.ts.map +1 -1
  182. package/dist/schema/unknown.js +2 -2
  183. package/dist/schema/unknown.js.map +1 -1
  184. package/dist/util/assertion-util.d.ts +8 -0
  185. package/dist/util/assertion-util.d.ts.map +1 -0
  186. package/dist/util/assertion-util.js +31 -0
  187. package/dist/util/assertion-util.js.map +1 -0
  188. package/dist/util/memoize.d.ts +3 -0
  189. package/dist/util/memoize.d.ts.map +1 -0
  190. package/dist/util/memoize.js +52 -0
  191. package/dist/util/memoize.js.map +1 -0
  192. package/package.json +6 -6
  193. package/src/core/$type.ts +4 -0
  194. package/src/core/result.ts +9 -8
  195. package/src/{validation → core}/schema.ts +29 -4
  196. package/src/core/string-format.ts +88 -68
  197. package/src/core/types.ts +4 -0
  198. package/src/{validation → core}/validation-error.ts +14 -6
  199. package/src/{validation → core}/validation-issue.ts +64 -2
  200. package/src/{validation → core}/validator.ts +17 -13
  201. package/src/core.ts +5 -0
  202. package/src/external.ts +75 -55
  203. package/src/helpers.test.ts +487 -0
  204. package/src/helpers.ts +75 -0
  205. package/src/schema/_parameters.test.ts +1 -0
  206. package/src/schema/_parameters.ts +1 -1
  207. package/src/schema/array.test.ts +1 -0
  208. package/src/schema/array.ts +1 -1
  209. package/src/schema/blob.test.ts +3 -4
  210. package/src/schema/blob.ts +32 -24
  211. package/src/schema/boolean.test.ts +1 -0
  212. package/src/schema/boolean.ts +1 -1
  213. package/src/schema/bytes.test.ts +1 -0
  214. package/src/schema/bytes.ts +1 -1
  215. package/src/schema/cid.test.ts +1 -0
  216. package/src/schema/cid.ts +1 -1
  217. package/src/schema/custom.test.ts +8 -7
  218. package/src/schema/custom.ts +2 -2
  219. package/src/schema/dict.test.ts +1 -0
  220. package/src/schema/dict.ts +1 -1
  221. package/src/schema/discriminated-union.test.ts +1 -0
  222. package/src/schema/discriminated-union.ts +1 -1
  223. package/src/schema/enum.test.ts +1 -0
  224. package/src/schema/enum.ts +1 -1
  225. package/src/schema/integer.test.ts +1 -0
  226. package/src/schema/integer.ts +3 -3
  227. package/src/schema/intersection.test.ts +1 -0
  228. package/src/schema/intersection.ts +2 -2
  229. package/src/schema/literal.test.ts +1 -0
  230. package/src/schema/literal.ts +1 -1
  231. package/src/schema/never.test.ts +1 -0
  232. package/src/schema/never.ts +1 -1
  233. package/src/schema/null.test.ts +1 -0
  234. package/src/schema/null.ts +1 -1
  235. package/src/schema/nullable.test.ts +1 -0
  236. package/src/schema/nullable.ts +1 -1
  237. package/src/schema/object.test.ts +1 -0
  238. package/src/schema/object.ts +3 -3
  239. package/src/schema/optional.test.ts +1 -0
  240. package/src/schema/optional.ts +1 -1
  241. package/src/schema/params.test.ts +1 -0
  242. package/src/schema/params.ts +3 -10
  243. package/src/schema/payload.test.ts +1 -0
  244. package/src/schema/payload.ts +67 -34
  245. package/src/schema/permission-set.test.ts +37 -36
  246. package/src/schema/permission.test.ts +1 -0
  247. package/src/schema/procedure.test.ts +2 -62
  248. package/src/schema/procedure.ts +8 -20
  249. package/src/schema/query.test.ts +23 -69
  250. package/src/schema/query.ts +7 -14
  251. package/src/schema/record.test.ts +1 -0
  252. package/src/schema/record.ts +13 -6
  253. package/src/schema/ref.test.ts +2 -1
  254. package/src/schema/ref.ts +1 -1
  255. package/src/schema/refine.test.ts +1 -0
  256. package/src/schema/refine.ts +19 -2
  257. package/src/schema/regexp.test.ts +1 -0
  258. package/src/schema/regexp.ts +1 -1
  259. package/src/schema/string.test.ts +1 -0
  260. package/src/schema/string.ts +8 -2
  261. package/src/schema/subscription.test.ts +31 -93
  262. package/src/schema/subscription.ts +11 -25
  263. package/src/schema/token.test.ts +1 -0
  264. package/src/schema/token.ts +1 -1
  265. package/src/schema/typed-object.test.ts +1 -0
  266. package/src/schema/typed-object.ts +10 -5
  267. package/src/schema/typed-ref.test.ts +1 -0
  268. package/src/schema/typed-ref.ts +1 -1
  269. package/src/schema/typed-union.test.ts +1 -0
  270. package/src/schema/typed-union.ts +4 -4
  271. package/src/schema/union.test.ts +1 -0
  272. package/src/schema/union.ts +2 -5
  273. package/src/schema/unknown-object.test.ts +1 -0
  274. package/src/schema/unknown-object.ts +1 -2
  275. package/src/schema/unknown.test.ts +1 -0
  276. package/src/schema/unknown.ts +1 -1
  277. package/src/util/array-agg.test.ts +1 -0
  278. package/src/util/assertion-util.ts +40 -0
  279. package/src/util/memoize.ts +57 -0
  280. package/tsconfig.tests.json +2 -2
  281. package/dist/validation/property-key.d.ts.map +0 -1
  282. package/dist/validation/property-key.js.map +0 -1
  283. package/dist/validation/schema.d.ts.map +0 -1
  284. package/dist/validation/schema.js.map +0 -1
  285. package/dist/validation/validation-error.d.ts +0 -9
  286. package/dist/validation/validation-error.d.ts.map +0 -1
  287. package/dist/validation/validation-error.js.map +0 -1
  288. package/dist/validation/validation-issue.d.ts.map +0 -1
  289. package/dist/validation/validation-issue.js.map +0 -1
  290. package/dist/validation/validator.d.ts.map +0 -1
  291. package/dist/validation/validator.js.map +0 -1
  292. package/dist/validation.d.ts +0 -6
  293. package/dist/validation.d.ts.map +0 -1
  294. package/dist/validation.js +0 -9
  295. package/dist/validation.js.map +0 -1
  296. package/src/validation.ts +0 -5
  297. /package/dist/{validation → core}/property-key.d.ts +0 -0
  298. /package/dist/{validation → core}/property-key.js +0 -0
  299. /package/src/{validation → core}/property-key.ts +0 -0
@@ -0,0 +1,487 @@
1
+ import { describe, test } from 'vitest'
2
+ import * as l from './external.js'
3
+
4
+ class BinaryValue {
5
+ declare readonly ['__binaryValue']: BinaryValue
6
+ }
7
+
8
+ const expectType = <T>(_: T) => {}
9
+ const binaryValue = new BinaryValue()
10
+
11
+ describe('InferMethodParams', () => {
12
+ describe('query', () => {
13
+ test('infers parameter types', () => {
14
+ const query = l.query(
15
+ 'com.example.query',
16
+ l.params({
17
+ feed: l.string({ format: 'at-uri' }),
18
+ limit: l.optional(l.integer()),
19
+ }),
20
+ l.payload('application/json', undefined),
21
+ )
22
+
23
+ type Params = l.InferMethodParams<typeof query>
24
+
25
+ expectType<Params>({
26
+ feed: 'at://did:plc:abc123/app.bsky.feed.post/xyz',
27
+ limit: 50,
28
+ })
29
+ // @ts-expect-error
30
+ expectType<Params>({ feed: 123, limit: 50 })
31
+ // @ts-expect-error
32
+ expectType<Params>({ limit: 50 })
33
+ })
34
+ })
35
+
36
+ describe('procedure', () => {
37
+ test('infers parameter types', () => {
38
+ const procedure = l.procedure(
39
+ 'com.example.list',
40
+ l.params({ limit: l.string() }),
41
+ l.payload(undefined, undefined),
42
+ l.payload(undefined, undefined),
43
+ undefined,
44
+ )
45
+
46
+ type Params = l.InferMethodParams<typeof procedure>
47
+
48
+ expectType<Params>({ limit: '10' })
49
+ // @ts-expect-error
50
+ expectType<Params>({ limit: 10 })
51
+ // @ts-expect-error
52
+ expectType<Params>({ limit: binaryValue })
53
+ // @ts-expect-error
54
+ expectType<Params>(binaryValue)
55
+ })
56
+ })
57
+
58
+ describe('subscription', () => {
59
+ test('infers parameter types', () => {
60
+ const subscription = l.subscription(
61
+ 'com.example.subscribe',
62
+ l.params({
63
+ cursor: l.optional(l.integer()),
64
+ }),
65
+ l.unknown(),
66
+ )
67
+
68
+ type Params = l.InferMethodParams<typeof subscription>
69
+
70
+ expectType<Params>({ cursor: 100 })
71
+ // @ts-expect-error
72
+ expectType<Params>({ cursor: '100' })
73
+ // @ts-expect-error
74
+ expectType<Params>({ cursor: binaryValue })
75
+ })
76
+ })
77
+ })
78
+
79
+ describe('InferMethodInput', () => {
80
+ describe('procedure', () => {
81
+ test('with payload schema', () => {
82
+ const procedure = l.procedure(
83
+ 'com.example.create',
84
+ l.params({}),
85
+ l.payload('application/json', l.object({ text: l.string() })),
86
+ l.payload(undefined, undefined),
87
+ undefined,
88
+ )
89
+
90
+ type Input = l.InferMethodInput<typeof procedure, BinaryValue>
91
+
92
+ expectType<Input>({ encoding: 'application/json', body: { text: 'hi' } })
93
+ // @ts-expect-error
94
+ expectType<Input>({ encoding: 'application/json', body: { text: 123 } })
95
+ // @ts-expect-error
96
+ expectType<Input>({ encoding: 'text/plain', body: 'hello' })
97
+ // @ts-expect-error
98
+ expectType<Input>({ encoding: 'text/plain', body: new Uint8Array() })
99
+ // @ts-expect-error
100
+ expectType<Input>({ encoding: 'text/plain', body: binaryValue })
101
+ })
102
+
103
+ test('without payload schema', () => {
104
+ const procedure = l.procedure(
105
+ 'com.example.create',
106
+ l.params({}),
107
+ l.payload('*/*', undefined),
108
+ l.payload(undefined, undefined),
109
+ undefined,
110
+ )
111
+
112
+ type Input = l.InferMethodInput<typeof procedure, BinaryValue>
113
+
114
+ expectType<Input>({ encoding: 'image/png', body: binaryValue })
115
+ // @ts-expect-error
116
+ expectType<Input>({ encoding: 'image/png', body: new Uint8Array() })
117
+ // @ts-expect-error
118
+ expectType<Input>({ encoding: 'image/png', body: [] })
119
+ // @ts-expect-error
120
+ expectType<Input>({ encoding: 'image/png', body: 'foo' })
121
+ })
122
+ })
123
+ })
124
+
125
+ describe('InferMethodInputBody', () => {
126
+ describe('procedure', () => {
127
+ test('with payload schema', () => {
128
+ const procedure = l.procedure(
129
+ 'com.example.create',
130
+ l.params({}),
131
+ l.payload('application/json', l.object({ text: l.string() })),
132
+ l.payload(undefined, undefined),
133
+ undefined,
134
+ )
135
+
136
+ type InputBody = l.InferMethodInputBody<typeof procedure, BinaryValue>
137
+
138
+ expectType<InputBody>({ text: 'hello' })
139
+ // @ts-expect-error
140
+ expectType<InputBody>({ text: 123 })
141
+ // @ts-expect-error
142
+ expectType<InputBody>(binaryValue)
143
+ })
144
+
145
+ test('without payload schema', () => {
146
+ const procedure = l.procedure(
147
+ 'com.example.upload',
148
+ l.params({}),
149
+ l.payload('*/*', undefined),
150
+ l.payload(undefined, undefined),
151
+ undefined,
152
+ )
153
+
154
+ type InputBody = l.InferMethodInputBody<typeof procedure, BinaryValue>
155
+
156
+ expectType<InputBody>(binaryValue)
157
+ // @ts-expect-error
158
+ expectType<InputBody>(new Uint8Array())
159
+ // @ts-expect-error
160
+ expectType<InputBody>({ text: 'hello' })
161
+ })
162
+ })
163
+ })
164
+
165
+ describe('InferMethodInputEncoding', () => {
166
+ describe('procedure', () => {
167
+ test('with payload schema', () => {
168
+ const procedure = l.procedure(
169
+ 'com.example.create',
170
+ l.params({}),
171
+ l.payload('application/json', l.object({ text: l.string() })),
172
+ l.payload(undefined, undefined),
173
+ undefined,
174
+ )
175
+
176
+ type InputEncoding = l.InferMethodInputEncoding<typeof procedure>
177
+
178
+ expectType<InputEncoding>('application/json')
179
+ // @ts-expect-error
180
+ expectType<InputEncoding>('text/plain')
181
+ // @ts-expect-error
182
+ expectType<InputEncoding>(123)
183
+ })
184
+
185
+ test('without payload schema', () => {
186
+ const procedure = l.procedure(
187
+ 'com.example.upload',
188
+ l.params({}),
189
+ l.payload('*/*', undefined),
190
+ l.payload(undefined, undefined),
191
+ undefined,
192
+ )
193
+
194
+ type InputEncoding = l.InferMethodInputEncoding<typeof procedure>
195
+
196
+ expectType<InputEncoding>('image/png')
197
+ expectType<InputEncoding>('application/octet-stream')
198
+ // @ts-expect-error
199
+ expectType<InputEncoding>(123)
200
+ })
201
+ })
202
+ })
203
+
204
+ describe('InferMethodOutput', () => {
205
+ describe('query', () => {
206
+ test('with payload schema', () => {
207
+ const query = l.query(
208
+ 'com.example.query',
209
+ l.params({}),
210
+ l.payload('application/json', l.object({ items: l.array(l.string()) })),
211
+ )
212
+
213
+ type Output = l.InferMethodOutput<typeof query, BinaryValue>
214
+
215
+ expectType<Output>({ encoding: 'application/json', body: { items: [] } })
216
+ // @ts-expect-error
217
+ expectType<Output>({ encoding: 'application/json', body: { items: [1] } })
218
+ // @ts-expect-error
219
+ expectType<Output>({ encoding: 'text/plain', body: { items: [] } })
220
+ })
221
+
222
+ test('without payload schema', () => {
223
+ const query = l.query(
224
+ 'com.example.query',
225
+ l.params({}),
226
+ l.payload('*/*', undefined),
227
+ )
228
+
229
+ type Output = l.InferMethodOutput<typeof query, BinaryValue>
230
+
231
+ expectType<Output>({ encoding: 'image/png', body: binaryValue })
232
+ // @ts-expect-error
233
+ expectType<Output>({ encoding: 'image/png', body: new Uint8Array() })
234
+ // @ts-expect-error
235
+ expectType<Output>({ encoding: 'image/png', body: 'string' })
236
+ })
237
+ })
238
+
239
+ describe('procedure', () => {
240
+ test('with payload schema', () => {
241
+ const procedure = l.procedure(
242
+ 'com.example.create',
243
+ l.params({}),
244
+ l.payload(undefined, undefined),
245
+ l.payload(
246
+ 'application/json',
247
+ l.object({ uri: l.string({ format: 'at-uri' }) }),
248
+ ),
249
+ undefined,
250
+ )
251
+
252
+ type Output = l.InferMethodOutput<typeof procedure, BinaryValue>
253
+
254
+ expectType<Output>({
255
+ encoding: 'application/json',
256
+ body: { uri: 'at://did:plc:abc/post/123' },
257
+ })
258
+ // @ts-expect-error
259
+ expectType<Output>({ encoding: 'application/json', body: { uri: 123 } })
260
+ // @ts-expect-error
261
+ expectType<Output>({ encoding: 'text/plain', body: { uri: 'at://...' } })
262
+ // @ts-expect-error
263
+ expectType<Output>({ encoding: 'text/plain', body: 'Hello' })
264
+ })
265
+
266
+ test('without payload schema', () => {
267
+ const procedure = l.procedure(
268
+ 'com.example.export',
269
+ l.params({}),
270
+ l.payload(undefined, undefined),
271
+ l.payload('*/*', undefined),
272
+ undefined,
273
+ )
274
+
275
+ type Output = l.InferMethodOutput<typeof procedure, BinaryValue>
276
+
277
+ expectType<Output>({
278
+ encoding: 'application/zip',
279
+ body: binaryValue,
280
+ })
281
+ expectType<Output>({
282
+ encoding: 'application/zip',
283
+ // @ts-expect-error
284
+ body: new Uint8Array(),
285
+ })
286
+ // @ts-expect-error
287
+ expectType<Output>({ encoding: 'application/zip', body: 'string' })
288
+ })
289
+ })
290
+ })
291
+
292
+ describe('InferMethodOutputBody', () => {
293
+ describe('query', () => {
294
+ test('with payload schema', () => {
295
+ const query = l.query(
296
+ 'com.example.query',
297
+ l.params({}),
298
+ l.payload(
299
+ 'application/json',
300
+ l.object({
301
+ cursor: l.optional(l.string()),
302
+ feed: l.string({ format: 'at-uri' }),
303
+ }),
304
+ ),
305
+ )
306
+
307
+ type OutputBody = l.InferMethodOutputBody<typeof query, BinaryValue>
308
+
309
+ expectType<OutputBody>({
310
+ cursor: 'abc123',
311
+ feed: 'at://did:plc:abc123/app.bsky.feed.post/xyz',
312
+ })
313
+ // @ts-expect-error
314
+ expectType<OutputBody>({ cursor: 123, feed: 'at://...' })
315
+ // @ts-expect-error
316
+ expectType<OutputBody>({ feed: 123 })
317
+ })
318
+
319
+ test('without payload schema', () => {
320
+ const query = l.query(
321
+ 'com.example.query',
322
+ l.params({}),
323
+ l.payload('*/*', undefined),
324
+ )
325
+
326
+ type OutputBody = l.InferMethodOutputBody<typeof query, BinaryValue>
327
+
328
+ expectType<OutputBody>(binaryValue)
329
+ // @ts-expect-error
330
+ expectType<OutputBody>(new Uint8Array())
331
+ // @ts-expect-error
332
+ expectType<OutputBody>({ data: 'foo' })
333
+ })
334
+ })
335
+
336
+ describe('procedure', () => {
337
+ test('with payload schema', () => {
338
+ const procedure = l.procedure(
339
+ 'com.example.get',
340
+ l.params({}),
341
+ l.payload(undefined, undefined),
342
+ l.payload(
343
+ 'application/json',
344
+ l.object({ uri: l.string({ format: 'at-uri' }) }),
345
+ ),
346
+ undefined,
347
+ )
348
+
349
+ type OutputBody = l.InferMethodOutputBody<typeof procedure, BinaryValue>
350
+
351
+ expectType<OutputBody>({ uri: 'at://did:plc:abc/post/123' })
352
+ // @ts-expect-error
353
+ expectType<OutputBody>({ uri: 123 })
354
+ // @ts-expect-error
355
+ expectType<OutputBody>(binaryValue)
356
+ })
357
+
358
+ test('without payload schema', () => {
359
+ const procedure = l.procedure(
360
+ 'com.example.export',
361
+ l.params({}),
362
+ l.payload(undefined, undefined),
363
+ l.payload('*/*', undefined),
364
+ undefined,
365
+ )
366
+
367
+ type OutputBody = l.InferMethodOutputBody<typeof procedure, BinaryValue>
368
+
369
+ expectType<OutputBody>(binaryValue)
370
+ // @ts-expect-error
371
+ expectType<OutputBody>(new Uint8Array())
372
+ // @ts-expect-error
373
+ expectType<OutputBody>({ data: 'foo' })
374
+ })
375
+ })
376
+ })
377
+
378
+ describe('InferMethodOutputEncoding', () => {
379
+ describe('query', () => {
380
+ test('with payload schema', () => {
381
+ const query = l.query(
382
+ 'com.example.query',
383
+ l.params({}),
384
+ l.payload('application/json', l.object({ data: l.string() })),
385
+ )
386
+
387
+ type OutputEncoding = l.InferMethodOutputEncoding<typeof query>
388
+
389
+ expectType<OutputEncoding>('application/json')
390
+ // @ts-expect-error
391
+ expectType<OutputEncoding>('text/plain')
392
+ // @ts-expect-error
393
+ expectType<OutputEncoding>(123)
394
+ })
395
+
396
+ test('without payload schema', () => {
397
+ const query = l.query(
398
+ 'com.example.query',
399
+ l.params({}),
400
+ l.payload('*/*', undefined),
401
+ )
402
+
403
+ type OutputEncoding = l.InferMethodOutputEncoding<typeof query>
404
+
405
+ expectType<OutputEncoding>('image/png')
406
+ expectType<OutputEncoding>('application/octet-stream')
407
+ // @ts-expect-error
408
+ expectType<OutputEncoding>(123)
409
+ })
410
+ })
411
+
412
+ describe('procedure', () => {
413
+ test('with payload schema', () => {
414
+ const procedure = l.procedure(
415
+ 'com.example.create',
416
+ l.params({}),
417
+ l.payload(undefined, undefined),
418
+ l.payload('application/json', l.object({ id: l.string() })),
419
+ undefined,
420
+ )
421
+
422
+ type OutputEncoding = l.InferMethodOutputEncoding<typeof procedure>
423
+
424
+ expectType<OutputEncoding>('application/json')
425
+ // @ts-expect-error
426
+ expectType<OutputEncoding>('text/plain')
427
+ // @ts-expect-error
428
+ expectType<OutputEncoding>(123)
429
+ })
430
+
431
+ test('without payload schema', () => {
432
+ const procedure = l.procedure(
433
+ 'com.example.export',
434
+ l.params({}),
435
+ l.payload(undefined, undefined),
436
+ l.payload('*/*', undefined),
437
+ undefined,
438
+ )
439
+
440
+ type OutputEncoding = l.InferMethodOutputEncoding<typeof procedure>
441
+
442
+ expectType<OutputEncoding>('application/zip')
443
+ expectType<OutputEncoding>('application/octet-stream')
444
+ // @ts-expect-error
445
+ expectType<OutputEncoding>(123)
446
+ })
447
+ })
448
+ })
449
+
450
+ describe('InferMethodMessage', () => {
451
+ describe('subscription', () => {
452
+ test('with message schema', () => {
453
+ const subscription = l.subscription(
454
+ 'com.example.subscribe',
455
+ l.params({}),
456
+ l.object({
457
+ seq: l.integer(),
458
+ event: l.string(),
459
+ }),
460
+ )
461
+
462
+ type Message = l.InferMethodMessage<typeof subscription>
463
+
464
+ expectType<Message>({ seq: 1, event: 'create' })
465
+ // @ts-expect-error
466
+ expectType<Message>({ seq: '1', event: 'create' })
467
+ // @ts-expect-error
468
+ expectType<Message>({ seq: 1 })
469
+ // @ts-expect-error
470
+ expectType<Message>(binaryValue)
471
+ })
472
+
473
+ test('without message schema', () => {
474
+ const subscription = l.subscription(
475
+ 'com.example.subscribe',
476
+ l.params({}),
477
+ l.unknown(),
478
+ )
479
+
480
+ type Message = l.InferMethodMessage<typeof subscription>
481
+
482
+ expectType<Message>(undefined as unknown)
483
+ expectType<Message>({ any: 'value' })
484
+ expectType<Message>(123)
485
+ })
486
+ })
487
+ })
package/src/helpers.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { LexErrorData } from '@atproto/lex-data'
2
+ import { Infer, Restricted, Schema } from './core.js'
3
+ import {
4
+ InferPayload,
5
+ InferPayloadBody,
6
+ InferPayloadEncoding,
7
+ ObjectSchema,
8
+ OptionalSchema,
9
+ Payload,
10
+ Procedure,
11
+ Query,
12
+ StringSchema,
13
+ Subscription,
14
+ } from './schema.js'
15
+
16
+ export type Main<T> = T | { main: T }
17
+
18
+ export function getMain<T extends object>(ns: Main<T>): T {
19
+ return 'main' in ns ? ns.main : ns
20
+ }
21
+
22
+ /**
23
+ * Every XRPC implementation should translate `application/json` and `text/*`
24
+ * payloads into their native equivalent ({@link LexValue} or string). Binary
25
+ * data payloads, however, can be represented differently depending on the
26
+ * environment and use case (e.g. `Uint8Array`, `Blob`, `Buffer`,
27
+ * `ReadableStream`, etc.). This type is a placeholder to represent binary data
28
+ * when not explicitly provided.
29
+ */
30
+ type BinaryData = Restricted<'Binary data'>
31
+
32
+ export type InferMethodParams<
33
+ //
34
+ M extends Procedure | Query | Subscription,
35
+ > = Infer<M['parameters']>
36
+
37
+ export type InferMethodInput<
38
+ M extends Procedure | Query | Subscription,
39
+ B = BinaryData,
40
+ > = M extends { input: Payload } ? InferPayload<M['input'], B> : undefined
41
+
42
+ export type InferMethodInputBody<
43
+ M extends Procedure | Query | Subscription,
44
+ B = BinaryData,
45
+ > = M extends { input: Payload } ? InferPayloadBody<M['input'], B> : undefined
46
+
47
+ export type InferMethodInputEncoding<
48
+ M extends Procedure | Query | Subscription,
49
+ > = M extends { input: Payload } ? InferPayloadEncoding<M['input']> : undefined
50
+
51
+ export type InferMethodOutput<
52
+ M extends Procedure | Query | Subscription,
53
+ B = BinaryData,
54
+ > = M extends { output: Payload } ? InferPayload<M['output'], B> : undefined
55
+
56
+ export type InferMethodOutputBody<
57
+ M extends Procedure | Query | Subscription,
58
+ B = BinaryData,
59
+ > = M extends { output: Payload } ? InferPayloadBody<M['output'], B> : undefined
60
+
61
+ export type InferMethodOutputEncoding<
62
+ M extends Procedure | Query | Subscription,
63
+ > = M extends { output: Payload }
64
+ ? InferPayloadEncoding<M['output']>
65
+ : undefined
66
+
67
+ export type InferMethodMessage<
68
+ //
69
+ M extends Procedure | Query | Subscription,
70
+ > = M extends { message: Schema } ? Infer<M['message']> : undefined
71
+
72
+ export const lexErrorData: Schema<LexErrorData> = new ObjectSchema({
73
+ error: new StringSchema({ minLength: 1 }),
74
+ message: new OptionalSchema(new StringSchema({})),
75
+ })
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest'
1
2
  import { paramSchema, paramsSchema } from './_parameters.js'
2
3
 
3
4
  describe('paramSchema', () => {
@@ -1,4 +1,4 @@
1
- import { Infer, Validator } from '../validation.js'
1
+ import { Infer, Validator } from '../core.js'
2
2
  import { ArraySchema } from './array.js'
3
3
  import { BooleanSchema } from './boolean.js'
4
4
  import { DictSchema } from './dict.js'
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest'
1
2
  import { ArraySchema } from './array.js'
2
3
  import { IntegerSchema } from './integer.js'
3
4
  import { ObjectSchema } from './object.js'
@@ -4,7 +4,7 @@ import {
4
4
  ValidationResult,
5
5
  Validator,
6
6
  ValidatorContext,
7
- } from '../validation.js'
7
+ } from '../core.js'
8
8
 
9
9
  export type ArraySchemaOptions = {
10
10
  minLength?: number
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest'
1
2
  import { parseCid } from '@atproto/lex-data'
2
3
  import { BlobSchema } from './blob.js'
3
4
 
@@ -353,8 +354,7 @@ describe('BlobSchema', () => {
353
354
  mimeType: 'image/gif',
354
355
  size: 10000,
355
356
  })
356
- // Accept constraints are not enforced (see comment in source)
357
- expect(result.success).toBe(true)
357
+ expect(result.success).toBe(false)
358
358
  })
359
359
 
360
360
  it('accepts blob with maxSize option (not enforced)', () => {
@@ -365,8 +365,7 @@ describe('BlobSchema', () => {
365
365
  mimeType: 'image/jpeg',
366
366
  size: 10000,
367
367
  })
368
- // MaxSize constraints are not enforced (see comment in source)
369
- expect(result.success).toBe(true)
368
+ expect(result.success).toBe(false)
370
369
  })
371
370
 
372
371
  it('accepts blob matching accept constraint', () => {
@@ -4,7 +4,7 @@ import {
4
4
  isBlobRef,
5
5
  isLegacyBlobRef,
6
6
  } from '@atproto/lex-data'
7
- import { Schema, ValidationResult, ValidatorContext } from '../validation.js'
7
+ import { Schema, ValidationResult, ValidatorContext } from '../core.js'
8
8
 
9
9
  export type BlobSchemaOptions = {
10
10
  /**
@@ -43,38 +43,46 @@ export class BlobSchema<O extends BlobSchemaOptions> extends Schema<
43
43
  input: unknown,
44
44
  ctx: ValidatorContext,
45
45
  ): ValidationResult<BlobSchemaOutput<O>> {
46
- if (!isBlob(input, this.options)) {
46
+ const blob: null | BlobRef | LegacyBlobRef =
47
+ (input as any)?.$type !== undefined
48
+ ? isBlobRef(input, this.options)
49
+ ? input
50
+ : null
51
+ : this.options.allowLegacy === true && isLegacyBlobRef(input)
52
+ ? input
53
+ : null
54
+
55
+ if (!blob) {
47
56
  return ctx.issueInvalidType(input, 'blob')
48
57
  }
49
58
 
50
- // @NOTE Historically, we did not enforce constraints on blob references
51
- // https://github.com/bluesky-social/atproto/blob/4c15fb47cec26060bff2e710e95869a90c9d7fdd/packages/lexicon/src/validators/blob.ts#L5-L19
52
-
53
- // const { accept } = this.options
54
- // if (accept && !accept.includes(input.mimeType)) {
55
- // return ctx.issueInvalidValue(input, accept)
56
- // }
59
+ const { accept } = this.options
60
+ if (accept && !matchesMime(blob.mimeType, accept)) {
61
+ return ctx.issueInvalidPropertyValue(blob, 'mimeType', accept)
62
+ }
57
63
 
58
- // const { maxSize } = this.options
59
- // if (maxSize != null && input.size != -1 && input.size > maxSize) {
60
- // return ctx.issueTooBig(input, 'blob', maxSize, input.size)
61
- // }
64
+ const { maxSize } = this.options
65
+ if (maxSize != null && 'size' in blob && blob.size > maxSize) {
66
+ return ctx.issueTooBig(blob, 'blob', maxSize, blob.size)
67
+ }
62
68
 
63
- return ctx.success(input)
69
+ return ctx.success(blob as BlobSchemaOutput<O>)
64
70
  }
65
- }
66
71
 
67
- function isBlob<O extends BlobSchemaOptions>(
68
- input: unknown,
69
- options: O,
70
- ): input is BlobSchemaOutput<O> {
71
- if ((input as any)?.$type !== undefined) {
72
- return isBlobRef(input, options)
72
+ matchesMime(mime: string): boolean {
73
+ const { accept } = this.options
74
+ if (!accept) return true
75
+ return matchesMime(mime, accept)
73
76
  }
77
+ }
74
78
 
75
- if (options.allowLegacy === true) {
76
- return isLegacyBlobRef(input)
79
+ function matchesMime(mime: string, accepted: string[]): boolean {
80
+ if (accepted.includes('*/*')) return true
81
+ if (accepted.includes(mime)) return true
82
+ for (const value of accepted) {
83
+ if (value.endsWith('/*') && mime.startsWith(value.slice(0, -1))) {
84
+ return true
85
+ }
77
86
  }
78
-
79
87
  return false
80
88
  }