@atproto/xrpc-server 0.4.1 → 0.4.3
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 +14 -0
- package/LICENSE.txt +1 -1
- package/dist/auth.d.ts +2 -2
- package/dist/index.js +336 -33
- package/dist/index.js.map +3 -3
- package/dist/rate-limiter.d.ts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/stream/server.d.ts +1 -1
- package/dist/stream/types.d.ts +4 -4
- package/dist/types.d.ts +52 -34
- package/dist/util.d.ts +2 -2
- package/package.json +4 -4
- package/src/rate-limiter.ts +3 -0
- package/src/server.ts +46 -9
- package/src/types.ts +22 -10
- package/src/util.ts +3 -3
package/dist/rate-limiter.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RateLimiterAbstract, RateLimiterRes } from 'rate-limiter-flexible';
|
|
2
2
|
import { CalcKeyFn, CalcPointsFn, RateLimitExceededError, RateLimiterConsume, RateLimiterI, RateLimiterStatus, XRPCReqContext } from './types';
|
|
3
|
-
export
|
|
3
|
+
export type RateLimiterOpts = {
|
|
4
4
|
keyPrefix: string;
|
|
5
5
|
durationMs: number;
|
|
6
6
|
points: number;
|
package/dist/server.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare class Server {
|
|
|
22
22
|
addLexicons(docs: LexiconDoc[]): void;
|
|
23
23
|
protected addRoute(nsid: string, def: LexXrpcQuery | LexXrpcProcedure, config: XRPCHandlerConfig): Promise<void>;
|
|
24
24
|
catchall(req: express.Request, _res: express.Response, next: NextFunction): Promise<void>;
|
|
25
|
-
createHandler(nsid: string, def: LexXrpcQuery | LexXrpcProcedure,
|
|
25
|
+
createHandler(nsid: string, def: LexXrpcQuery | LexXrpcProcedure, routeCfg: XRPCHandlerConfig): RequestHandler;
|
|
26
26
|
protected addSubscription(nsid: string, def: LexXrpcSubscription, config: XRPCStreamHandlerConfig): Promise<void>;
|
|
27
27
|
private enableStreamingOnListen;
|
|
28
28
|
private setupRouteRateLimits;
|
package/dist/stream/server.d.ts
CHANGED
|
@@ -8,4 +8,4 @@ export declare class XrpcStreamServer {
|
|
|
8
8
|
handler: Handler;
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
|
-
export
|
|
11
|
+
export type Handler = (req: IncomingMessage, signal: AbortSignal, socket: WebSocket, server: XrpcStreamServer) => AsyncIterable<Frame>;
|
package/dist/stream/types.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare const messageFrameHeader: z.ZodObject<{
|
|
|
13
13
|
op: FrameType.Message;
|
|
14
14
|
t?: string | undefined;
|
|
15
15
|
}>;
|
|
16
|
-
export
|
|
16
|
+
export type MessageFrameHeader = z.infer<typeof messageFrameHeader>;
|
|
17
17
|
export declare const errorFrameHeader: z.ZodObject<{
|
|
18
18
|
op: z.ZodLiteral<FrameType.Error>;
|
|
19
19
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -31,8 +31,8 @@ export declare const errorFrameBody: z.ZodObject<{
|
|
|
31
31
|
error: string;
|
|
32
32
|
message?: string | undefined;
|
|
33
33
|
}>;
|
|
34
|
-
export
|
|
35
|
-
export
|
|
34
|
+
export type ErrorFrameHeader = z.infer<typeof errorFrameHeader>;
|
|
35
|
+
export type ErrorFrameBody<T extends string = string> = {
|
|
36
36
|
error: T;
|
|
37
37
|
} & z.infer<typeof errorFrameBody>;
|
|
38
38
|
export declare const frameHeader: z.ZodUnion<[z.ZodObject<{
|
|
@@ -51,7 +51,7 @@ export declare const frameHeader: z.ZodUnion<[z.ZodObject<{
|
|
|
51
51
|
}, {
|
|
52
52
|
op: FrameType.Error;
|
|
53
53
|
}>]>;
|
|
54
|
-
export
|
|
54
|
+
export type FrameHeader = z.infer<typeof frameHeader>;
|
|
55
55
|
export declare class DisconnectError extends Error {
|
|
56
56
|
wsCode: CloseCode;
|
|
57
57
|
xrpcCode?: string | undefined;
|
package/dist/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { IncomingMessage } from 'http';
|
|
|
3
3
|
import express from 'express';
|
|
4
4
|
import zod from 'zod';
|
|
5
5
|
import { ResponseType } from '@atproto/xrpc';
|
|
6
|
-
export
|
|
6
|
+
export type Options = {
|
|
7
7
|
validateResponse?: boolean;
|
|
8
8
|
payload?: {
|
|
9
9
|
jsonLimit?: number;
|
|
@@ -16,9 +16,9 @@ export declare type Options = {
|
|
|
16
16
|
shared?: ServerRateLimitDescription[];
|
|
17
17
|
};
|
|
18
18
|
};
|
|
19
|
-
export
|
|
20
|
-
export
|
|
21
|
-
export
|
|
19
|
+
export type UndecodedParams = typeof express.request['query'];
|
|
20
|
+
export type Primitive = string | number | boolean;
|
|
21
|
+
export type Params = Record<string, Primitive | Primitive[] | undefined>;
|
|
22
22
|
export declare const handlerInput: zod.ZodObject<{
|
|
23
23
|
encoding: zod.ZodString;
|
|
24
24
|
body: zod.ZodAny;
|
|
@@ -29,7 +29,7 @@ export declare const handlerInput: zod.ZodObject<{
|
|
|
29
29
|
encoding: string;
|
|
30
30
|
body?: any;
|
|
31
31
|
}>;
|
|
32
|
-
export
|
|
32
|
+
export type HandlerInput = zod.infer<typeof handlerInput>;
|
|
33
33
|
export declare const handlerAuth: zod.ZodObject<{
|
|
34
34
|
credentials: zod.ZodAny;
|
|
35
35
|
artifacts: zod.ZodAny;
|
|
@@ -40,7 +40,7 @@ export declare const handlerAuth: zod.ZodObject<{
|
|
|
40
40
|
credentials?: any;
|
|
41
41
|
artifacts?: any;
|
|
42
42
|
}>;
|
|
43
|
-
export
|
|
43
|
+
export type HandlerAuth = zod.infer<typeof handlerAuth>;
|
|
44
44
|
export declare const handlerSuccess: zod.ZodObject<{
|
|
45
45
|
encoding: zod.ZodString;
|
|
46
46
|
body: zod.ZodAny;
|
|
@@ -54,7 +54,21 @@ export declare const handlerSuccess: zod.ZodObject<{
|
|
|
54
54
|
body?: any;
|
|
55
55
|
headers?: Record<string, string> | undefined;
|
|
56
56
|
}>;
|
|
57
|
-
export
|
|
57
|
+
export type HandlerSuccess = zod.infer<typeof handlerSuccess>;
|
|
58
|
+
export declare const handlerPipeThrough: zod.ZodObject<{
|
|
59
|
+
encoding: zod.ZodString;
|
|
60
|
+
buffer: zod.ZodType<ArrayBuffer, zod.ZodTypeDef, ArrayBuffer>;
|
|
61
|
+
headers: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodString>>;
|
|
62
|
+
}, "strip", zod.ZodTypeAny, {
|
|
63
|
+
encoding: string;
|
|
64
|
+
buffer: ArrayBuffer;
|
|
65
|
+
headers?: Record<string, string> | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
encoding: string;
|
|
68
|
+
buffer: ArrayBuffer;
|
|
69
|
+
headers?: Record<string, string> | undefined;
|
|
70
|
+
}>;
|
|
71
|
+
export type HandlerPipeThrough = zod.infer<typeof handlerPipeThrough>;
|
|
58
72
|
export declare const handlerError: zod.ZodObject<{
|
|
59
73
|
status: zod.ZodNumber;
|
|
60
74
|
error: zod.ZodOptional<zod.ZodString>;
|
|
@@ -68,67 +82,67 @@ export declare const handlerError: zod.ZodObject<{
|
|
|
68
82
|
error?: string | undefined;
|
|
69
83
|
message?: string | undefined;
|
|
70
84
|
}>;
|
|
71
|
-
export
|
|
72
|
-
export
|
|
73
|
-
export
|
|
85
|
+
export type HandlerError = zod.infer<typeof handlerError>;
|
|
86
|
+
export type HandlerOutput = HandlerSuccess | HandlerPipeThrough | HandlerError;
|
|
87
|
+
export type XRPCReqContext = {
|
|
74
88
|
auth: HandlerAuth | undefined;
|
|
75
89
|
params: Params;
|
|
76
90
|
input: HandlerInput | undefined;
|
|
77
91
|
req: express.Request;
|
|
78
92
|
res: express.Response;
|
|
79
93
|
};
|
|
80
|
-
export
|
|
81
|
-
export
|
|
94
|
+
export type XRPCHandler = (ctx: XRPCReqContext) => Promise<HandlerOutput> | HandlerOutput | undefined;
|
|
95
|
+
export type XRPCStreamHandler = (ctx: {
|
|
82
96
|
auth: HandlerAuth | undefined;
|
|
83
97
|
params: Params;
|
|
84
98
|
req: IncomingMessage;
|
|
85
99
|
signal: AbortSignal;
|
|
86
100
|
}) => AsyncIterable<unknown>;
|
|
87
|
-
export
|
|
88
|
-
export
|
|
101
|
+
export type AuthOutput = HandlerAuth | HandlerError;
|
|
102
|
+
export type AuthVerifier = (ctx: {
|
|
89
103
|
req: express.Request;
|
|
90
104
|
res: express.Response;
|
|
91
105
|
}) => Promise<AuthOutput> | AuthOutput;
|
|
92
|
-
export
|
|
106
|
+
export type StreamAuthVerifier = (ctx: {
|
|
93
107
|
req: IncomingMessage;
|
|
94
108
|
}) => Promise<AuthOutput> | AuthOutput;
|
|
95
|
-
export
|
|
96
|
-
export
|
|
109
|
+
export type CalcKeyFn = (ctx: XRPCReqContext) => string | null;
|
|
110
|
+
export type CalcPointsFn = (ctx: XRPCReqContext) => number;
|
|
97
111
|
export interface RateLimiterI {
|
|
98
112
|
consume: RateLimiterConsume;
|
|
99
113
|
}
|
|
100
|
-
export
|
|
114
|
+
export type RateLimiterConsume = (ctx: XRPCReqContext, opts?: {
|
|
101
115
|
calcKey?: CalcKeyFn;
|
|
102
116
|
calcPoints?: CalcPointsFn;
|
|
103
117
|
}) => Promise<RateLimiterStatus | RateLimitExceededError | null>;
|
|
104
|
-
export
|
|
118
|
+
export type RateLimiterCreator = (opts: {
|
|
105
119
|
keyPrefix: string;
|
|
106
120
|
durationMs: number;
|
|
107
121
|
points: number;
|
|
108
|
-
calcKey?:
|
|
109
|
-
calcPoints?:
|
|
122
|
+
calcKey?: CalcKeyFn;
|
|
123
|
+
calcPoints?: CalcPointsFn;
|
|
110
124
|
}) => RateLimiterI;
|
|
111
|
-
export
|
|
125
|
+
export type ServerRateLimitDescription = {
|
|
112
126
|
name: string;
|
|
113
127
|
durationMs: number;
|
|
114
128
|
points: number;
|
|
115
|
-
calcKey?:
|
|
116
|
-
calcPoints?:
|
|
129
|
+
calcKey?: CalcKeyFn;
|
|
130
|
+
calcPoints?: CalcPointsFn;
|
|
117
131
|
};
|
|
118
|
-
export
|
|
132
|
+
export type SharedRateLimitOpts = {
|
|
119
133
|
name: string;
|
|
120
|
-
calcKey?:
|
|
121
|
-
calcPoints?:
|
|
134
|
+
calcKey?: CalcKeyFn;
|
|
135
|
+
calcPoints?: CalcPointsFn;
|
|
122
136
|
};
|
|
123
|
-
export
|
|
137
|
+
export type RouteRateLimitOpts = {
|
|
124
138
|
durationMs: number;
|
|
125
139
|
points: number;
|
|
126
|
-
calcKey?:
|
|
127
|
-
calcPoints?:
|
|
140
|
+
calcKey?: CalcKeyFn;
|
|
141
|
+
calcPoints?: CalcPointsFn;
|
|
128
142
|
};
|
|
129
|
-
export
|
|
143
|
+
export type HandlerRateLimitOpts = SharedRateLimitOpts | RouteRateLimitOpts;
|
|
130
144
|
export declare const isShared: (opts: HandlerRateLimitOpts) => opts is SharedRateLimitOpts;
|
|
131
|
-
export
|
|
145
|
+
export type RateLimiterStatus = {
|
|
132
146
|
limit: number;
|
|
133
147
|
duration: number;
|
|
134
148
|
remainingPoints: number;
|
|
@@ -136,12 +150,16 @@ export declare type RateLimiterStatus = {
|
|
|
136
150
|
consumedPoints: number;
|
|
137
151
|
isFirstInDuration: boolean;
|
|
138
152
|
};
|
|
139
|
-
export
|
|
153
|
+
export type RouteOpts = {
|
|
154
|
+
blobLimit?: number;
|
|
155
|
+
};
|
|
156
|
+
export type XRPCHandlerConfig = {
|
|
157
|
+
opts?: RouteOpts;
|
|
140
158
|
rateLimit?: HandlerRateLimitOpts | HandlerRateLimitOpts[];
|
|
141
159
|
auth?: AuthVerifier;
|
|
142
160
|
handler: XRPCHandler;
|
|
143
161
|
};
|
|
144
|
-
export
|
|
162
|
+
export type XRPCStreamHandlerConfig = {
|
|
145
163
|
auth?: StreamAuthVerifier;
|
|
146
164
|
handler: XRPCStreamHandler;
|
|
147
165
|
};
|
package/dist/util.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { Lexicons, LexXrpcProcedure, LexXrpcQuery, LexXrpcSubscription } from '@atproto/lexicon';
|
|
3
|
-
import { UndecodedParams, Params, HandlerInput, HandlerSuccess,
|
|
3
|
+
import { UndecodedParams, Params, HandlerInput, HandlerSuccess, RouteOpts } from './types';
|
|
4
4
|
export declare function decodeQueryParams(def: LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription, params: UndecodedParams): Params;
|
|
5
5
|
export declare function decodeQueryParam(type: string, value: unknown): string | number | boolean | undefined;
|
|
6
6
|
export declare function getQueryParams(url?: string): Record<string, string | string[]>;
|
|
7
|
-
export declare function validateInput(nsid: string, def: LexXrpcProcedure | LexXrpcQuery, req: express.Request, opts:
|
|
7
|
+
export declare function validateInput(nsid: string, def: LexXrpcProcedure | LexXrpcQuery, req: express.Request, opts: RouteOpts, lexicons: Lexicons): HandlerInput | undefined;
|
|
8
8
|
export declare function validateOutput(nsid: string, def: LexXrpcProcedure | LexXrpcQuery, output: HandlerSuccess | undefined, lexicons: Lexicons): HandlerSuccess | undefined;
|
|
9
9
|
export declare function normalizeMime(v: string): any;
|
|
10
10
|
export declare function hasBody(req: express.Request): string | true | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/xrpc-server",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "atproto HTTP API (XRPC) server library",
|
|
6
6
|
"keywords": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"zod": "^3.21.4",
|
|
26
26
|
"@atproto/common": "^0.3.3",
|
|
27
27
|
"@atproto/crypto": "^0.3.0",
|
|
28
|
-
"@atproto/lexicon": "^0.3.
|
|
28
|
+
"@atproto/lexicon": "^0.3.2"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/express": "^4.17.13",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"jose": "^4.15.4",
|
|
37
37
|
"key-encoder": "^2.0.3",
|
|
38
38
|
"multiformats": "^9.9.0",
|
|
39
|
-
"@atproto/
|
|
40
|
-
"@atproto/
|
|
39
|
+
"@atproto/xrpc": "^0.4.2",
|
|
40
|
+
"@atproto/crypto": "^0.3.0"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"test": "jest",
|
package/src/rate-limiter.ts
CHANGED
|
@@ -75,6 +75,9 @@ export class RateLimiter implements RateLimiterI {
|
|
|
75
75
|
return null
|
|
76
76
|
}
|
|
77
77
|
const key = opts?.calcKey ? opts.calcKey(ctx) : this.calcKey(ctx)
|
|
78
|
+
if (key === null) {
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
78
81
|
const points = opts?.calcPoints
|
|
79
82
|
? opts.calcPoints(ctx)
|
|
80
83
|
: this.calcPoints(ctx)
|
package/src/server.ts
CHANGED
|
@@ -36,6 +36,8 @@ import {
|
|
|
36
36
|
RateLimiterConsume,
|
|
37
37
|
isShared,
|
|
38
38
|
RateLimitExceededError,
|
|
39
|
+
HandlerPipeThrough,
|
|
40
|
+
handlerPipeThrough,
|
|
39
41
|
} from './types'
|
|
40
42
|
import {
|
|
41
43
|
decodeQueryParams,
|
|
@@ -173,7 +175,7 @@ export class Server {
|
|
|
173
175
|
this.routes[verb](
|
|
174
176
|
`/xrpc/${nsid}`,
|
|
175
177
|
...middleware,
|
|
176
|
-
this.createHandler(nsid, def, config
|
|
178
|
+
this.createHandler(nsid, def, config),
|
|
177
179
|
)
|
|
178
180
|
}
|
|
179
181
|
|
|
@@ -206,10 +208,13 @@ export class Server {
|
|
|
206
208
|
createHandler(
|
|
207
209
|
nsid: string,
|
|
208
210
|
def: LexXrpcQuery | LexXrpcProcedure,
|
|
209
|
-
|
|
211
|
+
routeCfg: XRPCHandlerConfig,
|
|
210
212
|
): RequestHandler {
|
|
213
|
+
const routeOpts = {
|
|
214
|
+
blobLimit: routeCfg.opts?.blobLimit ?? this.options.payload?.blobLimit,
|
|
215
|
+
}
|
|
211
216
|
const validateReqInput = (req: express.Request) =>
|
|
212
|
-
validateInput(nsid, def, req,
|
|
217
|
+
validateInput(nsid, def, req, routeOpts, this.lex)
|
|
213
218
|
const validateResOutput =
|
|
214
219
|
this.options.validateResponse === false
|
|
215
220
|
? (output?: HandlerSuccess) => output
|
|
@@ -248,20 +253,32 @@ export class Server {
|
|
|
248
253
|
}
|
|
249
254
|
|
|
250
255
|
// handle rate limits
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return next(result)
|
|
255
|
-
}
|
|
256
|
+
const result = await consumeRateLimit(reqCtx)
|
|
257
|
+
if (result instanceof RateLimitExceededError) {
|
|
258
|
+
return next(result)
|
|
256
259
|
}
|
|
257
260
|
|
|
258
261
|
// run the handler
|
|
259
|
-
const outputUnvalidated = await handler(reqCtx)
|
|
262
|
+
const outputUnvalidated = await routeCfg.handler(reqCtx)
|
|
260
263
|
|
|
261
264
|
if (isHandlerError(outputUnvalidated)) {
|
|
262
265
|
throw XRPCError.fromError(outputUnvalidated)
|
|
263
266
|
}
|
|
264
267
|
|
|
268
|
+
if (outputUnvalidated && isHandlerPipeThrough(outputUnvalidated)) {
|
|
269
|
+
// set headers
|
|
270
|
+
if (outputUnvalidated?.headers) {
|
|
271
|
+
Object.entries(outputUnvalidated.headers).forEach(([name, val]) => {
|
|
272
|
+
res.header(name, val)
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
res
|
|
276
|
+
.header('Content-Type', outputUnvalidated.encoding)
|
|
277
|
+
.status(200)
|
|
278
|
+
.send(Buffer.from(outputUnvalidated.buffer))
|
|
279
|
+
return
|
|
280
|
+
}
|
|
281
|
+
|
|
265
282
|
if (!outputUnvalidated || isHandlerSuccess(outputUnvalidated)) {
|
|
266
283
|
// validate response
|
|
267
284
|
const output = validateResOutput(outputUnvalidated)
|
|
@@ -447,6 +464,26 @@ function isHandlerSuccess(v: HandlerOutput): v is HandlerSuccess {
|
|
|
447
464
|
return handlerSuccess.safeParse(v).success
|
|
448
465
|
}
|
|
449
466
|
|
|
467
|
+
function isHandlerPipeThrough(v: HandlerOutput): v is HandlerPipeThrough {
|
|
468
|
+
if (v === null || typeof v !== 'object') {
|
|
469
|
+
return false
|
|
470
|
+
}
|
|
471
|
+
if (!isString(v['encoding']) || !(v['buffer'] instanceof ArrayBuffer)) {
|
|
472
|
+
return false
|
|
473
|
+
}
|
|
474
|
+
if (v['headers'] !== undefined) {
|
|
475
|
+
if (v['headers'] === null || typeof v['headers'] !== 'object') {
|
|
476
|
+
return false
|
|
477
|
+
}
|
|
478
|
+
if (!Object.values(v['headers']).every(isString)) {
|
|
479
|
+
return false
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return true
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const isString = (val: unknown): val is string => typeof val === 'string'
|
|
486
|
+
|
|
450
487
|
const kRequestLocals = Symbol('requestLocals')
|
|
451
488
|
|
|
452
489
|
function createLocalsMiddleware(nsid: string): RequestHandler {
|
package/src/types.ts
CHANGED
|
@@ -46,6 +46,13 @@ export const handlerSuccess = zod.object({
|
|
|
46
46
|
})
|
|
47
47
|
export type HandlerSuccess = zod.infer<typeof handlerSuccess>
|
|
48
48
|
|
|
49
|
+
export const handlerPipeThrough = zod.object({
|
|
50
|
+
encoding: zod.string(),
|
|
51
|
+
buffer: zod.instanceof(ArrayBuffer),
|
|
52
|
+
headers: zod.record(zod.string()).optional(),
|
|
53
|
+
})
|
|
54
|
+
export type HandlerPipeThrough = zod.infer<typeof handlerPipeThrough>
|
|
55
|
+
|
|
49
56
|
export const handlerError = zod.object({
|
|
50
57
|
status: zod.number(),
|
|
51
58
|
error: zod.string().optional(),
|
|
@@ -53,7 +60,7 @@ export const handlerError = zod.object({
|
|
|
53
60
|
})
|
|
54
61
|
export type HandlerError = zod.infer<typeof handlerError>
|
|
55
62
|
|
|
56
|
-
export type HandlerOutput = HandlerSuccess | HandlerError
|
|
63
|
+
export type HandlerOutput = HandlerSuccess | HandlerPipeThrough | HandlerError
|
|
57
64
|
|
|
58
65
|
export type XRPCReqContext = {
|
|
59
66
|
auth: HandlerAuth | undefined
|
|
@@ -85,7 +92,7 @@ export type StreamAuthVerifier = (ctx: {
|
|
|
85
92
|
req: IncomingMessage
|
|
86
93
|
}) => Promise<AuthOutput> | AuthOutput
|
|
87
94
|
|
|
88
|
-
export type CalcKeyFn = (ctx: XRPCReqContext) => string
|
|
95
|
+
export type CalcKeyFn = (ctx: XRPCReqContext) => string | null
|
|
89
96
|
export type CalcPointsFn = (ctx: XRPCReqContext) => number
|
|
90
97
|
|
|
91
98
|
export interface RateLimiterI {
|
|
@@ -101,29 +108,29 @@ export type RateLimiterCreator = (opts: {
|
|
|
101
108
|
keyPrefix: string
|
|
102
109
|
durationMs: number
|
|
103
110
|
points: number
|
|
104
|
-
calcKey?:
|
|
105
|
-
calcPoints?:
|
|
111
|
+
calcKey?: CalcKeyFn
|
|
112
|
+
calcPoints?: CalcPointsFn
|
|
106
113
|
}) => RateLimiterI
|
|
107
114
|
|
|
108
115
|
export type ServerRateLimitDescription = {
|
|
109
116
|
name: string
|
|
110
117
|
durationMs: number
|
|
111
118
|
points: number
|
|
112
|
-
calcKey?:
|
|
113
|
-
calcPoints?:
|
|
119
|
+
calcKey?: CalcKeyFn
|
|
120
|
+
calcPoints?: CalcPointsFn
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
export type SharedRateLimitOpts = {
|
|
117
124
|
name: string
|
|
118
|
-
calcKey?:
|
|
119
|
-
calcPoints?:
|
|
125
|
+
calcKey?: CalcKeyFn
|
|
126
|
+
calcPoints?: CalcPointsFn
|
|
120
127
|
}
|
|
121
128
|
|
|
122
129
|
export type RouteRateLimitOpts = {
|
|
123
130
|
durationMs: number
|
|
124
131
|
points: number
|
|
125
|
-
calcKey?:
|
|
126
|
-
calcPoints?:
|
|
132
|
+
calcKey?: CalcKeyFn
|
|
133
|
+
calcPoints?: CalcPointsFn
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
export type HandlerRateLimitOpts = SharedRateLimitOpts | RouteRateLimitOpts
|
|
@@ -143,7 +150,12 @@ export type RateLimiterStatus = {
|
|
|
143
150
|
isFirstInDuration: boolean
|
|
144
151
|
}
|
|
145
152
|
|
|
153
|
+
export type RouteOpts = {
|
|
154
|
+
blobLimit?: number
|
|
155
|
+
}
|
|
156
|
+
|
|
146
157
|
export type XRPCHandlerConfig = {
|
|
158
|
+
opts?: RouteOpts
|
|
147
159
|
rateLimit?: HandlerRateLimitOpts | HandlerRateLimitOpts[]
|
|
148
160
|
auth?: AuthVerifier
|
|
149
161
|
handler: XRPCHandler
|
package/src/util.ts
CHANGED
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
handlerSuccess,
|
|
20
20
|
InvalidRequestError,
|
|
21
21
|
InternalServerError,
|
|
22
|
-
Options,
|
|
23
22
|
XRPCError,
|
|
23
|
+
RouteOpts,
|
|
24
24
|
} from './types'
|
|
25
25
|
|
|
26
26
|
export function decodeQueryParams(
|
|
@@ -82,7 +82,7 @@ export function validateInput(
|
|
|
82
82
|
nsid: string,
|
|
83
83
|
def: LexXrpcProcedure | LexXrpcQuery,
|
|
84
84
|
req: express.Request,
|
|
85
|
-
opts:
|
|
85
|
+
opts: RouteOpts,
|
|
86
86
|
lexicons: Lexicons,
|
|
87
87
|
): HandlerInput | undefined {
|
|
88
88
|
// request expectation
|
|
@@ -139,7 +139,7 @@ export function validateInput(
|
|
|
139
139
|
if (req.readableEnded) {
|
|
140
140
|
body = req.body
|
|
141
141
|
} else {
|
|
142
|
-
body = decodeBodyStream(req, opts.
|
|
142
|
+
body = decodeBodyStream(req, opts.blobLimit)
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
return {
|