@atproto/lex-client 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/agent.d.ts +5 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +15 -1
- package/dist/agent.js.map +1 -1
- package/dist/client.d.ts +4 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +48 -47
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +83 -64
- package/dist/errors.js.map +1 -1
- package/dist/response.d.ts +3 -2
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +26 -23
- package/dist/response.js.map +1 -1
- package/dist/www-authenticate.d.ts +12 -0
- package/dist/www-authenticate.d.ts.map +1 -0
- package/dist/www-authenticate.js +57 -0
- package/dist/www-authenticate.js.map +1 -0
- package/dist/xrpc.d.ts +2 -10
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +15 -28
- package/dist/xrpc.js.map +1 -1
- package/package.json +6 -6
- package/src/agent.ts +34 -1
- package/src/client.ts +8 -8
- package/src/errors.ts +153 -118
- package/src/response.ts +46 -44
- package/src/www-authenticate.test.ts +227 -0
- package/src/www-authenticate.ts +77 -0
- package/src/xrpc.ts +17 -49
package/dist/xrpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xrpc.js","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexValue, isLexScalar, isPlainObject } from '@atproto/lex-data'\nimport { lexStringify } from '@atproto/lex-json'\nimport {\n InferInput,\n InferPayload,\n Main,\n Params,\n Payload,\n Procedure,\n Query,\n Restricted,\n Subscription,\n getMain,\n} from '@atproto/lex-schema'\nimport { Agent } from './agent.js'\nimport {\n XrpcResponseError,\n XrpcUnexpectedError,\n XrpcUpstreamError,\n} from './errors.js'\nimport { XrpcResponse } from './response.js'\nimport { BinaryBodyInit, CallOptions } from './types.js'\nimport {\n XrpcPayload,\n buildAtprotoHeaders,\n isAsyncIterable,\n isBlobLike,\n toReadableStream,\n} from './util.js'\n\n// If all params are optional, allow omitting the params object\ntype XrpcParamsOptions<P extends Params> =\n NonNullable<unknown> extends P ? { params?: P } : { params: P }\n\nexport type XrpcRequestParams<M extends Procedure | Query | Subscription> =\n InferInput<M['parameters']>\n\ntype XrpcRequestPayload<M extends Procedure | Query> = M extends Procedure\n ? InferPayload<M['input'], BinaryBodyInit>\n : undefined\n\ntype XrpcInputOptions<In> = In extends { body: infer B; encoding: infer E }\n ? // encoding will be inferred from the schema at runtime if not provided\n { body: B; encoding?: E }\n : { body?: undefined; encoding?: undefined }\n\nexport type XrpcOptions<M extends Procedure | Query = Procedure | Query> =\n CallOptions &\n XrpcInputOptions<XrpcRequestPayload<M>> &\n XrpcParamsOptions<XrpcRequestParams<M>>\n\nexport type XrpcFailure<M extends Procedure | Query> =\n // The server returned a valid XRPC error response\n | XrpcResponseError<M>\n // The response was not a valid XRPC response, or it does not match the schema\n | XrpcUpstreamError\n // Something went wrong (network error, etc.)\n | XrpcUnexpectedError\n\nexport type XrpcResult<M extends Procedure | Query> =\n | XrpcResponse<M>\n | XrpcFailure<M>\n\n/**\n * Utility method to type cast the error thrown by {@link xrpc} to an\n * {@link XrpcFailure} matching the provided method. Only use this function\n * inside a catch block right after calling {@link xrpc}, and use the same\n * method type parameter as used in the {@link xrpc} call.\n */\nexport function asXrpcFailure<M extends Procedure | Query = Procedure | Query>(\n err: unknown,\n): XrpcFailure<M> {\n if (err instanceof XrpcResponseError) return err\n if (err instanceof XrpcUpstreamError) return err\n return XrpcUnexpectedError.from(err)\n}\n\n/**\n * @throws XrpcFailure<M>\n */\nexport async function xrpc<const M extends Query | Procedure>(\n agent: Agent,\n ns: NonNullable<unknown> extends XrpcOptions<M>\n ? Main<M>\n : Restricted<'This XRPC method requires an \"options\" argument'>,\n): Promise<XrpcResponse<M>>\nexport async function xrpc<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M>,\n): Promise<XrpcResponse<M>>\nexport async function xrpc<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResponse<M>> {\n try {\n return await lexRpcRequest<M>(agent, ns, options)\n } catch (err) {\n throw asXrpcFailure<M>(err)\n }\n}\n\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agent: Agent,\n ns: NonNullable<unknown> extends XrpcOptions<M>\n ? Main<M>\n : Restricted<'This XRPC method requires an \"options\" argument'>,\n): Promise<XrpcResult<M>>\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M>,\n): Promise<XrpcResult<M>>\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResult<M>> {\n return lexRpcRequest<M>(agent, ns, options).catch(asXrpcFailure<M>)\n}\n\nasync function lexRpcRequest<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResponse<M>> {\n const method = getMain(ns)\n options.signal?.throwIfAborted()\n const url = xrpcRequestUrl(method, options)\n const request = xrpcRequestInit(method, options)\n const response = await agent.fetchHandler(url, request)\n return XrpcResponse.fromFetchResponse<M>(method, response, options)\n}\n\nfunction xrpcRequestUrl<M extends Procedure | Query | Subscription>(\n method: M,\n options: CallOptions & { params?: Params },\n) {\n const path = `/xrpc/${method.nsid}`\n\n const queryString = method.parameters\n ?.toURLSearchParams(options.params ?? {})\n .toString()\n\n return queryString ? `${path}?${queryString}` : path\n}\n\nfunction xrpcRequestInit<T extends Procedure | Query>(\n schema: T,\n options: CallOptions & {\n body?: LexValue | BinaryBodyInit\n encoding?: string\n },\n): RequestInit & { duplex?: 'half' } {\n const headers = buildAtprotoHeaders(options)\n\n // Tell the server what type of response we're expecting\n if (schema.output.encoding) {\n headers.set('accept', schema.output.encoding)\n }\n\n // Caller should not set content-type header\n if (headers.has('content-type')) {\n const contentType = headers.get('content-type')\n throw new TypeError(`Unexpected content-type header (${contentType})`)\n }\n\n // Requests with body\n if ('input' in schema) {\n const encodingHint = options.encoding\n const input = xrpcProcedureInput(schema, options, encodingHint)\n\n if (input) {\n headers.set('content-type', input.encoding)\n } else if (encodingHint != null) {\n throw new TypeError(`Unexpected encoding hint (${encodingHint})`)\n }\n\n return {\n duplex: 'half',\n redirect: 'follow',\n referrerPolicy: 'strict-origin-when-cross-origin', // (default)\n mode: 'cors', // (default)\n signal: options.signal,\n method: 'POST',\n headers,\n body: input?.body,\n }\n }\n\n // Requests without body\n return {\n duplex: 'half',\n redirect: 'follow',\n referrerPolicy: 'strict-origin-when-cross-origin', // (default)\n mode: 'cors', // (default)\n signal: options.signal,\n method: 'GET',\n headers,\n }\n}\n\nfunction xrpcProcedureInput(\n method: Procedure,\n options: CallOptions & { body?: LexValue | BinaryBodyInit },\n encodingHint?: string,\n): null | XrpcPayload<BodyInit> {\n const { input } = method\n const { body } = options\n\n if (options.validateRequest) {\n input.schema?.check(body)\n }\n\n // Special handling for endpoints expecting application/json input\n if (input.encoding === 'application/json') {\n // @NOTE **NOT** using isLexValue here to avoid deep checks in order to\n // distinguish between LexValue and BinaryBodyInit.\n if (!isLexScalar(body) && !isPlainObject(body) && !Array.isArray(body)) {\n throw new TypeError(`Expected LexValue body, got ${typeof body}`)\n }\n\n return buildPayload(input, lexStringify(body), encodingHint)\n }\n\n // Other encodings will be sent unaltered (ie. as binary data)\n switch (typeof body) {\n case 'undefined':\n case 'string':\n return buildPayload(input, body, encodingHint)\n case 'object': {\n if (body === null) break\n if (\n ArrayBuffer.isView(body) ||\n body instanceof ArrayBuffer ||\n body instanceof ReadableStream\n ) {\n return buildPayload(input, body, encodingHint)\n } else if (isAsyncIterable(body)) {\n return buildPayload(input, toReadableStream(body), encodingHint)\n } else if (isBlobLike(body)) {\n return buildPayload(input, body, encodingHint || body.type)\n }\n }\n }\n\n throw new TypeError(\n `Invalid ${typeof body} body for ${input.encoding} encoding`,\n )\n}\n\nfunction buildPayload(\n schema: Payload,\n body: undefined | BodyInit,\n encodingHint?: string,\n): null | XrpcPayload<BodyInit> {\n if (schema.encoding === undefined) {\n if (body !== undefined) {\n throw new TypeError(\n `Cannot send a ${typeof body} body with undefined encoding`,\n )\n }\n\n return null\n }\n\n if (body === undefined) {\n // This error would be returned by the server, but we can catch it earlier\n // to avoid un-necessary requests. Note that a content-length of 0 does not\n // necessary mean that the body is \"empty\" (e.g. an empty txt file).\n throw new TypeError(`A request body is expected but none was provided`)\n }\n\n const encoding = buildEncoding(schema, encodingHint)\n return { encoding, body }\n}\n\nfunction buildEncoding(schema: Payload, encodingHint?: string): string {\n // Should never happen (required for type safety)\n if (!schema.encoding) {\n throw new TypeError('Unexpected payload')\n }\n\n if (encodingHint?.length) {\n if (!schema.matchesEncoding(encodingHint)) {\n throw new TypeError(\n `Cannot send a body with content-type \"${encodingHint}\" for \"${schema.encoding}\" encoding`,\n )\n }\n return encodingHint\n }\n\n // Fallback\n\n if (schema.encoding === '*/*') {\n return 'application/octet-stream'\n }\n\n if (schema.encoding.startsWith('text/')) {\n return schema.encoding.includes('*')\n ? 'text/plain; charset=utf-8'\n : `${schema.encoding}; charset=utf-8`\n }\n\n if (!schema.encoding.includes('*')) {\n return schema.encoding\n }\n\n throw new TypeError(\n `Unable to determine payload encoding. Please provide a 'content-type' header matching ${schema.encoding}.`,\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"xrpc.js","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexValue, isLexScalar, isPlainObject } from '@atproto/lex-data'\nimport { lexStringify } from '@atproto/lex-json'\nimport {\n InferInput,\n InferPayload,\n Main,\n Params,\n Payload,\n Procedure,\n Query,\n Restricted,\n Subscription,\n getMain,\n} from '@atproto/lex-schema'\nimport { Agent } from './agent.js'\nimport { XrpcFailure, asXrpcFailure } from './errors.js'\nimport { XrpcResponse } from './response.js'\nimport { BinaryBodyInit, CallOptions } from './types.js'\nimport {\n XrpcPayload,\n buildAtprotoHeaders,\n isAsyncIterable,\n isBlobLike,\n toReadableStream,\n} from './util.js'\n\n// If all params are optional, allow omitting the params object\ntype XrpcParamsOptions<P extends Params> =\n NonNullable<unknown> extends P ? { params?: P } : { params: P }\n\nexport type XrpcRequestParams<M extends Procedure | Query | Subscription> =\n InferInput<M['parameters']>\n\ntype XrpcRequestPayload<M extends Procedure | Query> = M extends Procedure\n ? InferPayload<M['input'], BinaryBodyInit>\n : undefined\n\ntype XrpcInputOptions<In> = In extends { body: infer B; encoding: infer E }\n ? // encoding will be inferred from the schema at runtime if not provided\n { body: B; encoding?: E }\n : { body?: undefined; encoding?: undefined }\n\nexport type XrpcOptions<M extends Procedure | Query = Procedure | Query> =\n CallOptions &\n XrpcInputOptions<XrpcRequestPayload<M>> &\n XrpcParamsOptions<XrpcRequestParams<M>>\n\n/**\n * @throws XrpcFailure<M>\n */\nexport async function xrpc<const M extends Query | Procedure>(\n agent: Agent,\n ns: NonNullable<unknown> extends XrpcOptions<M>\n ? Main<M>\n : Restricted<'This XRPC method requires an \"options\" argument'>,\n): Promise<XrpcResponse<M>>\nexport async function xrpc<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M>,\n): Promise<XrpcResponse<M>>\nexport async function xrpc<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResponse<M>> {\n const response = await xrpcSafe<M>(agent, ns, options)\n if (response.success) return response\n else throw response\n}\n\nexport type XrpcResult<M extends Procedure | Query> =\n | XrpcResponse<M>\n | XrpcFailure<M>\n\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agent: Agent,\n ns: NonNullable<unknown> extends XrpcOptions<M>\n ? Main<M>\n : Restricted<'This XRPC method requires an \"options\" argument'>,\n): Promise<XrpcResult<M>>\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M>,\n): Promise<XrpcResult<M>>\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agent: Agent,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResult<M>> {\n options.signal?.throwIfAborted()\n const method: M = getMain(ns)\n try {\n const url = xrpcRequestUrl(method, options)\n const request = xrpcRequestInit(method, options)\n const response = await agent.fetchHandler(url, request)\n return await XrpcResponse.fromFetchResponse<M>(method, response, options)\n } catch (cause) {\n return asXrpcFailure(method, cause)\n }\n}\n\nfunction xrpcRequestUrl<M extends Procedure | Query | Subscription>(\n method: M,\n options: CallOptions & { params?: Params },\n) {\n const path = `/xrpc/${method.nsid}`\n\n const queryString = method.parameters\n ?.toURLSearchParams(options.params ?? {})\n .toString()\n\n return queryString ? `${path}?${queryString}` : path\n}\n\nfunction xrpcRequestInit<T extends Procedure | Query>(\n schema: T,\n options: CallOptions & {\n body?: LexValue | BinaryBodyInit\n encoding?: string\n },\n): RequestInit & { duplex?: 'half' } {\n const headers = buildAtprotoHeaders(options)\n\n // Tell the server what type of response we're expecting\n if (schema.output.encoding) {\n headers.set('accept', schema.output.encoding)\n }\n\n // Caller should not set content-type header\n if (headers.has('content-type')) {\n const contentType = headers.get('content-type')\n throw new TypeError(`Unexpected content-type header (${contentType})`)\n }\n\n // Requests with body\n if ('input' in schema) {\n const encodingHint = options.encoding\n const input = xrpcProcedureInput(schema, options, encodingHint)\n\n if (input) {\n headers.set('content-type', input.encoding)\n } else if (encodingHint != null) {\n throw new TypeError(`Unexpected encoding hint (${encodingHint})`)\n }\n\n return {\n duplex: 'half',\n redirect: 'follow',\n referrerPolicy: 'strict-origin-when-cross-origin', // (default)\n mode: 'cors', // (default)\n signal: options.signal,\n method: 'POST',\n headers,\n body: input?.body,\n }\n }\n\n // Requests without body\n return {\n duplex: 'half',\n redirect: 'follow',\n referrerPolicy: 'strict-origin-when-cross-origin', // (default)\n mode: 'cors', // (default)\n signal: options.signal,\n method: 'GET',\n headers,\n }\n}\n\nfunction xrpcProcedureInput(\n method: Procedure,\n options: CallOptions & { body?: LexValue | BinaryBodyInit },\n encodingHint?: string,\n): null | XrpcPayload<BodyInit> {\n const { input } = method\n const { body } = options\n\n if (options.validateRequest) {\n input.schema?.check(body)\n }\n\n // Special handling for endpoints expecting application/json input\n if (input.encoding === 'application/json') {\n // @NOTE **NOT** using isLexValue here to avoid deep checks in order to\n // distinguish between LexValue and BinaryBodyInit.\n if (!isLexScalar(body) && !isPlainObject(body) && !Array.isArray(body)) {\n throw new TypeError(`Expected LexValue body, got ${typeof body}`)\n }\n\n return buildPayload(input, lexStringify(body), encodingHint)\n }\n\n // Other encodings will be sent unaltered (ie. as binary data)\n switch (typeof body) {\n case 'undefined':\n case 'string':\n return buildPayload(input, body, encodingHint)\n case 'object': {\n if (body === null) break\n if (\n ArrayBuffer.isView(body) ||\n body instanceof ArrayBuffer ||\n body instanceof ReadableStream\n ) {\n return buildPayload(input, body, encodingHint)\n } else if (isAsyncIterable(body)) {\n return buildPayload(input, toReadableStream(body), encodingHint)\n } else if (isBlobLike(body)) {\n return buildPayload(input, body, encodingHint || body.type)\n }\n }\n }\n\n throw new TypeError(\n `Invalid ${typeof body} body for ${input.encoding} encoding`,\n )\n}\n\nfunction buildPayload(\n schema: Payload,\n body: undefined | BodyInit,\n encodingHint?: string,\n): null | XrpcPayload<BodyInit> {\n if (schema.encoding === undefined) {\n if (body !== undefined) {\n throw new TypeError(\n `Cannot send a ${typeof body} body with undefined encoding`,\n )\n }\n\n return null\n }\n\n if (body === undefined) {\n // This error would be returned by the server, but we can catch it earlier\n // to avoid un-necessary requests. Note that a content-length of 0 does not\n // necessary mean that the body is \"empty\" (e.g. an empty txt file).\n throw new TypeError(`A request body is expected but none was provided`)\n }\n\n const encoding = buildEncoding(schema, encodingHint)\n return { encoding, body }\n}\n\nfunction buildEncoding(schema: Payload, encodingHint?: string): string {\n // Should never happen (required for type safety)\n if (!schema.encoding) {\n throw new TypeError('Unexpected payload')\n }\n\n if (encodingHint?.length) {\n if (!schema.matchesEncoding(encodingHint)) {\n throw new TypeError(\n `Cannot send a body with content-type \"${encodingHint}\" for \"${schema.encoding}\" encoding`,\n )\n }\n return encodingHint\n }\n\n // Fallback\n\n if (schema.encoding === '*/*') {\n return 'application/octet-stream'\n }\n\n if (schema.encoding.startsWith('text/')) {\n return schema.encoding.includes('*')\n ? 'text/plain; charset=utf-8'\n : `${schema.encoding}; charset=utf-8`\n }\n\n if (!schema.encoding.includes('*')) {\n return schema.encoding\n }\n\n throw new TypeError(\n `Unable to determine payload encoding. Please provide a 'content-type' header matching ${schema.encoding}.`,\n )\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "HTTP client for interacting with Lexicon based APIs",
|
|
6
6
|
"keywords": [
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"tslib": "^2.8.1",
|
|
40
|
-
"@atproto/lex-data": "0.0.
|
|
41
|
-
"@atproto/lex-json": "0.0.
|
|
42
|
-
"@atproto/lex-schema": "0.0.
|
|
40
|
+
"@atproto/lex-data": "^0.0.10",
|
|
41
|
+
"@atproto/lex-json": "^0.0.10",
|
|
42
|
+
"@atproto/lex-schema": "^0.0.11"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"vitest": "^4.0.16",
|
|
46
|
-
"@atproto/lex-cbor": "0.0.
|
|
47
|
-
"@atproto/lex-builder": "0.0.
|
|
46
|
+
"@atproto/lex-cbor": "^0.0.10",
|
|
47
|
+
"@atproto/lex-builder": "^0.0.13"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"prebuild": "node ./scripts/lex-build.mjs",
|
package/src/agent.ts
CHANGED
|
@@ -28,6 +28,12 @@ export type AgentConfig = {
|
|
|
28
28
|
*/
|
|
29
29
|
service: string | URL
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Optional headers to include with every request made by this agent, unless
|
|
33
|
+
* overridden by the request-specific headers provided to the fetch handler.
|
|
34
|
+
*/
|
|
35
|
+
headers?: HeadersInit
|
|
36
|
+
|
|
31
37
|
/**
|
|
32
38
|
* Bring your own fetch implementation. Typically useful for testing, logging,
|
|
33
39
|
* mocking, or adding retries, session management, signatures, proof of
|
|
@@ -61,7 +67,34 @@ export function buildAgent(options: Agent | AgentOptions): Agent {
|
|
|
61
67
|
},
|
|
62
68
|
|
|
63
69
|
async fetchHandler(path, init) {
|
|
64
|
-
|
|
70
|
+
const headers =
|
|
71
|
+
config.headers != null && init.headers != null
|
|
72
|
+
? mergeHeaders(config.headers, init.headers)
|
|
73
|
+
: config.headers || init.headers
|
|
74
|
+
|
|
75
|
+
return fetch(
|
|
76
|
+
new URL(path, service),
|
|
77
|
+
headers !== init.headers ? { ...init, headers } : init,
|
|
78
|
+
)
|
|
65
79
|
},
|
|
66
80
|
}
|
|
67
81
|
}
|
|
82
|
+
|
|
83
|
+
function mergeHeaders(
|
|
84
|
+
defaultHeaders: HeadersInit,
|
|
85
|
+
requestHeaders: HeadersInit,
|
|
86
|
+
): Headers {
|
|
87
|
+
// We don't want to alter the original Headers objects, so we create a new one
|
|
88
|
+
const result = new Headers(defaultHeaders)
|
|
89
|
+
|
|
90
|
+
const overrides =
|
|
91
|
+
requestHeaders instanceof Headers
|
|
92
|
+
? requestHeaders
|
|
93
|
+
: new Headers(requestHeaders)
|
|
94
|
+
|
|
95
|
+
for (const [key, value] of overrides.entries()) {
|
|
96
|
+
result.set(key, value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return result
|
|
100
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -19,17 +19,12 @@ import {
|
|
|
19
19
|
getMain,
|
|
20
20
|
} from '@atproto/lex-schema'
|
|
21
21
|
import { Agent, AgentOptions, buildAgent } from './agent.js'
|
|
22
|
+
import { XrpcFailure } from './errors.js'
|
|
22
23
|
import { com } from './lexicons/index.js'
|
|
23
24
|
import { XrpcResponse, XrpcResponseBody } from './response.js'
|
|
24
25
|
import { BinaryBodyInit, CallOptions, Service } from './types.js'
|
|
25
26
|
import { buildAtprotoHeaders } from './util.js'
|
|
26
|
-
import {
|
|
27
|
-
XrpcFailure,
|
|
28
|
-
XrpcOptions,
|
|
29
|
-
XrpcRequestParams,
|
|
30
|
-
xrpc,
|
|
31
|
-
xrpcSafe,
|
|
32
|
-
} from './xrpc.js'
|
|
27
|
+
import { XrpcOptions, XrpcRequestParams, xrpc, xrpcSafe } from './xrpc.js'
|
|
33
28
|
|
|
34
29
|
export type {
|
|
35
30
|
AtIdentifierString,
|
|
@@ -103,7 +98,7 @@ export type RecordKeyOptions<
|
|
|
103
98
|
: { rkey: InferRecordKey<T> }
|
|
104
99
|
|
|
105
100
|
export type CreateOptions<T extends RecordSchema> = CreateRecordOptions &
|
|
106
|
-
RecordKeyOptions<T, 'tid'>
|
|
101
|
+
RecordKeyOptions<T, 'tid' | 'any'>
|
|
107
102
|
export type CreateOutput = InferMethodOutputBody<
|
|
108
103
|
typeof com.atproto.repo.createRecord.main,
|
|
109
104
|
Uint8Array
|
|
@@ -355,6 +350,11 @@ export class Client implements Agent {
|
|
|
355
350
|
? Main<T>
|
|
356
351
|
: Restricted<'This query type requires a "params" argument'>,
|
|
357
352
|
): Promise<XrpcResponseBody<T>>
|
|
353
|
+
public async call<const T extends Procedure>(
|
|
354
|
+
ns: undefined extends InferMethodInputBody<T, Uint8Array>
|
|
355
|
+
? Main<T>
|
|
356
|
+
: Restricted<'This procedure type requires an "input" argument'>,
|
|
357
|
+
): Promise<XrpcResponseBody<T>>
|
|
358
358
|
public async call<const T extends Action>(
|
|
359
359
|
ns: void extends InferActionInput<T>
|
|
360
360
|
? Main<T>
|
package/src/errors.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { LexError, LexErrorCode, LexErrorData } from '@atproto/lex-data'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
InferMethodError,
|
|
4
|
+
Procedure,
|
|
5
|
+
Query,
|
|
6
|
+
ResultFailure,
|
|
7
|
+
lexErrorDataSchema,
|
|
8
|
+
} from '@atproto/lex-schema'
|
|
3
9
|
import { XrpcPayload } from './util.js'
|
|
10
|
+
import {
|
|
11
|
+
WWWAuthenticate,
|
|
12
|
+
parseWWWAuthenticateHeader,
|
|
13
|
+
} from './www-authenticate.js'
|
|
14
|
+
|
|
15
|
+
export const RETRYABLE_HTTP_STATUS_CODES: ReadonlySet<number> = new Set([
|
|
16
|
+
408, 425, 429, 500, 502, 503, 504, 522, 524,
|
|
17
|
+
])
|
|
4
18
|
|
|
5
19
|
export { LexError }
|
|
6
20
|
export type { LexErrorCode, LexErrorData }
|
|
@@ -8,20 +22,6 @@ export type { LexErrorCode, LexErrorData }
|
|
|
8
22
|
export type XrpcErrorPayload<N extends LexErrorCode = LexErrorCode> =
|
|
9
23
|
XrpcPayload<LexErrorData<N>, 'application/json'>
|
|
10
24
|
|
|
11
|
-
export class XrpcError<
|
|
12
|
-
N extends LexErrorCode = LexErrorCode,
|
|
13
|
-
> extends LexError<N> {
|
|
14
|
-
name = 'XrpcError'
|
|
15
|
-
|
|
16
|
-
constructor(
|
|
17
|
-
error: N,
|
|
18
|
-
message: string = `${error} Lexicon RPC error`,
|
|
19
|
-
options?: ErrorOptions,
|
|
20
|
-
) {
|
|
21
|
-
super(error, message, options)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
25
|
/**
|
|
26
26
|
* All unsuccessful responses should follow a standard error response
|
|
27
27
|
* schema. The Content-Type should be application/json, and the payload
|
|
@@ -40,168 +40,203 @@ export function isXrpcErrorPayload(
|
|
|
40
40
|
return (
|
|
41
41
|
payload !== null &&
|
|
42
42
|
payload.encoding === 'application/json' &&
|
|
43
|
-
|
|
43
|
+
lexErrorDataSchema.matches(payload.body)
|
|
44
44
|
)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
export abstract class XrpcError<
|
|
48
|
+
M extends Procedure | Query = Procedure | Query,
|
|
49
|
+
N extends LexErrorCode = LexErrorCode,
|
|
50
|
+
TReason = unknown,
|
|
51
|
+
>
|
|
52
|
+
extends LexError<N>
|
|
53
|
+
implements ResultFailure<TReason>
|
|
54
|
+
{
|
|
55
|
+
name = 'XrpcError'
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
readonly method: M,
|
|
59
|
+
error: N,
|
|
60
|
+
message: string = `${error} Lexicon RPC error`,
|
|
61
|
+
options?: ErrorOptions,
|
|
62
|
+
) {
|
|
63
|
+
super(error, message, options)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @see {@link ResultFailure.success}
|
|
68
|
+
*/
|
|
69
|
+
readonly success = false as const
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @see {@link ResultFailure.reason}
|
|
73
|
+
*/
|
|
74
|
+
abstract readonly reason: TReason
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Indicates whether the error is transient and can be retried.
|
|
78
|
+
*/
|
|
79
|
+
abstract shouldRetry(): boolean
|
|
80
|
+
|
|
81
|
+
matchesSchema(): this is XrpcError<M, InferMethodError<M>> {
|
|
82
|
+
return this.method.errors?.includes(this.error) ?? false
|
|
83
|
+
}
|
|
54
84
|
}
|
|
55
85
|
|
|
56
86
|
/**
|
|
57
|
-
* Class used to represent an HTTP request that resulted in an XRPC method
|
|
58
|
-
* That is, a non-2xx response with a valid XRPC error payload.
|
|
87
|
+
* Class used to represent an HTTP request that resulted in an XRPC method
|
|
88
|
+
* error. That is, a non-2xx response with a valid XRPC error payload.
|
|
59
89
|
*/
|
|
60
90
|
export class XrpcResponseError<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
extends XrpcError<N>
|
|
65
|
-
implements LexRpcFailureResult<N, XrpcResponseError<M, N>>
|
|
66
|
-
{
|
|
91
|
+
M extends Procedure | Query = Procedure | Query,
|
|
92
|
+
N extends LexErrorCode = InferMethodError<M> | LexErrorCode,
|
|
93
|
+
> extends XrpcError<M, N, XrpcResponseError<M, N>> {
|
|
67
94
|
name = 'XrpcResponseError'
|
|
68
95
|
|
|
69
96
|
constructor(
|
|
70
|
-
|
|
71
|
-
readonly
|
|
72
|
-
readonly headers: Headers,
|
|
97
|
+
method: M,
|
|
98
|
+
readonly response: Response,
|
|
73
99
|
readonly payload: XrpcErrorPayload<N>,
|
|
74
100
|
options?: ErrorOptions,
|
|
75
101
|
) {
|
|
76
102
|
const { error, message } = payload.body
|
|
77
|
-
super(error, message, options)
|
|
103
|
+
super(method, error, message, options)
|
|
78
104
|
}
|
|
79
105
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
get reason(): this {
|
|
83
|
-
return this as this
|
|
106
|
+
override get reason(): this {
|
|
107
|
+
return this
|
|
84
108
|
}
|
|
85
109
|
|
|
86
|
-
|
|
87
|
-
return this.
|
|
110
|
+
override shouldRetry(): boolean {
|
|
111
|
+
return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status)
|
|
88
112
|
}
|
|
89
113
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
? XrpcResponseError<M, E>
|
|
94
|
-
: never {
|
|
95
|
-
return this.method.errors?.includes(this.error) ?? false
|
|
114
|
+
override toJSON() {
|
|
115
|
+
return this.payload.body
|
|
96
116
|
}
|
|
97
117
|
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
if (this.
|
|
118
|
+
override toResponse(): Response {
|
|
119
|
+
// Re-expose schema-valid errors as-is to downstream clients
|
|
120
|
+
if (this.matchesSchema()) {
|
|
121
|
+
const status = this.response.status >= 500 ? 502 : this.response.status
|
|
122
|
+
return Response.json(this.toJSON(), { status })
|
|
123
|
+
}
|
|
101
124
|
|
|
102
|
-
return
|
|
125
|
+
return this.response.status >= 500
|
|
126
|
+
? // The upstream server had an error, return a generic upstream failure
|
|
127
|
+
Response.json({ error: 'UpstreamFailure' }, { status: 502 })
|
|
128
|
+
: // If the error is on our side, return a generic internal server error
|
|
129
|
+
Response.json({ error: 'InternalServerError' }, { status: 500 })
|
|
103
130
|
}
|
|
104
131
|
|
|
105
|
-
|
|
132
|
+
get body(): LexErrorData {
|
|
106
133
|
return this.payload.body
|
|
107
134
|
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type { WWWAuthenticate }
|
|
138
|
+
export class XrpcAuthenticationError<
|
|
139
|
+
M extends Procedure | Query = Procedure | Query,
|
|
140
|
+
N extends LexErrorCode = LexErrorCode,
|
|
141
|
+
> extends XrpcResponseError<M, N> {
|
|
142
|
+
name = 'XrpcAuthenticationError'
|
|
143
|
+
|
|
144
|
+
override shouldRetry(): boolean {
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
108
147
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return
|
|
148
|
+
#wwwAuthenticate?: WWWAuthenticate
|
|
149
|
+
get wwwAuthenticate(): WWWAuthenticate {
|
|
150
|
+
return (this.#wwwAuthenticate ??=
|
|
151
|
+
parseWWWAuthenticateHeader(
|
|
152
|
+
this.response.headers.get('www-authenticate'),
|
|
153
|
+
) ?? {})
|
|
112
154
|
}
|
|
113
155
|
}
|
|
114
156
|
|
|
115
157
|
/**
|
|
116
|
-
* This class represents
|
|
158
|
+
* This class represents invalid or unprocessable XRPC response from the
|
|
159
|
+
* upstream server.
|
|
117
160
|
*/
|
|
118
161
|
export class XrpcUpstreamError<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
>
|
|
123
|
-
extends XrpcError<N>
|
|
124
|
-
implements LexRpcFailureResult<N, XrpcUpstreamError<N>>
|
|
125
|
-
{
|
|
126
|
-
name = 'XrpcUpstreamError' as const
|
|
127
|
-
|
|
128
|
-
// For debugging purposes, we keep the response details here
|
|
129
|
-
readonly response: {
|
|
130
|
-
status: number
|
|
131
|
-
headers: Headers
|
|
132
|
-
payload: XrpcPayload | null
|
|
133
|
-
}
|
|
162
|
+
M extends Procedure | Query = Procedure | Query,
|
|
163
|
+
> extends XrpcError<M, 'UpstreamFailure', XrpcUpstreamError<M>> {
|
|
164
|
+
name = 'XrpcUpstreamError'
|
|
134
165
|
|
|
135
166
|
constructor(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
167
|
+
method: M,
|
|
168
|
+
readonly response: Response,
|
|
169
|
+
readonly payload: XrpcPayload | null,
|
|
170
|
+
message: string = `Unexpected upstream XRPC response`,
|
|
140
171
|
options?: ErrorOptions,
|
|
141
172
|
) {
|
|
142
|
-
super(
|
|
143
|
-
this.response = {
|
|
144
|
-
status: response.status,
|
|
145
|
-
headers: response.headers,
|
|
146
|
-
payload,
|
|
147
|
-
}
|
|
173
|
+
super(method, 'UpstreamFailure', message, options)
|
|
148
174
|
}
|
|
149
175
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
get reason(): this {
|
|
176
|
+
override get reason(): this {
|
|
153
177
|
return this
|
|
154
178
|
}
|
|
155
179
|
|
|
156
|
-
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
shouldRetry(): boolean {
|
|
161
|
-
// Do not retry client errors
|
|
162
|
-
return this.response.status >= 500
|
|
180
|
+
override shouldRetry(): boolean {
|
|
181
|
+
return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status)
|
|
163
182
|
}
|
|
164
183
|
|
|
165
|
-
toResponse(): Response {
|
|
184
|
+
override toResponse(): Response {
|
|
166
185
|
return Response.json(this.toJSON(), { status: 502 })
|
|
167
186
|
}
|
|
168
187
|
}
|
|
169
188
|
|
|
170
|
-
export class
|
|
171
|
-
extends
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
name = 'XrpcUnexpectedError' as const
|
|
189
|
+
export class XrpcInternalError<
|
|
190
|
+
M extends Procedure | Query = Procedure | Query,
|
|
191
|
+
> extends XrpcError<M, 'InternalServerError', XrpcInternalError<M>> {
|
|
192
|
+
name = 'XrpcInternalError'
|
|
175
193
|
|
|
176
|
-
|
|
177
|
-
super(
|
|
194
|
+
constructor(method: M, message?: string, options?: ErrorOptions) {
|
|
195
|
+
super(
|
|
196
|
+
method,
|
|
197
|
+
'InternalServerError',
|
|
198
|
+
message ?? 'Unable to fulfill XRPC request',
|
|
199
|
+
options,
|
|
200
|
+
)
|
|
178
201
|
}
|
|
179
202
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
get reason() {
|
|
183
|
-
return this.cause
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
matchesSchema(): false {
|
|
187
|
-
return false
|
|
203
|
+
override get reason(): this {
|
|
204
|
+
return this
|
|
188
205
|
}
|
|
189
206
|
|
|
190
|
-
shouldRetry():
|
|
207
|
+
override shouldRetry(): true {
|
|
208
|
+
// Ideally, we would inspect the reason to determine if it's retryable
|
|
209
|
+
// (by detecting network errors, timeouts, etc.). Since these cases are
|
|
210
|
+
// highly platform-dependent, we optimistically assume all internal
|
|
211
|
+
// errors are retryable.
|
|
191
212
|
return true
|
|
192
213
|
}
|
|
193
214
|
|
|
194
|
-
toResponse(): Response {
|
|
195
|
-
|
|
215
|
+
override toResponse(): Response {
|
|
216
|
+
// Do not expose internal error details to downstream clients
|
|
217
|
+
return Response.json({ error: this.error }, { status: 500 })
|
|
196
218
|
}
|
|
219
|
+
}
|
|
197
220
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
export type XrpcFailure<M extends Procedure | Query = Procedure | Query> =
|
|
222
|
+
// The server returned a valid XRPC error response
|
|
223
|
+
| XrpcResponseError<M>
|
|
224
|
+
// The response was not a valid XRPC response, or it does not match the schema
|
|
225
|
+
| XrpcUpstreamError<M>
|
|
226
|
+
// Something went wrong (network error, etc.)
|
|
227
|
+
| XrpcInternalError<M>
|
|
228
|
+
|
|
229
|
+
export function asXrpcFailure<M extends Procedure | Query>(
|
|
230
|
+
method: M,
|
|
231
|
+
cause: unknown,
|
|
232
|
+
): XrpcFailure<M> {
|
|
233
|
+
if (
|
|
234
|
+
cause instanceof XrpcResponseError ||
|
|
235
|
+
cause instanceof XrpcUpstreamError ||
|
|
236
|
+
cause instanceof XrpcInternalError
|
|
237
|
+
) {
|
|
238
|
+
if (cause.method === method) return cause
|
|
206
239
|
}
|
|
240
|
+
|
|
241
|
+
return new XrpcInternalError(method, undefined, { cause })
|
|
207
242
|
}
|