@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.
- package/CHANGELOG.md +15 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +3 -0
- package/dist/agent.js.map +1 -1
- package/dist/client.d.ts +30 -28
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +9 -13
- package/dist/client.js.map +1 -1
- package/dist/{xrpc-error.d.ts → errors.d.ts} +22 -27
- package/dist/errors.d.ts.map +1 -0
- package/dist/{xrpc-error.js → errors.js} +38 -33
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.js +5 -7
- package/dist/lexicons/com/atproto/repo/createRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js +5 -7
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js.map +1 -1
- 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.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.defs.js +2 -3
- package/dist/lexicons/com/atproto/repo/listRecords.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.defs.js +5 -7
- package/dist/lexicons/com/atproto/repo/putRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +3 -4
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -1
- package/dist/response.d.ts +38 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/{xrpc-response.js → response.js} +24 -20
- package/dist/response.js.map +1 -0
- package/dist/types.d.ts +0 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -4
- package/dist/types.js.map +1 -1
- package/dist/xrpc.d.ts +23 -17
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +15 -17
- package/dist/xrpc.js.map +1 -1
- package/package.json +10 -10
- package/src/agent.ts +6 -2
- package/src/client.ts +67 -61
- package/src/{xrpc-error.ts → errors.ts} +59 -48
- package/src/index.ts +2 -0
- package/src/lexicons/com/atproto/repo/createRecord.defs.ts +22 -28
- package/src/lexicons/com/atproto/repo/deleteRecord.defs.ts +18 -24
- package/src/lexicons/com/atproto/repo/getRecord.defs.ts +5 -10
- package/src/lexicons/com/atproto/repo/listRecords.defs.ts +6 -9
- package/src/lexicons/com/atproto/repo/putRecord.defs.ts +23 -29
- package/src/lexicons/com/atproto/repo/uploadBlob.defs.ts +4 -7
- package/src/{xrpc-response.ts → response.ts} +45 -30
- package/src/types.ts +0 -6
- package/src/xrpc.ts +56 -57
- package/tsconfig.tests.json +4 -7
- package/dist/xrpc-error.d.ts.map +0 -1
- package/dist/xrpc-error.js.map +0 -1
- package/dist/xrpc-response.d.ts +0 -35
- package/dist/xrpc-response.d.ts.map +0 -1
- 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.
|
|
19
|
-
'
|
|
20
|
-
/*#__PURE__*/ l.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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.
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from './
|
|
10
|
+
LexRpcResponseError,
|
|
11
|
+
LexRpcUpstreamError,
|
|
12
|
+
isLexRpcErrorPayload,
|
|
13
|
+
} from './errors.js'
|
|
14
|
+
import { Payload } from './util.js'
|
|
15
15
|
|
|
16
|
-
export type
|
|
16
|
+
export type LexRpcResponseBody<M extends Procedure | Query> =
|
|
17
17
|
InferMethodOutputBody<M, Uint8Array>
|
|
18
18
|
|
|
19
|
-
export type
|
|
19
|
+
export type LexRpcResponsePayload<M extends Procedure | Query> =
|
|
20
20
|
InferMethodOutputEncoding<M> extends infer E extends string
|
|
21
|
-
? Payload<
|
|
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<
|
|
27
|
+
* @implements {ResultSuccess<LexRpcResponse<M>>} for convenience in result handling contexts.
|
|
28
28
|
*/
|
|
29
|
-
export class
|
|
30
|
-
implements ResultSuccess<
|
|
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:
|
|
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
|
|
60
|
+
return this.payload?.body as LexRpcResponseBody<M>
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* @throws {
|
|
65
|
-
* to the
|
|
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<
|
|
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 &&
|
|
82
|
-
throw new
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
159
|
+
return new LexRpcResponse<M>(
|
|
146
160
|
method,
|
|
147
161
|
response.status,
|
|
148
162
|
response.headers,
|
|
149
|
-
payload as
|
|
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
|
|
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 {
|
|
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
|
|
33
|
+
type LexRpcParamsOptions<P extends Params> =
|
|
35
34
|
NonNullable<unknown> extends P ? { params?: P } : { params: P }
|
|
36
35
|
|
|
37
|
-
type
|
|
36
|
+
type LexRpcRequestPayload<M extends Procedure | Query> = InferMethodInput<
|
|
38
37
|
M,
|
|
39
38
|
BinaryBodyInit
|
|
40
39
|
>
|
|
41
40
|
|
|
42
|
-
type
|
|
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
|
|
46
|
+
export type LexRpcOptions<M extends Procedure | Query = Procedure | Query> =
|
|
48
47
|
CallOptions &
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
LexRpcInputOptions<LexRpcRequestPayload<M>> &
|
|
49
|
+
LexRpcParamsOptions<InferMethodParams<M>>
|
|
51
50
|
|
|
52
|
-
export type
|
|
51
|
+
export type LexRpcFailure<M extends Procedure | Query> =
|
|
53
52
|
// The server returned a valid XRPC error response
|
|
54
|
-
|
|
|
53
|
+
| LexRpcResponseError<M>
|
|
55
54
|
// The response was not a valid XRPC response, or it does not match the schema
|
|
56
|
-
|
|
|
55
|
+
| LexRpcUpstreamError
|
|
57
56
|
// Something went wrong (network error, etc.)
|
|
58
|
-
|
|
|
57
|
+
| LexRpcUnexpectedError
|
|
59
58
|
|
|
60
|
-
export type
|
|
61
|
-
|
|
|
62
|
-
|
|
|
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
|
|
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
|
|
71
|
-
|
|
72
|
-
):
|
|
73
|
-
if (err instanceof
|
|
74
|
-
if (err instanceof
|
|
75
|
-
return
|
|
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
|
|
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
|
|
84
|
-
?
|
|
82
|
+
ns: NonNullable<unknown> extends LexRpcOptions<M>
|
|
83
|
+
? Main<M>
|
|
85
84
|
: Restricted<'This XRPC method requires an "options" argument'>,
|
|
86
|
-
): Promise<
|
|
85
|
+
): Promise<LexRpcResponse<M>>
|
|
87
86
|
export async function xrpc<const M extends Query | Procedure>(
|
|
88
87
|
agent: Agent,
|
|
89
|
-
ns:
|
|
90
|
-
options:
|
|
91
|
-
): Promise<
|
|
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:
|
|
95
|
-
options:
|
|
96
|
-
): Promise<
|
|
93
|
+
ns: Main<M>,
|
|
94
|
+
options: LexRpcOptions<M> = {} as LexRpcOptions<M>,
|
|
95
|
+
): Promise<LexRpcResponse<M>> {
|
|
97
96
|
try {
|
|
98
|
-
return await
|
|
97
|
+
return await lexRpcRequest<M>(agent, ns, options)
|
|
99
98
|
} catch (err) {
|
|
100
|
-
throw
|
|
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
|
|
107
|
-
?
|
|
105
|
+
ns: NonNullable<unknown> extends LexRpcOptions<M>
|
|
106
|
+
? Main<M>
|
|
108
107
|
: Restricted<'This XRPC method requires an "options" argument'>,
|
|
109
|
-
): Promise<
|
|
108
|
+
): Promise<LexRpcResult<M>>
|
|
110
109
|
export async function xrpcSafe<const M extends Query | Procedure>(
|
|
111
110
|
agent: Agent,
|
|
112
|
-
ns:
|
|
113
|
-
options:
|
|
114
|
-
): Promise<
|
|
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:
|
|
118
|
-
options:
|
|
119
|
-
): Promise<
|
|
120
|
-
return
|
|
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
|
|
122
|
+
async function lexRpcRequest<const M extends Query | Procedure>(
|
|
124
123
|
agent: Agent,
|
|
125
|
-
ns:
|
|
126
|
-
options:
|
|
127
|
-
): Promise<
|
|
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
|
|
132
|
+
return LexRpcResponse.fromFetchResponse<M>(method, response, options)
|
|
134
133
|
}
|
|
135
134
|
|
|
136
135
|
function xrpcRequestUrl<M extends Procedure | Query | Subscription>(
|
package/tsconfig.tests.json
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"extends": "../../../tsconfig/
|
|
3
|
-
"include": ["./tests"],
|
|
2
|
+
"extends": "../../../tsconfig/vitest.json",
|
|
3
|
+
"include": ["./tests", "./src/**/*.test.ts"],
|
|
4
4
|
"compilerOptions": {
|
|
5
5
|
"noImplicitAny": true,
|
|
6
|
-
"rootDir": "./
|
|
7
|
-
"baseUrl": "./
|
|
8
|
-
"paths": {
|
|
9
|
-
"@atproto/lex-client": ["./dist/index.js"]
|
|
10
|
-
}
|
|
6
|
+
"rootDir": "./",
|
|
7
|
+
"baseUrl": "./"
|
|
11
8
|
}
|
|
12
9
|
}
|
package/dist/xrpc-error.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"xrpc-error.d.ts","sourceRoot":"","sources":["../src/xrpc-error.ts"],"names":[],"mappings":"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"}
|
package/dist/xrpc-error.js.map
DELETED
|
@@ -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"]}
|
package/dist/xrpc-response.d.ts
DELETED
|
@@ -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":"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","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"]}
|