@atproto/lex-client 0.0.5 → 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 (67) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/agent.d.ts +1 -1
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +3 -0
  5. package/dist/agent.js.map +1 -1
  6. package/dist/client.d.ts +30 -28
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +9 -13
  9. package/dist/client.js.map +1 -1
  10. package/dist/{xrpc-error.d.ts → errors.d.ts} +22 -27
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/{xrpc-error.js → errors.js} +38 -33
  13. package/dist/errors.js.map +1 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts.map +1 -1
  19. package/dist/lexicons/com/atproto/repo/createRecord.defs.js +5 -7
  20. package/dist/lexicons/com/atproto/repo/createRecord.defs.js.map +1 -1
  21. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts.map +1 -1
  22. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js +5 -7
  23. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js.map +1 -1
  24. package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts.map +1 -1
  25. package/dist/lexicons/com/atproto/repo/getRecord.defs.js +3 -5
  26. package/dist/lexicons/com/atproto/repo/getRecord.defs.js.map +1 -1
  27. package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts.map +1 -1
  28. package/dist/lexicons/com/atproto/repo/listRecords.defs.js +2 -3
  29. package/dist/lexicons/com/atproto/repo/listRecords.defs.js.map +1 -1
  30. package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts.map +1 -1
  31. package/dist/lexicons/com/atproto/repo/putRecord.defs.js +5 -7
  32. package/dist/lexicons/com/atproto/repo/putRecord.defs.js.map +1 -1
  33. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -1
  34. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +3 -4
  35. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -1
  36. package/dist/response.d.ts +38 -0
  37. package/dist/response.d.ts.map +1 -0
  38. package/dist/{xrpc-response.js → response.js} +24 -20
  39. package/dist/response.js.map +1 -0
  40. package/dist/types.d.ts +0 -4
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/types.js +0 -4
  43. package/dist/types.js.map +1 -1
  44. package/dist/xrpc.d.ts +23 -17
  45. package/dist/xrpc.d.ts.map +1 -1
  46. package/dist/xrpc.js +15 -17
  47. package/dist/xrpc.js.map +1 -1
  48. package/package.json +10 -10
  49. package/src/agent.ts +6 -2
  50. package/src/client.ts +67 -61
  51. package/src/{xrpc-error.ts → errors.ts} +59 -48
  52. package/src/index.ts +2 -0
  53. package/src/lexicons/com/atproto/repo/createRecord.defs.ts +22 -28
  54. package/src/lexicons/com/atproto/repo/deleteRecord.defs.ts +18 -24
  55. package/src/lexicons/com/atproto/repo/getRecord.defs.ts +5 -10
  56. package/src/lexicons/com/atproto/repo/listRecords.defs.ts +6 -9
  57. package/src/lexicons/com/atproto/repo/putRecord.defs.ts +23 -29
  58. package/src/lexicons/com/atproto/repo/uploadBlob.defs.ts +4 -7
  59. package/src/{xrpc-response.ts → response.ts} +45 -30
  60. package/src/types.ts +0 -6
  61. package/src/xrpc.ts +56 -57
  62. package/tsconfig.tests.json +4 -7
  63. package/dist/xrpc-error.d.ts.map +0 -1
  64. package/dist/xrpc-error.js.map +0 -1
  65. package/dist/xrpc-response.d.ts +0 -35
  66. package/dist/xrpc-response.d.ts.map +0 -1
  67. package/dist/xrpc-response.js.map +0 -1
@@ -14,36 +14,30 @@ const main =
14
14
  /*#__PURE__*/
15
15
  l.procedure(
16
16
  $nsid,
17
- /*#__PURE__*/ l.params({}),
18
- /*#__PURE__*/ l.payload(
19
- 'application/json',
20
- /*#__PURE__*/ l.object({
21
- repo: /*#__PURE__*/ l.string({ format: 'at-identifier' }),
22
- collection: /*#__PURE__*/ l.string({ format: 'nsid' }),
23
- rkey: /*#__PURE__*/ l.string({ format: 'record-key', maxLength: 512 }),
24
- validate: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.boolean()),
25
- record: /*#__PURE__*/ l.unknownObject(),
26
- swapRecord: /*#__PURE__*/ l.optional(
27
- /*#__PURE__*/ l.nullable(/*#__PURE__*/ l.string({ format: 'cid' })),
17
+ /*#__PURE__*/ l.params(),
18
+ /*#__PURE__*/ l.jsonPayload({
19
+ repo: /*#__PURE__*/ l.string({ format: 'at-identifier' }),
20
+ collection: /*#__PURE__*/ l.string({ format: 'nsid' }),
21
+ rkey: /*#__PURE__*/ l.string({ format: 'record-key', maxLength: 512 }),
22
+ validate: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.boolean()),
23
+ record: /*#__PURE__*/ l.unknownObject(),
24
+ swapRecord: /*#__PURE__*/ l.optional(
25
+ /*#__PURE__*/ l.nullable(/*#__PURE__*/ l.string({ format: 'cid' })),
26
+ ),
27
+ swapCommit: /*#__PURE__*/ l.optional(
28
+ /*#__PURE__*/ l.string({ format: 'cid' }),
29
+ ),
30
+ }),
31
+ /*#__PURE__*/ l.jsonPayload({
32
+ uri: /*#__PURE__*/ l.string({ format: 'at-uri' }),
33
+ cid: /*#__PURE__*/ l.string({ format: 'cid' }),
34
+ commit: /*#__PURE__*/ l.optional(
35
+ /*#__PURE__*/ l.ref<RepoDefs.CommitMeta>(
36
+ (() => RepoDefs.commitMeta) as any,
28
37
  ),
29
- swapCommit: /*#__PURE__*/ l.optional(
30
- /*#__PURE__*/ l.string({ format: 'cid' }),
31
- ),
32
- }),
33
- ),
34
- /*#__PURE__*/ l.payload(
35
- 'application/json',
36
- /*#__PURE__*/ l.object({
37
- uri: /*#__PURE__*/ l.string({ format: 'at-uri' }),
38
- cid: /*#__PURE__*/ l.string({ format: 'cid' }),
39
- commit: /*#__PURE__*/ l.optional(
40
- /*#__PURE__*/ l.ref<RepoDefs.CommitMeta>(
41
- (() => RepoDefs.commitMeta) as any,
42
- ),
43
- ),
44
- validationStatus: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.string()),
45
- }),
46
- ),
38
+ ),
39
+ validationStatus: /*#__PURE__*/ l.optional(/*#__PURE__*/ l.string()),
40
+ }),
47
41
  ['InvalidSwap'],
48
42
  )
49
43
  export { main }
@@ -13,14 +13,11 @@ const main =
13
13
  /*#__PURE__*/
14
14
  l.procedure(
15
15
  $nsid,
16
- /*#__PURE__*/ l.params({}),
16
+ /*#__PURE__*/ l.params(),
17
17
  /*#__PURE__*/ l.payload('*/*'),
18
- /*#__PURE__*/ l.payload(
19
- 'application/json',
20
- /*#__PURE__*/ l.object({
21
- blob: /*#__PURE__*/ l.blob({ allowLegacy: false }),
22
- }),
23
- ),
18
+ /*#__PURE__*/ l.jsonPayload({
19
+ blob: /*#__PURE__*/ l.blob({ allowLegacy: false }),
20
+ }),
24
21
  )
25
22
  export { main }
26
23
 
@@ -6,28 +6,28 @@ import {
6
6
  Query,
7
7
  ResultSuccess,
8
8
  } from '@atproto/lex-schema'
9
- import { Payload } from './util.js'
10
9
  import {
11
- XrpcInvalidResponseError,
12
- XrpcResponseError,
13
- isXrpcErrorPayload,
14
- } from './xrpc-error.js'
10
+ LexRpcResponseError,
11
+ LexRpcUpstreamError,
12
+ isLexRpcErrorPayload,
13
+ } from './errors.js'
14
+ import { Payload } from './util.js'
15
15
 
16
- export type XrpcResponseBody<M extends Procedure | Query> =
16
+ export type LexRpcResponseBody<M extends Procedure | Query> =
17
17
  InferMethodOutputBody<M, Uint8Array>
18
18
 
19
- export type XrpcResponsePayload<M extends Procedure | Query> =
19
+ export type LexRpcResponsePayload<M extends Procedure | Query> =
20
20
  InferMethodOutputEncoding<M> extends infer E extends string
21
- ? Payload<XrpcResponseBody<M>, E>
21
+ ? Payload<LexRpcResponseBody<M>, E>
22
22
  : null
23
23
 
24
24
  /**
25
25
  * Small container for XRPC response data.
26
26
  *
27
- * @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.
27
+ * @implements {ResultSuccess<LexRpcResponse<M>>} for convenience in result handling contexts.
28
28
  */
29
- export class XrpcResponse<const M extends Procedure | Query>
30
- implements ResultSuccess<XrpcResponse<M>>
29
+ export class LexRpcResponse<const M extends Procedure | Query>
30
+ implements ResultSuccess<LexRpcResponse<M>>
31
31
  {
32
32
  /** @see {@link ResultSuccess.success} */
33
33
  readonly success = true as const
@@ -41,7 +41,7 @@ export class XrpcResponse<const M extends Procedure | Query>
41
41
  readonly method: M,
42
42
  readonly status: number,
43
43
  readonly headers: Headers,
44
- readonly payload: XrpcResponsePayload<M>,
44
+ readonly payload: LexRpcResponsePayload<M>,
45
45
  ) {}
46
46
 
47
47
  /**
@@ -57,18 +57,21 @@ export class XrpcResponse<const M extends Procedure | Query>
57
57
  }
58
58
 
59
59
  get body() {
60
- return this.payload?.body as XrpcResponseBody<M>
60
+ return this.payload?.body as LexRpcResponseBody<M>
61
61
  }
62
62
 
63
63
  /**
64
- * @throws {XrpcInvalidResponseError} when the response is invalid according
65
- * to the method schema.
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
68
+ * response, or if the response does not conform to the method's schema.
66
69
  */
67
70
  static async fromFetchResponse<const M extends Procedure | Query>(
68
71
  method: M,
69
72
  response: Response,
70
73
  options?: { validateResponse?: boolean },
71
- ): Promise<XrpcResponse<M>> {
74
+ ): Promise<LexRpcResponse<M>> {
72
75
  // @NOTE The body MUST either be read or canceled to avoid resource leaks.
73
76
  // Since nothing should cause an exception before "readPayload" is
74
77
  // called, we can safely not use a try/finally here.
@@ -78,8 +81,8 @@ export class XrpcResponse<const M extends Procedure | Query>
78
81
  // Always parse json for error responses
79
82
  const payload = await readPayload(response, { parse: true })
80
83
 
81
- if (response.status >= 400 && isXrpcErrorPayload(payload)) {
82
- throw new XrpcResponseError(
84
+ if (response.status >= 400 && isLexRpcErrorPayload(payload)) {
85
+ throw new LexRpcResponseError(
83
86
  method,
84
87
  response.status,
85
88
  response.headers,
@@ -87,12 +90,20 @@ export class XrpcResponse<const M extends Procedure | Query>
87
90
  )
88
91
  }
89
92
 
90
- throw new XrpcInvalidResponseError(
91
- response.status >= 500
92
- ? `Upstream server encountered an error`
93
- : response.status >= 400
94
- ? `Upstream server returned an invalid response payload`
95
- : `Upstream server returned an invalid status code`,
93
+ if (response.status >= 500) {
94
+ throw new LexRpcUpstreamError(
95
+ 'UpstreamFailure',
96
+ `Upstream server encountered an error`,
97
+ response,
98
+ payload,
99
+ )
100
+ }
101
+
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`,
96
107
  response,
97
108
  payload,
98
109
  )
@@ -107,7 +118,8 @@ export class XrpcResponse<const M extends Procedure | Query>
107
118
  if (method.output.encoding == null) {
108
119
  // Schema expects no payload
109
120
  if (payload) {
110
- throw new XrpcInvalidResponseError(
121
+ throw new LexRpcUpstreamError(
122
+ 'InvalidResponse',
111
123
  `Expected response with no body, got ${payload.encoding}`,
112
124
  response,
113
125
  payload,
@@ -116,7 +128,8 @@ export class XrpcResponse<const M extends Procedure | Query>
116
128
  } else {
117
129
  // Schema expects a payload
118
130
  if (!payload || !method.output.matchesEncoding(payload.encoding)) {
119
- throw new XrpcInvalidResponseError(
131
+ throw new LexRpcUpstreamError(
132
+ 'InvalidResponse',
120
133
  payload
121
134
  ? `Expected ${method.output.encoding} response, got ${payload.encoding}`
122
135
  : `Expected non-empty response with content-type ${method.output.encoding}`,
@@ -132,7 +145,8 @@ export class XrpcResponse<const M extends Procedure | Query>
132
145
  })
133
146
 
134
147
  if (!result.success) {
135
- throw new XrpcInvalidResponseError(
148
+ throw new LexRpcUpstreamError(
149
+ 'InvalidResponse',
136
150
  `Response validation failed: ${result.reason.message}`,
137
151
  response,
138
152
  payload,
@@ -142,11 +156,11 @@ export class XrpcResponse<const M extends Procedure | Query>
142
156
  }
143
157
  }
144
158
 
145
- return new XrpcResponse<M>(
159
+ return new LexRpcResponse<M>(
146
160
  method,
147
161
  response.status,
148
162
  response.headers,
149
- payload as XrpcResponsePayload<M>,
163
+ payload as LexRpcResponsePayload<M>,
150
164
  )
151
165
  }
152
166
  }
@@ -200,7 +214,8 @@ async function readPayload(
200
214
  // @TODO verify statement above
201
215
  return { encoding, body: lexParse(text) }
202
216
  } catch (cause) {
203
- throw new XrpcInvalidResponseError(
217
+ throw new LexRpcUpstreamError(
218
+ 'InvalidResponse',
204
219
  'Invalid JSON response body',
205
220
  response,
206
221
  null,
package/src/types.ts CHANGED
@@ -40,9 +40,3 @@ export type BinaryBodyInit =
40
40
  | ReadableStream<Uint8Array>
41
41
  | AsyncIterable<Uint8Array>
42
42
  | string
43
-
44
- export type Namespace<T> = T | { main: T }
45
-
46
- export function getMain<T extends object>(ns: Namespace<T>): T {
47
- return 'main' in ns ? ns.main : ns
48
- }
package/src/xrpc.ts CHANGED
@@ -3,6 +3,7 @@ import { lexStringify } from '@atproto/lex-json'
3
3
  import {
4
4
  InferMethodInput,
5
5
  InferMethodParams,
6
+ Main,
6
7
  Params,
7
8
  ParamsSchema,
8
9
  Payload as LexPayload,
@@ -10,9 +11,16 @@ import {
10
11
  Query,
11
12
  Restricted,
12
13
  Subscription,
14
+ getMain,
13
15
  } from '@atproto/lex-schema'
14
16
  import { Agent } from './agent.js'
15
- import { BinaryBodyInit, CallOptions, Namespace, getMain } from './types.js'
17
+ import {
18
+ LexRpcResponseError,
19
+ LexRpcUnexpectedError,
20
+ LexRpcUpstreamError,
21
+ } from './errors.js'
22
+ import { LexRpcResponse } from './response.js'
23
+ import { BinaryBodyInit, CallOptions } from './types.js'
16
24
  import {
17
25
  Payload,
18
26
  buildAtprotoHeaders,
@@ -20,117 +28,108 @@ import {
20
28
  isBlobLike,
21
29
  toReadableStream,
22
30
  } from './util.js'
23
- import {
24
- XrpcInvalidResponseError,
25
- XrpcResponseError,
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
31
 
33
32
  // If all params are optional, allow omitting the params object
34
- type XrpcParamsOptions<P extends Params> =
33
+ type LexRpcParamsOptions<P extends Params> =
35
34
  NonNullable<unknown> extends P ? { params?: P } : { params: P }
36
35
 
37
- type XrpcRequestPayload<M extends Procedure | Query> = InferMethodInput<
36
+ type LexRpcRequestPayload<M extends Procedure | Query> = InferMethodInput<
38
37
  M,
39
38
  BinaryBodyInit
40
39
  >
41
40
 
42
- type XrpcInputOptions<In> = In extends { body: infer B; encoding: infer E }
41
+ type LexRpcInputOptions<In> = In extends { body: infer B; encoding: infer E }
43
42
  ? // encoding will be inferred from the schema at runtime if not provided
44
43
  { body: B; encoding?: E }
45
44
  : { body?: undefined; encoding?: undefined }
46
45
 
47
- export type XrpcOptions<M extends Procedure | Query = Procedure | Query> =
46
+ export type LexRpcOptions<M extends Procedure | Query = Procedure | Query> =
48
47
  CallOptions &
49
- XrpcInputOptions<XrpcRequestPayload<M>> &
50
- XrpcParamsOptions<InferMethodParams<M>>
48
+ LexRpcInputOptions<LexRpcRequestPayload<M>> &
49
+ LexRpcParamsOptions<InferMethodParams<M>>
51
50
 
52
- export type XrpcFailure<M extends Procedure | Query> =
51
+ export type LexRpcFailure<M extends Procedure | Query> =
53
52
  // The server returned a valid XRPC error response
54
- | XrpcResponseError<M>
53
+ | LexRpcResponseError<M>
55
54
  // The response was not a valid XRPC response, or it does not match the schema
56
- | XrpcInvalidResponseError
55
+ | LexRpcUpstreamError
57
56
  // Something went wrong (network error, etc.)
58
- | XrpcUnexpectedError
57
+ | LexRpcUnexpectedError
59
58
 
60
- export type XrpcResult<M extends Procedure | Query> =
61
- | XrpcResponse<M>
62
- | XrpcFailure<M>
59
+ export type LexRpcResult<M extends Procedure | Query> =
60
+ | LexRpcResponse<M>
61
+ | LexRpcFailure<M>
63
62
 
64
63
  /**
65
64
  * Utility method to type cast the error thrown by {@link xrpc} to an
66
- * {@link XrpcFailure} matching the provided method. Only use this function
65
+ * {@link LexRpcFailure} matching the provided method. Only use this function
67
66
  * inside a catch block right after calling {@link xrpc}, and use the same
68
67
  * method type parameter as used in the {@link xrpc} call.
69
68
  */
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)
69
+ export function asLexRpcFailure<
70
+ M extends Procedure | Query = Procedure | Query,
71
+ >(err: unknown): LexRpcFailure<M> {
72
+ if (err instanceof LexRpcResponseError) return err
73
+ if (err instanceof LexRpcUpstreamError) return err
74
+ return LexRpcUnexpectedError.from(err)
76
75
  }
77
76
 
78
77
  /**
79
- * @throws XrpcFailure<M>
78
+ * @throws LexRpcFailure<M>
80
79
  */
81
80
  export async function xrpc<const M extends Query | Procedure>(
82
81
  agent: Agent,
83
- ns: NonNullable<unknown> extends XrpcOptions<M>
84
- ? Namespace<M>
82
+ ns: NonNullable<unknown> extends LexRpcOptions<M>
83
+ ? Main<M>
85
84
  : Restricted<'This XRPC method requires an "options" argument'>,
86
- ): Promise<XrpcResponse<M>>
85
+ ): Promise<LexRpcResponse<M>>
87
86
  export async function xrpc<const M extends Query | Procedure>(
88
87
  agent: Agent,
89
- ns: Namespace<M>,
90
- options: XrpcOptions<M>,
91
- ): Promise<XrpcResponse<M>>
88
+ ns: Main<M>,
89
+ options: LexRpcOptions<M>,
90
+ ): Promise<LexRpcResponse<M>>
92
91
  export async function xrpc<const M extends Query | Procedure>(
93
92
  agent: Agent,
94
- ns: Namespace<M>,
95
- options: XrpcOptions<M> = {} as XrpcOptions<M>,
96
- ): Promise<XrpcResponse<M>> {
93
+ ns: Main<M>,
94
+ options: LexRpcOptions<M> = {} as LexRpcOptions<M>,
95
+ ): Promise<LexRpcResponse<M>> {
97
96
  try {
98
- return await xrpcRequest<M>(agent, ns, options)
97
+ return await lexRpcRequest<M>(agent, ns, options)
99
98
  } catch (err) {
100
- throw asXrpcFailure<M>(err)
99
+ throw asLexRpcFailure<M>(err)
101
100
  }
102
101
  }
103
102
 
104
103
  export async function xrpcSafe<const M extends Query | Procedure>(
105
104
  agent: Agent,
106
- ns: NonNullable<unknown> extends XrpcOptions<M>
107
- ? Namespace<M>
105
+ ns: NonNullable<unknown> extends LexRpcOptions<M>
106
+ ? Main<M>
108
107
  : Restricted<'This XRPC method requires an "options" argument'>,
109
- ): Promise<XrpcResult<M>>
108
+ ): Promise<LexRpcResult<M>>
110
109
  export async function xrpcSafe<const M extends Query | Procedure>(
111
110
  agent: Agent,
112
- ns: Namespace<M>,
113
- options: XrpcOptions<M>,
114
- ): Promise<XrpcResult<M>>
111
+ ns: Main<M>,
112
+ options: LexRpcOptions<M>,
113
+ ): Promise<LexRpcResult<M>>
115
114
  export async function xrpcSafe<const M extends Query | Procedure>(
116
115
  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>)
116
+ ns: Main<M>,
117
+ options: LexRpcOptions<M> = {} as LexRpcOptions<M>,
118
+ ): Promise<LexRpcResult<M>> {
119
+ return lexRpcRequest<M>(agent, ns, options).catch(asLexRpcFailure<M>)
121
120
  }
122
121
 
123
- async function xrpcRequest<const M extends Query | Procedure>(
122
+ async function lexRpcRequest<const M extends Query | Procedure>(
124
123
  agent: Agent,
125
- ns: Namespace<M>,
126
- options: XrpcOptions<M> = {} as XrpcOptions<M>,
127
- ): Promise<XrpcResponse<M>> {
124
+ ns: Main<M>,
125
+ options: LexRpcOptions<M> = {} as LexRpcOptions<M>,
126
+ ): Promise<LexRpcResponse<M>> {
128
127
  const method = getMain(ns)
129
128
  options.signal?.throwIfAborted()
130
129
  const url = xrpcRequestUrl(method, options)
131
130
  const request = xrpcRequestInit(method, options)
132
131
  const response = await agent.fetchHandler(url, request)
133
- return XrpcResponse.fromFetchResponse<M>(method, response, options)
132
+ return LexRpcResponse.fromFetchResponse<M>(method, response, options)
134
133
  }
135
134
 
136
135
  function xrpcRequestUrl<M extends Procedure | Query | Subscription>(
@@ -1,12 +1,9 @@
1
1
  {
2
- "extends": "../../../tsconfig/tests.json",
3
- "include": ["./tests"],
2
+ "extends": "../../../tsconfig/vitest.json",
3
+ "include": ["./tests", "./src/**/*.test.ts"],
4
4
  "compilerOptions": {
5
5
  "noImplicitAny": true,
6
- "rootDir": "./tests",
7
- "baseUrl": "./tests",
8
- "paths": {
9
- "@atproto/lex-client": ["./dist/index.js"]
10
- }
6
+ "rootDir": "./",
7
+ "baseUrl": "./"
11
8
  }
12
9
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"xrpc-error.d.ts","sourceRoot":"","sources":["../src/xrpc-error.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"xrpc-error.js","sourceRoot":"","sources":["../src/xrpc-error.ts"],"names":[],"mappings":"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","sourcesContent":["import { l } from '@atproto/lex-schema'\nimport { Payload } from './util.js'\n\nexport type XrpcErrorCode = string\nexport const xrpcErrorCodeSchema: l.Schema<XrpcErrorCode> = l.string({\n minLength: 1,\n})\n\nexport class XrpcError<N extends XrpcErrorCode = XrpcErrorCode> extends Error {\n name = 'XrpcError'\n\n constructor(\n readonly error: N,\n message: string = `An ${error} XRPC error occurred.`,\n options?: ErrorOptions,\n ) {\n super(message, options)\n }\n}\n\nexport type XrpcErrorBody<N extends XrpcErrorCode = XrpcErrorCode> = {\n error: N\n message?: string\n}\nexport type XrpcErrorPayload<N extends XrpcErrorCode = XrpcErrorCode> = {\n encoding: 'application/json'\n body: XrpcErrorBody<N>\n}\n\nconst xrpcErrorBodySchema: l.Schema<XrpcErrorBody> = l.object({\n error: xrpcErrorCodeSchema,\n message: l.optional(l.string()),\n})\n\n/**\n * All unsuccessful responses should follow a standard error response\n * schema. The Content-Type should be application/json, and the payload\n * should be a JSON object with the following fields:\n *\n * - `error` (string, required): type name of the error (generic ASCII\n * constant, no whitespace)\n * - `message` (string, optional): description of the error, appropriate for\n * display to humans\n *\n * This function checks whether a given payload matches this schema.\n */\nexport function isXrpcErrorPayload(\n payload: Payload | null,\n): payload is XrpcErrorPayload {\n return (\n payload !== null &&\n payload.encoding === 'application/json' &&\n xrpcErrorBodySchema.matches(payload.body)\n )\n}\n\n/**\n * Interface representing a failed XRPC request result.\n */\ntype XrpcFailureResult<N extends XrpcErrorCode, E> = l.ResultFailure<E> & {\n readonly error: N\n shouldRetry(): boolean\n matchesSchema(): boolean\n}\n\n/**\n * Class used to represent an HTTP request that resulted in an XRPC method error\n * That is, a non-2xx response with a valid XRPC error payload.\n */\nexport class XrpcResponseError<\n M extends l.Procedure | l.Query = l.Procedure | l.Query,\n N extends XrpcErrorCode = XrpcErrorCode,\n >\n extends XrpcError<N>\n implements XrpcFailureResult<N, XrpcResponseError<M, N>>\n{\n name = 'XrpcResponseError' as const\n\n constructor(\n readonly method: M,\n readonly status: number,\n readonly headers: Headers,\n readonly payload: XrpcErrorPayload<N>,\n options?: ErrorOptions,\n ) {\n const { error, message } = payload.body\n super(error, message, options)\n }\n\n readonly success = false\n\n get reason(): this {\n return this as this\n }\n\n get body(): XrpcErrorBody {\n return this.payload.body\n }\n\n matchesSchema(): this is M extends {\n errors: readonly (infer E extends string)[]\n }\n ? XrpcResponseError<M, E>\n : never {\n return this.method.errors?.includes(this.error) ?? false\n }\n\n shouldRetry(): boolean {\n // Do not retry client errors\n if (this.status < 500) return false\n\n return true\n }\n}\n\n/**\n * This class represents an invalid XRPC response from the server.\n */\nexport class XrpcInvalidResponseError\n extends XrpcError<'UpstreamFailure'>\n implements XrpcFailureResult<'UpstreamFailure', XrpcInvalidResponseError>\n{\n name = 'XrpcInvalidResponseError' as const\n\n // For debugging purposes, we keep the response details here\n readonly response: {\n status: number\n headers: Headers\n payload: Payload | null\n }\n\n constructor(\n message: string,\n response: { status: number; headers: Headers },\n payload: Payload | null,\n options?: ErrorOptions,\n ) {\n super('UpstreamFailure', message, { cause: options?.cause })\n this.response = {\n status: response.status,\n headers: response.headers,\n payload,\n }\n }\n\n readonly success = false as const\n\n get reason(): this {\n return this\n }\n\n matchesSchema(): false {\n return false\n }\n\n shouldRetry(): boolean {\n // Do not retry client errors\n return this.response.status >= 500\n }\n}\n\nexport class XrpcUnexpectedError\n extends XrpcError<'InternalServerError'>\n implements XrpcFailureResult<'InternalServerError', unknown>\n{\n name = 'XrpcUnexpectedError' as const\n\n protected constructor(message: string, options: Required<ErrorOptions>) {\n super('InternalServerError', message, options)\n }\n\n readonly success = false\n\n get reason() {\n return this.cause\n }\n\n matchesSchema(): false {\n return false\n }\n\n shouldRetry(): boolean {\n return true\n }\n\n static from(\n cause: unknown,\n message: string = cause instanceof XrpcError\n ? cause.message\n : 'XRPC request failed',\n ): XrpcUnexpectedError {\n if (cause instanceof XrpcUnexpectedError) return cause\n return new XrpcUnexpectedError(message, { cause })\n }\n}\n"]}
@@ -1,35 +0,0 @@
1
- import { InferMethodOutputBody, InferMethodOutputEncoding, Procedure, Query, ResultSuccess } from '@atproto/lex-schema';
2
- import { Payload } from './util.js';
3
- export type XrpcResponseBody<M extends Procedure | Query> = InferMethodOutputBody<M, Uint8Array>;
4
- export type XrpcResponsePayload<M extends Procedure | Query> = InferMethodOutputEncoding<M> extends infer E extends string ? Payload<XrpcResponseBody<M>, E> : null;
5
- /**
6
- * Small container for XRPC response data.
7
- *
8
- * @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.
9
- */
10
- export declare class XrpcResponse<const M extends Procedure | Query> implements ResultSuccess<XrpcResponse<M>> {
11
- readonly method: M;
12
- readonly status: number;
13
- readonly headers: Headers;
14
- readonly payload: XrpcResponsePayload<M>;
15
- /** @see {@link ResultSuccess.success} */
16
- readonly success: true;
17
- /** @see {@link ResultSuccess.value} */
18
- get value(): this;
19
- constructor(method: M, status: number, headers: Headers, payload: XrpcResponsePayload<M>);
20
- /**
21
- * Whether the response payload was parsed as {@link LexValue} (`true`) or is
22
- * in binary form {@link Uint8Array} (`false`).
23
- */
24
- get isParsed(): boolean;
25
- get encoding(): InferMethodOutputEncoding<M>;
26
- get body(): XrpcResponseBody<M>;
27
- /**
28
- * @throws {XrpcInvalidResponseError} when the response is invalid according
29
- * to the method schema.
30
- */
31
- static fromFetchResponse<const M extends Procedure | Query>(method: M, response: Response, options?: {
32
- validateResponse?: boolean;
33
- }): Promise<XrpcResponse<M>>;
34
- }
35
- //# sourceMappingURL=xrpc-response.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"xrpc-response.d.ts","sourceRoot":"","sources":["../src/xrpc-response.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,SAAS,EACT,KAAK,EACL,aAAa,EACd,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOnC,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,SAAS,GAAG,KAAK,IACtD,qBAAqB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;AAEtC,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,SAAS,GAAG,KAAK,IACzD,yBAAyB,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,SAAS,MAAM,GACvD,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAC/B,IAAI,CAAA;AAEV;;;;GAIG;AACH,qBAAa,YAAY,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,GAAG,KAAK,CACzD,YAAW,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAWvC,QAAQ,CAAC,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO;IACzB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAZ1C,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAG,IAAI,CAAS;IAEhC,uCAAuC;IACvC,IAAI,KAAK,IAAI,IAAI,CAEhB;gBAGU,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAG1C;;;OAGG;IACH,IAAI,QAAQ,YAEX;IAED,IAAI,QAAQ,IACuB,yBAAyB,CAAC,CAAC,CAAC,CAC9D;IAED,IAAI,IAAI,IACuB,gBAAgB,CAAC,CAAC,CAAC,CACjD;IAED;;;OAGG;WACU,iBAAiB,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,GAAG,KAAK,EAC9D,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAE,GACvC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;CAiF5B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"xrpc-response.js","sourceRoot":"","sources":["../src/xrpc-response.ts"],"names":[],"mappings":";;;AAAA,gDAA4C;AAS5C,mDAIwB;AAUxB;;;;GAIG;AACH,MAAa,YAAY;IAYZ;IACA;IACA;IACA;IAZX,yCAAyC;IAChC,OAAO,GAAG,IAAa,CAAA;IAEhC,uCAAuC;IACvC,IAAI,KAAK;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IAED,YACW,MAAS,EACT,MAAc,EACd,OAAgB,EAChB,OAA+B;QAH/B,WAAM,GAAN,MAAM,CAAG;QACT,WAAM,GAAN,MAAM,CAAQ;QACd,YAAO,GAAP,OAAO,CAAS;QAChB,YAAO,GAAP,OAAO,CAAwB;IACvC,CAAC;IAEJ;;;OAGG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,QAAQ,KAAK,kBAAkB,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACzE,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,EAAE,QAAwC,CAAA;IAC/D,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,EAAE,IAA2B,CAAA;IAClD,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAC5B,MAAS,EACT,QAAkB,EAClB,OAAwC;QAExC,0EAA0E;QAC1E,kEAAkE;QAClE,oDAAoD;QAEpD,4EAA4E;QAC5E,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACpD,wCAAwC;YACxC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YAE5D,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,IAAA,kCAAkB,EAAC,OAAO,CAAC,EAAE,CAAC;gBAC1D,MAAM,IAAI,iCAAiB,CACzB,MAAM,EACN,QAAQ,CAAC,MAAM,EACf,QAAQ,CAAC,OAAO,EAChB,OAAO,CACR,CAAA;YACH,CAAC;YAED,MAAM,IAAI,wCAAwB,CAChC,QAAQ,CAAC,MAAM,IAAI,GAAG;gBACpB,CAAC,CAAC,sCAAsC;gBACxC,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,GAAG;oBACtB,CAAC,CAAC,sDAAsD;oBACxD,CAAC,CAAC,iDAAiD,EACvD,QAAQ,EACR,OAAO,CACR,CAAA;QACH,CAAC;QAED,2CAA2C;QAC3C,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE;YAC1C,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC;SAC3B,CAAC,CAAA;QAEF,qFAAqF;QACrF,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;YACnC,4BAA4B;YAC5B,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,IAAI,wCAAwB,CAChC,uCAAuC,OAAO,CAAC,QAAQ,EAAE,EACzD,QAAQ,EACR,OAAO,CACR,CAAA;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,2BAA2B;YAC3B,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,wCAAwB,CAChC,OAAO;oBACL,CAAC,CAAC,YAAY,MAAM,CAAC,MAAM,CAAC,QAAQ,kBAAkB,OAAO,CAAC,QAAQ,EAAE;oBACxE,CAAC,CAAC,iDAAiD,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,EAC7E,QAAQ,EACR,OAAO,CACR,CAAA;YACH,CAAC;YAED,8BAA8B;YAC9B,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO,EAAE,gBAAgB,KAAK,KAAK,EAAE,CAAC;gBAChE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE;oBAC1D,cAAc,EAAE,KAAK;iBACtB,CAAC,CAAA;gBAEF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBACpB,MAAM,IAAI,wCAAwB,CAChC,+BAA+B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,EACtD,QAAQ,EACR,OAAO,EACP,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CACzB,CAAA;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,IAAI,YAAY,CACrB,MAAM,EACN,QAAQ,CAAC,MAAM,EACf,QAAQ,CAAC,OAAO,EAChB,OAAiC,CAClC,CAAA;IACH,CAAC;CACF;AA3HD,oCA2HC;AAED,SAAS,WAAW,CAAC,MAAyB;IAC5C,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,KAAK,kBAAkB,CAAA;AACtD,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,WAAW,CACxB,QAAkB,EAClB,OAA6B;IAE7B,2EAA2E;IAC3E,6BAA6B;IAE7B,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO;SAC9B,GAAG,CAAC,cAAc,CAAC;QACpB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SACd,IAAI,EAAE;SACN,WAAW,EAAE,CAAA;IAEhB,qCAAqC;IACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,mDAAmD;QACnD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAA;QACzC,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QAEtC,6DAA6D;QAC7D,OAAO;YACL,QAAQ,EAAE,0BAA0B;YACpC,IAAI,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC;SAC3B,CAAA;IACH,CAAC;IAED,IAAI,OAAO,EAAE,KAAK,IAAI,QAAQ,KAAK,kBAAkB,EAAE,CAAC;QACtD,wEAAwE;QACxE,2DAA2D;QAC3D,sEAAsE;QACtE,yEAAyE;QACzE,qBAAqB;QACrB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAElC,IAAI,CAAC;YACH,sEAAsE;YACtE,mEAAmE;YACnE,6CAA6C;YAE7C,+BAA+B;YAC/B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAA,mBAAQ,EAAC,IAAI,CAAC,EAAE,CAAA;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,wCAAwB,CAChC,4BAA4B,EAC5B,QAAQ,EACR,IAAI,EACJ,EAAE,KAAK,EAAE,CACV,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,EAAE,CAAA;AACzE,CAAC","sourcesContent":["import { lexParse } from '@atproto/lex-json'\nimport {\n InferMethodOutputBody,\n InferMethodOutputEncoding,\n Procedure,\n Query,\n ResultSuccess,\n} from '@atproto/lex-schema'\nimport { Payload } from './util.js'\nimport {\n XrpcInvalidResponseError,\n XrpcResponseError,\n isXrpcErrorPayload,\n} from './xrpc-error.js'\n\nexport type XrpcResponseBody<M extends Procedure | Query> =\n InferMethodOutputBody<M, Uint8Array>\n\nexport type XrpcResponsePayload<M extends Procedure | Query> =\n InferMethodOutputEncoding<M> extends infer E extends string\n ? Payload<XrpcResponseBody<M>, E>\n : null\n\n/**\n * Small container for XRPC response data.\n *\n * @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.\n */\nexport class XrpcResponse<const M extends Procedure | Query>\n implements ResultSuccess<XrpcResponse<M>>\n{\n /** @see {@link ResultSuccess.success} */\n readonly success = true as const\n\n /** @see {@link ResultSuccess.value} */\n get value(): this {\n return this\n }\n\n constructor(\n readonly method: M,\n readonly status: number,\n readonly headers: Headers,\n readonly payload: XrpcResponsePayload<M>,\n ) {}\n\n /**\n * Whether the response payload was parsed as {@link LexValue} (`true`) or is\n * in binary form {@link Uint8Array} (`false`).\n */\n get isParsed() {\n return this.encoding === 'application/json' && shouldParse(this.method)\n }\n\n get encoding() {\n return this.payload?.encoding as InferMethodOutputEncoding<M>\n }\n\n get body() {\n return this.payload?.body as XrpcResponseBody<M>\n }\n\n /**\n * @throws {XrpcInvalidResponseError} when the response is invalid according\n * to the method schema.\n */\n static async fromFetchResponse<const M extends Procedure | Query>(\n method: M,\n response: Response,\n options?: { validateResponse?: boolean },\n ): Promise<XrpcResponse<M>> {\n // @NOTE The body MUST either be read or canceled to avoid resource leaks.\n // Since nothing should cause an exception before \"readPayload\" is\n // called, we can safely not use a try/finally here.\n\n // @NOTE redirect is set to 'follow', so we shouldn't get 3xx responses here\n if (response.status < 200 || response.status >= 300) {\n // Always parse json for error responses\n const payload = await readPayload(response, { parse: true })\n\n if (response.status >= 400 && isXrpcErrorPayload(payload)) {\n throw new XrpcResponseError(\n method,\n response.status,\n response.headers,\n payload,\n )\n }\n\n throw new XrpcInvalidResponseError(\n response.status >= 500\n ? `Upstream server encountered an error`\n : response.status >= 400\n ? `Upstream server returned an invalid response payload`\n : `Upstream server returned an invalid status code`,\n response,\n payload,\n )\n }\n\n // Only parse json if the schema expects it\n const payload = await readPayload(response, {\n parse: shouldParse(method),\n })\n\n // Response is successful (2xx). Validate payload (data and encoding) against schema.\n if (method.output.encoding == null) {\n // Schema expects no payload\n if (payload) {\n throw new XrpcInvalidResponseError(\n `Expected response with no body, got ${payload.encoding}`,\n response,\n payload,\n )\n }\n } else {\n // Schema expects a payload\n if (!payload || !method.output.matchesEncoding(payload.encoding)) {\n throw new XrpcInvalidResponseError(\n payload\n ? `Expected ${method.output.encoding} response, got ${payload.encoding}`\n : `Expected non-empty response with content-type ${method.output.encoding}`,\n response,\n payload,\n )\n }\n\n // Assert valid response body.\n if (method.output.schema && options?.validateResponse !== false) {\n const result = method.output.schema.safeParse(payload.body, {\n allowTransform: false,\n })\n\n if (!result.success) {\n throw new XrpcInvalidResponseError(\n `Response validation failed: ${result.reason.message}`,\n response,\n payload,\n { cause: result.reason },\n )\n }\n }\n }\n\n return new XrpcResponse<M>(\n method,\n response.status,\n response.headers,\n payload as XrpcResponsePayload<M>,\n )\n }\n}\n\nfunction shouldParse(method: Procedure | Query) {\n return method.output.encoding === 'application/json'\n}\n\n/**\n * @note this function always consumes the response body\n */\nasync function readPayload(\n response: Response,\n options?: { parse?: boolean },\n): Promise<Payload | null> {\n // @TODO Should we limit the maximum response size here (this could also be\n // done by the FetchHandler)?\n\n const encoding = response.headers\n .get('content-type')\n ?.split(';')[0]\n .trim()\n .toLowerCase()\n\n // Response content-type is undefined\n if (!encoding) {\n // If the body is empty, return null (= no payload)\n const body = await response.arrayBuffer()\n if (body.byteLength === 0) return null\n\n // If we got data despite no content-type, treat it as binary\n return {\n encoding: 'application/octet-stream',\n body: new Uint8Array(body),\n }\n }\n\n if (options?.parse && encoding === 'application/json') {\n // @NOTE It might be worth returning the raw bytes here (Uint8Array) and\n // perform the lex parsing using cborg/json, allowing to do\n // bytes->LexValue in one step instead of bytes->text->JSON->LexValue.\n // This would require adding encode/decode utilities to lex-json (similar\n // to @ipld/dag-json)\n const text = await response.text()\n\n try {\n // @NOTE Using `lexParse(text)` (instead of `jsonToLex(json)`) here as\n // using a reviver function during JSON.parse should be faster than\n // parsing to JSON then converting to Lex (?)\n\n // @TODO verify statement above\n return { encoding, body: lexParse(text) }\n } catch (cause) {\n throw new XrpcInvalidResponseError(\n 'Invalid JSON response body',\n response,\n null,\n { cause },\n )\n }\n }\n\n return { encoding, body: new Uint8Array(await response.arrayBuffer()) }\n}\n"]}