@atproto/xrpc 0.6.0-rc.0 → 0.6.1
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 +567 -21
- package/dist/client.d.ts +1 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -13
- package/dist/client.js.map +1 -1
- package/dist/fetch-handler.d.ts +10 -1
- package/dist/fetch-handler.d.ts.map +1 -1
- package/dist/fetch-handler.js +6 -0
- package/dist/fetch-handler.js.map +1 -1
- package/dist/types.d.ts +4 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +19 -7
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +13 -78
- package/dist/util.js.map +1 -1
- package/dist/xrpc-client.d.ts +7 -3
- package/dist/xrpc-client.d.ts.map +1 -1
- package/dist/xrpc-client.js +22 -7
- package/dist/xrpc-client.js.map +1 -1
- package/package.json +2 -2
- package/src/client.ts +2 -11
- package/src/fetch-handler.ts +22 -1
- package/src/types.ts +29 -12
- package/src/util.ts +16 -85
- package/src/xrpc-client.ts +22 -7
package/src/util.ts
CHANGED
|
@@ -193,21 +193,21 @@ export function combineHeaders(
|
|
|
193
193
|
|
|
194
194
|
let headers: Headers | undefined = undefined
|
|
195
195
|
|
|
196
|
-
for (const [
|
|
196
|
+
for (const [name, definition] of defaultHeaders) {
|
|
197
197
|
// Ignore undefined values (allowed for convenience when using
|
|
198
198
|
// Object.entries).
|
|
199
|
-
if (
|
|
199
|
+
if (definition === undefined) continue
|
|
200
200
|
|
|
201
201
|
// Lazy initialization of the headers object
|
|
202
202
|
headers ??= new Headers(headersInit)
|
|
203
203
|
|
|
204
|
-
if (headers.has(
|
|
204
|
+
if (headers.has(name)) continue
|
|
205
205
|
|
|
206
|
-
const value = typeof
|
|
206
|
+
const value = typeof definition === 'function' ? definition() : definition
|
|
207
207
|
|
|
208
|
-
if (typeof value === 'string') headers.set(
|
|
209
|
-
else if (value === null) headers.delete(
|
|
210
|
-
else throw new TypeError(`Invalid "${
|
|
208
|
+
if (typeof value === 'string') headers.set(name, value)
|
|
209
|
+
else if (value === null) headers.delete(name)
|
|
210
|
+
else throw new TypeError(`Invalid "${name}" header value: ${typeof value}`)
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
return headers ?? headersInit
|
|
@@ -343,85 +343,16 @@ function iterableToReadableStream(
|
|
|
343
343
|
return ReadableStream.from(iterable)
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
// things simple, we'll just allow the anonymous ReadableStream constructor
|
|
350
|
-
// to throw an error in those environments, hinting the user of the lib to find
|
|
351
|
-
// an alternate solution in that case (e.g. use a Blob if available).
|
|
352
|
-
|
|
353
|
-
let generator: AsyncGenerator<unknown, void, undefined>
|
|
354
|
-
return new ReadableStream<Uint8Array>({
|
|
355
|
-
type: 'bytes',
|
|
356
|
-
start() {
|
|
357
|
-
// Wrap the iterable in an async generator to handle both sync and async
|
|
358
|
-
// iterables, and make sure that the return() method exists.
|
|
359
|
-
generator = (async function* () {
|
|
360
|
-
yield* iterable
|
|
361
|
-
})()
|
|
362
|
-
},
|
|
363
|
-
async pull(controller: ReadableStreamDefaultController) {
|
|
364
|
-
const { done, value } = await generator.next()
|
|
365
|
-
if (done) {
|
|
366
|
-
controller.close()
|
|
367
|
-
} else {
|
|
368
|
-
try {
|
|
369
|
-
const buf = toUint8Array(value)
|
|
370
|
-
if (buf) controller.enqueue(buf)
|
|
371
|
-
} catch (cause) {
|
|
372
|
-
// ReadableStream won't call cancel() if the stream is errored.
|
|
373
|
-
await generator.return()
|
|
374
|
-
|
|
375
|
-
controller.error(
|
|
376
|
-
new TypeError(
|
|
377
|
-
'Converting iterable body to ReadableStream requires Buffer, ArrayBuffer or string values',
|
|
378
|
-
{ cause },
|
|
379
|
-
),
|
|
380
|
-
)
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
async cancel() {
|
|
385
|
-
await generator.return()
|
|
386
|
-
},
|
|
387
|
-
})
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Browsers don't have Buffer. This syntax is to avoid bundlers from including
|
|
391
|
-
// a Buffer polyfill in the bundle if it's not used elsewhere.
|
|
392
|
-
const globalName = `${{ toString: () => 'Buf' }}fer` as 'Buffer'
|
|
393
|
-
const Buffer =
|
|
394
|
-
typeof globalThis[globalName] === 'function'
|
|
395
|
-
? globalThis[globalName]
|
|
396
|
-
: undefined
|
|
397
|
-
|
|
398
|
-
const toUint8Array: (value: unknown) => Uint8Array | undefined = Buffer
|
|
399
|
-
? (value) => {
|
|
400
|
-
// @ts-expect-error Buffer.from will throw if value is not a valid input
|
|
401
|
-
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)
|
|
402
|
-
return buf.byteLength ? new Uint8Array(buf) : undefined
|
|
403
|
-
}
|
|
404
|
-
: (value) => {
|
|
405
|
-
if (value instanceof ArrayBuffer) {
|
|
406
|
-
const buf = new Uint8Array(value)
|
|
407
|
-
return buf.byteLength ? buf : undefined
|
|
408
|
-
}
|
|
346
|
+
// If you see this error, consider using a polyfill for ReadableStream. For
|
|
347
|
+
// example, the "web-streams-polyfill" package:
|
|
348
|
+
// https://github.com/MattiasBuelens/web-streams-polyfill
|
|
409
349
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
const buf = new Uint8Array(coerced)
|
|
417
|
-
return buf.byteLength ? buf : undefined
|
|
418
|
-
} else if (typeof coerced === 'string') {
|
|
419
|
-
return coerced.length ? new TextEncoder().encode(coerced) : undefined
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
throw new TypeError(`Unable to convert "${typeof value}" to Uint8Array`)
|
|
424
|
-
}
|
|
350
|
+
throw new TypeError(
|
|
351
|
+
'ReadableStream.from() is not supported in this environment. ' +
|
|
352
|
+
'It is required to support using iterables as the request body. ' +
|
|
353
|
+
'Consider using a polyfill or re-write your code to use a different body type.',
|
|
354
|
+
)
|
|
355
|
+
}
|
|
425
356
|
|
|
426
357
|
export function httpResponseBodyParse(
|
|
427
358
|
mimeType: string | null,
|
package/src/xrpc-client.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { LexiconDoc, Lexicons, ValidationError } from '@atproto/lexicon'
|
|
2
2
|
import {
|
|
3
3
|
FetchHandler,
|
|
4
|
+
FetchHandlerObject,
|
|
4
5
|
FetchHandlerOptions,
|
|
5
6
|
buildFetchHandler,
|
|
6
7
|
} from './fetch-handler'
|
|
7
8
|
import {
|
|
8
9
|
CallOptions,
|
|
10
|
+
Gettable,
|
|
9
11
|
QueryParams,
|
|
10
12
|
ResponseType,
|
|
11
13
|
XRPCError,
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
httpResponseCodeToEnum,
|
|
15
17
|
} from './types'
|
|
16
18
|
import {
|
|
19
|
+
combineHeaders,
|
|
17
20
|
constructMethodCallHeaders,
|
|
18
21
|
constructMethodCallUrl,
|
|
19
22
|
encodeMethodCallBody,
|
|
@@ -24,20 +27,32 @@ import {
|
|
|
24
27
|
|
|
25
28
|
export class XrpcClient {
|
|
26
29
|
readonly fetchHandler: FetchHandler
|
|
30
|
+
readonly headers = new Map<string, Gettable<null | string>>()
|
|
27
31
|
readonly lex: Lexicons
|
|
28
32
|
|
|
29
33
|
constructor(
|
|
30
|
-
|
|
34
|
+
fetchHandlerOpts: FetchHandler | FetchHandlerObject | FetchHandlerOptions,
|
|
35
|
+
// "Lexicons" is redundant here (because that class implements
|
|
36
|
+
// "Iterable<LexiconDoc>") but we keep it for explicitness:
|
|
31
37
|
lex: Lexicons | Iterable<LexiconDoc>,
|
|
32
38
|
) {
|
|
33
|
-
this.fetchHandler =
|
|
34
|
-
typeof fetchHandler === 'function'
|
|
35
|
-
? fetchHandler
|
|
36
|
-
: buildFetchHandler(fetchHandler)
|
|
39
|
+
this.fetchHandler = buildFetchHandler(fetchHandlerOpts)
|
|
37
40
|
|
|
38
41
|
this.lex = lex instanceof Lexicons ? lex : new Lexicons(lex)
|
|
39
42
|
}
|
|
40
43
|
|
|
44
|
+
setHeader(key: string, value: Gettable<null | string>): void {
|
|
45
|
+
this.headers.set(key.toLowerCase(), value)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
unsetHeader(key: string): void {
|
|
49
|
+
this.headers.delete(key.toLowerCase())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
clearHeaders(): void {
|
|
53
|
+
this.headers.clear()
|
|
54
|
+
}
|
|
55
|
+
|
|
41
56
|
async call(
|
|
42
57
|
methodNsid: string,
|
|
43
58
|
params?: QueryParams,
|
|
@@ -51,7 +66,7 @@ export class XrpcClient {
|
|
|
51
66
|
)
|
|
52
67
|
}
|
|
53
68
|
|
|
54
|
-
|
|
69
|
+
// @TODO: should we validate the params and data here?
|
|
55
70
|
// this.lex.assertValidXrpcParams(methodNsid, params)
|
|
56
71
|
// if (data !== undefined) {
|
|
57
72
|
// this.lex.assertValidXrpcInput(methodNsid, data)
|
|
@@ -66,7 +81,7 @@ export class XrpcClient {
|
|
|
66
81
|
// anywhere in docs or types. See whatwg/fetch#1438, nodejs/node#46221.
|
|
67
82
|
const init: RequestInit & { duplex: 'half' } = {
|
|
68
83
|
method: reqMethod,
|
|
69
|
-
headers: reqHeaders,
|
|
84
|
+
headers: combineHeaders(reqHeaders, this.headers),
|
|
70
85
|
body: reqBody,
|
|
71
86
|
duplex: 'half',
|
|
72
87
|
signal: opts?.signal,
|