@atproto/lex-client 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE.txt +1 -1
  3. package/dist/agent.d.ts +5 -0
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +15 -1
  6. package/dist/agent.js.map +1 -1
  7. package/dist/client.d.ts +59 -40
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +2 -6
  10. package/dist/client.js.map +1 -1
  11. package/dist/errors.d.ts +52 -51
  12. package/dist/errors.d.ts.map +1 -1
  13. package/dist/errors.js +90 -71
  14. package/dist/errors.js.map +1 -1
  15. package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts +20 -10
  16. package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts.map +1 -1
  17. package/dist/lexicons/com/atproto/repo/defs.defs.d.ts +1 -1
  18. package/dist/lexicons/com/atproto/repo/defs.defs.d.ts.map +1 -1
  19. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts +14 -6
  20. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts.map +1 -1
  21. package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts +12 -4
  22. package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts.map +1 -1
  23. package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts +11 -11
  24. package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts.map +1 -1
  25. package/dist/lexicons/com/atproto/repo/listRecords.defs.js +2 -1
  26. package/dist/lexicons/com/atproto/repo/listRecords.defs.js.map +1 -1
  27. package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts +18 -10
  28. package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts.map +1 -1
  29. package/dist/response.d.ts +14 -13
  30. package/dist/response.d.ts.map +1 -1
  31. package/dist/response.js +36 -35
  32. package/dist/response.js.map +1 -1
  33. package/dist/util.d.ts +1 -1
  34. package/dist/util.d.ts.map +1 -1
  35. package/dist/util.js.map +1 -1
  36. package/dist/www-authenticate.d.ts +12 -0
  37. package/dist/www-authenticate.d.ts.map +1 -0
  38. package/dist/www-authenticate.js +57 -0
  39. package/dist/www-authenticate.js.map +1 -0
  40. package/dist/xrpc.d.ts +14 -21
  41. package/dist/xrpc.d.ts.map +1 -1
  42. package/dist/xrpc.js +18 -35
  43. package/dist/xrpc.js.map +1 -1
  44. package/package.json +6 -6
  45. package/src/agent.ts +34 -1
  46. package/src/client.ts +34 -33
  47. package/src/errors.ts +161 -128
  48. package/src/lexicons/com/atproto/repo/listRecords.defs.ts +4 -1
  49. package/src/response.ts +71 -71
  50. package/src/util.ts +1 -1
  51. package/src/www-authenticate.test.ts +227 -0
  52. package/src/www-authenticate.ts +77 -0
  53. package/src/xrpc.ts +53 -95
package/src/errors.ts CHANGED
@@ -1,28 +1,26 @@
1
1
  import { LexError, LexErrorCode, LexErrorData } from '@atproto/lex-data'
2
- import { l } from '@atproto/lex-schema'
3
- import { Payload } from './util.js'
2
+ import {
3
+ InferMethodError,
4
+ Procedure,
5
+ Query,
6
+ ResultFailure,
7
+ lexErrorDataSchema,
8
+ } from '@atproto/lex-schema'
9
+ import { XrpcPayload } from './util.js'
10
+ import {
11
+ WWWAuthenticate,
12
+ parseWWWAuthenticateHeader,
13
+ } from './www-authenticate.js'
14
+
15
+ export const RETRYABLE_HTTP_STATUS_CODES: ReadonlySet<number> = new Set([
16
+ 408, 425, 429, 500, 502, 503, 504, 522, 524,
17
+ ])
4
18
 
5
19
  export { LexError }
6
20
  export type { LexErrorCode, LexErrorData }
7
21
 
8
- export type LexRpcErrorPayload<N extends LexErrorCode = LexErrorCode> = Payload<
9
- LexErrorData<N>,
10
- 'application/json'
11
- >
12
-
13
- export class LexRpcError<
14
- N extends LexErrorCode = LexErrorCode,
15
- > extends LexError<N> {
16
- name = 'LexRpcError'
17
-
18
- constructor(
19
- error: N,
20
- message: string = `${error} Lexicon RPC error`,
21
- options?: ErrorOptions,
22
- ) {
23
- super(error, message, options)
24
- }
25
- }
22
+ export type XrpcErrorPayload<N extends LexErrorCode = LexErrorCode> =
23
+ XrpcPayload<LexErrorData<N>, 'application/json'>
26
24
 
27
25
  /**
28
26
  * All unsuccessful responses should follow a standard error response
@@ -36,174 +34,209 @@ export class LexRpcError<
36
34
  *
37
35
  * This function checks whether a given payload matches this schema.
38
36
  */
39
- export function isLexRpcErrorPayload(
40
- payload: Payload | null,
41
- ): payload is LexRpcErrorPayload {
37
+ export function isXrpcErrorPayload(
38
+ payload: XrpcPayload | null,
39
+ ): payload is XrpcErrorPayload {
42
40
  return (
43
41
  payload !== null &&
44
42
  payload.encoding === 'application/json' &&
45
- l.lexErrorData.matches(payload.body)
43
+ lexErrorDataSchema.matches(payload.body)
46
44
  )
47
45
  }
48
46
 
49
- /**
50
- * Interface representing a failed XRPC request result.
51
- */
52
- type LexRpcFailureResult<N extends LexErrorCode, E> = l.ResultFailure<E> & {
53
- readonly error: N
54
- shouldRetry(): boolean
55
- matchesSchema(): boolean
56
- }
57
-
58
- /**
59
- * Class used to represent an HTTP request that resulted in an XRPC method error
60
- * That is, a non-2xx response with a valid XRPC error payload.
61
- */
62
- export class LexRpcResponseError<
63
- M extends l.Procedure | l.Query = l.Procedure | l.Query,
47
+ export abstract class XrpcError<
48
+ M extends Procedure | Query = Procedure | Query,
64
49
  N extends LexErrorCode = LexErrorCode,
50
+ TReason = unknown,
65
51
  >
66
- extends LexRpcError<N>
67
- implements LexRpcFailureResult<N, LexRpcResponseError<M, N>>
52
+ extends LexError<N>
53
+ implements ResultFailure<TReason>
68
54
  {
69
- name = 'LexRpcResponseError'
55
+ name = 'XrpcError'
70
56
 
71
57
  constructor(
72
58
  readonly method: M,
73
- readonly status: number,
74
- readonly headers: Headers,
75
- readonly payload: LexRpcErrorPayload<N>,
59
+ error: N,
60
+ message: string = `${error} Lexicon RPC error`,
76
61
  options?: ErrorOptions,
77
62
  ) {
78
- const { error, message } = payload.body
79
63
  super(error, message, options)
80
64
  }
81
65
 
82
- readonly success = false
66
+ /**
67
+ * @see {@link ResultFailure.success}
68
+ */
69
+ readonly success = false as const
70
+
71
+ /**
72
+ * @see {@link ResultFailure.reason}
73
+ */
74
+ abstract readonly reason: TReason
75
+
76
+ /**
77
+ * Indicates whether the error is transient and can be retried.
78
+ */
79
+ abstract shouldRetry(): boolean
83
80
 
84
- get reason(): this {
85
- return this as this
81
+ matchesSchema(): this is XrpcError<M, InferMethodError<M>> {
82
+ return this.method.errors?.includes(this.error) ?? false
86
83
  }
84
+ }
87
85
 
88
- get body(): LexErrorData {
89
- return this.payload.body
86
+ /**
87
+ * Class used to represent an HTTP request that resulted in an XRPC method
88
+ * error. That is, a non-2xx response with a valid XRPC error payload.
89
+ */
90
+ export class XrpcResponseError<
91
+ M extends Procedure | Query = Procedure | Query,
92
+ N extends LexErrorCode = InferMethodError<M> | LexErrorCode,
93
+ > extends XrpcError<M, N, XrpcResponseError<M, N>> {
94
+ name = 'XrpcResponseError'
95
+
96
+ constructor(
97
+ method: M,
98
+ readonly response: Response,
99
+ readonly payload: XrpcErrorPayload<N>,
100
+ options?: ErrorOptions,
101
+ ) {
102
+ const { error, message } = payload.body
103
+ super(method, error, message, options)
90
104
  }
91
105
 
92
- matchesSchema(): this is M extends {
93
- errors: readonly (infer E extends string)[]
106
+ override get reason(): this {
107
+ return this
94
108
  }
95
- ? LexRpcResponseError<M, E>
96
- : never {
97
- return this.method.errors?.includes(this.error) ?? false
109
+
110
+ override shouldRetry(): boolean {
111
+ return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status)
98
112
  }
99
113
 
100
- shouldRetry(): boolean {
101
- // Do not retry client errors
102
- if (this.status < 500) return false
114
+ override toJSON() {
115
+ return this.payload.body
116
+ }
103
117
 
104
- return true
118
+ override toResponse(): Response {
119
+ // Re-expose schema-valid errors as-is to downstream clients
120
+ if (this.matchesSchema()) {
121
+ const status = this.response.status >= 500 ? 502 : this.response.status
122
+ return Response.json(this.toJSON(), { status })
123
+ }
124
+
125
+ return this.response.status >= 500
126
+ ? // The upstream server had an error, return a generic upstream failure
127
+ Response.json({ error: 'UpstreamFailure' }, { status: 502 })
128
+ : // If the error is on our side, return a generic internal server error
129
+ Response.json({ error: 'InternalServerError' }, { status: 500 })
105
130
  }
106
131
 
107
- toJSON() {
132
+ get body(): LexErrorData {
108
133
  return this.payload.body
109
134
  }
135
+ }
136
+
137
+ export type { WWWAuthenticate }
138
+ export class XrpcAuthenticationError<
139
+ M extends Procedure | Query = Procedure | Query,
140
+ N extends LexErrorCode = LexErrorCode,
141
+ > extends XrpcResponseError<M, N> {
142
+ name = 'XrpcAuthenticationError'
143
+
144
+ override shouldRetry(): boolean {
145
+ return false
146
+ }
110
147
 
111
- toResponse(): Response {
112
- const { status, headers } = this
113
- return Response.json(this.toJSON(), { status, headers })
148
+ #wwwAuthenticate?: WWWAuthenticate
149
+ get wwwAuthenticate(): WWWAuthenticate {
150
+ return (this.#wwwAuthenticate ??=
151
+ parseWWWAuthenticateHeader(
152
+ this.response.headers.get('www-authenticate'),
153
+ ) ?? {})
114
154
  }
115
155
  }
116
156
 
117
157
  /**
118
- * This class represents an invalid XRPC response from the server.
158
+ * This class represents invalid or unprocessable XRPC response from the
159
+ * upstream server.
119
160
  */
120
- export class LexRpcUpstreamError<
121
- N extends 'InvalidResponse' | 'UpstreamFailure' =
122
- | 'InvalidResponse'
123
- | 'UpstreamFailure',
124
- >
125
- extends LexRpcError<N>
126
- implements LexRpcFailureResult<N, LexRpcUpstreamError<N>>
127
- {
128
- name = 'LexRpcUpstreamError' as const
129
-
130
- // For debugging purposes, we keep the response details here
131
- readonly response: {
132
- status: number
133
- headers: Headers
134
- payload: Payload | null
135
- }
161
+ export class XrpcUpstreamError<
162
+ M extends Procedure | Query = Procedure | Query,
163
+ > extends XrpcError<M, 'UpstreamFailure', XrpcUpstreamError<M>> {
164
+ name = 'XrpcUpstreamError'
136
165
 
137
166
  constructor(
138
- error: N,
139
- message: string,
140
- response: { status: number; headers: Headers },
141
- payload: Payload | null,
167
+ method: M,
168
+ readonly response: Response,
169
+ readonly payload: XrpcPayload | null,
170
+ message: string = `Unexpected upstream XRPC response`,
142
171
  options?: ErrorOptions,
143
172
  ) {
144
- super(error, message, { cause: options?.cause })
145
- this.response = {
146
- status: response.status,
147
- headers: response.headers,
148
- payload,
149
- }
173
+ super(method, 'UpstreamFailure', message, options)
150
174
  }
151
175
 
152
- readonly success = false as const
153
-
154
- get reason(): this {
176
+ override get reason(): this {
155
177
  return this
156
178
  }
157
179
 
158
- matchesSchema(): false {
159
- return false
180
+ override shouldRetry(): boolean {
181
+ return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status)
160
182
  }
161
183
 
162
- shouldRetry(): boolean {
163
- // Do not retry client errors
164
- return this.response.status >= 500
165
- }
166
-
167
- toResponse(): Response {
184
+ override toResponse(): Response {
168
185
  return Response.json(this.toJSON(), { status: 502 })
169
186
  }
170
187
  }
171
188
 
172
- export class LexRpcUnexpectedError
173
- extends LexRpcError<'InternalServerError'>
174
- implements LexRpcFailureResult<'InternalServerError', unknown>
175
- {
176
- name = 'LexRpcUnexpectedError' as const
189
+ export class XrpcInternalError<
190
+ M extends Procedure | Query = Procedure | Query,
191
+ > extends XrpcError<M, 'InternalServerError', XrpcInternalError<M>> {
192
+ name = 'XrpcInternalError'
177
193
 
178
- protected constructor(message: string, options: Required<ErrorOptions>) {
179
- super('InternalServerError', message, options)
194
+ constructor(method: M, message?: string, options?: ErrorOptions) {
195
+ super(
196
+ method,
197
+ 'InternalServerError',
198
+ message ?? 'Unable to fulfill XRPC request',
199
+ options,
200
+ )
180
201
  }
181
202
 
182
- readonly success = false
183
-
184
- get reason() {
185
- return this.cause
186
- }
187
-
188
- matchesSchema(): false {
189
- return false
203
+ override get reason(): this {
204
+ return this
190
205
  }
191
206
 
192
- shouldRetry(): boolean {
207
+ override shouldRetry(): true {
208
+ // Ideally, we would inspect the reason to determine if it's retryable
209
+ // (by detecting network errors, timeouts, etc.). Since these cases are
210
+ // highly platform-dependent, we optimistically assume all internal
211
+ // errors are retryable.
193
212
  return true
194
213
  }
195
214
 
196
- toResponse(): Response {
197
- return Response.json(this.toJSON(), { status: 500 })
215
+ override toResponse(): Response {
216
+ // Do not expose internal error details to downstream clients
217
+ return Response.json({ error: this.error }, { status: 500 })
198
218
  }
219
+ }
199
220
 
200
- static from(
201
- cause: unknown,
202
- message: string = cause instanceof LexError
203
- ? cause.message
204
- : 'XRPC request failed',
205
- ): LexRpcUnexpectedError {
206
- if (cause instanceof LexRpcUnexpectedError) return cause
207
- return new LexRpcUnexpectedError(message, { cause })
221
+ export type XrpcFailure<M extends Procedure | Query = Procedure | Query> =
222
+ // The server returned a valid XRPC error response
223
+ | XrpcResponseError<M>
224
+ // The response was not a valid XRPC response, or it does not match the schema
225
+ | XrpcUpstreamError<M>
226
+ // Something went wrong (network error, etc.)
227
+ | XrpcInternalError<M>
228
+
229
+ export function asXrpcFailure<M extends Procedure | Query>(
230
+ method: M,
231
+ cause: unknown,
232
+ ): XrpcFailure<M> {
233
+ if (
234
+ cause instanceof XrpcResponseError ||
235
+ cause instanceof XrpcUpstreamError ||
236
+ cause instanceof XrpcInternalError
237
+ ) {
238
+ if (cause.method === method) return cause
208
239
  }
240
+
241
+ return new XrpcInternalError(method, undefined, { cause })
209
242
  }
@@ -17,7 +17,10 @@ const main =
17
17
  repo: /*#__PURE__*/ l.string({ format: 'at-identifier' }),
18
18
  collection: /*#__PURE__*/ l.string({ format: 'nsid' }),
19
19
  limit: /*#__PURE__*/ l.optional(
20
- /*#__PURE__*/ l.integer({ minimum: 1, maximum: 100, default: 50 }),
20
+ /*#__PURE__*/ l.withDefault(
21
+ /*#__PURE__*/ l.integer({ minimum: 1, maximum: 100 }),
22
+ 50,
23
+ ),
21
24
  ),
22
25
  cursor: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.string()),
23
26
  reverse: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.boolean()),
package/src/response.ts CHANGED
@@ -7,27 +7,28 @@ import {
7
7
  ResultSuccess,
8
8
  } from '@atproto/lex-schema'
9
9
  import {
10
- LexRpcResponseError,
11
- LexRpcUpstreamError,
12
- isLexRpcErrorPayload,
10
+ XrpcAuthenticationError,
11
+ XrpcResponseError,
12
+ XrpcUpstreamError,
13
+ isXrpcErrorPayload,
13
14
  } from './errors.js'
14
- import { Payload } from './util.js'
15
+ import { XrpcPayload } from './util.js'
15
16
 
16
- export type LexRpcResponseBody<M extends Procedure | Query> =
17
+ export type XrpcResponseBody<M extends Procedure | Query> =
17
18
  InferMethodOutputBody<M, Uint8Array>
18
19
 
19
- export type LexRpcResponsePayload<M extends Procedure | Query> =
20
+ export type XrpcResponsePayload<M extends Procedure | Query> =
20
21
  InferMethodOutputEncoding<M> extends infer E extends string
21
- ? Payload<LexRpcResponseBody<M>, E>
22
+ ? XrpcPayload<XrpcResponseBody<M>, E>
22
23
  : null
23
24
 
24
25
  /**
25
26
  * Small container for XRPC response data.
26
27
  *
27
- * @implements {ResultSuccess<LexRpcResponse<M>>} for convenience in result handling contexts.
28
+ * @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.
28
29
  */
29
- export class LexRpcResponse<const M extends Procedure | Query>
30
- implements ResultSuccess<LexRpcResponse<M>>
30
+ export class XrpcResponse<M extends Procedure | Query>
31
+ implements ResultSuccess<XrpcResponse<M>>
31
32
  {
32
33
  /** @see {@link ResultSuccess.success} */
33
34
  readonly success = true as const
@@ -41,7 +42,7 @@ export class LexRpcResponse<const M extends Procedure | Query>
41
42
  readonly method: M,
42
43
  readonly status: number,
43
44
  readonly headers: Headers,
44
- readonly payload: LexRpcResponsePayload<M>,
45
+ readonly payload: XrpcResponsePayload<M>,
45
46
  ) {}
46
47
 
47
48
  /**
@@ -57,21 +58,22 @@ export class LexRpcResponse<const M extends Procedure | Query>
57
58
  }
58
59
 
59
60
  get body() {
60
- return this.payload?.body as LexRpcResponseBody<M>
61
+ return this.payload?.body as XrpcResponseBody<M>
61
62
  }
62
63
 
63
64
  /**
64
- * @throws {LexRpcResponseError} in case of (valid) XRPC error responses. Use
65
- * {@link LexRpcResponseError.matchesSchema} to narrow the error type based on
66
- * the method's declared error schema.
67
- * @throws {LexRpcUpstreamError} when the response is not a valid XRPC
65
+ * @throws {XrpcResponseError} in case of (valid) XRPC error responses. Use
66
+ * {@link XrpcResponseError.matchesSchema} to narrow the error type based on
67
+ * the method's declared error schema. This can be narrowed further as a
68
+ * {@link XrpcAuthenticationError} if the error is an authentication error.
69
+ * @throws {XrpcUpstreamError} when the response is not a valid XRPC
68
70
  * response, or if the response does not conform to the method's schema.
69
71
  */
70
72
  static async fromFetchResponse<const M extends Procedure | Query>(
71
73
  method: M,
72
74
  response: Response,
73
75
  options?: { validateResponse?: boolean },
74
- ): Promise<LexRpcResponse<M>> {
76
+ ): Promise<XrpcResponse<M>> {
75
77
  // @NOTE The body MUST either be read or canceled to avoid resource leaks.
76
78
  // Since nothing should cause an exception before "readPayload" is
77
79
  // called, we can safely not use a try/finally here.
@@ -79,88 +81,96 @@ export class LexRpcResponse<const M extends Procedure | Query>
79
81
  // @NOTE redirect is set to 'follow', so we shouldn't get 3xx responses here
80
82
  if (response.status < 200 || response.status >= 300) {
81
83
  // Always parse json for error responses
82
- const payload = await readPayload(response, { parse: true })
83
-
84
- if (response.status >= 400 && isLexRpcErrorPayload(payload)) {
85
- throw new LexRpcResponseError(
86
- method,
87
- response.status,
88
- response.headers,
89
- payload,
90
- )
91
- }
84
+ const payload = await readPayload(response, { parse: true }).catch(
85
+ (cause) => {
86
+ throw new XrpcUpstreamError(
87
+ method,
88
+ response,
89
+ null,
90
+ 'Unable to parse response payload',
91
+ { cause },
92
+ )
93
+ },
94
+ )
92
95
 
93
- if (response.status >= 500) {
94
- throw new LexRpcUpstreamError(
95
- 'UpstreamFailure',
96
- `Upstream server encountered an error`,
97
- response,
98
- payload,
99
- )
96
+ // Properly formatted XRPC error response ?
97
+ if (response.status >= 400 && isXrpcErrorPayload(payload)) {
98
+ throw response.status === 401
99
+ ? new XrpcAuthenticationError<M>(method, response, payload)
100
+ : new XrpcResponseError<M>(method, response, payload)
100
101
  }
101
102
 
102
- throw new LexRpcUpstreamError(
103
- 'InvalidResponse',
104
- response.status >= 400
105
- ? `Upstream server returned an invalid response payload`
106
- : `Upstream server returned an invalid status code`,
103
+ // Invalid XRPC response (we probably did not hit an XRPC implementation)
104
+ throw new XrpcUpstreamError(
105
+ method,
107
106
  response,
108
107
  payload,
108
+ response.status >= 500
109
+ ? 'Upstream server encountered an error'
110
+ : response.status >= 400
111
+ ? 'Invalid response payload'
112
+ : 'Invalid response status code',
109
113
  )
110
114
  }
111
115
 
112
116
  // Only parse json if the schema expects it
113
117
  const payload = await readPayload(response, {
114
118
  parse: shouldParse(method),
119
+ }).catch((cause) => {
120
+ throw new XrpcUpstreamError(
121
+ method,
122
+ response,
123
+ null,
124
+ 'Unable to parse response payload',
125
+ { cause },
126
+ )
115
127
  })
116
128
 
117
129
  // Response is successful (2xx). Validate payload (data and encoding) against schema.
118
130
  if (method.output.encoding == null) {
119
131
  // Schema expects no payload
120
132
  if (payload) {
121
- throw new LexRpcUpstreamError(
122
- 'InvalidResponse',
123
- `Expected response with no body, got ${payload.encoding}`,
133
+ throw new XrpcUpstreamError(
134
+ method,
124
135
  response,
125
136
  payload,
137
+ `Expected response with no body, got ${payload.encoding}`,
126
138
  )
127
139
  }
128
140
  } else {
129
141
  // Schema expects a payload
130
142
  if (!payload || !method.output.matchesEncoding(payload.encoding)) {
131
- throw new LexRpcUpstreamError(
132
- 'InvalidResponse',
143
+ throw new XrpcUpstreamError(
144
+ method,
145
+ response,
146
+ payload,
133
147
  payload
134
148
  ? `Expected ${method.output.encoding} response, got ${payload.encoding}`
135
149
  : `Expected non-empty response with content-type ${method.output.encoding}`,
136
- response,
137
- payload,
138
150
  )
139
151
  }
140
152
 
141
153
  // Assert valid response body.
142
154
  if (method.output.schema && options?.validateResponse !== false) {
143
- const result = method.output.schema.safeParse(payload.body, {
144
- allowTransform: false,
145
- })
155
+ const result = method.output.schema.safeParse(payload.body)
146
156
 
147
157
  if (!result.success) {
148
- throw new LexRpcUpstreamError(
149
- 'InvalidResponse',
150
- `Response validation failed: ${result.reason.message}`,
158
+ throw new XrpcUpstreamError(
159
+ method,
151
160
  response,
152
161
  payload,
162
+ `Response validation failed: ${result.reason.message}`,
153
163
  { cause: result.reason },
154
164
  )
155
165
  }
156
166
  }
157
167
  }
158
168
 
159
- return new LexRpcResponse<M>(
169
+ return new XrpcResponse<M>(
160
170
  method,
161
171
  response.status,
162
172
  response.headers,
163
- payload as LexRpcResponsePayload<M>,
173
+ payload as XrpcResponsePayload<M>,
164
174
  )
165
175
  }
166
176
  }
@@ -175,7 +185,7 @@ function shouldParse(method: Procedure | Query) {
175
185
  async function readPayload(
176
186
  response: Response,
177
187
  options?: { parse?: boolean },
178
- ): Promise<Payload | null> {
188
+ ): Promise<XrpcPayload | null> {
179
189
  // @TODO Should we limit the maximum response size here (this could also be
180
190
  // done by the FetchHandler)?
181
191
 
@@ -206,22 +216,12 @@ async function readPayload(
206
216
  // to @ipld/dag-json)
207
217
  const text = await response.text()
208
218
 
209
- try {
210
- // @NOTE Using `lexParse(text)` (instead of `jsonToLex(json)`) here as
211
- // using a reviver function during JSON.parse should be faster than
212
- // parsing to JSON then converting to Lex (?)
213
-
214
- // @TODO verify statement above
215
- return { encoding, body: lexParse(text) }
216
- } catch (cause) {
217
- throw new LexRpcUpstreamError(
218
- 'InvalidResponse',
219
- 'Invalid JSON response body',
220
- response,
221
- null,
222
- { cause },
223
- )
224
- }
219
+ // @NOTE Using `lexParse(text)` (instead of `jsonToLex(json)`) here as
220
+ // using a reviver function during JSON.parse should be faster than
221
+ // parsing to JSON then converting to Lex (?)
222
+
223
+ // @TODO verify statement above
224
+ return { encoding, body: lexParse(text) }
225
225
  }
226
226
 
227
227
  return { encoding, body: new Uint8Array(await response.arrayBuffer()) }
package/src/util.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { DidString } from '@atproto/lex-schema'
2
2
 
3
- export type Payload<B = unknown, E extends string = string> = {
3
+ export type XrpcPayload<B = unknown, E extends string = string> = {
4
4
  body: B
5
5
  encoding: E
6
6
  }