@atproto/xrpc-server 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/auth.js +11 -11
  3. package/dist/auth.js.map +1 -1
  4. package/dist/errors.d.ts +67 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +202 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/index.d.ts +4 -3
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +5 -3
  11. package/dist/index.js.map +1 -1
  12. package/dist/rate-limiter.d.ts +69 -32
  13. package/dist/rate-limiter.d.ts.map +1 -1
  14. package/dist/rate-limiter.js +58 -41
  15. package/dist/rate-limiter.js.map +1 -1
  16. package/dist/server.d.ts +19 -14
  17. package/dist/server.d.ts.map +1 -1
  18. package/dist/server.js +151 -137
  19. package/dist/server.js.map +1 -1
  20. package/dist/types.d.ts +80 -178
  21. package/dist/types.d.ts.map +1 -1
  22. package/dist/types.js +9 -226
  23. package/dist/types.js.map +1 -1
  24. package/dist/util.d.ts +9 -8
  25. package/dist/util.d.ts.map +1 -1
  26. package/dist/util.js +148 -108
  27. package/dist/util.js.map +1 -1
  28. package/package.json +4 -3
  29. package/src/auth.ts +1 -1
  30. package/src/errors.ts +293 -0
  31. package/src/index.ts +9 -3
  32. package/src/rate-limiter.ts +188 -96
  33. package/src/server.ts +198 -154
  34. package/src/types.ts +144 -439
  35. package/src/util.ts +176 -125
  36. package/tests/auth.test.ts +2 -2
  37. package/tests/bodies.test.ts +18 -27
  38. package/tests/errors.test.ts +1 -1
  39. package/tests/ipld.test.ts +15 -14
  40. package/tests/parameters.test.ts +4 -7
  41. package/tests/procedures.test.ts +22 -34
  42. package/tests/queries.test.ts +9 -12
  43. package/tests/rate-limiter.test.ts +7 -7
  44. package/tests/responses.test.ts +12 -15
  45. package/tsconfig.build.tsbuildinfo +1 -1
package/src/errors.ts ADDED
@@ -0,0 +1,293 @@
1
+ import { isHttpError } from 'http-errors'
2
+ import { z } from 'zod'
3
+ import {
4
+ ResponseType,
5
+ ResponseTypeStrings,
6
+ XRPCError as XRPCClientError,
7
+ httpResponseCodeToName,
8
+ httpResponseCodeToString,
9
+ } from '@atproto/xrpc'
10
+
11
+ // @NOTE Do not depend (directly or indirectly) on "./types" here, as it would
12
+ // create a circular dependency.
13
+
14
+ export const errorResult = z.object({
15
+ status: z.number(),
16
+ error: z.string().optional(),
17
+ message: z.string().optional(),
18
+ })
19
+ export type ErrorResult = z.infer<typeof errorResult>
20
+
21
+ export function isErrorResult(v: unknown): v is ErrorResult {
22
+ return errorResult.safeParse(v).success
23
+ }
24
+
25
+ export function excludeErrorResult<V>(v: V) {
26
+ if (isErrorResult(v)) throw XRPCError.fromErrorResult(v)
27
+ return v as Exclude<V, ErrorResult>
28
+ }
29
+
30
+ export { ResponseType }
31
+
32
+ export class XRPCError extends Error {
33
+ constructor(
34
+ public type: ResponseType,
35
+ public errorMessage?: string,
36
+ public customErrorName?: string,
37
+ options?: ErrorOptions,
38
+ ) {
39
+ super(errorMessage, options)
40
+ }
41
+
42
+ get statusCode(): number {
43
+ const { type } = this
44
+
45
+ // Fool-proofing. `new XRPCError(123.5 as number, '')` does not generate a TypeScript error.
46
+ // Because of this, we can end-up with any numeric value instead of an actual `ResponseType`.
47
+ // For legacy reasons, the `type` argument is not checked in the constructor, so we check it here.
48
+ if (type < 400 || type >= 600 || !Number.isFinite(type)) {
49
+ return 500
50
+ }
51
+
52
+ return type
53
+ }
54
+
55
+ get payload() {
56
+ return {
57
+ error: this.customErrorName ?? this.typeName,
58
+ message:
59
+ this.type === ResponseType.InternalServerError
60
+ ? this.typeStr // Do not respond with error details for 500s
61
+ : this.errorMessage || this.typeStr,
62
+ }
63
+ }
64
+
65
+ get typeName(): string | undefined {
66
+ return ResponseType[this.type]
67
+ }
68
+
69
+ get typeStr(): string | undefined {
70
+ return ResponseTypeStrings[this.type]
71
+ }
72
+
73
+ static fromError(cause: unknown): XRPCError {
74
+ if (cause instanceof XRPCError) {
75
+ return cause
76
+ }
77
+
78
+ if (cause instanceof XRPCClientError) {
79
+ const { error, message, type } = mapFromClientError(cause)
80
+ return new XRPCError(type, message, error, { cause })
81
+ }
82
+
83
+ if (isHttpError(cause)) {
84
+ return new XRPCError(cause.status, cause.message, cause.name, { cause })
85
+ }
86
+
87
+ if (isErrorResult(cause)) {
88
+ return this.fromErrorResult(cause)
89
+ }
90
+
91
+ if (cause instanceof Error) {
92
+ return new InternalServerError(cause.message, undefined, { cause })
93
+ }
94
+
95
+ return new InternalServerError(
96
+ 'Unexpected internal server error',
97
+ undefined,
98
+ { cause },
99
+ )
100
+ }
101
+
102
+ static fromErrorResult(err: ErrorResult): XRPCError {
103
+ return new XRPCError(err.status, err.message, err.error, { cause: err })
104
+ }
105
+ }
106
+
107
+ export class InvalidRequestError extends XRPCError {
108
+ constructor(
109
+ errorMessage?: string,
110
+ customErrorName?: string,
111
+ options?: ErrorOptions,
112
+ ) {
113
+ super(ResponseType.InvalidRequest, errorMessage, customErrorName, options)
114
+ }
115
+
116
+ [Symbol.hasInstance](instance: unknown): boolean {
117
+ return (
118
+ instance instanceof XRPCError &&
119
+ instance.type === ResponseType.InvalidRequest
120
+ )
121
+ }
122
+ }
123
+
124
+ export class AuthRequiredError extends XRPCError {
125
+ constructor(
126
+ errorMessage?: string,
127
+ customErrorName?: string,
128
+ options?: ErrorOptions,
129
+ ) {
130
+ super(
131
+ ResponseType.AuthenticationRequired,
132
+ errorMessage,
133
+ customErrorName,
134
+ options,
135
+ )
136
+ }
137
+
138
+ [Symbol.hasInstance](instance: unknown): boolean {
139
+ return (
140
+ instance instanceof XRPCError &&
141
+ instance.type === ResponseType.AuthenticationRequired
142
+ )
143
+ }
144
+ }
145
+
146
+ export class ForbiddenError extends XRPCError {
147
+ constructor(
148
+ errorMessage?: string,
149
+ customErrorName?: string,
150
+ options?: ErrorOptions,
151
+ ) {
152
+ super(ResponseType.Forbidden, errorMessage, customErrorName, options)
153
+ }
154
+
155
+ [Symbol.hasInstance](instance: unknown): boolean {
156
+ return (
157
+ instance instanceof XRPCError && instance.type === ResponseType.Forbidden
158
+ )
159
+ }
160
+ }
161
+
162
+ export class InternalServerError extends XRPCError {
163
+ constructor(
164
+ errorMessage?: string,
165
+ customErrorName?: string,
166
+ options?: ErrorOptions,
167
+ ) {
168
+ super(
169
+ ResponseType.InternalServerError,
170
+ errorMessage,
171
+ customErrorName,
172
+ options,
173
+ )
174
+ }
175
+
176
+ [Symbol.hasInstance](instance: unknown): boolean {
177
+ return (
178
+ instance instanceof XRPCError &&
179
+ instance.type === ResponseType.InternalServerError
180
+ )
181
+ }
182
+ }
183
+
184
+ export class UpstreamFailureError extends XRPCError {
185
+ constructor(
186
+ errorMessage?: string,
187
+ customErrorName?: string,
188
+ options?: ErrorOptions,
189
+ ) {
190
+ super(ResponseType.UpstreamFailure, errorMessage, customErrorName, options)
191
+ }
192
+
193
+ [Symbol.hasInstance](instance: unknown): boolean {
194
+ return (
195
+ instance instanceof XRPCError &&
196
+ instance.type === ResponseType.UpstreamFailure
197
+ )
198
+ }
199
+ }
200
+
201
+ export class NotEnoughResourcesError extends XRPCError {
202
+ constructor(
203
+ errorMessage?: string,
204
+ customErrorName?: string,
205
+ options?: ErrorOptions,
206
+ ) {
207
+ super(
208
+ ResponseType.NotEnoughResources,
209
+ errorMessage,
210
+ customErrorName,
211
+ options,
212
+ )
213
+ }
214
+
215
+ [Symbol.hasInstance](instance: unknown): boolean {
216
+ return (
217
+ instance instanceof XRPCError &&
218
+ instance.type === ResponseType.NotEnoughResources
219
+ )
220
+ }
221
+ }
222
+
223
+ export class UpstreamTimeoutError extends XRPCError {
224
+ constructor(
225
+ errorMessage?: string,
226
+ customErrorName?: string,
227
+ options?: ErrorOptions,
228
+ ) {
229
+ super(ResponseType.UpstreamTimeout, errorMessage, customErrorName, options)
230
+ }
231
+
232
+ [Symbol.hasInstance](instance: unknown): boolean {
233
+ return (
234
+ instance instanceof XRPCError &&
235
+ instance.type === ResponseType.UpstreamTimeout
236
+ )
237
+ }
238
+ }
239
+
240
+ export class MethodNotImplementedError extends XRPCError {
241
+ constructor(
242
+ errorMessage?: string,
243
+ customErrorName?: string,
244
+ options?: ErrorOptions,
245
+ ) {
246
+ super(
247
+ ResponseType.MethodNotImplemented,
248
+ errorMessage,
249
+ customErrorName,
250
+ options,
251
+ )
252
+ }
253
+
254
+ [Symbol.hasInstance](instance: unknown): boolean {
255
+ return (
256
+ instance instanceof XRPCError &&
257
+ instance.type === ResponseType.MethodNotImplemented
258
+ )
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Converts an upstream XRPC {@link ResponseType} into a downstream {@link ResponseType}.
264
+ */
265
+ function mapFromClientError(error: XRPCClientError): {
266
+ error: string
267
+ message: string
268
+ type: ResponseType
269
+ } {
270
+ switch (error.status) {
271
+ case ResponseType.InvalidResponse:
272
+ // Upstream server returned an XRPC response that is not compatible with our internal lexicon definitions for that XRPC method.
273
+ // @NOTE This could be reflected as both a 500 ("we" are at fault) and 502 ("they" are at fault). Let's be gents about it.
274
+ return {
275
+ error: httpResponseCodeToName(ResponseType.InternalServerError),
276
+ message: httpResponseCodeToString(ResponseType.InternalServerError),
277
+ type: ResponseType.InternalServerError,
278
+ }
279
+ case ResponseType.Unknown:
280
+ // Typically a network error / unknown host
281
+ return {
282
+ error: httpResponseCodeToName(ResponseType.InternalServerError),
283
+ message: httpResponseCodeToString(ResponseType.InternalServerError),
284
+ type: ResponseType.InternalServerError,
285
+ }
286
+ default:
287
+ return {
288
+ error: error.error,
289
+ message: error.message,
290
+ type: error.status,
291
+ }
292
+ }
293
+ }
package/src/index.ts CHANGED
@@ -1,8 +1,14 @@
1
- export * from './types'
2
1
  export * from './auth'
2
+ export * from './errors'
3
+ export * from './rate-limiter'
3
4
  export * from './server'
4
5
  export * from './stream'
5
- export * from './rate-limiter'
6
+ export * from './types'
6
7
 
8
+ export {
9
+ ServerTimer,
10
+ parseReqEncoding,
11
+ parseReqNsid,
12
+ serverTimingHeader,
13
+ } from './util'
7
14
  export type { ServerTiming } from './util'
8
- export { ServerTimer, parseReqNsid, serverTimingHeader } from './util'