@atproto/lex-client 0.0.4 → 0.0.5
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 +27 -0
- package/dist/agent.d.ts +9 -8
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js.map +1 -1
- package/dist/client.d.ts +32 -96
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +31 -31
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts +7 -7
- package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.js +3 -5
- package/dist/lexicons/com/atproto/repo/createRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts +7 -7
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js +3 -5
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts +5 -6
- package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.defs.js +3 -5
- package/dist/lexicons/com/atproto/repo/getRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts +5 -6
- package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.defs.js +3 -5
- package/dist/lexicons/com/atproto/repo/listRecords.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts +7 -7
- package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.defs.js +3 -5
- package/dist/lexicons/com/atproto/repo/putRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts +7 -7
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +3 -5
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/sync/getBlob.d.ts +3 -0
- package/dist/lexicons/com/atproto/sync/getBlob.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync/getBlob.defs.d.ts +25 -0
- package/dist/lexicons/com/atproto/sync/getBlob.defs.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync/getBlob.defs.js +27 -0
- package/dist/lexicons/com/atproto/sync/getBlob.defs.js.map +1 -0
- package/dist/lexicons/com/atproto/sync/getBlob.js +10 -0
- package/dist/lexicons/com/atproto/sync/getBlob.js.map +1 -0
- package/dist/lexicons/com/atproto/sync.d.ts +2 -0
- package/dist/lexicons/com/atproto/sync.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync.js +9 -0
- package/dist/lexicons/com/atproto/sync.js.map +1 -0
- package/dist/lexicons/com/atproto.d.ts +1 -0
- package/dist/lexicons/com/atproto.d.ts.map +1 -1
- package/dist/lexicons/com/atproto.js +2 -1
- package/dist/lexicons/com/atproto.js.map +1 -1
- package/dist/lexicons.d.ts +2 -0
- package/dist/lexicons.d.ts.map +1 -0
- package/dist/lexicons.js +6 -0
- package/dist/lexicons.js.map +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +14 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +65 -0
- package/dist/util.js.map +1 -0
- package/dist/xrpc-error.d.ts +87 -0
- package/dist/xrpc-error.d.ts.map +1 -0
- package/dist/xrpc-error.js +127 -0
- package/dist/xrpc-error.js.map +1 -0
- package/dist/xrpc-response.d.ts +35 -0
- package/dist/xrpc-response.d.ts.map +1 -0
- package/dist/xrpc-response.js +140 -0
- package/dist/xrpc-response.js.map +1 -0
- package/dist/xrpc.d.ts +29 -32
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +119 -125
- package/dist/xrpc.js.map +1 -1
- package/package.json +6 -6
- package/src/agent.ts +12 -12
- package/src/client.ts +92 -77
- package/src/index.ts +0 -2
- package/src/lexicons/com/atproto/repo/createRecord.defs.ts +9 -8
- package/src/lexicons/com/atproto/repo/deleteRecord.defs.ts +9 -8
- package/src/lexicons/com/atproto/repo/getRecord.defs.ts +7 -7
- package/src/lexicons/com/atproto/repo/listRecords.defs.ts +7 -6
- package/src/lexicons/com/atproto/repo/putRecord.defs.ts +9 -8
- package/src/lexicons/com/atproto/repo/uploadBlob.defs.ts +9 -8
- package/src/lexicons/com/atproto/sync/getBlob.defs.ts +37 -0
- package/src/lexicons/com/atproto/sync/getBlob.ts +6 -0
- package/src/lexicons/com/atproto/sync.ts +5 -0
- package/src/lexicons/com/atproto.ts +1 -0
- package/src/lexicons.ts +1 -0
- package/src/types.ts +27 -0
- package/src/util.ts +84 -0
- package/src/xrpc-error.ts +195 -0
- package/src/xrpc-response.ts +213 -0
- package/src/xrpc.ts +209 -220
- package/dist/error.d.ts +0 -66
- package/dist/error.d.ts.map +0 -1
- package/dist/error.js +0 -100
- package/dist/error.js.map +0 -1
- package/dist/response.d.ts +0 -21
- package/dist/response.d.ts.map +0 -1
- package/dist/response.js +0 -31
- package/dist/response.js.map +0 -1
- package/src/error.ts +0 -145
- package/src/response.ts +0 -42
package/src/xrpc.ts
CHANGED
|
@@ -1,29 +1,83 @@
|
|
|
1
|
-
import { LexValue } from '@atproto/lex-data'
|
|
2
|
-
import {
|
|
1
|
+
import { LexValue, isLexScalar, isPlainObject } from '@atproto/lex-data'
|
|
2
|
+
import { lexStringify } from '@atproto/lex-json'
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
InferPayloadBody,
|
|
4
|
+
InferMethodInput,
|
|
5
|
+
InferMethodParams,
|
|
7
6
|
Params,
|
|
8
7
|
ParamsSchema,
|
|
8
|
+
Payload as LexPayload,
|
|
9
9
|
Procedure,
|
|
10
10
|
Query,
|
|
11
11
|
Restricted,
|
|
12
12
|
Subscription,
|
|
13
13
|
} from '@atproto/lex-schema'
|
|
14
14
|
import { Agent } from './agent.js'
|
|
15
|
+
import { BinaryBodyInit, CallOptions, Namespace, getMain } from './types.js'
|
|
15
16
|
import {
|
|
16
|
-
|
|
17
|
+
Payload,
|
|
18
|
+
buildAtprotoHeaders,
|
|
19
|
+
isAsyncIterable,
|
|
20
|
+
isBlobLike,
|
|
21
|
+
toReadableStream,
|
|
22
|
+
} from './util.js'
|
|
23
|
+
import {
|
|
24
|
+
XrpcInvalidResponseError,
|
|
17
25
|
XrpcResponseError,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} from './
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
XrpcUnexpectedError,
|
|
27
|
+
} from './xrpc-error.js'
|
|
28
|
+
import { XrpcResponse } from './xrpc-response.js'
|
|
29
|
+
|
|
30
|
+
export * from './xrpc-error.js'
|
|
31
|
+
export * from './xrpc-response.js'
|
|
32
|
+
|
|
33
|
+
// If all params are optional, allow omitting the params object
|
|
34
|
+
type XrpcParamsOptions<P extends Params> =
|
|
35
|
+
NonNullable<unknown> extends P ? { params?: P } : { params: P }
|
|
36
|
+
|
|
37
|
+
type XrpcRequestPayload<M extends Procedure | Query> = InferMethodInput<
|
|
38
|
+
M,
|
|
39
|
+
BinaryBodyInit
|
|
40
|
+
>
|
|
41
|
+
|
|
42
|
+
type XrpcInputOptions<In> = In extends { body: infer B; encoding: infer E }
|
|
43
|
+
? // encoding will be inferred from the schema at runtime if not provided
|
|
44
|
+
{ body: B; encoding?: E }
|
|
45
|
+
: { body?: undefined; encoding?: undefined }
|
|
23
46
|
|
|
24
47
|
export type XrpcOptions<M extends Procedure | Query = Procedure | Query> =
|
|
25
|
-
CallOptions &
|
|
48
|
+
CallOptions &
|
|
49
|
+
XrpcInputOptions<XrpcRequestPayload<M>> &
|
|
50
|
+
XrpcParamsOptions<InferMethodParams<M>>
|
|
51
|
+
|
|
52
|
+
export type XrpcFailure<M extends Procedure | Query> =
|
|
53
|
+
// The server returned a valid XRPC error response
|
|
54
|
+
| XrpcResponseError<M>
|
|
55
|
+
// The response was not a valid XRPC response, or it does not match the schema
|
|
56
|
+
| XrpcInvalidResponseError
|
|
57
|
+
// Something went wrong (network error, etc.)
|
|
58
|
+
| XrpcUnexpectedError
|
|
59
|
+
|
|
60
|
+
export type XrpcResult<M extends Procedure | Query> =
|
|
61
|
+
| XrpcResponse<M>
|
|
62
|
+
| XrpcFailure<M>
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Utility method to type cast the error thrown by {@link xrpc} to an
|
|
66
|
+
* {@link XrpcFailure} matching the provided method. Only use this function
|
|
67
|
+
* inside a catch block right after calling {@link xrpc}, and use the same
|
|
68
|
+
* method type parameter as used in the {@link xrpc} call.
|
|
69
|
+
*/
|
|
70
|
+
function asXrpcFailure<M extends Procedure | Query>(
|
|
71
|
+
err: unknown,
|
|
72
|
+
): XrpcFailure<M> {
|
|
73
|
+
if (err instanceof XrpcResponseError) return err
|
|
74
|
+
if (err instanceof XrpcInvalidResponseError) return err
|
|
75
|
+
return XrpcUnexpectedError.from(err)
|
|
76
|
+
}
|
|
26
77
|
|
|
78
|
+
/**
|
|
79
|
+
* @throws XrpcFailure<M>
|
|
80
|
+
*/
|
|
27
81
|
export async function xrpc<const M extends Query | Procedure>(
|
|
28
82
|
agent: Agent,
|
|
29
83
|
ns: NonNullable<unknown> extends XrpcOptions<M>
|
|
@@ -40,23 +94,48 @@ export async function xrpc<const M extends Query | Procedure>(
|
|
|
40
94
|
ns: Namespace<M>,
|
|
41
95
|
options: XrpcOptions<M> = {} as XrpcOptions<M>,
|
|
42
96
|
): Promise<XrpcResponse<M>> {
|
|
43
|
-
|
|
97
|
+
try {
|
|
98
|
+
return await xrpcRequest<M>(agent, ns, options)
|
|
99
|
+
} catch (err) {
|
|
100
|
+
throw asXrpcFailure<M>(err)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function xrpcSafe<const M extends Query | Procedure>(
|
|
105
|
+
agent: Agent,
|
|
106
|
+
ns: NonNullable<unknown> extends XrpcOptions<M>
|
|
107
|
+
? Namespace<M>
|
|
108
|
+
: Restricted<'This XRPC method requires an "options" argument'>,
|
|
109
|
+
): Promise<XrpcResult<M>>
|
|
110
|
+
export async function xrpcSafe<const M extends Query | Procedure>(
|
|
111
|
+
agent: Agent,
|
|
112
|
+
ns: Namespace<M>,
|
|
113
|
+
options: XrpcOptions<M>,
|
|
114
|
+
): Promise<XrpcResult<M>>
|
|
115
|
+
export async function xrpcSafe<const M extends Query | Procedure>(
|
|
116
|
+
agent: Agent,
|
|
117
|
+
ns: Namespace<M>,
|
|
118
|
+
options: XrpcOptions<M> = {} as XrpcOptions<M>,
|
|
119
|
+
): Promise<XrpcResult<M>> {
|
|
120
|
+
return xrpcRequest<M>(agent, ns, options).catch(asXrpcFailure<M>)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function xrpcRequest<const M extends Query | Procedure>(
|
|
124
|
+
agent: Agent,
|
|
125
|
+
ns: Namespace<M>,
|
|
126
|
+
options: XrpcOptions<M> = {} as XrpcOptions<M>,
|
|
127
|
+
): Promise<XrpcResponse<M>> {
|
|
44
128
|
const method = getMain(ns)
|
|
129
|
+
options.signal?.throwIfAborted()
|
|
45
130
|
const url = xrpcRequestUrl(method, options)
|
|
46
131
|
const request = xrpcRequestInit(method, options)
|
|
47
132
|
const response = await agent.fetchHandler(url, request)
|
|
48
|
-
return
|
|
133
|
+
return XrpcResponse.fromFetchResponse<M>(method, response, options)
|
|
49
134
|
}
|
|
50
135
|
|
|
51
|
-
|
|
52
|
-
CallOptions &
|
|
53
|
-
(undefined extends InferParamsSchema<M['parameters']>
|
|
54
|
-
? { params?: InferParamsSchema<M['parameters']> }
|
|
55
|
-
: { params: InferParamsSchema<M['parameters']> })
|
|
56
|
-
|
|
57
|
-
export function xrpcRequestUrl<M extends Procedure | Query | Subscription>(
|
|
136
|
+
function xrpcRequestUrl<M extends Procedure | Query | Subscription>(
|
|
58
137
|
method: M,
|
|
59
|
-
options:
|
|
138
|
+
options: CallOptions & { params?: Params },
|
|
60
139
|
) {
|
|
61
140
|
const path = `/xrpc/${method.nsid}`
|
|
62
141
|
|
|
@@ -67,7 +146,7 @@ export function xrpcRequestUrl<M extends Procedure | Query | Subscription>(
|
|
|
67
146
|
return queryString ? `${path}?${queryString}` : path
|
|
68
147
|
}
|
|
69
148
|
|
|
70
|
-
|
|
149
|
+
function xrpcRequestParams(
|
|
71
150
|
schema: ParamsSchema | undefined,
|
|
72
151
|
params: Params | undefined,
|
|
73
152
|
options: CallOptions,
|
|
@@ -78,32 +157,37 @@ export function xrpcRequestParams(
|
|
|
78
157
|
return urlSearchParams?.size ? urlSearchParams.toString() : undefined
|
|
79
158
|
}
|
|
80
159
|
|
|
81
|
-
|
|
82
|
-
(T extends Procedure
|
|
83
|
-
? never extends InferPayloadBody<T['input']>
|
|
84
|
-
? { body?: InferPayloadBody<T['input']> }
|
|
85
|
-
: { body: InferPayloadBody<T['input']> }
|
|
86
|
-
: { body?: never })
|
|
87
|
-
|
|
88
|
-
export function xrpcRequestInit<T extends Procedure | Query>(
|
|
160
|
+
function xrpcRequestInit<T extends Procedure | Query>(
|
|
89
161
|
schema: T,
|
|
90
|
-
options:
|
|
162
|
+
options: CallOptions & {
|
|
163
|
+
body?: LexValue | BinaryBodyInit
|
|
164
|
+
encoding?: string
|
|
165
|
+
},
|
|
91
166
|
): RequestInit & { duplex?: 'half' } {
|
|
92
|
-
const headers =
|
|
167
|
+
const headers = buildAtprotoHeaders(options)
|
|
168
|
+
|
|
169
|
+
// Tell the server what type of response we're expecting
|
|
170
|
+
if (schema.output.encoding) {
|
|
171
|
+
headers.set('accept', schema.output.encoding)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Caller should not set content-type header
|
|
175
|
+
if (headers.has('content-type')) {
|
|
176
|
+
const contentType = headers.get('content-type')
|
|
177
|
+
throw new TypeError(`Unexpected content-type header (${contentType})`)
|
|
178
|
+
}
|
|
93
179
|
|
|
94
180
|
// Requests with body
|
|
95
|
-
if ('input' in schema
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
)
|
|
181
|
+
if ('input' in schema) {
|
|
182
|
+
const encodingHint = options.encoding
|
|
183
|
+
const input = xrpcProcedureInput(schema, options, encodingHint)
|
|
184
|
+
|
|
185
|
+
if (input) {
|
|
186
|
+
headers.set('content-type', input.encoding)
|
|
187
|
+
} else if (encodingHint != null) {
|
|
188
|
+
throw new TypeError(`Unexpected encoding hint (${encodingHint})`)
|
|
104
189
|
}
|
|
105
190
|
|
|
106
|
-
headers.set('content-type', schema.input.encoding)
|
|
107
191
|
return {
|
|
108
192
|
duplex: 'half',
|
|
109
193
|
redirect: 'follow',
|
|
@@ -112,12 +196,7 @@ export function xrpcRequestInit<T extends Procedure | Query>(
|
|
|
112
196
|
signal: options.signal,
|
|
113
197
|
method: 'POST',
|
|
114
198
|
headers,
|
|
115
|
-
body:
|
|
116
|
-
schema.input?.encoding,
|
|
117
|
-
options.validateRequest
|
|
118
|
-
? schema.input?.body.parse(options.body)
|
|
119
|
-
: options.body,
|
|
120
|
-
),
|
|
199
|
+
body: input?.body,
|
|
121
200
|
}
|
|
122
201
|
}
|
|
123
202
|
|
|
@@ -128,208 +207,118 @@ export function xrpcRequestInit<T extends Procedure | Query>(
|
|
|
128
207
|
referrerPolicy: 'strict-origin-when-cross-origin', // (default)
|
|
129
208
|
mode: 'cors', // (default)
|
|
130
209
|
signal: options.signal,
|
|
131
|
-
method:
|
|
210
|
+
method: 'GET',
|
|
132
211
|
headers,
|
|
133
212
|
}
|
|
134
213
|
}
|
|
135
214
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
if (options.service && !headers.has('atproto-proxy')) {
|
|
144
|
-
headers.set('atproto-proxy', options.service)
|
|
145
|
-
}
|
|
215
|
+
function xrpcProcedureInput(
|
|
216
|
+
method: Procedure,
|
|
217
|
+
options: CallOptions & { body?: LexValue | BinaryBodyInit },
|
|
218
|
+
encodingHint?: string,
|
|
219
|
+
): null | Payload<BodyInit> {
|
|
220
|
+
const { input } = method
|
|
221
|
+
const { body } = options
|
|
146
222
|
|
|
147
|
-
if (options.
|
|
148
|
-
|
|
149
|
-
'atproto-accept-labelers',
|
|
150
|
-
[...options.labelers, headers.get('atproto-accept-labelers')?.trim()]
|
|
151
|
-
.filter(Boolean)
|
|
152
|
-
.join(', '),
|
|
153
|
-
)
|
|
223
|
+
if (options.validateRequest) {
|
|
224
|
+
input.schema?.check(body)
|
|
154
225
|
}
|
|
155
226
|
|
|
156
|
-
|
|
157
|
-
|
|
227
|
+
// Special handling for endpoints expecting application/json input
|
|
228
|
+
if (input.encoding === 'application/json') {
|
|
229
|
+
// @NOTE **NOT** using isLexValue here to avoid deep checks in order to
|
|
230
|
+
// distinguish between LexValue and BinaryBodyInit.
|
|
231
|
+
if (!isLexScalar(body) && !isPlainObject(body) && !Array.isArray(body)) {
|
|
232
|
+
throw new TypeError(`Expected LexValue body, got ${typeof body}`)
|
|
233
|
+
}
|
|
158
234
|
|
|
159
|
-
|
|
160
|
-
encoding: string | undefined,
|
|
161
|
-
body: LexValue | undefined,
|
|
162
|
-
): BodyInit | null {
|
|
163
|
-
if (encoding === undefined) {
|
|
164
|
-
return null
|
|
235
|
+
return buildPayload(input, lexStringify(body), encodingHint)
|
|
165
236
|
}
|
|
166
237
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
238
|
+
// Other encodings will be sent unaltered (ie. as binary data)
|
|
239
|
+
switch (typeof body) {
|
|
240
|
+
case 'undefined':
|
|
241
|
+
case 'string':
|
|
242
|
+
return buildPayload(input, body, encodingHint)
|
|
243
|
+
case 'object': {
|
|
244
|
+
if (body === null) break
|
|
245
|
+
if (
|
|
246
|
+
ArrayBuffer.isView(body) ||
|
|
247
|
+
body instanceof ArrayBuffer ||
|
|
248
|
+
body instanceof ReadableStream
|
|
249
|
+
) {
|
|
250
|
+
return buildPayload(input, body, encodingHint)
|
|
251
|
+
} else if (isAsyncIterable(body)) {
|
|
252
|
+
return buildPayload(input, toReadableStream(body), encodingHint)
|
|
253
|
+
} else if (isBlobLike(body)) {
|
|
254
|
+
return buildPayload(input, body, encodingHint || body.type)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
173
257
|
}
|
|
174
258
|
|
|
175
|
-
throw new TypeError(
|
|
259
|
+
throw new TypeError(
|
|
260
|
+
`Invalid ${typeof body} body for ${input.encoding} encoding`,
|
|
261
|
+
)
|
|
176
262
|
}
|
|
177
263
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
):
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const encoding = extractEncoding(response.headers)
|
|
188
|
-
|
|
189
|
-
const body = await readResponseBody(response, encoding).catch((cause) => {
|
|
190
|
-
throw new XrpcServiceError(
|
|
191
|
-
KnownError.InvalidResponse,
|
|
192
|
-
response.status,
|
|
193
|
-
response.headers,
|
|
194
|
-
undefined,
|
|
195
|
-
'Failed to read XRPC response',
|
|
196
|
-
{ cause },
|
|
197
|
-
)
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
// @NOTE redirect is set to 'follow', so we shouldn't get 3xx responses here
|
|
201
|
-
if (response.status < 200 || response.status >= 300) {
|
|
202
|
-
// All unsuccessful responses should follow a standard error response
|
|
203
|
-
// schema. The Content-Type should be application/json, and the payload
|
|
204
|
-
// should be a JSON object with the following fields:
|
|
205
|
-
// - error (string, required): type name of the error (generic ASCII
|
|
206
|
-
// constant, no whitespace)
|
|
207
|
-
// - message (string, optional): description of the error, appropriate for
|
|
208
|
-
// display to humans
|
|
209
|
-
if (
|
|
210
|
-
body != null &&
|
|
211
|
-
encoding === 'application/json' &&
|
|
212
|
-
xrpcErrorBodySchema.matches(body)
|
|
213
|
-
) {
|
|
214
|
-
throw new XrpcResponseError(
|
|
215
|
-
response.status,
|
|
216
|
-
response.headers,
|
|
217
|
-
encoding,
|
|
218
|
-
body,
|
|
264
|
+
function buildPayload(
|
|
265
|
+
schema: LexPayload,
|
|
266
|
+
body: undefined | BodyInit,
|
|
267
|
+
encodingHint?: string,
|
|
268
|
+
): null | Payload<BodyInit> {
|
|
269
|
+
if (schema.encoding === undefined) {
|
|
270
|
+
if (body !== undefined) {
|
|
271
|
+
throw new TypeError(
|
|
272
|
+
`Cannot send a ${typeof body} body with undefined encoding`,
|
|
219
273
|
)
|
|
220
274
|
}
|
|
221
275
|
|
|
222
|
-
|
|
223
|
-
response.status >= 500
|
|
224
|
-
? KnownError.InternalServerError
|
|
225
|
-
: KnownError.InvalidResponse,
|
|
226
|
-
response.status,
|
|
227
|
-
response.headers,
|
|
228
|
-
body,
|
|
229
|
-
)
|
|
276
|
+
return null
|
|
230
277
|
}
|
|
231
278
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
response.headers,
|
|
238
|
-
body,
|
|
239
|
-
`Expected response with content-type ${schema.output.encoding}, got ${encoding}`,
|
|
240
|
-
)
|
|
279
|
+
if (body === undefined) {
|
|
280
|
+
// This error would be returned by the server, but we can catch it earlier
|
|
281
|
+
// to avoid un-necessary requests. Note that a content-length of 0 does not
|
|
282
|
+
// necessary mean that the body is "empty" (e.g. an empty txt file).
|
|
283
|
+
throw new TypeError(`A request body is expected but none was provided`)
|
|
241
284
|
}
|
|
242
285
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
286
|
+
const encoding = buildEncoding(schema, encodingHint)
|
|
287
|
+
return { encoding, body }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function buildEncoding(schema: LexPayload, encodingHint?: string): string {
|
|
291
|
+
// Should never happen (required for type safety)
|
|
292
|
+
if (!schema.encoding) {
|
|
293
|
+
throw new TypeError('Unexpected payload')
|
|
294
|
+
}
|
|
253
295
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
undefined as XrpcResponseBody<M>,
|
|
259
|
-
)
|
|
260
|
-
} else {
|
|
261
|
-
// @NOTE this should already be enforced by readXrpcResponseBody
|
|
262
|
-
if (body === undefined) {
|
|
263
|
-
throw new XrpcServiceError(
|
|
264
|
-
KnownError.InvalidResponse,
|
|
265
|
-
response.status,
|
|
266
|
-
response.headers,
|
|
267
|
-
body,
|
|
268
|
-
`Expected non-empty response body`,
|
|
296
|
+
if (encodingHint?.length) {
|
|
297
|
+
if (!schema.matchesEncoding(encodingHint)) {
|
|
298
|
+
throw new TypeError(
|
|
299
|
+
`Cannot send a body with content-type "${encodingHint}" for "${schema.encoding}" encoding`,
|
|
269
300
|
)
|
|
270
301
|
}
|
|
271
|
-
|
|
272
|
-
return new XrpcResponse<M>(
|
|
273
|
-
schema,
|
|
274
|
-
response.status,
|
|
275
|
-
response.headers,
|
|
276
|
-
schema.output.schema == null || options?.validateResponse === false
|
|
277
|
-
? (body as XrpcResponseBody<M>)
|
|
278
|
-
: (schema.output.schema.parse(body) as XrpcResponseBody<M>),
|
|
279
|
-
)
|
|
302
|
+
return encodingHint
|
|
280
303
|
}
|
|
281
|
-
}
|
|
282
304
|
|
|
283
|
-
|
|
284
|
-
const contentType = headers.get('content-type')
|
|
285
|
-
if (!contentType) return undefined
|
|
286
|
-
return contentType.split(';')[0].trim()
|
|
287
|
-
}
|
|
305
|
+
// Fallback
|
|
288
306
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
encoding: string,
|
|
292
|
-
): Promise<LexValue>
|
|
293
|
-
export async function readResponseBody(
|
|
294
|
-
response: Response,
|
|
295
|
-
encoding: string | undefined,
|
|
296
|
-
): Promise<LexValue | undefined>
|
|
297
|
-
export async function readResponseBody(
|
|
298
|
-
response: Response,
|
|
299
|
-
encoding: string | undefined,
|
|
300
|
-
): Promise<LexValue | undefined> {
|
|
301
|
-
// When encoding is undefined or empty, we expect no body
|
|
302
|
-
if (encoding == null) {
|
|
303
|
-
if (response.body == null) return undefined
|
|
304
|
-
|
|
305
|
-
// Let's make sure the body is empty (while avoiding reading it all).
|
|
306
|
-
if (!('getReader' in response.body)) {
|
|
307
|
-
// Some environments may not support body.getReader(), fall back to
|
|
308
|
-
// reading the whole body.
|
|
309
|
-
const buffer = await response.arrayBuffer()
|
|
310
|
-
if (buffer.byteLength === 0) return undefined
|
|
311
|
-
} else {
|
|
312
|
-
const reader = response.body.getReader()
|
|
313
|
-
const next = await reader.read()
|
|
314
|
-
if (next.done) return undefined
|
|
315
|
-
await reader.cancel() // Drain the rest of the (non-empty) body stream
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
throw new SyntaxError('Content-type is undefined but body is not empty')
|
|
307
|
+
if (schema.encoding === '*/*') {
|
|
308
|
+
return 'application/octet-stream'
|
|
319
309
|
}
|
|
320
310
|
|
|
321
|
-
if (encoding
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
// @TODO verify statement above
|
|
327
|
-
return lexParse(await response.text())
|
|
311
|
+
if (schema.encoding.startsWith('text/')) {
|
|
312
|
+
return schema.encoding.includes('*')
|
|
313
|
+
? 'text/plain; charset=utf-8'
|
|
314
|
+
: `${schema.encoding}; charset=utf-8`
|
|
328
315
|
}
|
|
329
316
|
|
|
330
|
-
if (encoding.
|
|
331
|
-
return
|
|
317
|
+
if (!schema.encoding.includes('*')) {
|
|
318
|
+
return schema.encoding
|
|
332
319
|
}
|
|
333
320
|
|
|
334
|
-
|
|
321
|
+
throw new TypeError(
|
|
322
|
+
`Unable to determine payload encoding. Please provide a 'content-type' header matching ${schema.encoding}.`,
|
|
323
|
+
)
|
|
335
324
|
}
|
package/dist/error.d.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { LexValue } from '@atproto/lex-data';
|
|
2
|
-
import { l } from '@atproto/lex-schema';
|
|
3
|
-
export declare enum KnownError {
|
|
4
|
-
Unknown = "Unknown",
|
|
5
|
-
AuthenticationRequired = "AuthenticationRequired",
|
|
6
|
-
Forbidden = "Forbidden",
|
|
7
|
-
InternalServerError = "InternalServerError",
|
|
8
|
-
InvalidRequest = "InvalidRequest",
|
|
9
|
-
InvalidResponse = "InvalidResponse",
|
|
10
|
-
MethodNotImplemented = "MethodNotImplemented",
|
|
11
|
-
NotAcceptable = "NotAcceptable",
|
|
12
|
-
NotEnoughResources = "NotEnoughResources",
|
|
13
|
-
PayloadTooLarge = "PayloadTooLarge",
|
|
14
|
-
RateLimitExceeded = "RateLimitExceeded",
|
|
15
|
-
UnsupportedMediaType = "UnsupportedMediaType",
|
|
16
|
-
UpstreamFailure = "UpstreamFailure",
|
|
17
|
-
UpstreamTimeout = "UpstreamTimeout",
|
|
18
|
-
XRPCNotSupported = "XRPCNotSupported"
|
|
19
|
-
}
|
|
20
|
-
export type XrpcFailure<N extends string, E> = l.ResultFailure<E> & {
|
|
21
|
-
name: N;
|
|
22
|
-
};
|
|
23
|
-
export type XrpcErrorName = l.UnknownString | KnownError;
|
|
24
|
-
export declare const xrpcErrorNameSchema: l.StringSchema<{
|
|
25
|
-
readonly minLength: 1;
|
|
26
|
-
}>;
|
|
27
|
-
export type XrpcErrorBody<N extends XrpcErrorName = XrpcErrorName> = {
|
|
28
|
-
error: N;
|
|
29
|
-
message?: string;
|
|
30
|
-
};
|
|
31
|
-
export declare const xrpcErrorBodySchema: l.ObjectSchema<{
|
|
32
|
-
readonly error: l.StringSchema<{
|
|
33
|
-
readonly minLength: 1;
|
|
34
|
-
}>;
|
|
35
|
-
readonly message: l.OptionalSchema<string>;
|
|
36
|
-
}>;
|
|
37
|
-
/**
|
|
38
|
-
* @implements {XrpcFailure<N, XrpcError<N>>} for convenience in result handling contexts.
|
|
39
|
-
*/
|
|
40
|
-
export declare class XrpcError<N extends XrpcErrorName = XrpcErrorName> extends Error implements XrpcFailure<N, XrpcError<N>> {
|
|
41
|
-
readonly name: N;
|
|
42
|
-
constructor(name: N, message?: string, options?: ErrorOptions);
|
|
43
|
-
/** @see {@link l.ResultFailure.success} */
|
|
44
|
-
readonly success: false;
|
|
45
|
-
/** @see {@link l.ResultFailure.error} */
|
|
46
|
-
get error(): this;
|
|
47
|
-
static from(cause: unknown, message?: string): XrpcError;
|
|
48
|
-
}
|
|
49
|
-
export declare class XrpcServiceError<N extends XrpcErrorName = XrpcErrorName> extends XrpcError<N> {
|
|
50
|
-
readonly status: number;
|
|
51
|
-
readonly headers: Headers;
|
|
52
|
-
readonly body: undefined | LexValue;
|
|
53
|
-
constructor(name: N, status: number, headers: Headers, body: undefined | LexValue, message?: string, options?: ErrorOptions);
|
|
54
|
-
}
|
|
55
|
-
export declare class XrpcResponseError<N extends XrpcErrorName = XrpcErrorName, B extends XrpcErrorBody<N> = XrpcErrorBody<N>> extends XrpcError<N> {
|
|
56
|
-
readonly status: number;
|
|
57
|
-
readonly headers: Headers;
|
|
58
|
-
readonly encoding: undefined | string;
|
|
59
|
-
readonly body: B;
|
|
60
|
-
constructor(status: number, headers: Headers, encoding: undefined | string, body: B, options?: ErrorOptions);
|
|
61
|
-
}
|
|
62
|
-
export type XrpcRequestFailure<M extends l.Procedure | l.Query> = (M extends {
|
|
63
|
-
errors: readonly (infer N extends string)[];
|
|
64
|
-
} ? XrpcResponseError<N> : never) | XrpcFailure<'Unknown', XrpcResponseError<string>> | XrpcFailure<'UnexpectedError', unknown>;
|
|
65
|
-
export declare function asXrpcRequestFailureFor<M extends l.Procedure | l.Query>(schema: M): (error: unknown) => XrpcRequestFailure<M>;
|
|
66
|
-
//# sourceMappingURL=error.d.ts.map
|
package/dist/error.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"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"}
|