@atproto/lex-client 0.0.10 → 0.0.12
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 +32 -0
- package/dist/agent.d.ts +72 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +46 -1
- package/dist/agent.js.map +1 -1
- package/dist/client.d.ts +442 -46
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +145 -1
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +202 -48
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +208 -65
- package/dist/errors.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts +20 -20
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts +12 -12
- package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts +6 -6
- package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts +6 -6
- package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts +22 -22
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts +2 -2
- package/dist/response.d.ts +17 -6
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +45 -32
- package/dist/response.js.map +1 -1
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +40 -5
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +22 -0
- package/dist/util.js.map +1 -1
- package/dist/www-authenticate.d.ts +35 -0
- package/dist/www-authenticate.d.ts.map +1 -0
- package/dist/www-authenticate.js +57 -0
- package/dist/www-authenticate.js.map +1 -0
- package/dist/xrpc.d.ts +82 -10
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +15 -28
- package/dist/xrpc.js.map +1 -1
- package/package.json +7 -7
- package/src/agent.ts +101 -1
- package/src/client.ts +428 -15
- package/src/errors.ts +308 -120
- package/src/response.ts +68 -63
- package/src/types.ts +52 -0
- package/src/util.ts +50 -5
- package/src/www-authenticate.test.ts +227 -0
- package/src/www-authenticate.ts +101 -0
- package/src/xrpc.ts +100 -53
package/src/errors.ts
CHANGED
|
@@ -1,25 +1,48 @@
|
|
|
1
1
|
import { LexError, LexErrorCode, LexErrorData } from '@atproto/lex-data'
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
InferMethodError,
|
|
4
|
+
Procedure,
|
|
5
|
+
Query,
|
|
6
|
+
ResultFailure,
|
|
7
|
+
lexErrorDataSchema,
|
|
8
|
+
} from '@atproto/lex-schema'
|
|
9
|
+
import { XrpcResponsePayload } from './util.js'
|
|
10
|
+
import {
|
|
11
|
+
WWWAuthenticate,
|
|
12
|
+
parseWWWAuthenticateHeader,
|
|
13
|
+
} from './www-authenticate.js'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* HTTP status codes that indicate a transient error that may succeed on retry.
|
|
17
|
+
*
|
|
18
|
+
* Includes:
|
|
19
|
+
* - 408 Request Timeout
|
|
20
|
+
* - 425 Too Early
|
|
21
|
+
* - 429 Too Many Requests (rate limited)
|
|
22
|
+
* - 500 Internal Server Error
|
|
23
|
+
* - 502 Bad Gateway
|
|
24
|
+
* - 503 Service Unavailable
|
|
25
|
+
* - 504 Gateway Timeout
|
|
26
|
+
* - 522 Connection Timed Out (Cloudflare)
|
|
27
|
+
* - 524 A Timeout Occurred (Cloudflare)
|
|
28
|
+
*/
|
|
29
|
+
export const RETRYABLE_HTTP_STATUS_CODES: ReadonlySet<number> = new Set([
|
|
30
|
+
408, 425, 429, 500, 502, 503, 504, 522, 524,
|
|
31
|
+
])
|
|
4
32
|
|
|
5
33
|
export { LexError }
|
|
6
34
|
export type { LexErrorCode, LexErrorData }
|
|
7
35
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
message: string = `${error} Lexicon RPC error`,
|
|
19
|
-
options?: ErrorOptions,
|
|
20
|
-
) {
|
|
21
|
-
super(error, message, options)
|
|
22
|
-
}
|
|
36
|
+
/**
|
|
37
|
+
* The payload structure for XRPC error responses.
|
|
38
|
+
*
|
|
39
|
+
* All XRPC errors return JSON with an `error` code and optional `message`.
|
|
40
|
+
*
|
|
41
|
+
* @typeParam N - The specific error code type
|
|
42
|
+
*/
|
|
43
|
+
export type XrpcErrorPayload<N extends LexErrorCode = LexErrorCode> = {
|
|
44
|
+
body: LexErrorData<N>
|
|
45
|
+
encoding: 'application/json'
|
|
23
46
|
}
|
|
24
47
|
|
|
25
48
|
/**
|
|
@@ -35,173 +58,338 @@ export class XrpcError<
|
|
|
35
58
|
* This function checks whether a given payload matches this schema.
|
|
36
59
|
*/
|
|
37
60
|
export function isXrpcErrorPayload(
|
|
38
|
-
payload:
|
|
61
|
+
payload: XrpcResponsePayload | null | undefined,
|
|
39
62
|
): payload is XrpcErrorPayload {
|
|
40
63
|
return (
|
|
41
|
-
payload
|
|
64
|
+
payload != null &&
|
|
42
65
|
payload.encoding === 'application/json' &&
|
|
43
|
-
|
|
66
|
+
lexErrorDataSchema.matches(payload.body)
|
|
44
67
|
)
|
|
45
68
|
}
|
|
46
69
|
|
|
47
70
|
/**
|
|
48
|
-
*
|
|
71
|
+
* Abstract base class for all XRPC errors.
|
|
72
|
+
*
|
|
73
|
+
* Extends {@link LexError} and implements {@link ResultFailure} for use with
|
|
74
|
+
* safe/result-based error handling patterns.
|
|
75
|
+
*
|
|
76
|
+
* @typeParam M - The XRPC method type (Procedure or Query)
|
|
77
|
+
* @typeParam N - The error code type
|
|
78
|
+
* @typeParam TReason - The reason type for ResultFailure
|
|
79
|
+
*
|
|
80
|
+
* @see {@link XrpcResponseError} - For valid XRPC error responses
|
|
81
|
+
* @see {@link XrpcUpstreamError} - For invalid/unexpected responses
|
|
82
|
+
* @see {@link XrpcInternalError} - For network/internal errors
|
|
49
83
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
84
|
+
export abstract class XrpcError<
|
|
85
|
+
M extends Procedure | Query = Procedure | Query,
|
|
86
|
+
N extends LexErrorCode = LexErrorCode,
|
|
87
|
+
TReason = unknown,
|
|
88
|
+
>
|
|
89
|
+
extends LexError<N>
|
|
90
|
+
implements ResultFailure<TReason>
|
|
91
|
+
{
|
|
92
|
+
name = 'XrpcError'
|
|
93
|
+
|
|
94
|
+
constructor(
|
|
95
|
+
readonly method: M,
|
|
96
|
+
error: N,
|
|
97
|
+
message: string = `${error} Lexicon RPC error`,
|
|
98
|
+
options?: ErrorOptions,
|
|
99
|
+
) {
|
|
100
|
+
super(error, message, options)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @see {@link ResultFailure.success}
|
|
105
|
+
*/
|
|
106
|
+
readonly success = false as const
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @see {@link ResultFailure.reason}
|
|
110
|
+
*/
|
|
111
|
+
abstract readonly reason: TReason
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Indicates whether the error is transient and can be retried.
|
|
115
|
+
*/
|
|
116
|
+
abstract shouldRetry(): boolean
|
|
117
|
+
|
|
118
|
+
matchesSchema(): this is XrpcError<M, InferMethodError<M>> {
|
|
119
|
+
return this.method.errors?.includes(this.error) ?? false
|
|
120
|
+
}
|
|
54
121
|
}
|
|
55
122
|
|
|
56
123
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
124
|
+
* Error class for valid XRPC error responses from the server.
|
|
125
|
+
*
|
|
126
|
+
* This represents a properly formatted XRPC error where the server returned
|
|
127
|
+
* a non-2xx status with a valid JSON error payload containing `error` and
|
|
128
|
+
* optional `message` fields.
|
|
129
|
+
*
|
|
130
|
+
* Use {@link matchesSchema} to check if the error matches the method's declared
|
|
131
|
+
* error types for type-safe error handling.
|
|
132
|
+
*
|
|
133
|
+
* @typeParam M - The XRPC method type
|
|
134
|
+
* @typeParam N - The error code type (inferred from method or generic)
|
|
135
|
+
*
|
|
136
|
+
* @example Handling specific errors
|
|
137
|
+
* ```typescript
|
|
138
|
+
* try {
|
|
139
|
+
* await client.xrpc(someMethod, options)
|
|
140
|
+
* } catch (err) {
|
|
141
|
+
* if (err instanceof XrpcResponseError && err.error === 'RecordNotFound') {
|
|
142
|
+
* // Handle not found case
|
|
143
|
+
* }
|
|
144
|
+
* }
|
|
145
|
+
* ```
|
|
59
146
|
*/
|
|
60
147
|
export class XrpcResponseError<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
extends XrpcError<N>
|
|
65
|
-
implements LexRpcFailureResult<N, XrpcResponseError<M, N>>
|
|
66
|
-
{
|
|
148
|
+
M extends Procedure | Query = Procedure | Query,
|
|
149
|
+
N extends LexErrorCode = InferMethodError<M> | LexErrorCode,
|
|
150
|
+
> extends XrpcError<M, N, XrpcResponseError<M, N>> {
|
|
67
151
|
name = 'XrpcResponseError'
|
|
68
152
|
|
|
69
153
|
constructor(
|
|
70
|
-
|
|
71
|
-
readonly
|
|
72
|
-
readonly headers: Headers,
|
|
154
|
+
method: M,
|
|
155
|
+
readonly response: Response,
|
|
73
156
|
readonly payload: XrpcErrorPayload<N>,
|
|
74
157
|
options?: ErrorOptions,
|
|
75
158
|
) {
|
|
76
159
|
const { error, message } = payload.body
|
|
77
|
-
super(error, message, options)
|
|
160
|
+
super(method, error, message, options)
|
|
78
161
|
}
|
|
79
162
|
|
|
80
|
-
|
|
163
|
+
override get reason(): this {
|
|
164
|
+
return this
|
|
165
|
+
}
|
|
81
166
|
|
|
82
|
-
|
|
83
|
-
return this
|
|
167
|
+
override shouldRetry(): boolean {
|
|
168
|
+
return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status)
|
|
84
169
|
}
|
|
85
170
|
|
|
86
|
-
|
|
171
|
+
override toJSON() {
|
|
87
172
|
return this.payload.body
|
|
88
173
|
}
|
|
89
174
|
|
|
90
|
-
|
|
91
|
-
errors
|
|
175
|
+
override toResponse(): Response {
|
|
176
|
+
// Re-expose schema-valid errors as-is to downstream clients
|
|
177
|
+
if (this.matchesSchema()) {
|
|
178
|
+
const status = this.response.status >= 500 ? 502 : this.response.status
|
|
179
|
+
return Response.json(this.toJSON(), { status })
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return this.response.status >= 500
|
|
183
|
+
? // The upstream server had an error, return a generic upstream failure
|
|
184
|
+
Response.json({ error: 'UpstreamFailure' }, { status: 502 })
|
|
185
|
+
: // If the error is on our side, return a generic internal server error
|
|
186
|
+
Response.json({ error: 'InternalServerError' }, { status: 500 })
|
|
92
187
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return this.
|
|
188
|
+
|
|
189
|
+
get body(): LexErrorData {
|
|
190
|
+
return this.payload.body
|
|
96
191
|
}
|
|
192
|
+
}
|
|
97
193
|
|
|
98
|
-
|
|
99
|
-
// Do not retry client errors
|
|
100
|
-
if (this.status < 500) return false
|
|
194
|
+
export type { WWWAuthenticate }
|
|
101
195
|
|
|
102
|
-
|
|
103
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Error class for 401 Unauthorized XRPC responses.
|
|
198
|
+
*
|
|
199
|
+
* Extends {@link XrpcResponseError} with access to parsed WWW-Authenticate header
|
|
200
|
+
* information, useful for implementing authentication flows.
|
|
201
|
+
*
|
|
202
|
+
* Authentication errors are never retryable as they require user intervention
|
|
203
|
+
* (e.g., re-authentication, token refresh).
|
|
204
|
+
*
|
|
205
|
+
* @typeParam M - The XRPC method type
|
|
206
|
+
* @typeParam N - The error code type
|
|
207
|
+
*
|
|
208
|
+
* @example Handling authentication errors
|
|
209
|
+
* ```typescript
|
|
210
|
+
* try {
|
|
211
|
+
* await client.xrpc(someMethod, options)
|
|
212
|
+
* } catch (err) {
|
|
213
|
+
* if (err instanceof XrpcAuthenticationError) {
|
|
214
|
+
* const { DPoP } = err.wwwAuthenticate
|
|
215
|
+
* if (DPoP?.error === 'use_dpop_nonce') {
|
|
216
|
+
* // Handle DPoP nonce requirement
|
|
217
|
+
* }
|
|
218
|
+
* }
|
|
219
|
+
* }
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
export class XrpcAuthenticationError<
|
|
223
|
+
M extends Procedure | Query = Procedure | Query,
|
|
224
|
+
N extends LexErrorCode = LexErrorCode,
|
|
225
|
+
> extends XrpcResponseError<M, N> {
|
|
226
|
+
name = 'XrpcAuthenticationError'
|
|
104
227
|
|
|
105
|
-
|
|
106
|
-
return
|
|
228
|
+
override shouldRetry(): boolean {
|
|
229
|
+
return false
|
|
107
230
|
}
|
|
108
231
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
232
|
+
#wwwAuthenticateCached?: WWWAuthenticate
|
|
233
|
+
/**
|
|
234
|
+
* Parsed WWW-Authenticate header from the response.
|
|
235
|
+
* Contains authentication scheme parameters (e.g., Bearer realm, DPoP nonce).
|
|
236
|
+
*/
|
|
237
|
+
get wwwAuthenticate(): WWWAuthenticate {
|
|
238
|
+
return (this.#wwwAuthenticateCached ??=
|
|
239
|
+
parseWWWAuthenticateHeader(
|
|
240
|
+
this.response.headers.get('www-authenticate'),
|
|
241
|
+
) ?? {})
|
|
112
242
|
}
|
|
113
243
|
}
|
|
114
244
|
|
|
115
245
|
/**
|
|
116
|
-
*
|
|
246
|
+
* Error class for invalid or unprocessable XRPC responses from upstream servers.
|
|
247
|
+
*
|
|
248
|
+
* This occurs when the server returns a response that doesn't conform to the
|
|
249
|
+
* XRPC protocol, such as:
|
|
250
|
+
* - Missing or invalid Content-Type header
|
|
251
|
+
* - Response body that doesn't match the method's output schema
|
|
252
|
+
* - Non-JSON error responses
|
|
253
|
+
* - Responses from non-XRPC endpoints
|
|
254
|
+
*
|
|
255
|
+
* The error code is always 'UpstreamFailure' and maps to HTTP 502 Bad Gateway
|
|
256
|
+
* when converted to a response.
|
|
257
|
+
*
|
|
258
|
+
* @typeParam M - The XRPC method type
|
|
117
259
|
*/
|
|
118
260
|
export class XrpcUpstreamError<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
>
|
|
123
|
-
extends XrpcError<N>
|
|
124
|
-
implements LexRpcFailureResult<N, XrpcUpstreamError<N>>
|
|
125
|
-
{
|
|
126
|
-
name = 'XrpcUpstreamError' as const
|
|
127
|
-
|
|
128
|
-
// For debugging purposes, we keep the response details here
|
|
129
|
-
readonly response: {
|
|
130
|
-
status: number
|
|
131
|
-
headers: Headers
|
|
132
|
-
payload: XrpcPayload | null
|
|
133
|
-
}
|
|
261
|
+
M extends Procedure | Query = Procedure | Query,
|
|
262
|
+
> extends XrpcError<M, 'UpstreamFailure', XrpcUpstreamError<M>> {
|
|
263
|
+
name = 'XrpcUpstreamError'
|
|
134
264
|
|
|
135
265
|
constructor(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
266
|
+
method: M,
|
|
267
|
+
readonly response: Response,
|
|
268
|
+
readonly payload: XrpcResponsePayload | null = null,
|
|
269
|
+
message: string = `Unexpected upstream XRPC response`,
|
|
140
270
|
options?: ErrorOptions,
|
|
141
271
|
) {
|
|
142
|
-
super(
|
|
143
|
-
this.response = {
|
|
144
|
-
status: response.status,
|
|
145
|
-
headers: response.headers,
|
|
146
|
-
payload,
|
|
147
|
-
}
|
|
272
|
+
super(method, 'UpstreamFailure', message, options)
|
|
148
273
|
}
|
|
149
274
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
get reason(): this {
|
|
275
|
+
override get reason(): this {
|
|
153
276
|
return this
|
|
154
277
|
}
|
|
155
278
|
|
|
156
|
-
|
|
157
|
-
return
|
|
279
|
+
override shouldRetry(): boolean {
|
|
280
|
+
return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status)
|
|
158
281
|
}
|
|
159
282
|
|
|
160
|
-
|
|
161
|
-
// Do not retry client errors
|
|
162
|
-
return this.response.status >= 500
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
toResponse(): Response {
|
|
283
|
+
override toResponse(): Response {
|
|
166
284
|
return Response.json(this.toJSON(), { status: 502 })
|
|
167
285
|
}
|
|
168
286
|
}
|
|
169
287
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Error class for internal/client-side errors during XRPC requests.
|
|
290
|
+
*
|
|
291
|
+
* This represents errors that occur before or during the request that are not
|
|
292
|
+
* server responses, such as:
|
|
293
|
+
* - Network errors (connection refused, DNS failure)
|
|
294
|
+
* - Request timeouts
|
|
295
|
+
* - Request aborted via AbortSignal
|
|
296
|
+
* - Invalid request construction
|
|
297
|
+
*
|
|
298
|
+
* The error code is always 'InternalServerError' and these errors are
|
|
299
|
+
* optimistically considered retryable.
|
|
300
|
+
*
|
|
301
|
+
* @typeParam M - The XRPC method type
|
|
302
|
+
*/
|
|
303
|
+
export class XrpcInternalError<
|
|
304
|
+
M extends Procedure | Query = Procedure | Query,
|
|
305
|
+
> extends XrpcError<M, 'InternalServerError', XrpcInternalError<M>> {
|
|
306
|
+
name = 'XrpcInternalError'
|
|
307
|
+
|
|
308
|
+
constructor(method: M, message?: string, options?: ErrorOptions) {
|
|
309
|
+
super(
|
|
310
|
+
method,
|
|
311
|
+
'InternalServerError',
|
|
312
|
+
message ?? 'Unable to fulfill XRPC request',
|
|
313
|
+
options,
|
|
314
|
+
)
|
|
184
315
|
}
|
|
185
316
|
|
|
186
|
-
|
|
187
|
-
return
|
|
317
|
+
override get reason(): this {
|
|
318
|
+
return this
|
|
188
319
|
}
|
|
189
320
|
|
|
190
|
-
shouldRetry():
|
|
321
|
+
override shouldRetry(): true {
|
|
322
|
+
// Ideally, we would inspect the reason to determine if it's retryable
|
|
323
|
+
// (by detecting network errors, timeouts, etc.). Since these cases are
|
|
324
|
+
// highly platform-dependent, we optimistically assume all internal
|
|
325
|
+
// errors are retryable.
|
|
191
326
|
return true
|
|
192
327
|
}
|
|
193
328
|
|
|
194
|
-
toResponse(): Response {
|
|
195
|
-
|
|
329
|
+
override toResponse(): Response {
|
|
330
|
+
// Do not expose internal error details to downstream clients
|
|
331
|
+
return Response.json({ error: this.error }, { status: 500 })
|
|
196
332
|
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Union type of all possible XRPC failure types.
|
|
337
|
+
*
|
|
338
|
+
* Used as the return type for safe/non-throwing XRPC methods. Check the
|
|
339
|
+
* `success` property to distinguish between success and failure:
|
|
340
|
+
*
|
|
341
|
+
* @typeParam M - The XRPC method type
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```typescript
|
|
345
|
+
* const result = await client.xrpcSafe(someMethod, options)
|
|
346
|
+
* if (result.success) {
|
|
347
|
+
* console.log(result.body) // XrpcResponse
|
|
348
|
+
* } else {
|
|
349
|
+
* // result is XrpcFailure (XrpcResponseError | XrpcUpstreamError | XrpcInternalError)
|
|
350
|
+
* console.error(result.error, result.message)
|
|
351
|
+
* }
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
export type XrpcFailure<M extends Procedure | Query = Procedure | Query> =
|
|
355
|
+
// The server returned a valid XRPC error response
|
|
356
|
+
| XrpcResponseError<M>
|
|
357
|
+
// The response was not a valid XRPC response, or it does not match the schema
|
|
358
|
+
| XrpcUpstreamError<M>
|
|
359
|
+
// Something went wrong (network error, etc.)
|
|
360
|
+
| XrpcInternalError<M>
|
|
197
361
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Converts an unknown error into an appropriate {@link XrpcFailure} type.
|
|
364
|
+
*
|
|
365
|
+
* If the error is already an XrpcFailure for the given method, returns it as-is.
|
|
366
|
+
* Otherwise, wraps it in an {@link XrpcInternalError}.
|
|
367
|
+
*
|
|
368
|
+
* @param method - The XRPC method that was called
|
|
369
|
+
* @param cause - The error to convert
|
|
370
|
+
* @returns An XrpcFailure instance
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* try {
|
|
375
|
+
* const response = await fetch(...)
|
|
376
|
+
* // ... process response
|
|
377
|
+
* } catch (err) {
|
|
378
|
+
* return asXrpcFailure(method, err)
|
|
379
|
+
* }
|
|
380
|
+
* ```
|
|
381
|
+
*/
|
|
382
|
+
export function asXrpcFailure<M extends Procedure | Query>(
|
|
383
|
+
method: M,
|
|
384
|
+
cause: unknown,
|
|
385
|
+
): XrpcFailure<M> {
|
|
386
|
+
if (
|
|
387
|
+
cause instanceof XrpcResponseError ||
|
|
388
|
+
cause instanceof XrpcUpstreamError ||
|
|
389
|
+
cause instanceof XrpcInternalError
|
|
390
|
+
) {
|
|
391
|
+
if (cause.method === method) return cause
|
|
206
392
|
}
|
|
393
|
+
|
|
394
|
+
return new XrpcInternalError(method, undefined, { cause })
|
|
207
395
|
}
|