@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/errors.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.XrpcInternalError = exports.XrpcUpstreamError = exports.XrpcAuthenticationError = exports.XrpcResponseError = exports.XrpcError = exports.LexError = exports.RETRYABLE_HTTP_STATUS_CODES = void 0;
|
|
4
4
|
exports.isXrpcErrorPayload = isXrpcErrorPayload;
|
|
5
|
+
exports.asXrpcFailure = asXrpcFailure;
|
|
5
6
|
const lex_data_1 = require("@atproto/lex-data");
|
|
6
7
|
Object.defineProperty(exports, "LexError", { enumerable: true, get: function () { return lex_data_1.LexError; } });
|
|
7
8
|
const lex_schema_1 = require("@atproto/lex-schema");
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
exports.XrpcError = XrpcError;
|
|
9
|
+
const www_authenticate_js_1 = require("./www-authenticate.js");
|
|
10
|
+
exports.RETRYABLE_HTTP_STATUS_CODES = new Set([
|
|
11
|
+
408, 425, 429, 500, 502, 503, 504, 522, 524,
|
|
12
|
+
]);
|
|
15
13
|
/**
|
|
16
14
|
* All unsuccessful responses should follow a standard error response
|
|
17
15
|
* schema. The Content-Type should be application/json, and the payload
|
|
@@ -27,107 +25,128 @@ exports.XrpcError = XrpcError;
|
|
|
27
25
|
function isXrpcErrorPayload(payload) {
|
|
28
26
|
return (payload !== null &&
|
|
29
27
|
payload.encoding === 'application/json' &&
|
|
30
|
-
lex_schema_1.
|
|
28
|
+
lex_schema_1.lexErrorDataSchema.matches(payload.body));
|
|
29
|
+
}
|
|
30
|
+
class XrpcError extends lex_data_1.LexError {
|
|
31
|
+
method;
|
|
32
|
+
name = 'XrpcError';
|
|
33
|
+
constructor(method, error, message = `${error} Lexicon RPC error`, options) {
|
|
34
|
+
super(error, message, options);
|
|
35
|
+
this.method = method;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* @see {@link ResultFailure.success}
|
|
39
|
+
*/
|
|
40
|
+
success = false;
|
|
41
|
+
matchesSchema() {
|
|
42
|
+
return this.method.errors?.includes(this.error) ?? false;
|
|
43
|
+
}
|
|
31
44
|
}
|
|
45
|
+
exports.XrpcError = XrpcError;
|
|
32
46
|
/**
|
|
33
|
-
* Class used to represent an HTTP request that resulted in an XRPC method
|
|
34
|
-
* That is, a non-2xx response with a valid XRPC error payload.
|
|
47
|
+
* Class used to represent an HTTP request that resulted in an XRPC method
|
|
48
|
+
* error. That is, a non-2xx response with a valid XRPC error payload.
|
|
35
49
|
*/
|
|
36
50
|
class XrpcResponseError extends XrpcError {
|
|
37
|
-
|
|
38
|
-
status;
|
|
39
|
-
headers;
|
|
51
|
+
response;
|
|
40
52
|
payload;
|
|
41
53
|
name = 'XrpcResponseError';
|
|
42
|
-
constructor(method,
|
|
54
|
+
constructor(method, response, payload, options) {
|
|
43
55
|
const { error, message } = payload.body;
|
|
44
|
-
super(error, message, options);
|
|
45
|
-
this.
|
|
46
|
-
this.status = status;
|
|
47
|
-
this.headers = headers;
|
|
56
|
+
super(method, error, message, options);
|
|
57
|
+
this.response = response;
|
|
48
58
|
this.payload = payload;
|
|
49
59
|
}
|
|
50
|
-
success = false;
|
|
51
60
|
get reason() {
|
|
52
61
|
return this;
|
|
53
62
|
}
|
|
54
|
-
get body() {
|
|
55
|
-
return this.payload.body;
|
|
56
|
-
}
|
|
57
|
-
matchesSchema() {
|
|
58
|
-
return this.method.errors?.includes(this.error) ?? false;
|
|
59
|
-
}
|
|
60
63
|
shouldRetry() {
|
|
61
|
-
|
|
62
|
-
if (this.status < 500)
|
|
63
|
-
return false;
|
|
64
|
-
return true;
|
|
64
|
+
return exports.RETRYABLE_HTTP_STATUS_CODES.has(this.response.status);
|
|
65
65
|
}
|
|
66
66
|
toJSON() {
|
|
67
67
|
return this.payload.body;
|
|
68
68
|
}
|
|
69
69
|
toResponse() {
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
// Re-expose schema-valid errors as-is to downstream clients
|
|
71
|
+
if (this.matchesSchema()) {
|
|
72
|
+
const status = this.response.status >= 500 ? 502 : this.response.status;
|
|
73
|
+
return Response.json(this.toJSON(), { status });
|
|
74
|
+
}
|
|
75
|
+
return this.response.status >= 500
|
|
76
|
+
? // The upstream server had an error, return a generic upstream failure
|
|
77
|
+
Response.json({ error: 'UpstreamFailure' }, { status: 502 })
|
|
78
|
+
: // If the error is on our side, return a generic internal server error
|
|
79
|
+
Response.json({ error: 'InternalServerError' }, { status: 500 });
|
|
80
|
+
}
|
|
81
|
+
get body() {
|
|
82
|
+
return this.payload.body;
|
|
72
83
|
}
|
|
73
84
|
}
|
|
74
85
|
exports.XrpcResponseError = XrpcResponseError;
|
|
86
|
+
class XrpcAuthenticationError extends XrpcResponseError {
|
|
87
|
+
name = 'XrpcAuthenticationError';
|
|
88
|
+
shouldRetry() {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
#wwwAuthenticate;
|
|
92
|
+
get wwwAuthenticate() {
|
|
93
|
+
return (this.#wwwAuthenticate ??=
|
|
94
|
+
(0, www_authenticate_js_1.parseWWWAuthenticateHeader)(this.response.headers.get('www-authenticate')) ?? {});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.XrpcAuthenticationError = XrpcAuthenticationError;
|
|
75
98
|
/**
|
|
76
|
-
* This class represents
|
|
99
|
+
* This class represents invalid or unprocessable XRPC response from the
|
|
100
|
+
* upstream server.
|
|
77
101
|
*/
|
|
78
102
|
class XrpcUpstreamError extends XrpcError {
|
|
79
|
-
name = 'XrpcUpstreamError';
|
|
80
|
-
// For debugging purposes, we keep the response details here
|
|
81
103
|
response;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
};
|
|
104
|
+
payload;
|
|
105
|
+
name = 'XrpcUpstreamError';
|
|
106
|
+
constructor(method, response, payload, message = `Unexpected upstream XRPC response`, options) {
|
|
107
|
+
super(method, 'UpstreamFailure', message, options);
|
|
108
|
+
this.response = response;
|
|
109
|
+
this.payload = payload;
|
|
89
110
|
}
|
|
90
|
-
success = false;
|
|
91
111
|
get reason() {
|
|
92
112
|
return this;
|
|
93
113
|
}
|
|
94
|
-
matchesSchema() {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
114
|
shouldRetry() {
|
|
98
|
-
|
|
99
|
-
return this.response.status >= 500;
|
|
115
|
+
return exports.RETRYABLE_HTTP_STATUS_CODES.has(this.response.status);
|
|
100
116
|
}
|
|
101
117
|
toResponse() {
|
|
102
118
|
return Response.json(this.toJSON(), { status: 502 });
|
|
103
119
|
}
|
|
104
120
|
}
|
|
105
121
|
exports.XrpcUpstreamError = XrpcUpstreamError;
|
|
106
|
-
class
|
|
107
|
-
name = '
|
|
108
|
-
constructor(message, options) {
|
|
109
|
-
super('InternalServerError', message, options);
|
|
122
|
+
class XrpcInternalError extends XrpcError {
|
|
123
|
+
name = 'XrpcInternalError';
|
|
124
|
+
constructor(method, message, options) {
|
|
125
|
+
super(method, 'InternalServerError', message ?? 'Unable to fulfill XRPC request', options);
|
|
110
126
|
}
|
|
111
|
-
success = false;
|
|
112
127
|
get reason() {
|
|
113
|
-
return this
|
|
114
|
-
}
|
|
115
|
-
matchesSchema() {
|
|
116
|
-
return false;
|
|
128
|
+
return this;
|
|
117
129
|
}
|
|
118
130
|
shouldRetry() {
|
|
131
|
+
// Ideally, we would inspect the reason to determine if it's retryable
|
|
132
|
+
// (by detecting network errors, timeouts, etc.). Since these cases are
|
|
133
|
+
// highly platform-dependent, we optimistically assume all internal
|
|
134
|
+
// errors are retryable.
|
|
119
135
|
return true;
|
|
120
136
|
}
|
|
121
137
|
toResponse() {
|
|
122
|
-
|
|
138
|
+
// Do not expose internal error details to downstream clients
|
|
139
|
+
return Response.json({ error: this.error }, { status: 500 });
|
|
123
140
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
}
|
|
142
|
+
exports.XrpcInternalError = XrpcInternalError;
|
|
143
|
+
function asXrpcFailure(method, cause) {
|
|
144
|
+
if (cause instanceof XrpcResponseError ||
|
|
145
|
+
cause instanceof XrpcUpstreamError ||
|
|
146
|
+
cause instanceof XrpcInternalError) {
|
|
147
|
+
if (cause.method === method)
|
|
128
148
|
return cause;
|
|
129
|
-
return new XrpcUnexpectedError(message, { cause });
|
|
130
149
|
}
|
|
150
|
+
return new XrpcInternalError(method, undefined, { cause });
|
|
131
151
|
}
|
|
132
|
-
exports.XrpcUnexpectedError = XrpcUnexpectedError;
|
|
133
152
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexError, LexErrorCode, LexErrorData } from '@atproto/lex-data'\nimport { l } from '@atproto/lex-schema'\nimport { XrpcPayload } from './util.js'\n\nexport { LexError }\nexport type { LexErrorCode, LexErrorData }\n\nexport type XrpcErrorPayload<N extends LexErrorCode = LexErrorCode> =\n XrpcPayload<LexErrorData<N>, 'application/json'>\n\nexport class XrpcError<\n N extends LexErrorCode = LexErrorCode,\n> extends LexError<N> {\n name = 'XrpcError'\n\n constructor(\n error: N,\n message: string = `${error} Lexicon RPC error`,\n options?: ErrorOptions,\n ) {\n super(error, message, options)\n }\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: XrpcPayload | null,\n): payload is XrpcErrorPayload {\n return (\n payload !== null &&\n payload.encoding === 'application/json' &&\n l.lexErrorData.matches(payload.body)\n )\n}\n\n/**\n * Interface representing a failed XRPC request result.\n */\ntype LexRpcFailureResult<N extends LexErrorCode, 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 LexErrorCode = LexErrorCode,\n >\n extends XrpcError<N>\n implements LexRpcFailureResult<N, XrpcResponseError<M, N>>\n{\n name = 'XrpcResponseError'\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(): LexErrorData {\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 toJSON() {\n return this.payload.body\n }\n\n toResponse(): Response {\n const { status, headers } = this\n return Response.json(this.toJSON(), { status, headers })\n }\n}\n\n/**\n * This class represents an invalid XRPC response from the server.\n */\nexport class XrpcUpstreamError<\n N extends 'InvalidResponse' | 'UpstreamFailure' =\n | 'InvalidResponse'\n | 'UpstreamFailure',\n >\n extends XrpcError<N>\n implements LexRpcFailureResult<N, XrpcUpstreamError<N>>\n{\n name = 'XrpcUpstreamError' as const\n\n // For debugging purposes, we keep the response details here\n readonly response: {\n status: number\n headers: Headers\n payload: XrpcPayload | null\n }\n\n constructor(\n error: N,\n message: string,\n response: { status: number; headers: Headers },\n payload: XrpcPayload | null,\n options?: ErrorOptions,\n ) {\n super(error, 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 toResponse(): Response {\n return Response.json(this.toJSON(), { status: 502 })\n }\n}\n\nexport class XrpcUnexpectedError\n extends XrpcError<'InternalServerError'>\n implements LexRpcFailureResult<'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 toResponse(): Response {\n return Response.json(this.toJSON(), { status: 500 })\n }\n\n static from(\n cause: unknown,\n message: string = cause instanceof LexError\n ? cause.message\n : 'XRPC request failed',\n ): XrpcUnexpectedError {\n if (cause instanceof XrpcUnexpectedError) return cause\n return new XrpcUnexpectedError(message, { cause })\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexError, LexErrorCode, LexErrorData } from '@atproto/lex-data'\nimport {\n InferMethodError,\n Procedure,\n Query,\n ResultFailure,\n lexErrorDataSchema,\n} from '@atproto/lex-schema'\nimport { XrpcPayload } from './util.js'\nimport {\n WWWAuthenticate,\n parseWWWAuthenticateHeader,\n} from './www-authenticate.js'\n\nexport const RETRYABLE_HTTP_STATUS_CODES: ReadonlySet<number> = new Set([\n 408, 425, 429, 500, 502, 503, 504, 522, 524,\n])\n\nexport { LexError }\nexport type { LexErrorCode, LexErrorData }\n\nexport type XrpcErrorPayload<N extends LexErrorCode = LexErrorCode> =\n XrpcPayload<LexErrorData<N>, 'application/json'>\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: XrpcPayload | null,\n): payload is XrpcErrorPayload {\n return (\n payload !== null &&\n payload.encoding === 'application/json' &&\n lexErrorDataSchema.matches(payload.body)\n )\n}\n\nexport abstract class XrpcError<\n M extends Procedure | Query = Procedure | Query,\n N extends LexErrorCode = LexErrorCode,\n TReason = unknown,\n >\n extends LexError<N>\n implements ResultFailure<TReason>\n{\n name = 'XrpcError'\n\n constructor(\n readonly method: M,\n error: N,\n message: string = `${error} Lexicon RPC error`,\n options?: ErrorOptions,\n ) {\n super(error, message, options)\n }\n\n /**\n * @see {@link ResultFailure.success}\n */\n readonly success = false as const\n\n /**\n * @see {@link ResultFailure.reason}\n */\n abstract readonly reason: TReason\n\n /**\n * Indicates whether the error is transient and can be retried.\n */\n abstract shouldRetry(): boolean\n\n matchesSchema(): this is XrpcError<M, InferMethodError<M>> {\n return this.method.errors?.includes(this.error) ?? false\n }\n}\n\n/**\n * Class used to represent an HTTP request that resulted in an XRPC method\n * error. That is, a non-2xx response with a valid XRPC error payload.\n */\nexport class XrpcResponseError<\n M extends Procedure | Query = Procedure | Query,\n N extends LexErrorCode = InferMethodError<M> | LexErrorCode,\n> extends XrpcError<M, N, XrpcResponseError<M, N>> {\n name = 'XrpcResponseError'\n\n constructor(\n method: M,\n readonly response: Response,\n readonly payload: XrpcErrorPayload<N>,\n options?: ErrorOptions,\n ) {\n const { error, message } = payload.body\n super(method, error, message, options)\n }\n\n override get reason(): this {\n return this\n }\n\n override shouldRetry(): boolean {\n return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status)\n }\n\n override toJSON() {\n return this.payload.body\n }\n\n override toResponse(): Response {\n // Re-expose schema-valid errors as-is to downstream clients\n if (this.matchesSchema()) {\n const status = this.response.status >= 500 ? 502 : this.response.status\n return Response.json(this.toJSON(), { status })\n }\n\n return this.response.status >= 500\n ? // The upstream server had an error, return a generic upstream failure\n Response.json({ error: 'UpstreamFailure' }, { status: 502 })\n : // If the error is on our side, return a generic internal server error\n Response.json({ error: 'InternalServerError' }, { status: 500 })\n }\n\n get body(): LexErrorData {\n return this.payload.body\n }\n}\n\nexport type { WWWAuthenticate }\nexport class XrpcAuthenticationError<\n M extends Procedure | Query = Procedure | Query,\n N extends LexErrorCode = LexErrorCode,\n> extends XrpcResponseError<M, N> {\n name = 'XrpcAuthenticationError'\n\n override shouldRetry(): boolean {\n return false\n }\n\n #wwwAuthenticate?: WWWAuthenticate\n get wwwAuthenticate(): WWWAuthenticate {\n return (this.#wwwAuthenticate ??=\n parseWWWAuthenticateHeader(\n this.response.headers.get('www-authenticate'),\n ) ?? {})\n }\n}\n\n/**\n * This class represents invalid or unprocessable XRPC response from the\n * upstream server.\n */\nexport class XrpcUpstreamError<\n M extends Procedure | Query = Procedure | Query,\n> extends XrpcError<M, 'UpstreamFailure', XrpcUpstreamError<M>> {\n name = 'XrpcUpstreamError'\n\n constructor(\n method: M,\n readonly response: Response,\n readonly payload: XrpcPayload | null,\n message: string = `Unexpected upstream XRPC response`,\n options?: ErrorOptions,\n ) {\n super(method, 'UpstreamFailure', message, options)\n }\n\n override get reason(): this {\n return this\n }\n\n override shouldRetry(): boolean {\n return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status)\n }\n\n override toResponse(): Response {\n return Response.json(this.toJSON(), { status: 502 })\n }\n}\n\nexport class XrpcInternalError<\n M extends Procedure | Query = Procedure | Query,\n> extends XrpcError<M, 'InternalServerError', XrpcInternalError<M>> {\n name = 'XrpcInternalError'\n\n constructor(method: M, message?: string, options?: ErrorOptions) {\n super(\n method,\n 'InternalServerError',\n message ?? 'Unable to fulfill XRPC request',\n options,\n )\n }\n\n override get reason(): this {\n return this\n }\n\n override shouldRetry(): true {\n // Ideally, we would inspect the reason to determine if it's retryable\n // (by detecting network errors, timeouts, etc.). Since these cases are\n // highly platform-dependent, we optimistically assume all internal\n // errors are retryable.\n return true\n }\n\n override toResponse(): Response {\n // Do not expose internal error details to downstream clients\n return Response.json({ error: this.error }, { status: 500 })\n }\n}\n\nexport type XrpcFailure<M extends Procedure | Query = 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<M>\n // Something went wrong (network error, etc.)\n | XrpcInternalError<M>\n\nexport function asXrpcFailure<M extends Procedure | Query>(\n method: M,\n cause: unknown,\n): XrpcFailure<M> {\n if (\n cause instanceof XrpcResponseError ||\n cause instanceof XrpcUpstreamError ||\n cause instanceof XrpcInternalError\n ) {\n if (cause.method === method) return cause\n }\n\n return new XrpcInternalError(method, undefined, { cause })\n}\n"]}
|
package/dist/response.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type XrpcResponsePayload<M extends Procedure | Query> = InferMethodOutput
|
|
|
7
7
|
*
|
|
8
8
|
* @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.
|
|
9
9
|
*/
|
|
10
|
-
export declare class XrpcResponse<
|
|
10
|
+
export declare class XrpcResponse<M extends Procedure | Query> implements ResultSuccess<XrpcResponse<M>> {
|
|
11
11
|
readonly method: M;
|
|
12
12
|
readonly status: number;
|
|
13
13
|
readonly headers: Headers;
|
|
@@ -27,7 +27,8 @@ export declare class XrpcResponse<const M extends Procedure | Query> implements
|
|
|
27
27
|
/**
|
|
28
28
|
* @throws {XrpcResponseError} in case of (valid) XRPC error responses. Use
|
|
29
29
|
* {@link XrpcResponseError.matchesSchema} to narrow the error type based on
|
|
30
|
-
* the method's declared error schema.
|
|
30
|
+
* the method's declared error schema. This can be narrowed further as a
|
|
31
|
+
* {@link XrpcAuthenticationError} if the error is an authentication error.
|
|
31
32
|
* @throws {XrpcUpstreamError} when the response is not a valid XRPC
|
|
32
33
|
* response, or if the response does not conform to the method's schema.
|
|
33
34
|
*/
|
package/dist/response.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,SAAS,EACT,KAAK,EACL,aAAa,EACd,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,SAAS,EACT,KAAK,EACL,aAAa,EACd,MAAM,qBAAqB,CAAA;AAO5B,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEvC,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,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GACnC,IAAI,CAAA;AAEV;;;;GAIG;AACH,qBAAa,YAAY,CAAC,CAAC,SAAS,SAAS,GAAG,KAAK,CACnD,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;;;;;;;OAOG;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;CAoG5B"}
|
package/dist/response.js
CHANGED
|
@@ -41,7 +41,8 @@ class XrpcResponse {
|
|
|
41
41
|
/**
|
|
42
42
|
* @throws {XrpcResponseError} in case of (valid) XRPC error responses. Use
|
|
43
43
|
* {@link XrpcResponseError.matchesSchema} to narrow the error type based on
|
|
44
|
-
* the method's declared error schema.
|
|
44
|
+
* the method's declared error schema. This can be narrowed further as a
|
|
45
|
+
* {@link XrpcAuthenticationError} if the error is an authentication error.
|
|
45
46
|
* @throws {XrpcUpstreamError} when the response is not a valid XRPC
|
|
46
47
|
* response, or if the response does not conform to the method's schema.
|
|
47
48
|
*/
|
|
@@ -52,40 +53,47 @@ class XrpcResponse {
|
|
|
52
53
|
// @NOTE redirect is set to 'follow', so we shouldn't get 3xx responses here
|
|
53
54
|
if (response.status < 200 || response.status >= 300) {
|
|
54
55
|
// Always parse json for error responses
|
|
55
|
-
const payload = await readPayload(response, { parse: true })
|
|
56
|
+
const payload = await readPayload(response, { parse: true }).catch((cause) => {
|
|
57
|
+
throw new errors_js_1.XrpcUpstreamError(method, response, null, 'Unable to parse response payload', { cause });
|
|
58
|
+
});
|
|
59
|
+
// Properly formatted XRPC error response ?
|
|
56
60
|
if (response.status >= 400 && (0, errors_js_1.isXrpcErrorPayload)(payload)) {
|
|
57
|
-
throw
|
|
61
|
+
throw response.status === 401
|
|
62
|
+
? new errors_js_1.XrpcAuthenticationError(method, response, payload)
|
|
63
|
+
: new errors_js_1.XrpcResponseError(method, response, payload);
|
|
58
64
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
// Invalid XRPC response (we probably did not hit an XRPC implementation)
|
|
66
|
+
throw new errors_js_1.XrpcUpstreamError(method, response, payload, response.status >= 500
|
|
67
|
+
? 'Upstream server encountered an error'
|
|
68
|
+
: response.status >= 400
|
|
69
|
+
? 'Invalid response payload'
|
|
70
|
+
: 'Invalid response status code');
|
|
65
71
|
}
|
|
66
72
|
// Only parse json if the schema expects it
|
|
67
73
|
const payload = await readPayload(response, {
|
|
68
74
|
parse: shouldParse(method),
|
|
75
|
+
}).catch((cause) => {
|
|
76
|
+
throw new errors_js_1.XrpcUpstreamError(method, response, null, 'Unable to parse response payload', { cause });
|
|
69
77
|
});
|
|
70
78
|
// Response is successful (2xx). Validate payload (data and encoding) against schema.
|
|
71
79
|
if (method.output.encoding == null) {
|
|
72
80
|
// Schema expects no payload
|
|
73
81
|
if (payload) {
|
|
74
|
-
throw new errors_js_1.XrpcUpstreamError(
|
|
82
|
+
throw new errors_js_1.XrpcUpstreamError(method, response, payload, `Expected response with no body, got ${payload.encoding}`);
|
|
75
83
|
}
|
|
76
84
|
}
|
|
77
85
|
else {
|
|
78
86
|
// Schema expects a payload
|
|
79
87
|
if (!payload || !method.output.matchesEncoding(payload.encoding)) {
|
|
80
|
-
throw new errors_js_1.XrpcUpstreamError(
|
|
88
|
+
throw new errors_js_1.XrpcUpstreamError(method, response, payload, payload
|
|
81
89
|
? `Expected ${method.output.encoding} response, got ${payload.encoding}`
|
|
82
|
-
: `Expected non-empty response with content-type ${method.output.encoding}
|
|
90
|
+
: `Expected non-empty response with content-type ${method.output.encoding}`);
|
|
83
91
|
}
|
|
84
92
|
// Assert valid response body.
|
|
85
93
|
if (method.output.schema && options?.validateResponse !== false) {
|
|
86
94
|
const result = method.output.schema.safeParse(payload.body);
|
|
87
95
|
if (!result.success) {
|
|
88
|
-
throw new errors_js_1.XrpcUpstreamError(
|
|
96
|
+
throw new errors_js_1.XrpcUpstreamError(method, response, payload, `Response validation failed: ${result.reason.message}`, { cause: result.reason });
|
|
89
97
|
}
|
|
90
98
|
}
|
|
91
99
|
}
|
|
@@ -126,16 +134,11 @@ async function readPayload(response, options) {
|
|
|
126
134
|
// This would require adding encode/decode utilities to lex-json (similar
|
|
127
135
|
// to @ipld/dag-json)
|
|
128
136
|
const text = await response.text();
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return { encoding, body: (0, lex_json_1.lexParse)(text) };
|
|
135
|
-
}
|
|
136
|
-
catch (cause) {
|
|
137
|
-
throw new errors_js_1.XrpcUpstreamError('InvalidResponse', 'Invalid JSON response body', response, null, { cause });
|
|
138
|
-
}
|
|
137
|
+
// @NOTE Using `lexParse(text)` (instead of `jsonToLex(json)`) here as
|
|
138
|
+
// using a reviver function during JSON.parse should be faster than
|
|
139
|
+
// parsing to JSON then converting to Lex (?)
|
|
140
|
+
// @TODO verify statement above
|
|
141
|
+
return { encoding, body: (0, lex_json_1.lexParse)(text) };
|
|
139
142
|
}
|
|
140
143
|
return { encoding, body: new Uint8Array(await response.arrayBuffer()) };
|
|
141
144
|
}
|
package/dist/response.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.js","sourceRoot":"","sources":["../src/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 {\n XrpcResponseError,\n XrpcUpstreamError,\n isXrpcErrorPayload,\n} from './errors.js'\nimport { XrpcPayload } from './util.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 ? XrpcPayload<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 {XrpcResponseError} in case of (valid) XRPC error responses. Use\n * {@link XrpcResponseError.matchesSchema} to narrow the error type based on\n * the method's declared error schema.\n * @throws {XrpcUpstreamError} when the response is not a valid XRPC\n * response, or if the response does not conform to the method's 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 if (response.status >= 500) {\n throw new XrpcUpstreamError(\n 'UpstreamFailure',\n `Upstream server encountered an error`,\n response,\n payload,\n )\n }\n\n throw new XrpcUpstreamError(\n 'InvalidResponse',\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 XrpcUpstreamError(\n 'InvalidResponse',\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 XrpcUpstreamError(\n 'InvalidResponse',\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\n if (!result.success) {\n throw new XrpcUpstreamError(\n 'InvalidResponse',\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<XrpcPayload | 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 XrpcUpstreamError(\n 'InvalidResponse',\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"]}
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../src/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 {\n XrpcAuthenticationError,\n XrpcResponseError,\n XrpcUpstreamError,\n isXrpcErrorPayload,\n} from './errors.js'\nimport { XrpcPayload } from './util.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 ? XrpcPayload<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<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 {XrpcResponseError} in case of (valid) XRPC error responses. Use\n * {@link XrpcResponseError.matchesSchema} to narrow the error type based on\n * the method's declared error schema. This can be narrowed further as a\n * {@link XrpcAuthenticationError} if the error is an authentication error.\n * @throws {XrpcUpstreamError} when the response is not a valid XRPC\n * response, or if the response does not conform to the method's 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 }).catch(\n (cause) => {\n throw new XrpcUpstreamError(\n method,\n response,\n null,\n 'Unable to parse response payload',\n { cause },\n )\n },\n )\n\n // Properly formatted XRPC error response ?\n if (response.status >= 400 && isXrpcErrorPayload(payload)) {\n throw response.status === 401\n ? new XrpcAuthenticationError<M>(method, response, payload)\n : new XrpcResponseError<M>(method, response, payload)\n }\n\n // Invalid XRPC response (we probably did not hit an XRPC implementation)\n throw new XrpcUpstreamError(\n method,\n response,\n payload,\n response.status >= 500\n ? 'Upstream server encountered an error'\n : response.status >= 400\n ? 'Invalid response payload'\n : 'Invalid response status code',\n )\n }\n\n // Only parse json if the schema expects it\n const payload = await readPayload(response, {\n parse: shouldParse(method),\n }).catch((cause) => {\n throw new XrpcUpstreamError(\n method,\n response,\n null,\n 'Unable to parse response payload',\n { cause },\n )\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 XrpcUpstreamError(\n method,\n response,\n payload,\n `Expected response with no body, got ${payload.encoding}`,\n )\n }\n } else {\n // Schema expects a payload\n if (!payload || !method.output.matchesEncoding(payload.encoding)) {\n throw new XrpcUpstreamError(\n method,\n response,\n payload,\n payload\n ? `Expected ${method.output.encoding} response, got ${payload.encoding}`\n : `Expected non-empty response with content-type ${method.output.encoding}`,\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\n if (!result.success) {\n throw new XrpcUpstreamError(\n method,\n response,\n payload,\n `Response validation failed: ${result.reason.message}`,\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<XrpcPayload | 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 // @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 }\n\n return { encoding, body: new Uint8Array(await response.arrayBuffer()) }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type WWWAuthenticateParams = {
|
|
2
|
+
[authParam in string]: string;
|
|
3
|
+
};
|
|
4
|
+
export type WWWAuthenticate = {
|
|
5
|
+
[authScheme in string]: string | WWWAuthenticateParams;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Returns `undefined` if the header is malformed.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseWWWAuthenticateHeader(header?: unknown): undefined | WWWAuthenticate;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=www-authenticate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"www-authenticate.d.ts","sourceRoot":"","sources":["../src/www-authenticate.ts"],"names":[],"mappings":"AAAA,KAAK,qBAAqB,GAAG;KAAG,SAAS,IAAI,MAAM,GAAG,MAAM;CAAE,CAAA;AAC9D,MAAM,MAAM,eAAe,GAAG;KAC3B,UAAU,IAAI,MAAM,GACjB,MAAM,GACN,qBAAqB;CAC1B,CAAA;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,CAAC,EAAE,OAAO,GACf,SAAS,GAAG,eAAe,CAgE7B"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseWWWAuthenticateHeader = parseWWWAuthenticateHeader;
|
|
4
|
+
/**
|
|
5
|
+
* Returns `undefined` if the header is malformed.
|
|
6
|
+
*/
|
|
7
|
+
function parseWWWAuthenticateHeader(header) {
|
|
8
|
+
if (typeof header !== 'string')
|
|
9
|
+
return undefined;
|
|
10
|
+
const wwwAuthenticate = {};
|
|
11
|
+
// Split over commas, not within quoted strings
|
|
12
|
+
const trimmedHeader = header.trim();
|
|
13
|
+
if (!trimmedHeader)
|
|
14
|
+
return wwwAuthenticate;
|
|
15
|
+
const parts = trimmedHeader.split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/);
|
|
16
|
+
let currentParams = null;
|
|
17
|
+
for (let part of parts) {
|
|
18
|
+
// Check if the part starts with an auth scheme
|
|
19
|
+
const schemeMatch = part.trim().match(/^([^"=\s]+)(\s+.*)?$/);
|
|
20
|
+
if (schemeMatch) {
|
|
21
|
+
const scheme = schemeMatch[1];
|
|
22
|
+
// Duplicate scheme
|
|
23
|
+
if (Object.hasOwn(wwwAuthenticate, scheme))
|
|
24
|
+
return undefined;
|
|
25
|
+
const rest = schemeMatch[2]?.trim();
|
|
26
|
+
if (!rest) {
|
|
27
|
+
// Scheme only (no params or token68)
|
|
28
|
+
currentParams = null;
|
|
29
|
+
wwwAuthenticate[scheme] = Object.create(null);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!rest.includes('=')) {
|
|
33
|
+
// Scheme with token68
|
|
34
|
+
currentParams = null;
|
|
35
|
+
wwwAuthenticate[scheme] = rest;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// Scheme with params
|
|
39
|
+
currentParams = Object.create(null);
|
|
40
|
+
wwwAuthenticate[scheme] = currentParams;
|
|
41
|
+
// Fall through to parse params
|
|
42
|
+
part = rest;
|
|
43
|
+
}
|
|
44
|
+
// Invalid header
|
|
45
|
+
if (!currentParams)
|
|
46
|
+
return undefined;
|
|
47
|
+
const param = part.match(/^\s*([^"\s=]+)=(?:("[^"\\]*(?:\\.[^"\\]*)*")|([^\s,"]*))\s*$/);
|
|
48
|
+
// invalid param
|
|
49
|
+
if (!param)
|
|
50
|
+
return undefined;
|
|
51
|
+
const paramName = param[1];
|
|
52
|
+
const paramValue = param[3] ?? param[2].slice(1, -1).replaceAll(/\\(.)/g, '$1');
|
|
53
|
+
currentParams[paramName] = paramValue;
|
|
54
|
+
}
|
|
55
|
+
return wwwAuthenticate;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=www-authenticate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"www-authenticate.js","sourceRoot":"","sources":["../src/www-authenticate.ts"],"names":[],"mappings":";;AAUA,gEAkEC;AArED;;GAEG;AACH,SAAgB,0BAA0B,CACxC,MAAgB;IAEhB,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAEhD,MAAM,eAAe,GAAoB,EAAE,CAAA;IAE3C,+CAA+C;IAC/C,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;IACnC,IAAI,CAAC,aAAa;QAAE,OAAO,eAAe,CAAA;IAE1C,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAA;IAEjE,IAAI,aAAa,GAAiC,IAAI,CAAA;IAEtD,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,+CAA+C;QAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;QAC7D,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;YAE7B,mBAAmB;YACnB,IAAI,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC;gBAAE,OAAO,SAAS,CAAA;YAE5D,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAA;YACnC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,qCAAqC;gBACrC,aAAa,GAAG,IAAI,CAAA;gBACpB,eAAe,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBAC7C,SAAQ;YACV,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,sBAAsB;gBACtB,aAAa,GAAG,IAAI,CAAA;gBACpB,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;gBAC9B,SAAQ;YACV,CAAC;YAED,qBAAqB;YAErB,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA0B,CAAA;YAC5D,eAAe,CAAC,MAAM,CAAC,GAAG,aAAa,CAAA;YAEvC,+BAA+B;YAC/B,IAAI,GAAG,IAAI,CAAA;QACb,CAAC;QAED,iBAAiB;QACjB,IAAI,CAAC,aAAa;YAAE,OAAO,SAAS,CAAA;QAEpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CACtB,8DAA8D,CAC/D,CAAA;QAED,gBAAgB;QAChB,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAA;QAE5B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QAC1B,MAAM,UAAU,GACd,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QAE/D,aAAa,CAAC,SAAS,CAAC,GAAG,UAAU,CAAA;IACvC,CAAC;IAED,OAAO,eAAe,CAAA;AACxB,CAAC","sourcesContent":["type WWWAuthenticateParams = { [authParam in string]: string }\nexport type WWWAuthenticate = {\n [authScheme in string]:\n | string // token68\n | WWWAuthenticateParams\n}\n\n/**\n * Returns `undefined` if the header is malformed.\n */\nexport function parseWWWAuthenticateHeader(\n header?: unknown,\n): undefined | WWWAuthenticate {\n if (typeof header !== 'string') return undefined\n\n const wwwAuthenticate: WWWAuthenticate = {}\n\n // Split over commas, not within quoted strings\n const trimmedHeader = header.trim()\n if (!trimmedHeader) return wwwAuthenticate\n\n const parts = trimmedHeader.split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/)\n\n let currentParams: WWWAuthenticateParams | null = null\n\n for (let part of parts) {\n // Check if the part starts with an auth scheme\n const schemeMatch = part.trim().match(/^([^\"=\\s]+)(\\s+.*)?$/)\n if (schemeMatch) {\n const scheme = schemeMatch[1]\n\n // Duplicate scheme\n if (Object.hasOwn(wwwAuthenticate, scheme)) return undefined\n\n const rest = schemeMatch[2]?.trim()\n if (!rest) {\n // Scheme only (no params or token68)\n currentParams = null\n wwwAuthenticate[scheme] = Object.create(null)\n continue\n }\n\n if (!rest.includes('=')) {\n // Scheme with token68\n currentParams = null\n wwwAuthenticate[scheme] = rest\n continue\n }\n\n // Scheme with params\n\n currentParams = Object.create(null) as WWWAuthenticateParams\n wwwAuthenticate[scheme] = currentParams\n\n // Fall through to parse params\n part = rest\n }\n\n // Invalid header\n if (!currentParams) return undefined\n\n const param = part.match(\n /^\\s*([^\"\\s=]+)=(?:(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")|([^\\s,\"]*))\\s*$/,\n )\n\n // invalid param\n if (!param) return undefined\n\n const paramName = param[1]\n const paramValue =\n param[3] ?? param[2]!.slice(1, -1).replaceAll(/\\\\(.)/g, '$1')\n\n currentParams[paramName] = paramValue\n }\n\n return wwwAuthenticate\n}\n"]}
|
package/dist/xrpc.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InferInput, InferPayload, Main, Params, Procedure, Query, Restricted, Subscription } from '@atproto/lex-schema';
|
|
2
2
|
import { Agent } from './agent.js';
|
|
3
|
-
import {
|
|
3
|
+
import { XrpcFailure } from './errors.js';
|
|
4
4
|
import { XrpcResponse } from './response.js';
|
|
5
5
|
import { BinaryBodyInit, CallOptions } from './types.js';
|
|
6
6
|
type XrpcParamsOptions<P extends Params> = NonNullable<unknown> extends P ? {
|
|
@@ -21,20 +21,12 @@ type XrpcInputOptions<In> = In extends {
|
|
|
21
21
|
encoding?: undefined;
|
|
22
22
|
};
|
|
23
23
|
export type XrpcOptions<M extends Procedure | Query = Procedure | Query> = CallOptions & XrpcInputOptions<XrpcRequestPayload<M>> & XrpcParamsOptions<XrpcRequestParams<M>>;
|
|
24
|
-
export type XrpcFailure<M extends Procedure | Query> = XrpcResponseError<M> | XrpcUpstreamError | XrpcUnexpectedError;
|
|
25
|
-
export type XrpcResult<M extends Procedure | Query> = XrpcResponse<M> | XrpcFailure<M>;
|
|
26
|
-
/**
|
|
27
|
-
* Utility method to type cast the error thrown by {@link xrpc} to an
|
|
28
|
-
* {@link XrpcFailure} matching the provided method. Only use this function
|
|
29
|
-
* inside a catch block right after calling {@link xrpc}, and use the same
|
|
30
|
-
* method type parameter as used in the {@link xrpc} call.
|
|
31
|
-
*/
|
|
32
|
-
export declare function asXrpcFailure<M extends Procedure | Query = Procedure | Query>(err: unknown): XrpcFailure<M>;
|
|
33
24
|
/**
|
|
34
25
|
* @throws XrpcFailure<M>
|
|
35
26
|
*/
|
|
36
27
|
export declare function xrpc<const M extends Query | Procedure>(agent: Agent, ns: NonNullable<unknown> extends XrpcOptions<M> ? Main<M> : Restricted<'This XRPC method requires an "options" argument'>): Promise<XrpcResponse<M>>;
|
|
37
28
|
export declare function xrpc<const M extends Query | Procedure>(agent: Agent, ns: Main<M>, options: XrpcOptions<M>): Promise<XrpcResponse<M>>;
|
|
29
|
+
export type XrpcResult<M extends Procedure | Query> = XrpcResponse<M> | XrpcFailure<M>;
|
|
38
30
|
export declare function xrpcSafe<const M extends Query | Procedure>(agent: Agent, ns: NonNullable<unknown> extends XrpcOptions<M> ? Main<M> : Restricted<'This XRPC method requires an "options" argument'>): Promise<XrpcResult<M>>;
|
|
39
31
|
export declare function xrpcSafe<const M extends Query | Procedure>(agent: Agent, ns: Main<M>, options: XrpcOptions<M>): Promise<XrpcResult<M>>;
|
|
40
32
|
export {};
|
package/dist/xrpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,IAAI,EACJ,MAAM,EAEN,SAAS,EACT,KAAK,EACL,UAAU,EACV,YAAY,EAEb,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,
|
|
1
|
+
{"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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"}
|
package/dist/xrpc.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.asXrpcFailure = asXrpcFailure;
|
|
4
3
|
exports.xrpc = xrpc;
|
|
5
4
|
exports.xrpcSafe = xrpcSafe;
|
|
6
5
|
const lex_data_1 = require("@atproto/lex-data");
|
|
@@ -9,37 +8,25 @@ const lex_schema_1 = require("@atproto/lex-schema");
|
|
|
9
8
|
const errors_js_1 = require("./errors.js");
|
|
10
9
|
const response_js_1 = require("./response.js");
|
|
11
10
|
const util_js_1 = require("./util.js");
|
|
12
|
-
/**
|
|
13
|
-
* Utility method to type cast the error thrown by {@link xrpc} to an
|
|
14
|
-
* {@link XrpcFailure} matching the provided method. Only use this function
|
|
15
|
-
* inside a catch block right after calling {@link xrpc}, and use the same
|
|
16
|
-
* method type parameter as used in the {@link xrpc} call.
|
|
17
|
-
*/
|
|
18
|
-
function asXrpcFailure(err) {
|
|
19
|
-
if (err instanceof errors_js_1.XrpcResponseError)
|
|
20
|
-
return err;
|
|
21
|
-
if (err instanceof errors_js_1.XrpcUpstreamError)
|
|
22
|
-
return err;
|
|
23
|
-
return errors_js_1.XrpcUnexpectedError.from(err);
|
|
24
|
-
}
|
|
25
11
|
async function xrpc(agent, ns, options = {}) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
throw
|
|
31
|
-
}
|
|
12
|
+
const response = await xrpcSafe(agent, ns, options);
|
|
13
|
+
if (response.success)
|
|
14
|
+
return response;
|
|
15
|
+
else
|
|
16
|
+
throw response;
|
|
32
17
|
}
|
|
33
18
|
async function xrpcSafe(agent, ns, options = {}) {
|
|
34
|
-
return lexRpcRequest(agent, ns, options).catch((asXrpcFailure));
|
|
35
|
-
}
|
|
36
|
-
async function lexRpcRequest(agent, ns, options = {}) {
|
|
37
|
-
const method = (0, lex_schema_1.getMain)(ns);
|
|
38
19
|
options.signal?.throwIfAborted();
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
20
|
+
const method = (0, lex_schema_1.getMain)(ns);
|
|
21
|
+
try {
|
|
22
|
+
const url = xrpcRequestUrl(method, options);
|
|
23
|
+
const request = xrpcRequestInit(method, options);
|
|
24
|
+
const response = await agent.fetchHandler(url, request);
|
|
25
|
+
return await response_js_1.XrpcResponse.fromFetchResponse(method, response, options);
|
|
26
|
+
}
|
|
27
|
+
catch (cause) {
|
|
28
|
+
return (0, errors_js_1.asXrpcFailure)(method, cause);
|
|
29
|
+
}
|
|
43
30
|
}
|
|
44
31
|
function xrpcRequestUrl(method, options) {
|
|
45
32
|
const path = `/xrpc/${method.nsid}`;
|