@atproto/lex-client 0.0.4 → 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 +42 -0
- package/dist/agent.d.ts +10 -9
- 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 +51 -113
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +38 -42
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +82 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +132 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts +7 -7
- package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.js +8 -12
- package/dist/lexicons/com/atproto/repo/createRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts +7 -7
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js +8 -12
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts +5 -6
- package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.defs.js +6 -10
- package/dist/lexicons/com/atproto/repo/getRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts +5 -6
- package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.defs.js +5 -8
- package/dist/lexicons/com/atproto/repo/listRecords.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts +7 -7
- package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.defs.js +8 -12
- package/dist/lexicons/com/atproto/repo/putRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts +7 -7
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +6 -9
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/sync/getBlob.d.ts +3 -0
- package/dist/lexicons/com/atproto/sync/getBlob.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync/getBlob.defs.d.ts +25 -0
- package/dist/lexicons/com/atproto/sync/getBlob.defs.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync/getBlob.defs.js +27 -0
- package/dist/lexicons/com/atproto/sync/getBlob.defs.js.map +1 -0
- package/dist/lexicons/com/atproto/sync/getBlob.js +10 -0
- package/dist/lexicons/com/atproto/sync/getBlob.js.map +1 -0
- package/dist/lexicons/com/atproto/sync.d.ts +2 -0
- package/dist/lexicons/com/atproto/sync.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync.js +9 -0
- package/dist/lexicons/com/atproto/sync.js.map +1 -0
- package/dist/lexicons/com/atproto.d.ts +1 -0
- package/dist/lexicons/com/atproto.d.ts.map +1 -1
- package/dist/lexicons/com/atproto.js +2 -1
- package/dist/lexicons/com/atproto.js.map +1 -1
- package/dist/lexicons.d.ts +2 -0
- package/dist/lexicons.d.ts.map +1 -0
- package/dist/lexicons.js +6 -0
- package/dist/lexicons.js.map +1 -0
- package/dist/response.d.ts +25 -8
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +123 -10
- package/dist/response.js.map +1 -1
- package/dist/types.d.ts +18 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -4
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +14 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +65 -0
- package/dist/util.js.map +1 -0
- package/dist/xrpc.d.ts +35 -32
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +116 -124
- package/dist/xrpc.js.map +1 -1
- package/package.json +10 -10
- package/src/agent.ts +18 -14
- package/src/client.ts +135 -114
- package/src/errors.ts +206 -0
- package/src/index.ts +1 -1
- package/src/lexicons/com/atproto/repo/createRecord.defs.ts +31 -36
- package/src/lexicons/com/atproto/repo/deleteRecord.defs.ts +27 -32
- package/src/lexicons/com/atproto/repo/getRecord.defs.ts +12 -17
- package/src/lexicons/com/atproto/repo/listRecords.defs.ts +13 -15
- package/src/lexicons/com/atproto/repo/putRecord.defs.ts +32 -37
- package/src/lexicons/com/atproto/repo/uploadBlob.defs.ts +13 -15
- package/src/lexicons/com/atproto/sync/getBlob.defs.ts +37 -0
- package/src/lexicons/com/atproto/sync/getBlob.ts +6 -0
- package/src/lexicons/com/atproto/sync.ts +5 -0
- package/src/lexicons/com/atproto.ts +1 -0
- package/src/lexicons.ts +1 -0
- package/src/response.ts +201 -15
- package/src/types.ts +26 -5
- package/src/util.ts +84 -0
- package/src/xrpc.ts +220 -232
- package/tsconfig.tests.json +4 -7
- package/dist/error.d.ts +0 -66
- package/dist/error.d.ts.map +0 -1
- package/dist/error.js +0 -100
- package/dist/error.js.map +0 -1
- package/src/error.ts +0 -145
package/src/client.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { LexMap, LexValue } from '@atproto/lex-data'
|
|
1
|
+
import { LexError, LexMap, LexValue } from '@atproto/lex-data'
|
|
2
2
|
import {
|
|
3
3
|
AtIdentifierString,
|
|
4
|
+
CidString,
|
|
4
5
|
DidString,
|
|
5
6
|
Infer,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
InferQueryParameters,
|
|
7
|
+
InferMethodInputBody,
|
|
8
|
+
InferMethodOutputBody,
|
|
9
|
+
InferMethodParams,
|
|
10
10
|
InferRecordKey,
|
|
11
11
|
LexiconRecordKey,
|
|
12
|
+
Main,
|
|
12
13
|
NsidString,
|
|
13
14
|
Params,
|
|
14
15
|
Procedure,
|
|
@@ -16,18 +17,34 @@ import {
|
|
|
16
17
|
RecordSchema,
|
|
17
18
|
Restricted,
|
|
18
19
|
Schema,
|
|
20
|
+
getMain,
|
|
19
21
|
} from '@atproto/lex-schema'
|
|
20
22
|
import { Agent, AgentOptions, buildAgent } from './agent.js'
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
import { com } from './lexicons.js'
|
|
24
|
+
import { LexRpcResponse, LexRpcResponseBody } from './response.js'
|
|
25
|
+
import { BinaryBodyInit, CallOptions, Service } from './types.js'
|
|
26
|
+
import { buildAtprotoHeaders } from './util.js'
|
|
27
|
+
import { LexRpcFailure, LexRpcOptions, xrpc, xrpcSafe } from './xrpc.js'
|
|
28
|
+
|
|
29
|
+
export type {
|
|
30
|
+
AtIdentifierString,
|
|
31
|
+
CidString,
|
|
32
|
+
DidString,
|
|
33
|
+
InferMethodInputBody,
|
|
34
|
+
InferMethodOutputBody,
|
|
35
|
+
InferMethodParams,
|
|
36
|
+
InferRecordKey,
|
|
37
|
+
LexMap,
|
|
38
|
+
LexValue,
|
|
39
|
+
LexiconRecordKey,
|
|
40
|
+
NsidString,
|
|
41
|
+
Params,
|
|
42
|
+
Procedure,
|
|
43
|
+
Query,
|
|
44
|
+
RecordSchema,
|
|
45
|
+
Restricted,
|
|
46
|
+
Schema,
|
|
47
|
+
}
|
|
31
48
|
|
|
32
49
|
export type ClientOptions = {
|
|
33
50
|
labelers?: Iterable<DidString>
|
|
@@ -84,29 +101,35 @@ export type RecordKeyOptions<
|
|
|
84
101
|
|
|
85
102
|
export type CreateOptions<T extends RecordSchema> = CreateRecordOptions &
|
|
86
103
|
RecordKeyOptions<T, 'tid'>
|
|
87
|
-
export type CreateOutput =
|
|
88
|
-
typeof com.atproto.repo.createRecord.main
|
|
104
|
+
export type CreateOutput = InferMethodOutputBody<
|
|
105
|
+
typeof com.atproto.repo.createRecord.main,
|
|
106
|
+
Uint8Array
|
|
89
107
|
>
|
|
90
108
|
|
|
91
109
|
export type DeleteOptions<T extends RecordSchema> = DeleteRecordOptions &
|
|
92
110
|
RecordKeyOptions<T>
|
|
93
|
-
export type DeleteOutput =
|
|
94
|
-
typeof com.atproto.repo.deleteRecord.main
|
|
111
|
+
export type DeleteOutput = InferMethodOutputBody<
|
|
112
|
+
typeof com.atproto.repo.deleteRecord.main,
|
|
113
|
+
Uint8Array
|
|
95
114
|
>
|
|
96
115
|
export type GetOptions<T extends RecordSchema> = GetRecordOptions &
|
|
97
116
|
RecordKeyOptions<T>
|
|
98
117
|
export type GetOutput<T extends RecordSchema> = Omit<
|
|
99
|
-
|
|
118
|
+
InferMethodOutputBody<typeof com.atproto.repo.getRecord.main, Uint8Array>,
|
|
100
119
|
'value'
|
|
101
120
|
> & { value: Infer<T> }
|
|
102
121
|
|
|
103
122
|
export type PutOptions<T extends RecordSchema> = PutRecordOptions &
|
|
104
123
|
RecordKeyOptions<T>
|
|
105
|
-
export type PutOutput =
|
|
124
|
+
export type PutOutput = InferMethodOutputBody<
|
|
125
|
+
typeof com.atproto.repo.putRecord.main,
|
|
126
|
+
Uint8Array
|
|
127
|
+
>
|
|
106
128
|
|
|
107
129
|
export type ListOptions = ListRecordsOptions
|
|
108
|
-
export type ListOutput<T extends RecordSchema> =
|
|
109
|
-
typeof com.atproto.repo.listRecords.main
|
|
130
|
+
export type ListOutput<T extends RecordSchema> = InferMethodOutputBody<
|
|
131
|
+
typeof com.atproto.repo.listRecords.main,
|
|
132
|
+
Uint8Array
|
|
110
133
|
> & {
|
|
111
134
|
records: ListRecord<T>[]
|
|
112
135
|
// @NOTE Because the schema uses "type": "unknown" instead of an open union,
|
|
@@ -134,10 +157,7 @@ export class Client implements Agent {
|
|
|
134
157
|
public readonly labelers: Set<DidString>
|
|
135
158
|
|
|
136
159
|
constructor(agent: Agent | AgentOptions, options: ClientOptions = {}) {
|
|
137
|
-
this.agent =
|
|
138
|
-
typeof agent === 'object' && 'fetchHandler' in agent
|
|
139
|
-
? agent
|
|
140
|
-
: buildAgent(agent)
|
|
160
|
+
this.agent = buildAgent(agent)
|
|
141
161
|
this.service = options.service
|
|
142
162
|
this.labelers = new Set(options.labelers)
|
|
143
163
|
this.headers = new Headers(options.headers)
|
|
@@ -153,7 +173,7 @@ export class Client implements Agent {
|
|
|
153
173
|
}
|
|
154
174
|
|
|
155
175
|
public assertAuthenticated(): asserts this is { did: DidString } {
|
|
156
|
-
if (!this.did) throw new
|
|
176
|
+
if (!this.did) throw new LexError('AuthenticationRequired')
|
|
157
177
|
}
|
|
158
178
|
|
|
159
179
|
public setLabelers(labelers: Iterable<DidString> = []) {
|
|
@@ -170,7 +190,7 @@ export class Client implements Agent {
|
|
|
170
190
|
}
|
|
171
191
|
|
|
172
192
|
public fetchHandler(path: string, init: RequestInit): Promise<Response> {
|
|
173
|
-
const headers =
|
|
193
|
+
const headers = buildAtprotoHeaders({
|
|
174
194
|
headers: init.headers,
|
|
175
195
|
service: this.service,
|
|
176
196
|
labelers: [
|
|
@@ -186,40 +206,43 @@ export class Client implements Agent {
|
|
|
186
206
|
if (!headers.has(key)) headers.set(key, value)
|
|
187
207
|
}
|
|
188
208
|
|
|
209
|
+
// @NOTE The agent here could be another Client instance.
|
|
189
210
|
return this.agent.fetchHandler(path, { ...init, headers })
|
|
190
211
|
}
|
|
191
212
|
|
|
213
|
+
/**
|
|
214
|
+
* @throws {LexRpcFailure<M>} when the request fails or the response is an error
|
|
215
|
+
*/
|
|
192
216
|
async xrpc<const M extends Query | Procedure>(
|
|
193
|
-
ns: NonNullable<unknown> extends
|
|
194
|
-
?
|
|
217
|
+
ns: NonNullable<unknown> extends LexRpcOptions<M>
|
|
218
|
+
? Main<M>
|
|
195
219
|
: Restricted<'This XRPC method requires an "options" argument'>,
|
|
196
|
-
): Promise<
|
|
220
|
+
): Promise<LexRpcResponse<M>>
|
|
197
221
|
async xrpc<const M extends Query | Procedure>(
|
|
198
|
-
ns:
|
|
199
|
-
options:
|
|
200
|
-
): Promise<
|
|
222
|
+
ns: Main<M>,
|
|
223
|
+
options: LexRpcOptions<M>,
|
|
224
|
+
): Promise<LexRpcResponse<M>>
|
|
201
225
|
async xrpc<const M extends Query | Procedure>(
|
|
202
|
-
ns:
|
|
203
|
-
options:
|
|
204
|
-
): Promise<
|
|
226
|
+
ns: Main<M>,
|
|
227
|
+
options: LexRpcOptions<M> = {} as LexRpcOptions<M>,
|
|
228
|
+
): Promise<LexRpcResponse<M>> {
|
|
205
229
|
return xrpc(this, ns, options)
|
|
206
230
|
}
|
|
207
231
|
|
|
208
232
|
async xrpcSafe<const M extends Query | Procedure>(
|
|
209
|
-
ns: NonNullable<unknown> extends
|
|
210
|
-
?
|
|
233
|
+
ns: NonNullable<unknown> extends LexRpcOptions<M>
|
|
234
|
+
? Main<M>
|
|
211
235
|
: Restricted<'This XRPC method requires an "options" argument'>,
|
|
212
|
-
): Promise<
|
|
236
|
+
): Promise<LexRpcResponse<M> | LexRpcFailure<M>>
|
|
213
237
|
async xrpcSafe<const M extends Query | Procedure>(
|
|
214
|
-
ns:
|
|
215
|
-
options:
|
|
216
|
-
): Promise<
|
|
238
|
+
ns: Main<M>,
|
|
239
|
+
options: LexRpcOptions<M>,
|
|
240
|
+
): Promise<LexRpcResponse<M> | LexRpcFailure<M>>
|
|
217
241
|
async xrpcSafe<const M extends Query | Procedure>(
|
|
218
|
-
ns:
|
|
219
|
-
options:
|
|
220
|
-
): Promise<
|
|
221
|
-
|
|
222
|
-
return this.xrpc(schema, options).catch(asXrpcRequestFailureFor(schema))
|
|
242
|
+
ns: Main<M>,
|
|
243
|
+
options: LexRpcOptions<M> = {} as LexRpcOptions<M>,
|
|
244
|
+
): Promise<LexRpcResponse<M> | LexRpcFailure<M>> {
|
|
245
|
+
return xrpcSafe(this, ns, options)
|
|
223
246
|
}
|
|
224
247
|
|
|
225
248
|
/**
|
|
@@ -243,12 +266,6 @@ export class Client implements Agent {
|
|
|
243
266
|
})
|
|
244
267
|
}
|
|
245
268
|
|
|
246
|
-
async createRecordsSafe(...args: Parameters<Client['createRecord']>) {
|
|
247
|
-
return this.createRecord(...args).catch(
|
|
248
|
-
asXrpcRequestFailureFor(com.atproto.repo.createRecord.main),
|
|
249
|
-
)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
269
|
async deleteRecord(
|
|
253
270
|
collection: NsidString,
|
|
254
271
|
rkey: string,
|
|
@@ -266,12 +283,6 @@ export class Client implements Agent {
|
|
|
266
283
|
})
|
|
267
284
|
}
|
|
268
285
|
|
|
269
|
-
async deleteRecordsSafe(...args: Parameters<Client['deleteRecord']>) {
|
|
270
|
-
return this.deleteRecord(...args).catch(
|
|
271
|
-
asXrpcRequestFailureFor(com.atproto.repo.deleteRecord.main),
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
286
|
public async getRecord(
|
|
276
287
|
collection: NsidString,
|
|
277
288
|
rkey: string,
|
|
@@ -287,12 +298,6 @@ export class Client implements Agent {
|
|
|
287
298
|
})
|
|
288
299
|
}
|
|
289
300
|
|
|
290
|
-
async getRecordsSafe(...args: Parameters<Client['getRecord']>) {
|
|
291
|
-
return this.getRecord(...args).catch(
|
|
292
|
-
asXrpcRequestFailureFor(com.atproto.repo.getRecord.main),
|
|
293
|
-
)
|
|
294
|
-
}
|
|
295
|
-
|
|
296
301
|
async putRecord(
|
|
297
302
|
record: { $type: NsidString } & LexMap,
|
|
298
303
|
rkey: string,
|
|
@@ -312,12 +317,6 @@ export class Client implements Agent {
|
|
|
312
317
|
})
|
|
313
318
|
}
|
|
314
319
|
|
|
315
|
-
async putRecordsSafe(...args: Parameters<Client['putRecord']>) {
|
|
316
|
-
return this.putRecord(...args).catch(
|
|
317
|
-
asXrpcRequestFailureFor(com.atproto.repo.putRecord.main),
|
|
318
|
-
)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
320
|
async listRecords(nsid: NsidString, options?: ListRecordsOptions) {
|
|
322
321
|
return this.xrpc(com.atproto.repo.listRecords.main, {
|
|
323
322
|
...options,
|
|
@@ -331,30 +330,54 @@ export class Client implements Agent {
|
|
|
331
330
|
})
|
|
332
331
|
}
|
|
333
332
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
333
|
+
async uploadBlob(
|
|
334
|
+
body: BinaryBodyInit,
|
|
335
|
+
options?: CallOptions & { encoding?: `${string}/${string}` },
|
|
336
|
+
) {
|
|
337
|
+
return this.xrpc(com.atproto.repo.uploadBlob.main, {
|
|
338
|
+
...options,
|
|
339
|
+
body,
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async getBlob(did: DidString, cid: CidString, options?: CallOptions) {
|
|
344
|
+
return this.xrpc(com.atproto.sync.getBlob.main, {
|
|
345
|
+
...options,
|
|
346
|
+
params: { did, cid },
|
|
347
|
+
})
|
|
348
|
+
}
|
|
349
|
+
|
|
344
350
|
public async call<const T extends Query>(
|
|
345
|
-
ns: NonNullable<unknown> extends
|
|
346
|
-
?
|
|
351
|
+
ns: NonNullable<unknown> extends InferMethodParams<T>
|
|
352
|
+
? Main<T>
|
|
347
353
|
: Restricted<'This query type requires a "params" argument'>,
|
|
348
|
-
): Promise<
|
|
349
|
-
public async call<const T extends
|
|
350
|
-
ns:
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
+
): Promise<LexRpcResponseBody<T>>
|
|
355
|
+
public async call<const T extends Action>(
|
|
356
|
+
ns: void extends InferActionInput<T>
|
|
357
|
+
? Main<T>
|
|
358
|
+
: Restricted<'This action type requires an "input" argument'>,
|
|
359
|
+
): Promise<InferActionOutput<T>>
|
|
360
|
+
public async call<const T extends Action | Procedure | Query>(
|
|
361
|
+
ns: Main<T>,
|
|
362
|
+
arg: T extends Action
|
|
363
|
+
? InferActionInput<T>
|
|
364
|
+
: T extends Procedure
|
|
365
|
+
? InferMethodInputBody<T, Uint8Array>
|
|
366
|
+
: T extends Query
|
|
367
|
+
? InferMethodParams<T>
|
|
368
|
+
: never,
|
|
354
369
|
options?: CallOptions,
|
|
355
|
-
): Promise<
|
|
370
|
+
): Promise<
|
|
371
|
+
T extends Action
|
|
372
|
+
? InferActionOutput<T>
|
|
373
|
+
: T extends Procedure
|
|
374
|
+
? LexRpcResponseBody<T>
|
|
375
|
+
: T extends Query
|
|
376
|
+
? LexRpcResponseBody<T>
|
|
377
|
+
: never
|
|
378
|
+
>
|
|
356
379
|
public async call(
|
|
357
|
-
ns:
|
|
380
|
+
ns: Main<Action> | Main<Procedure> | Main<Query>,
|
|
358
381
|
arg?: LexValue | Params,
|
|
359
382
|
options: CallOptions = {},
|
|
360
383
|
): Promise<unknown> {
|
|
@@ -365,12 +388,10 @@ export class Client implements Agent {
|
|
|
365
388
|
}
|
|
366
389
|
|
|
367
390
|
if (method instanceof Procedure) {
|
|
368
|
-
const
|
|
369
|
-
const result = await this.xrpc(method, { ...options, body })
|
|
391
|
+
const result = await this.xrpc(method, { ...options, body: arg as any })
|
|
370
392
|
return result.body
|
|
371
393
|
} else if (method instanceof Query) {
|
|
372
|
-
const
|
|
373
|
-
const result = await this.xrpc(method, { ...options, params })
|
|
394
|
+
const result = await this.xrpc(method, { ...options, params: arg as any })
|
|
374
395
|
return result.body
|
|
375
396
|
} else {
|
|
376
397
|
throw new TypeError('Invalid lexicon')
|
|
@@ -379,24 +400,23 @@ export class Client implements Agent {
|
|
|
379
400
|
|
|
380
401
|
public async create<const T extends RecordSchema>(
|
|
381
402
|
ns: NonNullable<unknown> extends CreateOptions<T>
|
|
382
|
-
?
|
|
403
|
+
? Main<T>
|
|
383
404
|
: Restricted<'This record type requires an "options" argument'>,
|
|
384
405
|
input: Omit<Infer<T>, '$type'>,
|
|
385
406
|
): Promise<CreateOutput>
|
|
386
407
|
public async create<const T extends RecordSchema>(
|
|
387
|
-
ns:
|
|
408
|
+
ns: Main<T>,
|
|
388
409
|
input: Omit<Infer<T>, '$type'>,
|
|
389
410
|
options: CreateOptions<T>,
|
|
390
411
|
): Promise<CreateOutput>
|
|
391
412
|
public async create<const T extends RecordSchema>(
|
|
392
|
-
ns:
|
|
413
|
+
ns: Main<T>,
|
|
393
414
|
input: Omit<Infer<T>, '$type'>,
|
|
394
415
|
options: CreateOptions<T> = {} as CreateOptions<T>,
|
|
395
416
|
): Promise<CreateOutput> {
|
|
396
417
|
const schema: T = getMain(ns)
|
|
397
|
-
const record =
|
|
398
|
-
|
|
399
|
-
: schema.build(input)
|
|
418
|
+
const record = schema.build(input)
|
|
419
|
+
if (options.validateRequest) schema.assert(record)
|
|
400
420
|
const rkey = options.rkey ?? getDefaultRecordKey(schema)
|
|
401
421
|
if (rkey !== undefined) schema.keySchema.assert(rkey)
|
|
402
422
|
const response = await this.createRecord(record, rkey, options)
|
|
@@ -405,15 +425,15 @@ export class Client implements Agent {
|
|
|
405
425
|
|
|
406
426
|
public async delete<const T extends RecordSchema>(
|
|
407
427
|
ns: NonNullable<unknown> extends DeleteOptions<T>
|
|
408
|
-
?
|
|
428
|
+
? Main<T>
|
|
409
429
|
: Restricted<'This record type requires an "options" argument'>,
|
|
410
430
|
): Promise<DeleteOutput>
|
|
411
431
|
public async delete<const T extends RecordSchema>(
|
|
412
|
-
ns:
|
|
432
|
+
ns: Main<T>,
|
|
413
433
|
options?: DeleteOptions<T>,
|
|
414
434
|
): Promise<DeleteOutput>
|
|
415
435
|
public async delete<const T extends RecordSchema>(
|
|
416
|
-
ns:
|
|
436
|
+
ns: Main<T>,
|
|
417
437
|
options: DeleteOptions<T> = {} as DeleteOptions<T>,
|
|
418
438
|
): Promise<DeleteOutput> {
|
|
419
439
|
const schema = getMain(ns)
|
|
@@ -426,15 +446,15 @@ export class Client implements Agent {
|
|
|
426
446
|
|
|
427
447
|
public async get<const T extends RecordSchema>(
|
|
428
448
|
ns: T['key'] extends `literal:${string}`
|
|
429
|
-
?
|
|
449
|
+
? Main<T>
|
|
430
450
|
: Restricted<'This record type requires an "options" argument'>,
|
|
431
451
|
): Promise<GetOutput<T>>
|
|
432
452
|
public async get<const T extends RecordSchema>(
|
|
433
|
-
ns:
|
|
453
|
+
ns: Main<T>,
|
|
434
454
|
options?: GetOptions<T>,
|
|
435
455
|
): Promise<GetOutput<T>>
|
|
436
456
|
public async get<const T extends RecordSchema>(
|
|
437
|
-
ns:
|
|
457
|
+
ns: Main<T>,
|
|
438
458
|
options: GetOptions<T> = {} as GetOptions<T>,
|
|
439
459
|
): Promise<GetOutput<T>> {
|
|
440
460
|
const schema = getMain(ns)
|
|
@@ -448,29 +468,30 @@ export class Client implements Agent {
|
|
|
448
468
|
|
|
449
469
|
public async put<const T extends RecordSchema>(
|
|
450
470
|
ns: NonNullable<unknown> extends PutOptions<T>
|
|
451
|
-
?
|
|
471
|
+
? Main<T>
|
|
452
472
|
: Restricted<'This record type requires an "options" argument'>,
|
|
453
473
|
input: Omit<Infer<T>, '$type'>,
|
|
454
474
|
): Promise<PutOutput>
|
|
455
475
|
public async put<const T extends RecordSchema>(
|
|
456
|
-
ns:
|
|
476
|
+
ns: Main<T>,
|
|
457
477
|
input: Omit<Infer<T>, '$type'>,
|
|
458
478
|
options: PutOptions<T>,
|
|
459
479
|
): Promise<PutOutput>
|
|
460
480
|
public async put<const T extends RecordSchema>(
|
|
461
|
-
ns:
|
|
481
|
+
ns: Main<T>,
|
|
462
482
|
input: Omit<Infer<T>, '$type'>,
|
|
463
483
|
options: PutOptions<T> = {} as PutOptions<T>,
|
|
464
484
|
): Promise<PutOutput> {
|
|
465
|
-
const schema = getMain(ns)
|
|
485
|
+
const schema: T = getMain(ns)
|
|
466
486
|
const record = schema.build(input)
|
|
487
|
+
if (options.validateRequest) schema.assert(record)
|
|
467
488
|
const rkey = options.rkey ?? getLiteralRecordKey(schema)
|
|
468
489
|
const response = await this.putRecord(record, rkey, options)
|
|
469
490
|
return response.body
|
|
470
491
|
}
|
|
471
492
|
|
|
472
493
|
async list<const T extends RecordSchema>(
|
|
473
|
-
ns:
|
|
494
|
+
ns: Main<T>,
|
|
474
495
|
options?: ListOptions,
|
|
475
496
|
): Promise<ListOutput<T>> {
|
|
476
497
|
const schema = getMain(ns)
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { LexError, LexErrorCode, LexErrorData } from '@atproto/lex-data'
|
|
2
|
+
import { l } from '@atproto/lex-schema'
|
|
3
|
+
import { Payload } from './util.js'
|
|
4
|
+
|
|
5
|
+
export type LexRpcErrorPayload<N extends LexErrorCode = LexErrorCode> = Payload<
|
|
6
|
+
LexErrorData<N>,
|
|
7
|
+
'application/json'
|
|
8
|
+
>
|
|
9
|
+
|
|
10
|
+
export class LexRpcError<
|
|
11
|
+
N extends LexErrorCode = LexErrorCode,
|
|
12
|
+
> extends LexError<N> {
|
|
13
|
+
name = 'LexRpcError'
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
error: N,
|
|
17
|
+
message: string = `${error} Lexicon RPC error`,
|
|
18
|
+
options?: ErrorOptions,
|
|
19
|
+
) {
|
|
20
|
+
super(error, message, options)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* All unsuccessful responses should follow a standard error response
|
|
26
|
+
* schema. The Content-Type should be application/json, and the payload
|
|
27
|
+
* should be a JSON object with the following fields:
|
|
28
|
+
*
|
|
29
|
+
* - `error` (string, required): type name of the error (generic ASCII
|
|
30
|
+
* constant, no whitespace)
|
|
31
|
+
* - `message` (string, optional): description of the error, appropriate for
|
|
32
|
+
* display to humans
|
|
33
|
+
*
|
|
34
|
+
* This function checks whether a given payload matches this schema.
|
|
35
|
+
*/
|
|
36
|
+
export function isLexRpcErrorPayload(
|
|
37
|
+
payload: Payload | null,
|
|
38
|
+
): payload is LexRpcErrorPayload {
|
|
39
|
+
return (
|
|
40
|
+
payload !== null &&
|
|
41
|
+
payload.encoding === 'application/json' &&
|
|
42
|
+
l.lexErrorData.matches(payload.body)
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Interface representing a failed XRPC request result.
|
|
48
|
+
*/
|
|
49
|
+
type LexRpcFailureResult<N extends LexErrorCode, E> = l.ResultFailure<E> & {
|
|
50
|
+
readonly error: N
|
|
51
|
+
shouldRetry(): boolean
|
|
52
|
+
matchesSchema(): boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Class used to represent an HTTP request that resulted in an XRPC method error
|
|
57
|
+
* That is, a non-2xx response with a valid XRPC error payload.
|
|
58
|
+
*/
|
|
59
|
+
export class LexRpcResponseError<
|
|
60
|
+
M extends l.Procedure | l.Query = l.Procedure | l.Query,
|
|
61
|
+
N extends LexErrorCode = LexErrorCode,
|
|
62
|
+
>
|
|
63
|
+
extends LexRpcError<N>
|
|
64
|
+
implements LexRpcFailureResult<N, LexRpcResponseError<M, N>>
|
|
65
|
+
{
|
|
66
|
+
name = 'LexRpcResponseError'
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
readonly method: M,
|
|
70
|
+
readonly status: number,
|
|
71
|
+
readonly headers: Headers,
|
|
72
|
+
readonly payload: LexRpcErrorPayload<N>,
|
|
73
|
+
options?: ErrorOptions,
|
|
74
|
+
) {
|
|
75
|
+
const { error, message } = payload.body
|
|
76
|
+
super(error, message, options)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
readonly success = false
|
|
80
|
+
|
|
81
|
+
get reason(): this {
|
|
82
|
+
return this as this
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get body(): LexErrorData {
|
|
86
|
+
return this.payload.body
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
matchesSchema(): this is M extends {
|
|
90
|
+
errors: readonly (infer E extends string)[]
|
|
91
|
+
}
|
|
92
|
+
? LexRpcResponseError<M, E>
|
|
93
|
+
: never {
|
|
94
|
+
return this.method.errors?.includes(this.error) ?? false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
shouldRetry(): boolean {
|
|
98
|
+
// Do not retry client errors
|
|
99
|
+
if (this.status < 500) return false
|
|
100
|
+
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
toJSON() {
|
|
105
|
+
return this.payload.body
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
toResponse(): Response {
|
|
109
|
+
const { status, headers } = this
|
|
110
|
+
return Response.json(this.toJSON(), { status, headers })
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* This class represents an invalid XRPC response from the server.
|
|
116
|
+
*/
|
|
117
|
+
export class LexRpcUpstreamError<
|
|
118
|
+
N extends 'InvalidResponse' | 'UpstreamFailure' =
|
|
119
|
+
| 'InvalidResponse'
|
|
120
|
+
| 'UpstreamFailure',
|
|
121
|
+
>
|
|
122
|
+
extends LexRpcError<N>
|
|
123
|
+
implements LexRpcFailureResult<N, LexRpcUpstreamError<N>>
|
|
124
|
+
{
|
|
125
|
+
name = 'LexRpcUpstreamError' as const
|
|
126
|
+
|
|
127
|
+
// For debugging purposes, we keep the response details here
|
|
128
|
+
readonly response: {
|
|
129
|
+
status: number
|
|
130
|
+
headers: Headers
|
|
131
|
+
payload: Payload | null
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
constructor(
|
|
135
|
+
error: N,
|
|
136
|
+
message: string,
|
|
137
|
+
response: { status: number; headers: Headers },
|
|
138
|
+
payload: Payload | null,
|
|
139
|
+
options?: ErrorOptions,
|
|
140
|
+
) {
|
|
141
|
+
super(error, message, { cause: options?.cause })
|
|
142
|
+
this.response = {
|
|
143
|
+
status: response.status,
|
|
144
|
+
headers: response.headers,
|
|
145
|
+
payload,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
readonly success = false as const
|
|
150
|
+
|
|
151
|
+
get reason(): this {
|
|
152
|
+
return this
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
matchesSchema(): false {
|
|
156
|
+
return false
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
shouldRetry(): boolean {
|
|
160
|
+
// Do not retry client errors
|
|
161
|
+
return this.response.status >= 500
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
toResponse(): Response {
|
|
165
|
+
return Response.json(this.toJSON(), { status: 502 })
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export class LexRpcUnexpectedError
|
|
170
|
+
extends LexRpcError<'InternalServerError'>
|
|
171
|
+
implements LexRpcFailureResult<'InternalServerError', unknown>
|
|
172
|
+
{
|
|
173
|
+
name = 'LexRpcUnexpectedError' as const
|
|
174
|
+
|
|
175
|
+
protected constructor(message: string, options: Required<ErrorOptions>) {
|
|
176
|
+
super('InternalServerError', message, options)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
readonly success = false
|
|
180
|
+
|
|
181
|
+
get reason() {
|
|
182
|
+
return this.cause
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
matchesSchema(): false {
|
|
186
|
+
return false
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
shouldRetry(): boolean {
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
toResponse(): Response {
|
|
194
|
+
return Response.json(this.toJSON(), { status: 500 })
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static from(
|
|
198
|
+
cause: unknown,
|
|
199
|
+
message: string = cause instanceof LexError
|
|
200
|
+
? cause.message
|
|
201
|
+
: 'XRPC request failed',
|
|
202
|
+
): LexRpcUnexpectedError {
|
|
203
|
+
if (cause instanceof LexRpcUnexpectedError) return cause
|
|
204
|
+
return new LexRpcUnexpectedError(message, { cause })
|
|
205
|
+
}
|
|
206
|
+
}
|