web-listener 0.1.0

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/index.d.ts ADDED
@@ -0,0 +1,1147 @@
1
+ import { IncomingMessage, ServerResponse, RequestListener, Server, ServerOptions, OutgoingHttpHeaders, IncomingHttpHeaders, Agent } from 'node:http';
2
+ import { Duplex, Readable, Writable } from 'node:stream';
3
+ import { AddressInfo } from 'node:net';
4
+ import { Stats } from 'node:fs';
5
+ import { FileHandle } from 'node:fs/promises';
6
+ import { ReadableStream, TextDecoderOptions, TransformStream } from 'node:stream/web';
7
+ import { AgentOptions, Agent as Agent$1 } from 'node:https';
8
+
9
+ type ClientErrorListener = (err: Error, socket: Duplex) => void;
10
+ type UpgradeListener = (req: IncomingMessage, socket: Duplex, head: Buffer) => void;
11
+ type ShouldUpgradeCallback = (req: IncomingMessage) => boolean;
12
+
13
+ interface Address {
14
+ type: 'IPv4' | 'IPv6' | 'alias';
15
+ ip: string;
16
+ port: number | undefined;
17
+ }
18
+ declare function parseAddress(address: string | undefined): Address | undefined;
19
+ declare function makeAddressTester(cidrRanges: string[]): (address: Address | undefined) => boolean;
20
+
21
+ declare class BlockingQueue<T> {
22
+ constructor();
23
+ push(value: T): void;
24
+ shift(timeout?: number | undefined): Promise<T>;
25
+ close(reason: unknown): void;
26
+ fail(reason: unknown): void;
27
+ [Symbol.asyncIterator](): AsyncIterator<T, unknown, undefined>;
28
+ }
29
+
30
+ declare function findCause<T>(error: unknown, errorType: {
31
+ new (...args: any[]): T;
32
+ }): T | undefined;
33
+
34
+ declare function getAddressURL(address: string | AddressInfo | null | undefined): string;
35
+
36
+ declare class Queue<T> {
37
+ constructor(initialItem?: T);
38
+ isEmpty(): boolean;
39
+ clear(): void;
40
+ push(item: T): void;
41
+ shift(): T | null;
42
+ remove(item: T): void;
43
+ [Symbol.iterator](): Iterator<T, unknown, undefined>;
44
+ }
45
+
46
+ type UpgradeErrorHandler = (error: unknown, req: IncomingMessage, socket: Duplex) => void;
47
+ type ServerGeneralErrorCallback = (error: unknown, action: string, req?: IncomingMessage) => void;
48
+ type ServerErrorCallback = (error: unknown, action: string, req: IncomingMessage) => void;
49
+
50
+ type MaybePromise<T> = Promise<T> | T;
51
+
52
+ type SoftCloseHandler = (reason: string) => MaybePromise<void>;
53
+ declare function setSoftCloseHandler(req: IncomingMessage, fn: SoftCloseHandler): void;
54
+ declare const isSoftClosed: (req: IncomingMessage) => boolean;
55
+ declare const defer: (req: IncomingMessage, fn: () => MaybePromise<void>) => void;
56
+ declare const addTeardown: (req: IncomingMessage, fn: () => MaybePromise<void>) => void;
57
+ declare const getAbortSignal: (req: IncomingMessage) => AbortSignal;
58
+
59
+ type AcceptUpgradeHandler<T> = (...args: Parameters<UpgradeListener>) => Promise<AcceptUpgradeResult<T>>;
60
+ interface AcceptUpgradeResult<T> {
61
+ return: T;
62
+ onError: UpgradeErrorHandler;
63
+ softCloseHandler: SoftCloseHandler;
64
+ }
65
+ declare function acceptUpgrade<T>(req: IncomingMessage, upgrade: AcceptUpgradeHandler<T>): Promise<T>;
66
+
67
+ declare const PATH_PARAMETERS: unique symbol;
68
+ type WithPathParameters<PathParameters> = {
69
+ [PATH_PARAMETERS]: PathParameters;
70
+ };
71
+ type WithoutPathParameters = {
72
+ [PATH_PARAMETERS]?: {
73
+ [k in PropertyKey]?: never;
74
+ };
75
+ };
76
+ declare function getPathParameters<PathParameters extends {}>(req: IncomingMessage & WithPathParameters<PathParameters>): Readonly<PathParameters>;
77
+ declare const getPathParameter: <PathParameters extends {}, ID extends keyof PathParameters>(req: IncomingMessage & WithPathParameters<PathParameters>, id: ID) => Readonly<PathParameters>[ID];
78
+ declare function getAbsolutePath(req: IncomingMessage): string;
79
+ declare function restoreAbsolutePath(req: IncomingMessage): void;
80
+
81
+ declare class RoutingInstruction extends Error {
82
+ }
83
+ declare const STOP: RoutingInstruction;
84
+ declare const CONTINUE: RoutingInstruction;
85
+ declare const NEXT_ROUTE: RoutingInstruction;
86
+ declare const NEXT_ROUTER: RoutingInstruction;
87
+
88
+ type ReturnHandlerParameter = object | null | undefined;
89
+ type HandlerResult = void | null | undefined | typeof STOP | typeof CONTINUE | typeof NEXT_ROUTE | typeof NEXT_ROUTER | ReturnHandlerParameter;
90
+ type RequestHandlerFn<Req = {}> = (req: IncomingMessage & Req, res: ServerResponse) => MaybePromise<HandlerResult>;
91
+ interface RequestHandler<Req = {}> {
92
+ handleRequest: RequestHandlerFn<Req>;
93
+ }
94
+ declare const requestHandler: <Req>(handler: RequestHandler<Req> | RequestHandlerFn<Req>) => RequestHandler<Req>;
95
+ type UpgradeHandlerFn<Req = {}> = (req: IncomingMessage & Req, socket: Duplex, head: Buffer) => MaybePromise<HandlerResult>;
96
+ type ShouldUpgradeFn<Req = {}> = (req: IncomingMessage & Req) => boolean;
97
+ interface UpgradeHandler<Req = {}> {
98
+ handleUpgrade: UpgradeHandlerFn<Req>;
99
+ shouldUpgrade?: ShouldUpgradeFn<Req> | undefined;
100
+ }
101
+ declare const upgradeHandler: <Req>(handler: UpgradeHandler<Req> | UpgradeHandlerFn<Req>, shouldUpgrade?: ShouldUpgradeFn<Req>) => UpgradeHandler<Req>;
102
+ declare const anyHandler: <Req>(handler: UpgradeHandlerFn<Req> & RequestHandlerFn<Req>, shouldUpgrade?: ShouldUpgradeFn<Req>) => RequestHandler<Req> & UpgradeHandler<Req>;
103
+ type ErrorHandlerFn<Req = {}> = (error: unknown, req: IncomingMessage & Req, output: {
104
+ response: ServerResponse;
105
+ socket?: never;
106
+ head?: never;
107
+ } | {
108
+ response?: never;
109
+ socket: Duplex;
110
+ head: Buffer;
111
+ }) => MaybePromise<HandlerResult>;
112
+ interface ErrorHandler<Req = {}> {
113
+ handleError: ErrorHandlerFn<Req>;
114
+ }
115
+ declare const errorHandler: <Req>(handler: ErrorHandler<Req> | ErrorHandlerFn<Req>) => ErrorHandler<Req>;
116
+ type Handler<Req = {}> = Partial<RequestHandler<Req> & UpgradeHandler<Req> & ErrorHandler<Req>>;
117
+ type RequestReturnHandlerFn<Req = {}> = (value: ReturnHandlerParameter, req: IncomingMessage & Req, res: ServerResponse) => MaybePromise<void>;
118
+
119
+ interface NativeListenersOptions {
120
+ onError?: ServerGeneralErrorCallback;
121
+ socketCloseTimeout?: number;
122
+ }
123
+ interface NativeListeners {
124
+ request: RequestListener;
125
+ upgrade: UpgradeListener;
126
+ shouldUpgrade: ShouldUpgradeCallback;
127
+ clientError: ClientErrorListener;
128
+ softClose(reason: string, onError: ServerErrorCallback, callback?: () => void): void;
129
+ hardClose(callback?: () => void): void;
130
+ countConnections(): number;
131
+ }
132
+ declare function toListeners(handler: Handler<WithoutPathParameters>, { onError, socketCloseTimeout }?: NativeListenersOptions): NativeListeners;
133
+
134
+ interface TypedEventTargetInstance<This, Mapping> extends EventTarget {
135
+ addEventListener<K extends keyof Mapping>(type: K, listener: (this: This, event: Mapping[K], options?: AddEventListenerOptions | boolean) => void): void;
136
+ addEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean): void;
137
+ }
138
+ type TypedEventTarget<This, Mapping> = {
139
+ new (): TypedEventTargetInstance<This, Mapping>;
140
+ };
141
+
142
+ interface ListenOptions {
143
+ /**
144
+ * `backlog` parameter sent to `server.listen`.
145
+ *
146
+ * @default 511
147
+ */
148
+ backlog?: number | undefined;
149
+ /**
150
+ * If given, this is passed to `server.timeout`. The number of milliseconds of inactivity before a socket is presumed to have timed out.
151
+ */
152
+ socketTimeout?: number | undefined;
153
+ }
154
+ interface ListenerOptions extends Omit<NativeListenersOptions, 'onError'> {
155
+ /**
156
+ * Automatically send `417 Expectation Failed` for any request with a non-standard `Expect` header.
157
+ * Set to `false` to allow application-specific use of this header.
158
+ *
159
+ * @default true (matching Node.js behaviour)
160
+ */
161
+ rejectNonStandardExpect?: boolean | undefined;
162
+ /**
163
+ * Automatically send `100 Continue` for any request with `Expect: 100-continue`.
164
+ * If set to `false`, all handlers MUST call `acceptBody(req)` before attempting
165
+ * to read the body of the request (bundled body parsing middleware does this
166
+ * automatically).
167
+ *
168
+ * @default true (matching Node.js behaviour)
169
+ */
170
+ autoContinue?: boolean | undefined;
171
+ /**
172
+ * Override the shouldUpgradeCallback of the server with one that attempts to detect whether an
173
+ * upgrade request would be handled by the current routes. The detection does not invoke any
174
+ * handlers, but checks their `shouldUpgrade` function if it is present.
175
+ *
176
+ * @default true
177
+ */
178
+ overrideShouldUpgradeCallback?: boolean | undefined;
179
+ }
180
+ type CombinedServerOptions = ServerOptions & ListenOptions & ListenerOptions;
181
+ declare const WebListener_base: TypedEventTarget<WebListener, {
182
+ error: CustomEvent<RequestErrorDetail>;
183
+ }>;
184
+ declare class WebListener extends WebListener_base {
185
+ constructor(handler: Handler<WithoutPathParameters>);
186
+ attach(server: Server, options?: ListenerOptions): (reason?: string, existingConnectionTimeout?: number, forceCloseAll?: boolean) => NativeListeners;
187
+ listen(port: number, host: string, options?: CombinedServerOptions): Promise<AugmentedServer>;
188
+ }
189
+ interface RequestErrorDetail {
190
+ error: unknown;
191
+ server: Server;
192
+ action: string;
193
+ request: IncomingMessage | undefined;
194
+ }
195
+ interface AugmentedServer extends Server {
196
+ closeWithTimeout(reason: string, timeout: number): Promise<void>;
197
+ }
198
+
199
+ type MessageBody = string | Buffer | NodeJS.ArrayBufferView | ArrayBuffer | SharedArrayBuffer;
200
+ type AnyHeaders = HeadersInit | OutgoingHttpHeaders | Map<string, string | number | Readonly<string[]>> | undefined;
201
+ interface HTTPErrorOptions {
202
+ message?: string;
203
+ statusMessage?: string;
204
+ headers?: AnyHeaders;
205
+ body?: MessageBody;
206
+ cause?: unknown;
207
+ }
208
+ interface PartialServerResponse {
209
+ setHeaders(headers: Headers): void;
210
+ setHeader(name: string, value: string): void;
211
+ writeHead(status: number, message: string, extraHeaders?: OutgoingHttpHeaders): void;
212
+ end(chunk: MessageBody, encoding: BufferEncoding): void;
213
+ }
214
+ declare class HTTPError extends Error {
215
+ readonly statusCode: number;
216
+ readonly statusMessage: string;
217
+ readonly headers: AnyHeaders;
218
+ readonly body: MessageBody;
219
+ constructor(statusCode: number, { message, statusMessage, headers, body, ...options }?: HTTPErrorOptions);
220
+ static readonly INTERNAL_SERVER_ERROR: HTTPError;
221
+ send(res: PartialServerResponse, extraHeaders?: OutgoingHttpHeaders): void;
222
+ }
223
+
224
+ type ParameterPrefixes = [':', '*'];
225
+ type ParameterTerminators = ['/', '-', '.', ...ParameterPrefixes];
226
+ interface ParameterTypes {
227
+ ':': string;
228
+ '*': string[];
229
+ }
230
+ type Resplit<S extends [string, string, string], D extends string> = S[0] extends `${infer A}${D}${infer B}` ? [A, D, `${B}${S[1]}${S[2]}`] : S;
231
+ type ResplitRecur<S extends [string, string, string], Ds extends string[]> = Ds extends [
232
+ infer D extends string,
233
+ ...infer Rest extends string[]
234
+ ] ? Resplit<ResplitRecur<S, Rest>, D> : S;
235
+ type SplitFirst<S extends string, Ds extends string[]> = ResplitRecur<[S, '', ''], Ds>;
236
+ type ReadParam<D, R extends [string, string, string]> = R[1] extends keyof ParameterTypes ? never : {
237
+ [k in R[0]]: D extends keyof ParameterTypes ? ParameterTypes[D] : never;
238
+ } & FindNextParam<SplitFirst<R[2], ParameterPrefixes>>;
239
+ type FindNextParam<S extends [string, string, string]> = S[1] extends '' ? {} : ReadParam<S[1], SplitFirst<S[2], ParameterTerminators>>;
240
+ type ReadParams<Path extends string> = FindNextParam<SplitFirst<Path, ParameterPrefixes>>;
241
+ type ParametersFromPath<Path extends string> = Path extends `${infer Start}{${infer Optional}}${infer Rest}` ? ReadParams<Start> & Partial<ReadParams<Optional>> & ParametersFromPath<Rest> : ReadParams<Path>;
242
+
243
+ declare const getSearch: (req: IncomingMessage) => string;
244
+ declare const getSearchParams: (req: IncomingMessage) => URLSearchParams;
245
+ declare const getQuery: (req: IncomingMessage, id: string) => string | null;
246
+
247
+ type CommonMethod = 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE';
248
+ type CommonUpgrade = 'http/2' | 'http/3' | 'https' | 'h2c' | 'websocket';
249
+ type RelaxedRequestHandler<Req = {}> = RequestHandlerFn<Req> | RequestHandler<Req> | ErrorHandler<Req>;
250
+ type RelaxedUpgradeHandler<Req = {}> = UpgradeHandlerFn<Req> | UpgradeHandler<Req> | ErrorHandler<Req>;
251
+ type MethodWrapper<Req, This> = <Path extends string>(path: Path, ...handlers: RelaxedRequestHandler<Req & WithPathParameters<ParametersFromPath<Path>>>[]) => This;
252
+ type UpgradeWrapper<Req, This> = <Path extends string>(path: Path, ...handlers: RelaxedUpgradeHandler<Req & WithPathParameters<ParametersFromPath<Path>>>[]) => This;
253
+ declare class Router<Req = {}> implements Required<Handler<Req>> {
254
+ constructor();
255
+ /**
256
+ * Register handlers or routers for all requests, upgrades, and errors, on all methods and paths.
257
+ */
258
+ use(...handlers: Handler<Req>[]): this;
259
+ /**
260
+ * Register handlers or routers for all requests, upgrades, and errors, on all methods for all
261
+ * paths within the given path root.
262
+ *
263
+ * To register handlers at the path but not subpaths, use `.at` instead.
264
+ */
265
+ mount<Path extends string>(path: Path, ...handlers: Handler<Req & WithPathParameters<ParametersFromPath<Path>>>[]): this;
266
+ /**
267
+ * Create a new router mounted at the path, and call the given function to configure it.
268
+ */
269
+ within<Path extends string>(path: Path, init: (subRouter: Router<Req & WithPathParameters<ParametersFromPath<Path>>>) => void): this;
270
+ /**
271
+ * Register handlers or routers for all requests, upgrades, and errors, on all methods for a
272
+ * specific path.
273
+ *
274
+ * To register handlers at the path and all subpaths, use `.mount` instead.
275
+ */
276
+ at<Path extends string>(path: Path, ...handlers: Handler<Req & WithPathParameters<ParametersFromPath<Path>>>[]): this;
277
+ /**
278
+ * Register handlers for requests or errors (not upgrades) on a specific method for a specific
279
+ * path.
280
+ */
281
+ onRequest<Path extends string, Method extends string = CommonMethod>(method: Method | Iterable<Method>, path: Path, ...handlers: RelaxedRequestHandler<Req & WithPathParameters<ParametersFromPath<Path>>>[]): this;
282
+ /**
283
+ * Register handlers for upgrades or errors (not requests) on a specific method and protocol
284
+ * for a specific path.
285
+ */
286
+ onUpgrade<Path extends string, Method extends string = CommonMethod, Protocol extends string = CommonUpgrade>(method: Method | Iterable<Method> | null, protocol: Protocol, path: Path, ...handlers: RelaxedUpgradeHandler<Req & WithPathParameters<ParametersFromPath<Path>>>[]): this;
287
+ /**
288
+ * Register error handlers.
289
+ *
290
+ * Error handlers are called if a request or upgrade handler which is before them in the chain
291
+ * throws. Error handlers can send a response and return to complete processing of the request.
292
+ * Error handlers can also return `CONTINUE` (or any other routing instruction) to pass control
293
+ * back to request or upgrade handlers. If an error handler throws, a combined error
294
+ * (`SuppressedError`) will be sent to the next error handler.
295
+ */
296
+ onError(...handlers: (ErrorHandler<Req> | ErrorHandlerFn<Req>)[]): this;
297
+ /**
298
+ * Register return handlers.
299
+ *
300
+ * Return handlers are called if a request or error handler returns a value which is not a
301
+ * routing instruction. They can be used for features like templating or ensuring connections
302
+ * are always closed when a handler returns.
303
+ *
304
+ * Return hanlders are called in the order they were registered, and from the innermost router
305
+ * to the outermost rooter. Return handlers are not called for upgrade requests.
306
+ *
307
+ * Return handlers are not ordered with the other handlers, so they can be registered upfront if
308
+ * desired. If a return handler throws, the error will be passed to the next error handler after
309
+ * the request handler which triggered it.
310
+ */
311
+ onReturn(...handlers: RequestReturnHandlerFn<Req>[]): this;
312
+ /**
313
+ * Convenience wrapper for `.onRequest`, accepting paths with a HTTP verb at the start (separated
314
+ * by a space).
315
+ */
316
+ on<Path extends string>(path: `${CommonMethod} ${Path}`, ...handlers: RelaxedRequestHandler<Req & WithPathParameters<ParametersFromPath<Path>>>[]): this;
317
+ /**
318
+ * Registers handlers for GET requests at the given path. Note that these handlers will also
319
+ * be used for HEAD requests by default. To use different HEAD handling, you can register an
320
+ * explicit HEAD handler earlier in the router, or you can use `.getOnly` instead of `.get`.
321
+ *
322
+ * @param path
323
+ * @param handlers
324
+ * @returns
325
+ */
326
+ get: MethodWrapper<Req, this>;
327
+ /** Alias for `onRequest('DELETE', path, ...handlers)` */
328
+ delete: MethodWrapper<Req, this>;
329
+ /**
330
+ * Alias for `onRequest('GET', path, ...handlers)`
331
+ *
332
+ * Unlike `.get`, this will _not_ also use the handler for HEAD requests.
333
+ */
334
+ getOnly: MethodWrapper<Req, this>;
335
+ /** Alias for `onRequest('HEAD', path, ...handlers)` */
336
+ head: MethodWrapper<Req, this>;
337
+ /** Alias for `onRequest('OPTIONS', path, ...handlers)` */
338
+ options: MethodWrapper<Req, this>;
339
+ /** Alias for `onRequest('PATCH', path, ...handlers)` */
340
+ patch: MethodWrapper<Req, this>;
341
+ /** Alias for `onRequest('POST', path, ...handlers)` */
342
+ post: MethodWrapper<Req, this>;
343
+ /** Alias for `onRequest('PUT', path, ...handlers)` */
344
+ put: MethodWrapper<Req, this>;
345
+ /** Alias for `onUpgrade('GET', 'websocket', path, ...handlers)` */
346
+ ws: UpgradeWrapper<Req, this>;
347
+ /**
348
+ * Run routing on a request.
349
+ * The request must already be registered (i.e. have come through a WebListener).
350
+ *
351
+ * This can be used for complex custom sub-routing, but is otherwise not useful.
352
+ *
353
+ * To register an upgrade handler, use `.onRequest` or a convenience method.
354
+ */
355
+ handleRequest(req: IncomingMessage & Req): Promise<HandlerResult>;
356
+ /**
357
+ * Run routing on an upgrade request.
358
+ * The request must already be registered (i.e. have come through a WebListener).
359
+ *
360
+ * This can be used for complex custom sub-routing, but is otherwise not useful.
361
+ *
362
+ * To register an upgrade handler, use `.onUpgrade` or a convenience method.
363
+ */
364
+ handleUpgrade(req: IncomingMessage & Req): Promise<HandlerResult>;
365
+ /**
366
+ * Run routing on an error.
367
+ * The request must already be registered (i.e. have come through a WebListener).
368
+ *
369
+ * This can be used for complex custom sub-routing, but is otherwise not useful.
370
+ *
371
+ * To register an error handler, use `.onError`.
372
+ */
373
+ handleError(error: unknown, req: IncomingMessage & Req): Promise<HandlerResult>;
374
+ shouldUpgrade(req: IncomingMessage & Req): boolean;
375
+ }
376
+
377
+ interface BearerAuthOptions<Req, Token> {
378
+ realm: string | ((req: IncomingMessage & Req) => MaybePromise<string>);
379
+ extractAndValidateToken: (token: string, realm: string, req: IncomingMessage & Req) => MaybePromise<Token>;
380
+ fallbackTokenFetcher?: (req: IncomingMessage & Req) => MaybePromise<string | undefined>;
381
+ closeOnExpiry?: boolean;
382
+ softCloseBufferTime?: number;
383
+ onSoftCloseError?: ServerErrorCallback;
384
+ }
385
+ interface JWTToken {
386
+ nbf?: number;
387
+ exp?: number;
388
+ scopes?: Record<string, boolean> | string[] | string;
389
+ }
390
+ declare function requireBearerAuth<Req = {}, Token = JWTToken>({ realm, extractAndValidateToken, fallbackTokenFetcher, closeOnExpiry, softCloseBufferTime, onSoftCloseError, }: BearerAuthOptions<Req, Token>): RequestHandler<Req> & UpgradeHandler<Req>;
391
+ declare const getAuthData: <Token>(req: IncomingMessage) => Token | null;
392
+ declare const hasAuthScope: (req: IncomingMessage, scope: string) => boolean;
393
+ declare const requireAuthScope: (scope: string) => RequestHandler & UpgradeHandler;
394
+
395
+ declare function generateWeakETag(contentEncoding: string | number | string[] | undefined, stats: Pick<Stats, 'mtimeMs' | 'size'>): string;
396
+ declare function generateStrongETag(file: string | FileHandle): Promise<string>;
397
+
398
+ interface HTTPRange {
399
+ ranges: RangePart[];
400
+ totalSize?: number | undefined;
401
+ }
402
+ interface RangePart {
403
+ start: number;
404
+ end: number;
405
+ }
406
+ interface SimplifyRangeOptions {
407
+ forceSequential?: boolean;
408
+ mergeOverlapDistance?: number;
409
+ }
410
+ declare function simplifyRange(original: HTTPRange, { forceSequential, mergeOverlapDistance }?: SimplifyRangeOptions): HTTPRange;
411
+
412
+ declare function getAuthorization(req: IncomingMessage): [string, string] | undefined;
413
+ declare function getCharset(req: IncomingMessage): string | undefined;
414
+ declare function getIfRange(req: IncomingMessage): {
415
+ etag?: string[] | undefined;
416
+ modifiedSeconds?: number | undefined;
417
+ };
418
+ interface GetRangeOptions {
419
+ /**
420
+ * Maximum number of sequential, non-overlapping ranges a client can request in a
421
+ * single message.
422
+ *
423
+ * Typical browser clients only request a single range. In rare cases, clients may
424
+ * request 2 ranges. Bespoke clients may be able to make use of more ranges.
425
+ *
426
+ * @default 10
427
+ */
428
+ maxRanges?: number;
429
+ /**
430
+ * Maximum number of ranges a client can request in a single message if any ranges are
431
+ * non-sequential (i.e. a later range begins at lower offset than an earlier range).
432
+ *
433
+ * As suggested in https://datatracker.ietf.org/doc/html/rfc7233, this uses a default
434
+ * limit of 2, as more ranges are likely to be a broken or malicious client.
435
+ *
436
+ * @default 2
437
+ */
438
+ maxNonSequential?: number;
439
+ /**
440
+ * Maximum number of ranges a client can request in a single message if any ranges are
441
+ * overlapping.
442
+ *
443
+ * As suggested in https://datatracker.ietf.org/doc/html/rfc7233, this uses a default
444
+ * limit of 2, as more ranges are likely to be a broken or malicious client.
445
+ *
446
+ * @default 2
447
+ */
448
+ maxWithOverlap?: number;
449
+ }
450
+ declare function getRange(req: IncomingMessage, totalSize: number, { maxRanges, maxNonSequential, maxWithOverlap }?: GetRangeOptions): HTTPRange | undefined;
451
+ interface QualityValue {
452
+ name: string;
453
+ specifiers: Map<string, string>;
454
+ specificity: number;
455
+ q: number;
456
+ }
457
+ declare function readHTTPUnquotedCommaSeparated(raw: LooseHeaderValue | undefined): string[] | undefined;
458
+ declare function readHTTPInteger(raw: string | undefined): number | undefined;
459
+ declare function readHTTPQualityValues(raw: string | undefined): QualityValue[] | undefined;
460
+ declare function readHTTPKeyValues(raw: string): Map<string, string>;
461
+ declare function readHTTPDateSeconds(raw: LooseHeaderValue | undefined): number | undefined;
462
+ type LooseHeaderValue = string | number | string[];
463
+
464
+ declare const HEADERS: {
465
+ mime: string;
466
+ language: string;
467
+ encoding: string;
468
+ };
469
+ type NegotiationType = keyof typeof HEADERS;
470
+ interface FileNegotiation {
471
+ type: NegotiationType;
472
+ options: FileNegotiationOption[];
473
+ }
474
+ interface FileNegotiationOption {
475
+ /**
476
+ * Value to match in `Accept-*` header.
477
+ * The comparison is case-insensitive.
478
+ *
479
+ * @example 'gzip'
480
+ * @example 'en-GB'
481
+ * @example /^en/
482
+ * @example 'text/html'
483
+ * @example /^text\//
484
+ */
485
+ match: string | RegExp;
486
+ /**
487
+ * Override value to return in `Content-Type` or `Content-Encoding`.
488
+ * You can use this to give a concrete type when matching against a wildcard:
489
+ *
490
+ * @example { match: 'text/*', as: 'text/plain', file: '{base}.txt' }
491
+ */
492
+ as?: string | undefined;
493
+ /**
494
+ * Filename modifier to apply. Several tokens are available:
495
+ *
496
+ * - `{file}` - the original filename (does not include the path)
497
+ * - `{base}` - the part of the original filename before the last `.` (or the entire filename if there is no dot)
498
+ * - `{ext}` - the original file extension, including the `.` (or blank of there is no dot)
499
+ *
500
+ * The resulting filename must not contain any path components (i.e. `/` and `\` are not allowed)
501
+ *
502
+ * @example '{file}.gz'
503
+ * @example '{base}-en{ext}'
504
+ * @example 'negotiated-{file}'
505
+ */
506
+ file: string;
507
+ }
508
+ declare const ENCODING_MAPPING: {
509
+ zstd: string;
510
+ br: string;
511
+ gzip: string;
512
+ deflate: string;
513
+ identity: string;
514
+ };
515
+ type FileEncodingFormat = keyof typeof ENCODING_MAPPING;
516
+ declare const negotiateEncoding: (options: FileEncodingFormat[] | Record<FileEncodingFormat, string>) => FileNegotiation;
517
+ interface NegotiationOutputInfo {
518
+ mime?: string | undefined;
519
+ language?: string | undefined;
520
+ encoding?: string | undefined;
521
+ }
522
+ interface NegotiationOutput {
523
+ filename: string;
524
+ info: NegotiationOutputInfo;
525
+ }
526
+ type NegotiationInput = Partial<Record<NegotiationType, QualityValue[] | undefined>>;
527
+ interface Negotiator {
528
+ options(base: string, negotiation: NegotiationInput): Generator<NegotiationOutput, undefined, undefined>;
529
+ vary: string;
530
+ }
531
+ declare function makeNegotiator(rules: FileNegotiation[], maxFailedAttempts?: number): Negotiator;
532
+
533
+ interface FileFinderOptions {
534
+ /**
535
+ * Also serve files from sub-directories.
536
+ * Can be set to a number to control the depth of subdirectories served (where 0 means no
537
+ * subdirectories).
538
+ *
539
+ * @default true
540
+ */
541
+ subDirectories?: boolean | number | undefined;
542
+ /**
543
+ * Configure case sensitivity of paths.
544
+ *
545
+ * `exact`: ensure that the path exactly matches the filesystem, even if the filesystem
546
+ * itself is case insensitive.
547
+ *
548
+ * `filesystem`: use the case sensitivity of the filesystem.
549
+ *
550
+ * `force-lowercase`: lowercase all requests before forwarding to the filesystem (this
551
+ * can achieve case insensitivity on otherwise case sensitive filesystems, but only if
552
+ * all files and folders are stored as lowercase).
553
+ *
554
+ * @default 'exact'
555
+ */
556
+ caseSensitive?: 'exact' | 'filesystem' | 'force-lowercase' | undefined;
557
+ /**
558
+ * Allow serving dotfiles.
559
+ * If `true`, all dotfiles will be available (note this can be a security risk if you have
560
+ * sensitive hidden files or directories in the directory being served, such as `.git`).
561
+ * If you only need some dotfiles to be made available, you can use `permit` instead, which
562
+ * lets you allow specific file / directory names.
563
+ *
564
+ * @default false
565
+ */
566
+ allowAllDotfiles?: boolean | undefined;
567
+ /**
568
+ * Allow serving files which begin or end with a tilde.
569
+ * These are commonly backup files (e.g. generated by vim when editing a file), and may or
570
+ * may not be a security risk to expose, depending on your setup.
571
+ * If `true`, all tilde files will be available.
572
+ * If you only need some tilde files to be made available, you can use `permit` instead, which
573
+ * lets you allow specific file / directory names.
574
+ *
575
+ * @default false
576
+ */
577
+ allowAllTildefiles?: boolean | undefined;
578
+ /**
579
+ * Serve index files when requested directly (e.g. `/foo/index.htm`).
580
+ * By default, index files (listed in `indexFiles`) are only served if the directory is requested.
581
+ *
582
+ * @default false
583
+ */
584
+ allowDirectIndexAccess?: boolean | undefined;
585
+ /**
586
+ * Hide arbitrary files from direct access. For example, when supporting content negotiation
587
+ * you may wish to block direct access to the "negotiated" variants of files such as `/\.gz$/`.
588
+ *
589
+ * Matches are performed on each path component. If any match, the file is hidden. Regular
590
+ * expressions are automatically made case insensitive if `caseSensitive` is `'filesystem'` or
591
+ * `'force-lowercase'`
592
+ *
593
+ * In cases where both match the same path component, `allow` overrides `hide`.
594
+ *
595
+ * @default []
596
+ */
597
+ hide?: (string | RegExp)[] | undefined;
598
+ /**
599
+ * Allow access to specific files which would otherwise be blocked as a dotfile, tilde file, or
600
+ * hidden by `hide`.
601
+ *
602
+ * @default ['.well-known']
603
+ */
604
+ allow?: string[] | undefined;
605
+ /**
606
+ * Files to look for if a directory is requested.
607
+ * Note that direct access to these files will be blocked unless `allowDirectIndexAccess` is `true`.
608
+ *
609
+ * If an empty list is provided, no index files will be served.
610
+ *
611
+ * @default ['index.htm', 'index.html']
612
+ */
613
+ indexFiles?: string[] | undefined;
614
+ /**
615
+ * Content negotiation rules to apply to files.
616
+ *
617
+ * This can be used to respond to the `Accept`, `Accept-Language`, and `Accept-Encoding` headers.
618
+ *
619
+ * For example: on a server with `foo.txt`, `foo.txt.gz`, and a negotiation rule mapping
620
+ * `gzip` => `{name}.gz`:
621
+ * - users requesting `foo.txt` may get `foo.txt.gz` with `Content-Encoding: gzip` if their
622
+ * client supports gzip encoding
623
+ * - users requesting `foo.txt` may get `foo.txt` with no `Content-Encoding` if their client
624
+ * does not support gzip encoding
625
+ *
626
+ * Note that file access is checked *before* content negotiation, so you must still provide a
627
+ * base "un-negotiated" file for each file you wish to serve (which will also be used in cases
628
+ * where users do not send any `Accept-*` headers, and where no match is found)
629
+ *
630
+ * Multiple rules can match simultaneously, if a specific enough file exists (for example you might
631
+ * have `foo-en.txt.gz` for `Accept-Language: en` and `Accept-Encoding: gzip`).
632
+ *
633
+ * In the case of conflicting rules, earlier rules take priority (so `encoding` rules should
634
+ * typically be specified last)
635
+ *
636
+ * See the helper `negotiateEncoding` for a simple way to support pre-compressed files.
637
+ *
638
+ * @default []
639
+ */
640
+ negotiation?: FileNegotiation[] | undefined;
641
+ }
642
+ interface FileFinderCore {
643
+ find(pathParts: string[], negotiation?: NegotiationInput): Promise<ResolvedFileInfo | null>;
644
+ readonly vary: string;
645
+ }
646
+ declare class FileFinder implements FileFinderCore {
647
+ readonly vary: string;
648
+ static build(absBasePath: string, options: FileFinderOptions): Promise<FileFinder>;
649
+ /**
650
+ * Find a file which matches the path.
651
+ *
652
+ * Note that the returned value contains an active `FileHandle`, which must be closed.
653
+ *
654
+ * @param pathParts the request path, split into separate components
655
+ * @param negotiation any client-sent negotiation options to apply
656
+ * @returns details about the resolved file (including an active `FileHandle`), or `null`
657
+ */
658
+ find(pathParts: string[], negotiation?: NegotiationInput): Promise<ResolvedFileInfo | null>;
659
+ precompute(): Promise<FileFinderCore>;
660
+ }
661
+ interface ResolvedFileInfo extends NegotiationOutputInfo {
662
+ handle: FileHandle;
663
+ canonicalPath: string;
664
+ negotiatedPath: string;
665
+ stats: Stats;
666
+ }
667
+
668
+ interface SavedFile {
669
+ path: string;
670
+ size: number;
671
+ }
672
+ interface TempFileStorage {
673
+ dir: string;
674
+ nextFile: () => string;
675
+ save: (stream: Readable | ReadableStream, options?: {
676
+ mode?: number | undefined;
677
+ }) => Promise<SavedFile>;
678
+ }
679
+ declare const makeTempFileStorage: (req: IncomingMessage) => Promise<TempFileStorage>;
680
+
681
+ type ProxyHeader = 'forwarded' | 'x-forwarded-for' | 'x-forwarded-host' | 'x-forwarded-proto' | 'x-forwarded-protocol' | 'x-url-scheme' | 'via';
682
+ interface GetClientOptions {
683
+ /**
684
+ * The number of proxies to trust.
685
+ *
686
+ * Although `trustedProxyAddresses` makes it easier to guarantee security, specifying a count of
687
+ * proxies is useful in cases where the IP of proxies is not known or can change.
688
+ *
689
+ * This should be set to the number of proxies in the chain for your deployment: 0 when exposed
690
+ * directly to the internet, 1 when running behind a proxy, 2 when using a local reverse proxy as
691
+ * well as a load balancer, etc.
692
+ *
693
+ * Note: you must make sure there is no way to access your server without going through at least
694
+ * this many proxies, otherwise users will be able to spoof their client details, such as IP
695
+ * address, requested hostname, and connection protocol.
696
+ *
697
+ * @default 0 if trustedProxyAddresses is not set
698
+ * @default infinity if trustedProxyAddresses is set
699
+ */
700
+ trustedProxyCount?: number;
701
+ /**
702
+ * The proxies to trust.
703
+ *
704
+ * For maximum security this can be an array of IPv4 or IPv6 addresses (or CIDR ranges) of
705
+ * trusted proxies. You can also specify alias names here, for cases where a proxy will be
706
+ * labelled by an alias by the next proxy in the chain.
707
+ *
708
+ * @default none (trust all proxies within trustedProxyCount hops)
709
+ */
710
+ trustedProxyAddresses?: string[];
711
+ /**
712
+ * The headers which are set (or cleared) by your proxy, and can therefore be trusted.
713
+ *
714
+ * To use proxy data, you must explicitly configure these headers to match those set by your
715
+ * proxy. Do not list headers which are not modified by your proxy, as this will allow clients
716
+ * to spoof data.
717
+ *
718
+ * @default [] (no headers are trusted)
719
+ */
720
+ trustedHeaders: ProxyHeader[];
721
+ }
722
+ declare function makeGetClient({ trustedProxyCount, trustedProxyAddresses, trustedHeaders, }: GetClientOptions): (req: IncomingMessage) => ProxyChain;
723
+ interface ProxyChain {
724
+ trusted: ProxyNode[];
725
+ untrusted: ProxyNode[];
726
+ outwardChain: ProxyNode[];
727
+ edge: ProxyNode;
728
+ }
729
+ interface ProxyNode {
730
+ client: Address | undefined;
731
+ server: Address | undefined;
732
+ host: string | undefined;
733
+ proto: string | undefined;
734
+ }
735
+
736
+ type ProxyRequestHeaderAdapter = (request: IncomingMessage, headers: OutgoingHttpHeaders) => OutgoingHttpHeaders;
737
+ type ProxyResponseHeaderAdapter = (request: IncomingMessage, proxyResponse: IncomingMessage, headers: OutgoingHttpHeaders) => OutgoingHttpHeaders;
738
+ declare function removeForwarded(_: IncomingMessage, headers: IncomingHttpHeaders): OutgoingHttpHeaders;
739
+ declare function replaceForwarded(req: IncomingMessage, headers: IncomingHttpHeaders): OutgoingHttpHeaders;
740
+ declare const sanitiseAndAppendForwarded: (getClient: (req: IncomingMessage) => {
741
+ outwardChain: ProxyNode[];
742
+ trusted: ProxyNode[];
743
+ }, { onlyTrusted }?: {
744
+ onlyTrusted?: boolean | undefined;
745
+ }) => (req: IncomingMessage, headers: IncomingHttpHeaders) => OutgoingHttpHeaders;
746
+ declare function simpleAppendForwarded(req: IncomingMessage, headers: IncomingHttpHeaders): OutgoingHttpHeaders;
747
+
748
+ interface ProxyOptions extends AgentOptions {
749
+ agent?: Agent | Agent$1 | undefined;
750
+ /**
751
+ * A list of headers to remove from proxied requests (runs before `requestHeaders`).
752
+ *
753
+ * @default ['connection', 'expect', 'host', 'keep-alive', 'proxy-authorization', 'transfer-encoding', 'upgrade', 'via']
754
+ */
755
+ blockRequestHeaders?: string[] | undefined;
756
+ /**
757
+ * A list of headers to remove from proxied responses (runs before `responseHeaders`).
758
+ *
759
+ * @default ['connection', 'keep-alive', 'transfer-encoding']
760
+ */
761
+ blockResponseHeaders?: string[] | undefined;
762
+ /**
763
+ * Mutators for the proxied request headers. e.g. `sanitiseAndAppendForwarded`.
764
+ */
765
+ requestHeaders?: ProxyRequestHeaderAdapter[] | undefined;
766
+ /**
767
+ * Mutators for the proxied response headers.
768
+ */
769
+ responseHeaders?: ProxyResponseHeaderAdapter[] | undefined;
770
+ }
771
+ declare function proxy(forwardHost: string, { blockRequestHeaders, requestHeaders, blockResponseHeaders, responseHeaders, agent, keepAlive, maxSockets, ...options }?: ProxyOptions): RequestHandler<unknown>;
772
+
773
+ declare function registerCharset(charset: string, transformerFactory: (options: TextDecoderOptions) => TransformStream<Uint8Array, string>): void;
774
+ declare function registerUTF32(): void;
775
+
776
+ declare function resetMime(): void;
777
+ /**
778
+ * Reads mime types from an Apache .types file format.
779
+ * See https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types for an example.
780
+ */
781
+ declare function readMimeTypes(types: string): Map<string, string>;
782
+ declare function decompressMime(definitions: string): Map<string, string>;
783
+ declare function registerMime(definitions: Map<string, string>): void;
784
+ declare function getMime(ext: string, charset?: string): string;
785
+
786
+ declare function checkIfModified(req: IncomingMessage, res: ServerResponse, fileStats: Pick<Stats, 'mtimeMs' | 'size'>): boolean;
787
+ declare function checkIfRange(req: IncomingMessage, res: ServerResponse, fileStats: Pick<Stats, 'mtimeMs' | 'size'>): boolean;
788
+ declare function compareETag(res: ServerResponse, stats: Pick<Stats, 'mtimeMs' | 'size'>, etags: string[]): boolean;
789
+
790
+ interface GetBodyOptions {
791
+ maxExpandedLength?: number;
792
+ maxContentLength?: number;
793
+ maxEncodingSteps?: number;
794
+ }
795
+ interface GetBodyTextOptions extends GetBodyOptions, TextDecoderOptions {
796
+ defaultCharset?: string;
797
+ }
798
+ interface GetBodyJsonOptions extends GetBodyOptions, TextDecoderOptions {
799
+ }
800
+ declare function getBodyStream(req: IncomingMessage, { maxExpandedLength, maxContentLength, maxEncodingSteps, }?: GetBodyOptions): ReadableStream<Uint8Array>;
801
+ declare function getBodyTextStream(req: IncomingMessage, options?: GetBodyTextOptions): ReadableStream<string>;
802
+ declare function getBodyText(req: IncomingMessage, options?: GetBodyTextOptions): Promise<string>;
803
+ declare function getBodyJson(req: IncomingMessage, options?: GetBodyJsonOptions): Promise<unknown>;
804
+
805
+ declare function acceptBody(req: IncomingMessage): void;
806
+
807
+ declare function getFormFields(req: IncomingMessage, { allowMultipart, closeAfterErrorDelay, limits }?: GetFormFieldsConfig): AsyncIterable<FormField, unknown, undefined>;
808
+ declare function getFormData(req: IncomingMessage, config?: GetFormDataConfig): Promise<AugmentedFormData>;
809
+ interface GetFormFieldsConfig {
810
+ allowMultipart?: boolean;
811
+ closeAfterErrorDelay?: number;
812
+ limits?: Limits;
813
+ }
814
+ interface GetFormDataConfig extends GetFormFieldsConfig {
815
+ /** true to apply .trim() to all field values */
816
+ trimAllValues?: boolean;
817
+ /** function to apply to all uploaded files (e.g. to check available disk space) */
818
+ preCheckFile?: PreCheckFile;
819
+ }
820
+ type PreCheckFile = (info: PreCheckFileInfo) => MaybePromise<PostCheckFile | void>;
821
+ interface PreCheckFileInfo {
822
+ fieldName: string;
823
+ filename: string;
824
+ encoding: string;
825
+ mimeType: string;
826
+ maxBytes: number | undefined;
827
+ }
828
+ type PostCheckFile = (actual: PostCheckFileInfo) => MaybePromise<void>;
829
+ interface PostCheckFileInfo {
830
+ actualBytes: number;
831
+ }
832
+ type FormField = {
833
+ name: string;
834
+ mimeType: string;
835
+ encoding: string;
836
+ } & ({
837
+ type: 'string';
838
+ value: string;
839
+ } | {
840
+ type: 'file';
841
+ value: Readable;
842
+ filename: string;
843
+ });
844
+ interface AugmentedFormData extends FormData {
845
+ getTempFilePath(file: Blob): string;
846
+ getBoolean(name: string): boolean | null;
847
+ }
848
+ interface Limits {
849
+ /**
850
+ * Max field name size (in bytes).
851
+ *
852
+ * @default 100
853
+ */
854
+ fieldNameSize?: number | undefined;
855
+ /**
856
+ * Max field value size (in bytes).
857
+ *
858
+ * @default 1048576 (1MB)
859
+ */
860
+ fieldSize?: number | undefined;
861
+ /**
862
+ * Max number of non-file fields.
863
+ *
864
+ * @default Infinity
865
+ */
866
+ fields?: number | undefined;
867
+ /**
868
+ * For multipart forms, the max file size (in bytes).
869
+ *
870
+ * @default Infinity
871
+ */
872
+ fileSize?: number | undefined;
873
+ /**
874
+ * For multipart forms, the max number of file fields.
875
+ *
876
+ * @default Infinity
877
+ */
878
+ files?: number | undefined;
879
+ /**
880
+ * For multipart forms, the max number of parts (fields + files).
881
+ *
882
+ * @default Infinity
883
+ */
884
+ parts?: number | undefined;
885
+ /**
886
+ * For multipart forms, the max number of header key-value pairs to parse.
887
+ *
888
+ * @default 2000 (same as node's http module)
889
+ */
890
+ headerPairs?: number | undefined;
891
+ }
892
+
893
+ declare function getRemainingPathComponents(req: IncomingMessage, { rejectPotentiallyUnsafe }?: {
894
+ rejectPotentiallyUnsafe?: boolean | undefined;
895
+ }): string[];
896
+
897
+ interface CSVOptions {
898
+ /**
899
+ * The delimiter to use between cells
900
+ * @default ','
901
+ */
902
+ delimiter?: string | Uint8Array;
903
+ /**
904
+ * The delimiter to use between rows
905
+ * @default '\n'
906
+ */
907
+ newline?: string | Uint8Array;
908
+ /**
909
+ * The quote character to use. Typically should be `"` or (if the target application supports it) `'`
910
+ * @default '"'
911
+ */
912
+ quote?: string;
913
+ /**
914
+ * The text encoding to use.
915
+ * @default 'utf-8'
916
+ */
917
+ encoding?: BufferEncoding;
918
+ /**
919
+ * For use with `ServerResponse` targets.
920
+ * If `true`, adds header=present to the mime type; if `false`, adds header=absent to the mime type.
921
+ * @default undefined
922
+ */
923
+ headerRow?: boolean | undefined;
924
+ /**
925
+ * If `true`, will call `end()` on target stream after writing.
926
+ * @default true
927
+ */
928
+ end?: boolean;
929
+ }
930
+ type MaybeAsyncIterable<T> = Iterable<T> | AsyncIterable<T>;
931
+ /**
932
+ * Output a CSV formatted table, using the format from RFC4180.
933
+ * Specifically:
934
+ * - lines are separated by newlines;
935
+ * - cells are separated by commas;
936
+ * - cells containing whitespace, commas, newlines, etc. are quoted with double quotes;
937
+ * - double quotes appearing inside cell values are escaped by doubling: `"a""b"`.
938
+ *
939
+ * The line and cell delimiters can be configured (e.g. `{ delimiter: '\t', newline: '\r\n' }`)
940
+ */
941
+ declare function sendCSVStream(target: Writable, table: MaybeAsyncIterable<MaybeAsyncIterable<string | null | undefined>>, { delimiter, newline, quote, encoding, headerRow, end, }?: CSVOptions): Promise<void>;
942
+
943
+ declare function sendFile(req: IncomingMessage, res: ServerResponse, file: string | FileHandle | Readable | ReadableStream<Uint8Array>, fileStats: Pick<Stats, 'mtimeMs' | 'size'> | null, options?: GetRangeOptions & SimplifyRangeOptions): Promise<void>;
944
+
945
+ interface JSONOptions {
946
+ replacer?: ((this: unknown, key: string, value: unknown) => unknown) | (number | string)[] | null;
947
+ space?: string | number;
948
+ undefinedAsNull?: boolean;
949
+ encoding?: BufferEncoding;
950
+ end?: boolean;
951
+ }
952
+ declare function sendJSON(target: Writable, entity: unknown, { replacer, space, undefinedAsNull, encoding, end }?: JSONOptions): void;
953
+ declare function sendJSONStream(target: Writable, entity: unknown, { replacer, space, undefinedAsNull, encoding, end, }?: JSONOptions): Promise<void>;
954
+
955
+ declare function sendRanges(req: IncomingMessage, res: ServerResponse, file: string | FileHandle | Readable | ReadableStream<Uint8Array>, httpRange: HTTPRange): Promise<void>;
956
+
957
+ interface ServerSentEventsOptions {
958
+ keepaliveInterval?: number;
959
+ softCloseReconnectDelay?: number;
960
+ softCloseReconnectStagger?: number;
961
+ }
962
+ interface ServerSentEvent {
963
+ event?: string;
964
+ data?: string;
965
+ id?: string;
966
+ reconnectDelay?: number;
967
+ }
968
+ declare class ServerSentEvents {
969
+ constructor(req: IncomingMessage, res: ServerResponse, { keepaliveInterval, softCloseReconnectDelay, softCloseReconnectStagger, }?: ServerSentEventsOptions);
970
+ get signal(): AbortSignal;
971
+ get open(): boolean;
972
+ ping(): void;
973
+ send({ event, id, data, reconnectDelay }: ServerSentEvent): Promise<void>;
974
+ sendFields(parts: [string, string | undefined][]): Promise<void>;
975
+ close(reconnectDelay?: number, reconnectStagger?: number): Promise<void>;
976
+ }
977
+
978
+ interface FileServerOptions extends FileFinderOptions {
979
+ /**
980
+ * The mode of serving files to use.
981
+ *
982
+ * 'dynamic' checks the filesystem for each request.
983
+ * This is a good choice for local development, and for production in cases where files in a
984
+ * directory are able to change at runtime (e.g. uploaded content).
985
+ *
986
+ * 'static-paths' scans the directory at startup then uses an in-memory reference to check
987
+ * requested paths.
988
+ * This can improve performance and increase security, as long as the list of avalable files
989
+ * will not change at runtime. The contents of the files are still loaded for each request.
990
+ * This is usually a good choice for production deployments.
991
+ *
992
+ * @default 'dynamic'
993
+ */
994
+ mode?: 'dynamic' | 'static-paths' | undefined;
995
+ /**
996
+ * Serve a file if the requested path is not found.
997
+ * By default, the handler will return `CONTINUE`, but by providing this option you can
998
+ * automatically return a particular file as fallback content (e.g. for Single-Page-Apps).
999
+ *
1000
+ * Compression, cache control, and range requests will continue to work as usual.
1001
+ */
1002
+ fallback?: FallbackOptions | undefined;
1003
+ /**
1004
+ * A function to call when a file is being served. Can modify headers in the response.
1005
+ * The default implementation is:
1006
+ *
1007
+ * ```
1008
+ * res.setHeader('etag', generateWeakETag(res.getHeader('content-encoding'), file.stats));
1009
+ * res.setHeader('last-modified', file.stats.mtime.toUTCString());
1010
+ * ```
1011
+ *
1012
+ * If you override this function, you must decide which cache headers you wish to set.
1013
+ *
1014
+ * This function is called after the `Content-Type` and `Content-Encoding` headers have been set,
1015
+ * so you can inspect those if you need to know the mime type or encoding of the response, or
1016
+ * change them if you want to set different values.
1017
+ *
1018
+ * @param req
1019
+ * @param res
1020
+ * @param file information about the file, including `fs.Stats` and an active `FileHandle`
1021
+ * @param isFallback `true` if the file is being served as a fallback (e.g. error page or Single-Page-App)
1022
+ */
1023
+ callback?: ((req: IncomingMessage, res: ServerResponse, file: ResolvedFileInfo, isFallback: boolean) => MaybePromise<void>) | undefined;
1024
+ }
1025
+ interface FallbackOptions {
1026
+ /**
1027
+ * The status code to return with the fallback content.
1028
+ *
1029
+ * @default 200 (OK)
1030
+ */
1031
+ statusCode?: number | undefined;
1032
+ /**
1033
+ * The path of the fallback content to use.
1034
+ */
1035
+ filePath: string;
1036
+ }
1037
+ /**
1038
+ * Set up a server for static files in a directory. The options are secure by default.
1039
+ * Requests for files which are not permitted or do not exist will return NEXT_ROUTE and can be
1040
+ * handled by subsequent routes.
1041
+ *
1042
+ * @param basePath the path to the directory to serve (relative paths are to the current working directory)
1043
+ * @param options custom configuration to apply
1044
+ * @returns a promise of a server handler function (note this should be `await`ed before being used as a handler!)
1045
+ */
1046
+ declare const fileServer: (basePath: string, { mode, fallback, callback, ...options }?: FileServerOptions) => Promise<RequestHandler>;
1047
+ declare function setDefaultCacheHeaders(req: IncomingMessage, res: ServerResponse, file: ResolvedFileInfo): void;
1048
+
1049
+ interface InternalWebSocketServerOptions {
1050
+ noServer?: true;
1051
+ clientTracking?: false;
1052
+ WebSocket?: any;
1053
+ }
1054
+ type ForbiddenWebSocketServerOptions = 'backlog' | 'server' | 'noServer' | 'clientTracking' | 'host' | 'port' | 'path' | 'verifyClient';
1055
+ declare class WebSocketServer<T, Options> {
1056
+ constructor(options: Options);
1057
+ handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer, callback: (ws: T) => void): void;
1058
+ once(event: 'wsClientError', handler: (error: Error) => void): void;
1059
+ off(event: 'wsClientError', handler: (error: Error) => void): void;
1060
+ }
1061
+ interface ClosableWebSocket {
1062
+ close(status: number, message?: string): void;
1063
+ }
1064
+ interface WebSocketOptions {
1065
+ softCloseStatusCode?: number | undefined;
1066
+ }
1067
+ declare function makeAcceptWebSocket<T extends ClosableWebSocket, PassThroughOptions>(wsServerClass: typeof WebSocketServer<T, InternalWebSocketServerOptions & PassThroughOptions>, { softCloseStatusCode, ...options }?: Partial<PassThroughOptions & Record<ForbiddenWebSocketServerOptions, never>> & {
1068
+ WebSocket?: (new (...args: any) => T) | undefined;
1069
+ } & WebSocketOptions): (req: IncomingMessage) => Promise<T>;
1070
+
1071
+ type MessageHandler = ((data: string | Buffer) => void) & ((data: Buffer, isBinary: boolean) => void);
1072
+ type CloseHandler = () => void;
1073
+ interface ListenableWebSocket {
1074
+ on(event: 'message', handler: MessageHandler): void;
1075
+ on(event: 'close', handler: CloseHandler): void;
1076
+ off(event: 'message', handler: MessageHandler): void;
1077
+ off(event: 'close', handler: CloseHandler): void;
1078
+ readyState?: number;
1079
+ }
1080
+ declare class WebSocketMessage {
1081
+ readonly data: Buffer;
1082
+ readonly isBinary: boolean;
1083
+ constructor(data: Buffer, isBinary: boolean);
1084
+ get text(): string;
1085
+ get binary(): Buffer<ArrayBufferLike>;
1086
+ }
1087
+ interface WebSocketMessagesOptions {
1088
+ limit?: number;
1089
+ signal?: AbortSignal | undefined;
1090
+ }
1091
+ declare class WebSocketMessages implements AsyncIterable<WebSocketMessage, unknown, undefined> {
1092
+ readonly detach: () => void;
1093
+ constructor(ws: ListenableWebSocket, { limit, signal }?: WebSocketMessagesOptions);
1094
+ next(timeout?: number): Promise<WebSocketMessage>;
1095
+ [Symbol.asyncIterator](): AsyncIterator<WebSocketMessage, unknown, undefined>;
1096
+ }
1097
+ declare function nextWebSocketMessage(ws: ListenableWebSocket, { timeout, signal }?: {
1098
+ timeout?: number;
1099
+ signal?: AbortSignal;
1100
+ }): Promise<WebSocketMessage>;
1101
+
1102
+ declare function isWebSocketRequest(req: IncomingMessage): boolean;
1103
+ declare const getWebSocketOrigin: (req: IncomingMessage) => string | undefined;
1104
+ /**
1105
+ * Factory for a fallback authentication token fetcher for websockets. Browsers do not allow setting the
1106
+ * Authorization header when creating a websocket, so this allows the token to be sent as the first message
1107
+ * instead.
1108
+ *
1109
+ * @param acceptWebSocket a function for accepting a websocket (e.g. `makeAcceptWebSocket(WebSocketServer)`)
1110
+ * @returns a function suitable for use with `requireBearerAuth`'s `fallbackTokenFetcher` option.
1111
+ */
1112
+ declare const makeWebSocketFallbackTokenFetcher: <Req>(acceptWebSocket: (req: IncomingMessage & Req) => Promise<ListenableWebSocket>, timeout?: number) => (req: IncomingMessage & Req) => Promise<string | undefined>;
1113
+
1114
+ interface WebSocketErrorOptions {
1115
+ message?: string;
1116
+ statusMessage?: string;
1117
+ cause?: unknown;
1118
+ }
1119
+ declare class WebSocketError extends Error {
1120
+ readonly statusCode: number;
1121
+ readonly statusMessage: string;
1122
+ constructor(statusCode: number, { message, statusMessage, ...options }?: WebSocketErrorOptions);
1123
+ static readonly NORMAL_CLOSURE = 1000;
1124
+ static readonly GOING_AWAY = 1001;
1125
+ static readonly UNSUPPORTED_DATA = 1003;
1126
+ static readonly POLICY_VIOLATION = 1008;
1127
+ static readonly MESSAGE_TOO_BIG = 1009;
1128
+ static readonly INTERNAL_SERVER_ERROR = 1011;
1129
+ static readonly SERVICE_RESTART = 1012;
1130
+ static readonly TRY_AGAIN_LATER = 1013;
1131
+ static readonly BAD_GATEWAY = 1014;
1132
+ }
1133
+
1134
+ interface Property<T> {
1135
+ factory: (req: IncomingMessage) => T;
1136
+ set(req: IncomingMessage, value: T): void;
1137
+ get(req: IncomingMessage): T;
1138
+ clear(req: IncomingMessage): void;
1139
+ }
1140
+ declare function makeProperty<T>(defaultValue: T | ((req: IncomingMessage) => T)): Property<T>;
1141
+ declare const makeMemo: <T, Args extends any[] = []>(fn: (req: IncomingMessage, ...args: Args) => T, ...args: Args) => ((req: IncomingMessage) => T);
1142
+ declare function setProperty<T>(req: IncomingMessage, property: Property<T>, value: T): void;
1143
+ declare function getProperty<T>(req: IncomingMessage, property: Property<T>): T;
1144
+ declare function clearProperty<T>(req: IncomingMessage, property: Property<T>): void;
1145
+
1146
+ export { BlockingQueue, CONTINUE, FileFinder, HTTPError, NEXT_ROUTE, NEXT_ROUTER, Queue, Router, STOP, ServerSentEvents, WebListener, WebSocketError, WebSocketMessage, WebSocketMessages, acceptBody, acceptUpgrade, addTeardown, anyHandler, checkIfModified, checkIfRange, clearProperty, compareETag, decompressMime, defer, errorHandler, fileServer, findCause, generateStrongETag, generateWeakETag, getAbortSignal, getAbsolutePath, getAddressURL, getAuthData, getAuthorization, getBodyJson, getBodyStream, getBodyText, getBodyTextStream, getCharset, getFormData, getFormFields, getIfRange, getMime, getPathParameter, getPathParameters, getProperty, getQuery, getRange, getRemainingPathComponents, getSearch, getSearchParams, getWebSocketOrigin, hasAuthScope, isSoftClosed, isWebSocketRequest, makeAcceptWebSocket, makeAddressTester, makeGetClient, makeMemo, makeNegotiator, makeProperty, makeTempFileStorage, makeWebSocketFallbackTokenFetcher, negotiateEncoding, nextWebSocketMessage, parseAddress, proxy, readHTTPDateSeconds, readHTTPInteger, readHTTPKeyValues, readHTTPQualityValues, readHTTPUnquotedCommaSeparated, readMimeTypes, registerCharset, registerMime, registerUTF32, removeForwarded, replaceForwarded, requestHandler, requireAuthScope, requireBearerAuth, resetMime, restoreAbsolutePath, sanitiseAndAppendForwarded, sendCSVStream, sendFile, sendJSON, sendJSONStream, sendRanges, setDefaultCacheHeaders, setProperty, setSoftCloseHandler, simpleAppendForwarded, simplifyRange, toListeners, upgradeHandler };
1147
+ export type { AcceptUpgradeHandler, AcceptUpgradeResult, Address, AugmentedFormData, CSVOptions, CombinedServerOptions, CommonMethod, CommonUpgrade, ErrorHandler, FileFinderCore, FileFinderOptions, FileNegotiation, FileNegotiationOption, FileServerOptions, FormField, GetClientOptions, GetFormDataConfig, GetFormFieldsConfig, GetRangeOptions, HTTPErrorOptions, HTTPRange, HandlerResult, JSONOptions, ListenOptions, ListenerOptions, NativeListeners, NegotiationInput, NegotiationOutput, NegotiationOutputInfo, Negotiator, ParametersFromPath, PostCheckFile, PostCheckFileInfo, PreCheckFile, PreCheckFileInfo, Property, ProxyChain, ProxyNode, ProxyOptions, ProxyRequestHeaderAdapter, ProxyResponseHeaderAdapter, QualityValue, RangePart, RequestHandler, ResolvedFileInfo, ServerErrorCallback, ServerGeneralErrorCallback, ServerSentEvent, SimplifyRangeOptions, UpgradeHandler, UpgradeListener, WithPathParameters, WithoutPathParameters };