@atproto/lex-client 0.0.0-spaces-alpha-20260818021221

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.
Files changed (136) hide show
  1. package/CHANGELOG.md +456 -0
  2. package/LICENSE.txt +7 -0
  3. package/README.md +19 -0
  4. package/dist/agent.d.ts +127 -0
  5. package/dist/agent.d.ts.map +1 -0
  6. package/dist/agent.js +41 -0
  7. package/dist/agent.js.map +1 -0
  8. package/dist/client.d.ts +685 -0
  9. package/dist/client.d.ts.map +1 -0
  10. package/dist/client.js +511 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/errors.d.ts +277 -0
  13. package/dist/errors.d.ts.map +1 -0
  14. package/dist/errors.js +388 -0
  15. package/dist/errors.js.map +1 -0
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +7 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/lexicons/com/atproto/repo/applyWrites.d.ts +3 -0
  21. package/dist/lexicons/com/atproto/repo/applyWrites.d.ts.map +1 -0
  22. package/dist/lexicons/com/atproto/repo/applyWrites.defs.d.ts +103 -0
  23. package/dist/lexicons/com/atproto/repo/applyWrites.defs.d.ts.map +1 -0
  24. package/dist/lexicons/com/atproto/repo/applyWrites.defs.js +82 -0
  25. package/dist/lexicons/com/atproto/repo/applyWrites.defs.js.map +1 -0
  26. package/dist/lexicons/com/atproto/repo/applyWrites.js +6 -0
  27. package/dist/lexicons/com/atproto/repo/applyWrites.js.map +1 -0
  28. package/dist/lexicons/com/atproto/repo/createRecord.d.ts +3 -0
  29. package/dist/lexicons/com/atproto/repo/createRecord.d.ts.map +1 -0
  30. package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts +74 -0
  31. package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts.map +1 -0
  32. package/dist/lexicons/com/atproto/repo/createRecord.defs.js +34 -0
  33. package/dist/lexicons/com/atproto/repo/createRecord.defs.js.map +1 -0
  34. package/dist/lexicons/com/atproto/repo/createRecord.js +6 -0
  35. package/dist/lexicons/com/atproto/repo/createRecord.js.map +1 -0
  36. package/dist/lexicons/com/atproto/repo/defs.d.ts +2 -0
  37. package/dist/lexicons/com/atproto/repo/defs.d.ts.map +1 -0
  38. package/dist/lexicons/com/atproto/repo/defs.defs.d.ts +13 -0
  39. package/dist/lexicons/com/atproto/repo/defs.defs.d.ts.map +1 -0
  40. package/dist/lexicons/com/atproto/repo/defs.defs.js +13 -0
  41. package/dist/lexicons/com/atproto/repo/defs.defs.js.map +1 -0
  42. package/dist/lexicons/com/atproto/repo/defs.js +5 -0
  43. package/dist/lexicons/com/atproto/repo/defs.js.map +1 -0
  44. package/dist/lexicons/com/atproto/repo/deleteRecord.d.ts +3 -0
  45. package/dist/lexicons/com/atproto/repo/deleteRecord.d.ts.map +1 -0
  46. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts +56 -0
  47. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts.map +1 -0
  48. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js +29 -0
  49. package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js.map +1 -0
  50. package/dist/lexicons/com/atproto/repo/deleteRecord.js +6 -0
  51. package/dist/lexicons/com/atproto/repo/deleteRecord.js.map +1 -0
  52. package/dist/lexicons/com/atproto/repo/getRecord.d.ts +3 -0
  53. package/dist/lexicons/com/atproto/repo/getRecord.d.ts.map +1 -0
  54. package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts +58 -0
  55. package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts.map +1 -0
  56. package/dist/lexicons/com/atproto/repo/getRecord.defs.js +23 -0
  57. package/dist/lexicons/com/atproto/repo/getRecord.defs.js.map +1 -0
  58. package/dist/lexicons/com/atproto/repo/getRecord.js +6 -0
  59. package/dist/lexicons/com/atproto/repo/getRecord.js.map +1 -0
  60. package/dist/lexicons/com/atproto/repo/listRecords.d.ts +3 -0
  61. package/dist/lexicons/com/atproto/repo/listRecords.d.ts.map +1 -0
  62. package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts +51 -0
  63. package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts.map +1 -0
  64. package/dist/lexicons/com/atproto/repo/listRecords.defs.js +33 -0
  65. package/dist/lexicons/com/atproto/repo/listRecords.defs.js.map +1 -0
  66. package/dist/lexicons/com/atproto/repo/listRecords.js +6 -0
  67. package/dist/lexicons/com/atproto/repo/listRecords.js.map +1 -0
  68. package/dist/lexicons/com/atproto/repo/putRecord.d.ts +3 -0
  69. package/dist/lexicons/com/atproto/repo/putRecord.d.ts.map +1 -0
  70. package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts +80 -0
  71. package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts.map +1 -0
  72. package/dist/lexicons/com/atproto/repo/putRecord.defs.js +35 -0
  73. package/dist/lexicons/com/atproto/repo/putRecord.defs.js.map +1 -0
  74. package/dist/lexicons/com/atproto/repo/putRecord.js +6 -0
  75. package/dist/lexicons/com/atproto/repo/putRecord.js.map +1 -0
  76. package/dist/lexicons/com/atproto/repo/uploadBlob.d.ts +3 -0
  77. package/dist/lexicons/com/atproto/repo/uploadBlob.d.ts.map +1 -0
  78. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts +23 -0
  79. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -0
  80. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +17 -0
  81. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -0
  82. package/dist/lexicons/com/atproto/repo/uploadBlob.js +6 -0
  83. package/dist/lexicons/com/atproto/repo/uploadBlob.js.map +1 -0
  84. package/dist/lexicons/com/atproto/repo.d.ts +9 -0
  85. package/dist/lexicons/com/atproto/repo.d.ts.map +1 -0
  86. package/dist/lexicons/com/atproto/repo.js +12 -0
  87. package/dist/lexicons/com/atproto/repo.js.map +1 -0
  88. package/dist/lexicons/com/atproto/sync/getBlob.d.ts +3 -0
  89. package/dist/lexicons/com/atproto/sync/getBlob.d.ts.map +1 -0
  90. package/dist/lexicons/com/atproto/sync/getBlob.defs.d.ts +30 -0
  91. package/dist/lexicons/com/atproto/sync/getBlob.defs.d.ts.map +1 -0
  92. package/dist/lexicons/com/atproto/sync/getBlob.defs.js +23 -0
  93. package/dist/lexicons/com/atproto/sync/getBlob.defs.js.map +1 -0
  94. package/dist/lexicons/com/atproto/sync/getBlob.js +6 -0
  95. package/dist/lexicons/com/atproto/sync/getBlob.js.map +1 -0
  96. package/dist/lexicons/com/atproto/sync.d.ts +2 -0
  97. package/dist/lexicons/com/atproto/sync.d.ts.map +1 -0
  98. package/dist/lexicons/com/atproto/sync.js +5 -0
  99. package/dist/lexicons/com/atproto/sync.js.map +1 -0
  100. package/dist/lexicons/com/atproto.d.ts +3 -0
  101. package/dist/lexicons/com/atproto.d.ts.map +1 -0
  102. package/dist/lexicons/com/atproto.js +6 -0
  103. package/dist/lexicons/com/atproto.js.map +1 -0
  104. package/dist/lexicons/com.d.ts +2 -0
  105. package/dist/lexicons/com.d.ts.map +1 -0
  106. package/dist/lexicons/com.js +5 -0
  107. package/dist/lexicons/com.js.map +1 -0
  108. package/dist/lexicons/index.d.ts +2 -0
  109. package/dist/lexicons/index.d.ts.map +1 -0
  110. package/dist/lexicons/index.js +5 -0
  111. package/dist/lexicons/index.js.map +1 -0
  112. package/dist/response.d.ts +124 -0
  113. package/dist/response.d.ts.map +1 -0
  114. package/dist/response.js +178 -0
  115. package/dist/response.js.map +1 -0
  116. package/dist/types.d.ts +54 -0
  117. package/dist/types.d.ts.map +1 -0
  118. package/dist/types.js +16 -0
  119. package/dist/types.js.map +1 -0
  120. package/dist/util.d.ts +85 -0
  121. package/dist/util.d.ts.map +1 -0
  122. package/dist/util.js +194 -0
  123. package/dist/util.js.map +1 -0
  124. package/dist/write-operation-builder.d.ts +19 -0
  125. package/dist/write-operation-builder.d.ts.map +1 -0
  126. package/dist/write-operation-builder.js +35 -0
  127. package/dist/write-operation-builder.js.map +1 -0
  128. package/dist/www-authenticate.d.ts +35 -0
  129. package/dist/www-authenticate.d.ts.map +1 -0
  130. package/dist/www-authenticate.js +54 -0
  131. package/dist/www-authenticate.js.map +1 -0
  132. package/dist/xrpc.d.ts +194 -0
  133. package/dist/xrpc.d.ts.map +1 -0
  134. package/dist/xrpc.js +235 -0
  135. package/dist/xrpc.js.map +1 -0
  136. package/package.json +51 -0
@@ -0,0 +1,277 @@
1
+ import type { LexErrorCode, LexErrorData, LexValue } from '@atproto/lex-data';
2
+ import { LexError } from '@atproto/lex-data';
3
+ import { type InferMethodError, type LexValidationError, type Procedure, type Query, type ResultFailure } from '@atproto/lex-schema';
4
+ import type { XrpcUnknownResponsePayload } from './types.js';
5
+ import type { WWWAuthenticate } from './www-authenticate.js';
6
+ export type { XrpcUnknownResponsePayload };
7
+ export type DownstreamError<N extends LexErrorCode = LexErrorCode> = {
8
+ status: number;
9
+ headers?: Headers;
10
+ encoding?: 'application/json';
11
+ body: LexErrorData<N>;
12
+ };
13
+ /**
14
+ * HTTP status codes that indicate a transient error that may succeed on retry.
15
+ *
16
+ * Includes:
17
+ * - 408 Request Timeout
18
+ * - 425 Too Early
19
+ * - 429 Too Many Requests (rate limited)
20
+ * - 500 Internal Server Error
21
+ * - 502 Bad Gateway
22
+ * - 503 Service Unavailable
23
+ * - 504 Gateway Timeout
24
+ * - 522 Connection Timed Out (Cloudflare)
25
+ * - 524 A Timeout Occurred (Cloudflare)
26
+ */
27
+ export declare const RETRYABLE_HTTP_STATUS_CODES: ReadonlySet<number>;
28
+ export { LexError };
29
+ export type { LexErrorCode, LexErrorData };
30
+ /**
31
+ * The payload structure for XRPC error responses.
32
+ *
33
+ * All XRPC errors return JSON with an `error` code and optional `message`.
34
+ *
35
+ * @typeParam N - The specific error code type
36
+ */
37
+ export type XrpcErrorPayload<N extends LexErrorCode = LexErrorCode> = {
38
+ body: LexErrorData<N>;
39
+ encoding: 'application/json';
40
+ };
41
+ /**
42
+ * All unsuccessful responses should follow a standard error response
43
+ * schema. The Content-Type should be application/json, and the payload
44
+ * should be a JSON object with the following fields:
45
+ *
46
+ * - `error` (string, required): type name of the error (generic ASCII
47
+ * constant, no whitespace)
48
+ * - `message` (string, optional): description of the error, appropriate for
49
+ * display to humans
50
+ *
51
+ * This function checks whether a given payload matches this schema.
52
+ */
53
+ export declare function isXrpcErrorPayload(payload: XrpcUnknownResponsePayload | null | undefined): payload is XrpcErrorPayload;
54
+ /**
55
+ * Abstract base class for all XRPC errors.
56
+ *
57
+ * Extends {@link LexError} and implements {@link ResultFailure} for use with
58
+ * safe/result-based error handling patterns.
59
+ *
60
+ * @typeParam M - The XRPC method type (Procedure or Query)
61
+ * @typeParam N - The error code type
62
+ * @typeParam TReason - The reason type for ResultFailure
63
+ *
64
+ * @see {@link XrpcResponseError} - For valid XRPC error responses
65
+ * @see {@link XrpcInvalidResponseError} - For invalid/unexpected responses
66
+ * @see {@link XrpcInternalError} - For network/internal errors
67
+ */
68
+ export declare abstract class XrpcError<M extends Procedure | Query = Procedure | Query, N extends LexErrorCode = LexErrorCode, TReason = unknown> extends LexError<N> implements ResultFailure<TReason> {
69
+ readonly method: M;
70
+ name: string;
71
+ constructor(method: M, error: N, message?: string, options?: ErrorOptions);
72
+ /**
73
+ * @see {@link ResultFailure.success}
74
+ */
75
+ readonly success: false;
76
+ /**
77
+ * @see {@link ResultFailure.reason}
78
+ */
79
+ abstract readonly reason: TReason;
80
+ /**
81
+ * Indicates whether the error is transient and can be retried.
82
+ */
83
+ abstract shouldRetry(): boolean;
84
+ abstract toDownstreamError(): DownstreamError;
85
+ matchesSchemaErrors(): this is XrpcError<M, InferMethodError<M>>;
86
+ }
87
+ /**
88
+ * Error class for valid XRPC error responses from the server.
89
+ *
90
+ * This represents a properly formatted XRPC error where the server returned
91
+ * a non-2xx status with a valid JSON error payload containing `error` and
92
+ * optional `message` fields.
93
+ *
94
+ * Use {@link matchesSchemaErrors} to check if the error matches the method's declared
95
+ * error types for type-safe error handling.
96
+ *
97
+ * @typeParam M - The XRPC method type
98
+ * @typeParam N - The error code type (inferred from method or generic)
99
+ *
100
+ * @example Handling specific errors
101
+ * ```typescript
102
+ * try {
103
+ * await client.xrpc(someMethod, options)
104
+ * } catch (err) {
105
+ * if (err instanceof XrpcResponseError && err.error === 'RecordNotFound') {
106
+ * // Handle not found case
107
+ * }
108
+ * }
109
+ * ```
110
+ */
111
+ export declare class XrpcResponseError<M extends Procedure | Query = Procedure | Query> extends XrpcError<M, LexErrorCode, XrpcResponseError<M>> {
112
+ readonly response: Response;
113
+ readonly payload?: XrpcUnknownResponsePayload | undefined;
114
+ name: string;
115
+ constructor(method: M, response: Response, payload?: XrpcUnknownResponsePayload | undefined, options?: ErrorOptions);
116
+ get reason(): this;
117
+ shouldRetry(): boolean;
118
+ toJSON(): LexErrorData;
119
+ toDownstreamError(): DownstreamError;
120
+ get status(): number;
121
+ get headers(): Headers;
122
+ get body(): undefined | Uint8Array | LexValue;
123
+ }
124
+ export type { WWWAuthenticate };
125
+ /**
126
+ * Error class for 401 Unauthorized XRPC responses.
127
+ *
128
+ * Extends {@link XrpcResponseError} with access to parsed WWW-Authenticate header
129
+ * information, useful for implementing authentication flows.
130
+ *
131
+ * Authentication errors are never retryable as they require user intervention
132
+ * (e.g., re-authentication, token refresh).
133
+ *
134
+ * @typeParam M - The XRPC method type
135
+ * @typeParam N - The error code type
136
+ *
137
+ * @example Handling authentication errors
138
+ * ```typescript
139
+ * try {
140
+ * await client.xrpc(someMethod, options)
141
+ * } catch (err) {
142
+ * if (err instanceof XrpcAuthenticationError) {
143
+ * const { DPoP } = err.wwwAuthenticate
144
+ * if (DPoP?.error === 'use_dpop_nonce') {
145
+ * // Handle DPoP nonce requirement
146
+ * }
147
+ * }
148
+ * }
149
+ * ```
150
+ */
151
+ export declare class XrpcAuthenticationError<M extends Procedure | Query = Procedure | Query> extends XrpcResponseError<M> {
152
+ #private;
153
+ name: string;
154
+ shouldRetry(): boolean;
155
+ /**
156
+ * Parsed WWW-Authenticate header from the response.
157
+ * Contains authentication scheme parameters (e.g., Bearer realm, DPoP nonce).
158
+ */
159
+ get wwwAuthenticate(): WWWAuthenticate;
160
+ }
161
+ /**
162
+ * Error class for invalid or unprocessable XRPC responses from upstream servers.
163
+ *
164
+ * This occurs when the server returns a response that doesn't conform to the
165
+ * XRPC protocol, such as:
166
+ * - Missing or invalid Content-Type header
167
+ * - Response body that doesn't match the method's output schema
168
+ * - Non-JSON error responses
169
+ * - Responses from non-XRPC endpoints
170
+ *
171
+ * The error code is always 'InvalidResponse' and maps to HTTP 502 Bad Gateway
172
+ * when converted to a response. This should allow downstream clients to
173
+ * determine at which boundary the error occurred.
174
+ *
175
+ * @typeParam M - The XRPC method type
176
+ */
177
+ export declare class XrpcInvalidResponseError<M extends Procedure | Query = Procedure | Query> extends XrpcError<M, 'InvalidResponse', XrpcInvalidResponseError<M>> {
178
+ readonly response: Response;
179
+ readonly payload?: XrpcUnknownResponsePayload | undefined;
180
+ name: string;
181
+ constructor(method: M, response: Response, payload?: XrpcUnknownResponsePayload | undefined, message?: string, options?: ErrorOptions);
182
+ get reason(): this;
183
+ shouldRetry(): boolean;
184
+ toDownstreamError(): DownstreamError;
185
+ }
186
+ /**
187
+ * Error class for invalid XRPC responses that fail schema validation.
188
+ *
189
+ * This is a specific type of {@link XrpcInvalidResponseError} that indicates the
190
+ * upstream server returned a response that was structurally valid but did not
191
+ * conform to the expected schema for the method. This likely indicates a
192
+ * mismatch between client and server versions or an issue with the server's
193
+ * XRPC implementation.
194
+ *
195
+ * @typeParam M - The XRPC method type
196
+ */
197
+ export declare class XrpcResponseValidationError<M extends Procedure | Query = Procedure | Query> extends XrpcInvalidResponseError<M> {
198
+ readonly cause: LexValidationError;
199
+ name: string;
200
+ constructor(method: M, response: Response, payload: XrpcUnknownResponsePayload, cause: LexValidationError);
201
+ }
202
+ /**
203
+ * Error class for unexpected internal/client-side errors during XRPC requests.
204
+ *
205
+ * The error code is always 'InternalServerError' and these errors not
206
+ * considered retryable as they stem from unforeseen issues in the
207
+ * implementation.
208
+ *
209
+ * @typeParam M - The XRPC method type
210
+ */
211
+ export declare class XrpcInternalError<M extends Procedure | Query = Procedure | Query> extends XrpcError<M, 'InternalServerError', XrpcInternalError<M>> {
212
+ name: string;
213
+ constructor(method: M, message?: string, options?: ErrorOptions);
214
+ get reason(): this;
215
+ shouldRetry(): boolean;
216
+ toJSON(): LexErrorData;
217
+ toDownstreamError(): DownstreamError;
218
+ }
219
+ /**
220
+ * Special case of XrpcInternalError that specifically represents errors thrown
221
+ * by {@link Agent.fetchHandler} during the XRPC request. This includes:
222
+ * - Network errors (connection refused, DNS failure)
223
+ * - Request timeouts
224
+ * - Request aborted via AbortSignal
225
+ *
226
+ * These errors are optimistically considered retryable, as many fetch errors
227
+ * are transient and may succeed on retry.
228
+ */
229
+ export declare class XrpcFetchError<M extends Procedure | Query = Procedure | Query> extends XrpcInternalError<M> {
230
+ name: string;
231
+ constructor(method: M, cause: unknown);
232
+ shouldRetry(): boolean;
233
+ toJSON(): LexErrorData;
234
+ toDownstreamError(): DownstreamError;
235
+ }
236
+ /**
237
+ * Union type of all possible XRPC failure types.
238
+ *
239
+ * Used as the return type for safe/non-throwing XRPC methods. Check the
240
+ * `success` property to distinguish between success and failure:
241
+ *
242
+ * @typeParam M - The XRPC method type
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const result = await client.xrpcSafe(someMethod, options)
247
+ * if (result.success) {
248
+ * console.log(result.body) // XrpcResponse
249
+ * } else {
250
+ * // result is XrpcFailure (XrpcResponseError | XrpcInvalidResponseError | XrpcInternalError)
251
+ * console.error(result.error, result.message)
252
+ * }
253
+ * ```
254
+ */
255
+ export type XrpcFailure<M extends Procedure | Query = Procedure | Query> = XrpcResponseError<M> | XrpcInvalidResponseError<M> | XrpcInternalError<M>;
256
+ /**
257
+ * Converts an unknown error into an appropriate {@link XrpcFailure} type.
258
+ *
259
+ * If the error is already an XrpcFailure for the given method, returns it as-is.
260
+ * Otherwise, wraps it in an {@link XrpcInternalError}.
261
+ *
262
+ * @param method - The XRPC method that was called
263
+ * @param cause - The error to convert
264
+ * @returns An XrpcFailure instance
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * try {
269
+ * const response = await fetch(...)
270
+ * // ... process response
271
+ * } catch (err) {
272
+ * return asXrpcFailure(method, err)
273
+ * }
274
+ * ```
275
+ */
276
+ export declare function asXrpcFailure<M extends Procedure | Query>(method: M, cause: unknown): XrpcFailure<M>;
277
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/errors.js ADDED
@@ -0,0 +1,388 @@
1
+ import { LexError } from '@atproto/lex-data';
2
+ import { lexErrorDataSchema, } from '@atproto/lex-schema';
3
+ import { parseWWWAuthenticateHeader } from './www-authenticate.js';
4
+ /**
5
+ * Mapping that allows generating an XRPC error code from an HTTP status code
6
+ * when the response does not contain a valid XRPC error payload. This is used
7
+ * to convert non-XRPC error responses from upstream servers into a standardized
8
+ * XRPC error for downstream clients.
9
+ */
10
+ const StatusErrorCodes = new Map([
11
+ [400, 'InvalidRequest'],
12
+ [401, 'AuthenticationRequired'],
13
+ [403, 'Forbidden'],
14
+ [404, 'XRPCNotSupported'],
15
+ [406, 'NotAcceptable'],
16
+ [413, 'PayloadTooLarge'],
17
+ [415, 'UnsupportedMediaType'],
18
+ [429, 'RateLimitExceeded'],
19
+ [500, 'InternalServerError'],
20
+ [501, 'MethodNotImplemented'],
21
+ [502, 'UpstreamFailure'],
22
+ [503, 'NotEnoughResources'],
23
+ [504, 'UpstreamTimeout'],
24
+ ]);
25
+ /**
26
+ * HTTP status codes that indicate a transient error that may succeed on retry.
27
+ *
28
+ * Includes:
29
+ * - 408 Request Timeout
30
+ * - 425 Too Early
31
+ * - 429 Too Many Requests (rate limited)
32
+ * - 500 Internal Server Error
33
+ * - 502 Bad Gateway
34
+ * - 503 Service Unavailable
35
+ * - 504 Gateway Timeout
36
+ * - 522 Connection Timed Out (Cloudflare)
37
+ * - 524 A Timeout Occurred (Cloudflare)
38
+ */
39
+ export const RETRYABLE_HTTP_STATUS_CODES = new Set([
40
+ 408, 425, 429, 500, 502, 503, 504, 522, 524,
41
+ ]);
42
+ export { LexError };
43
+ /**
44
+ * All unsuccessful responses should follow a standard error response
45
+ * schema. The Content-Type should be application/json, and the payload
46
+ * should be a JSON object with the following fields:
47
+ *
48
+ * - `error` (string, required): type name of the error (generic ASCII
49
+ * constant, no whitespace)
50
+ * - `message` (string, optional): description of the error, appropriate for
51
+ * display to humans
52
+ *
53
+ * This function checks whether a given payload matches this schema.
54
+ */
55
+ export function isXrpcErrorPayload(payload) {
56
+ return (payload != null &&
57
+ payload.encoding === 'application/json' &&
58
+ lexErrorDataSchema.matches(payload.body));
59
+ }
60
+ /**
61
+ * Abstract base class for all XRPC errors.
62
+ *
63
+ * Extends {@link LexError} and implements {@link ResultFailure} for use with
64
+ * safe/result-based error handling patterns.
65
+ *
66
+ * @typeParam M - The XRPC method type (Procedure or Query)
67
+ * @typeParam N - The error code type
68
+ * @typeParam TReason - The reason type for ResultFailure
69
+ *
70
+ * @see {@link XrpcResponseError} - For valid XRPC error responses
71
+ * @see {@link XrpcInvalidResponseError} - For invalid/unexpected responses
72
+ * @see {@link XrpcInternalError} - For network/internal errors
73
+ */
74
+ export class XrpcError extends LexError {
75
+ constructor(method, error, message = `${error} Lexicon RPC error`, options) {
76
+ super(error, message, options);
77
+ this.method = method;
78
+ this.name = 'XrpcError';
79
+ /**
80
+ * @see {@link ResultFailure.success}
81
+ */
82
+ this.success = false;
83
+ }
84
+ matchesSchemaErrors() {
85
+ return this.method.errors?.includes(this.error) ?? false;
86
+ }
87
+ }
88
+ /**
89
+ * Error class for valid XRPC error responses from the server.
90
+ *
91
+ * This represents a properly formatted XRPC error where the server returned
92
+ * a non-2xx status with a valid JSON error payload containing `error` and
93
+ * optional `message` fields.
94
+ *
95
+ * Use {@link matchesSchemaErrors} to check if the error matches the method's declared
96
+ * error types for type-safe error handling.
97
+ *
98
+ * @typeParam M - The XRPC method type
99
+ * @typeParam N - The error code type (inferred from method or generic)
100
+ *
101
+ * @example Handling specific errors
102
+ * ```typescript
103
+ * try {
104
+ * await client.xrpc(someMethod, options)
105
+ * } catch (err) {
106
+ * if (err instanceof XrpcResponseError && err.error === 'RecordNotFound') {
107
+ * // Handle not found case
108
+ * }
109
+ * }
110
+ * ```
111
+ */
112
+ export class XrpcResponseError extends XrpcError {
113
+ constructor(method, response, payload, options) {
114
+ const { error, message } = isXrpcErrorPayload(payload)
115
+ ? payload.body
116
+ : {
117
+ error: StatusErrorCodes.get(response.status) ??
118
+ (response.status >= 500 ? 'UpstreamFailure' : 'InvalidRequest'),
119
+ message: buildResponseOverviewMessage(response),
120
+ };
121
+ super(method, error, message, options);
122
+ this.response = response;
123
+ this.payload = payload;
124
+ this.name = 'XrpcResponseError';
125
+ }
126
+ get reason() {
127
+ return this;
128
+ }
129
+ shouldRetry() {
130
+ return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status);
131
+ }
132
+ toJSON() {
133
+ // Return the original error payload if it's a valid XRPC error, otherwise
134
+ // convert to an XRPC error format.
135
+ const { payload } = this;
136
+ if (isXrpcErrorPayload(payload)) {
137
+ return payload.body;
138
+ }
139
+ return super.toJSON();
140
+ }
141
+ toDownstreamError() {
142
+ const { status, headers } = this.response;
143
+ // If the upstream server returned a 500 error, we want to return a 502 Bad
144
+ // Gateway to downstream clients, as the issue is with the upstream server,
145
+ // not us. We still return the original error code and message in the body
146
+ // for transparency, but we do not want to expose internal server errors
147
+ // from the upstream server as-is to downstream clients.
148
+ return {
149
+ status: status === 500 ? 502 : status,
150
+ headers: stripHopByHopHeaders(headers),
151
+ body: this.toJSON(),
152
+ };
153
+ }
154
+ get status() {
155
+ return this.response.status;
156
+ }
157
+ get headers() {
158
+ return this.response.headers;
159
+ }
160
+ get body() {
161
+ return this.payload?.body;
162
+ }
163
+ }
164
+ /**
165
+ * Error class for 401 Unauthorized XRPC responses.
166
+ *
167
+ * Extends {@link XrpcResponseError} with access to parsed WWW-Authenticate header
168
+ * information, useful for implementing authentication flows.
169
+ *
170
+ * Authentication errors are never retryable as they require user intervention
171
+ * (e.g., re-authentication, token refresh).
172
+ *
173
+ * @typeParam M - The XRPC method type
174
+ * @typeParam N - The error code type
175
+ *
176
+ * @example Handling authentication errors
177
+ * ```typescript
178
+ * try {
179
+ * await client.xrpc(someMethod, options)
180
+ * } catch (err) {
181
+ * if (err instanceof XrpcAuthenticationError) {
182
+ * const { DPoP } = err.wwwAuthenticate
183
+ * if (DPoP?.error === 'use_dpop_nonce') {
184
+ * // Handle DPoP nonce requirement
185
+ * }
186
+ * }
187
+ * }
188
+ * ```
189
+ */
190
+ export class XrpcAuthenticationError extends XrpcResponseError {
191
+ constructor() {
192
+ super(...arguments);
193
+ this.name = 'XrpcAuthenticationError';
194
+ }
195
+ shouldRetry() {
196
+ return false;
197
+ }
198
+ #wwwAuthenticateCached;
199
+ /**
200
+ * Parsed WWW-Authenticate header from the response.
201
+ * Contains authentication scheme parameters (e.g., Bearer realm, DPoP nonce).
202
+ */
203
+ get wwwAuthenticate() {
204
+ return (this.#wwwAuthenticateCached ??=
205
+ parseWWWAuthenticateHeader(this.response.headers.get('www-authenticate')) ?? {});
206
+ }
207
+ }
208
+ /**
209
+ * Error class for invalid or unprocessable XRPC responses from upstream servers.
210
+ *
211
+ * This occurs when the server returns a response that doesn't conform to the
212
+ * XRPC protocol, such as:
213
+ * - Missing or invalid Content-Type header
214
+ * - Response body that doesn't match the method's output schema
215
+ * - Non-JSON error responses
216
+ * - Responses from non-XRPC endpoints
217
+ *
218
+ * The error code is always 'InvalidResponse' and maps to HTTP 502 Bad Gateway
219
+ * when converted to a response. This should allow downstream clients to
220
+ * determine at which boundary the error occurred.
221
+ *
222
+ * @typeParam M - The XRPC method type
223
+ */
224
+ export class XrpcInvalidResponseError extends XrpcError {
225
+ constructor(method, response, payload, message = buildResponseOverviewMessage(response), options) {
226
+ super(method, 'InvalidResponse', message, options);
227
+ this.response = response;
228
+ this.payload = payload;
229
+ this.name = 'XrpcInvalidResponseError';
230
+ }
231
+ get reason() {
232
+ return this;
233
+ }
234
+ shouldRetry() {
235
+ return RETRYABLE_HTTP_STATUS_CODES.has(this.response.status);
236
+ }
237
+ toDownstreamError() {
238
+ return { status: 502, body: this.toJSON() };
239
+ }
240
+ }
241
+ /**
242
+ * Error class for invalid XRPC responses that fail schema validation.
243
+ *
244
+ * This is a specific type of {@link XrpcInvalidResponseError} that indicates the
245
+ * upstream server returned a response that was structurally valid but did not
246
+ * conform to the expected schema for the method. This likely indicates a
247
+ * mismatch between client and server versions or an issue with the server's
248
+ * XRPC implementation.
249
+ *
250
+ * @typeParam M - The XRPC method type
251
+ */
252
+ export class XrpcResponseValidationError extends XrpcInvalidResponseError {
253
+ constructor(method, response, payload, cause) {
254
+ super(method, response, payload, `Invalid response payload: ${cause.message}`, { cause });
255
+ this.cause = cause;
256
+ this.name = 'XrpcResponseValidationError';
257
+ }
258
+ }
259
+ /**
260
+ * Error class for unexpected internal/client-side errors during XRPC requests.
261
+ *
262
+ * The error code is always 'InternalServerError' and these errors not
263
+ * considered retryable as they stem from unforeseen issues in the
264
+ * implementation.
265
+ *
266
+ * @typeParam M - The XRPC method type
267
+ */
268
+ export class XrpcInternalError extends XrpcError {
269
+ constructor(method, message, options) {
270
+ super(method, 'InternalServerError', message ?? 'Unable to fulfill XRPC request', options);
271
+ this.name = 'XrpcInternalError';
272
+ }
273
+ get reason() {
274
+ return this;
275
+ }
276
+ shouldRetry() {
277
+ return false;
278
+ }
279
+ toJSON() {
280
+ // @NOTE Do not expose internal error details to downstream clients
281
+ return { error: this.error, message: 'Internal Server Error' };
282
+ }
283
+ toDownstreamError() {
284
+ return { status: 500, body: this.toJSON() };
285
+ }
286
+ }
287
+ /**
288
+ * Special case of XrpcInternalError that specifically represents errors thrown
289
+ * by {@link Agent.fetchHandler} during the XRPC request. This includes:
290
+ * - Network errors (connection refused, DNS failure)
291
+ * - Request timeouts
292
+ * - Request aborted via AbortSignal
293
+ *
294
+ * These errors are optimistically considered retryable, as many fetch errors
295
+ * are transient and may succeed on retry.
296
+ */
297
+ export class XrpcFetchError extends XrpcInternalError {
298
+ constructor(method, cause) {
299
+ const message = cause instanceof Error ? cause.message : String(cause);
300
+ super(method, `Unexpected fetchHandler() error: ${message}`, { cause });
301
+ this.name = 'XrpcFetchError';
302
+ }
303
+ shouldRetry() {
304
+ // Ideally, we would inspect the reason to determine if it's retryable (by
305
+ // detecting network errors, timeouts, etc.). Since these cases are highly
306
+ // platform-dependent, we optimistically assume all fetch errors are
307
+ // transient and retryable.
308
+ return true;
309
+ }
310
+ toJSON() {
311
+ // @NOTE Do not expose internal error details to downstream clients
312
+ return { error: this.error, message: 'Failed to perform upstream request' };
313
+ }
314
+ toDownstreamError() {
315
+ // While it might technically be a 500 error, we use 502 Bad Gateway here to
316
+ // indicate that the error occurred while communicating with the upstream
317
+ // server, allowing downstream clients to distinguish between errors in our
318
+ // internal processing (500) and errors in the upstream server or network
319
+ // (502).
320
+ return { status: 502, body: this.toJSON() };
321
+ }
322
+ }
323
+ /**
324
+ * Converts an unknown error into an appropriate {@link XrpcFailure} type.
325
+ *
326
+ * If the error is already an XrpcFailure for the given method, returns it as-is.
327
+ * Otherwise, wraps it in an {@link XrpcInternalError}.
328
+ *
329
+ * @param method - The XRPC method that was called
330
+ * @param cause - The error to convert
331
+ * @returns An XrpcFailure instance
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * try {
336
+ * const response = await fetch(...)
337
+ * // ... process response
338
+ * } catch (err) {
339
+ * return asXrpcFailure(method, err)
340
+ * }
341
+ * ```
342
+ */
343
+ export function asXrpcFailure(method, cause) {
344
+ if (cause instanceof XrpcResponseError ||
345
+ cause instanceof XrpcInvalidResponseError ||
346
+ cause instanceof XrpcInternalError) {
347
+ if (cause.method === method)
348
+ return cause;
349
+ }
350
+ return new XrpcInternalError(method, undefined, { cause });
351
+ }
352
+ const HOP_BY_HOP_HEADERS = new Set([
353
+ 'connection',
354
+ 'keep-alive',
355
+ 'proxy-authenticate',
356
+ 'proxy-authorization',
357
+ 'te',
358
+ 'trailer',
359
+ 'transfer-encoding',
360
+ 'upgrade',
361
+ ]);
362
+ function stripHopByHopHeaders(headers) {
363
+ const result = new Headers(headers);
364
+ // Remove statically known hop-by-hop headers
365
+ for (const name of HOP_BY_HOP_HEADERS) {
366
+ result.delete(name);
367
+ }
368
+ // Remove headers listed in the "Connection" header
369
+ const connection = headers.get('connection');
370
+ if (connection) {
371
+ for (const name of connection.split(',')) {
372
+ result.delete(name.trim());
373
+ }
374
+ }
375
+ // These are not actually hop-by-hop headers, but we remove them because the
376
+ // upstream payload gets parsed and re-serialized, so content length and
377
+ // encoding may no longer be accurate.
378
+ result.delete('content-length');
379
+ result.delete('content-encoding');
380
+ return result;
381
+ }
382
+ function buildResponseOverviewMessage(response) {
383
+ if (response.status < 400) {
384
+ return `Upstream server responded with an invalid status code (${response.status})`;
385
+ }
386
+ return `Upstream server responded with a ${response.status} error`;
387
+ }
388
+ //# sourceMappingURL=errors.js.map