323 lines
10 KiB
TypeScript
323 lines
10 KiB
TypeScript
import { Serve as BunServe, type Server as BunServer } from 'bun';
|
||
import type { Equal, MaybePromise } from '../types';
|
||
export interface ErrorLike extends Error {
|
||
code?: string;
|
||
errno?: number;
|
||
syscall?: string;
|
||
}
|
||
export interface GenericServeOptions {
|
||
/**
|
||
* What URI should be used to make {@link Request.url} absolute?
|
||
*
|
||
* By default, looks at {@link hostname}, {@link port}, and whether or not SSL is enabled to generate one
|
||
*
|
||
* @example
|
||
* ```js
|
||
* "http://my-app.com"
|
||
* ```
|
||
*
|
||
* @example
|
||
* ```js
|
||
* "https://wongmjane.com/"
|
||
* ```
|
||
*
|
||
* This should be the public, absolute URL – include the protocol and {@link hostname}. If the port isn't 80 or 443, then include the {@link port} too.
|
||
*
|
||
* @example
|
||
* "http://localhost:3000"
|
||
*/
|
||
/**
|
||
* What is the maximum size of a request body? (in bytes)
|
||
* @default 1024 * 1024 * 128 // 128MB
|
||
*/
|
||
maxRequestBodySize?: number;
|
||
/**
|
||
* Render contextual errors? This enables bun's error page
|
||
* @default process.env.NODE_ENV !== 'production'
|
||
*/
|
||
development?: boolean;
|
||
error?: (this: Server, request: ErrorLike) => Response | Promise<Response> | undefined | Promise<undefined>;
|
||
/**
|
||
* Uniquely identify a server instance with an ID
|
||
*
|
||
* ### When bun is started with the `--hot` flag
|
||
*
|
||
* This string will be used to hot reload the server without interrupting
|
||
* pending requests or websockets. If not provided, a value will be
|
||
* generated. To disable hot reloading, set this value to `null`.
|
||
*
|
||
* ### When bun is not started with the `--hot` flag
|
||
*
|
||
* This string will currently do nothing. But in the future it could be useful for logs or metrics.
|
||
*/
|
||
id?: string | null;
|
||
}
|
||
export interface ServeOptions extends GenericServeOptions {
|
||
/**
|
||
* What port should the server listen on?
|
||
* @default process.env.PORT || "3000"
|
||
*/
|
||
port?: string | number;
|
||
/**
|
||
* If the `SO_REUSEPORT` flag should be set.
|
||
*
|
||
* This allows multiple processes to bind to the same port, which is useful for load balancing.
|
||
*
|
||
* @default false
|
||
*/
|
||
reusePort?: boolean;
|
||
/**
|
||
* What hostname should the server listen on?
|
||
*
|
||
* @default
|
||
* ```js
|
||
* "0.0.0.0" // listen on all interfaces
|
||
* ```
|
||
* @example
|
||
* ```js
|
||
* "127.0.0.1" // Only listen locally
|
||
* ```
|
||
* @example
|
||
* ```js
|
||
* "remix.run" // Only listen on remix.run
|
||
* ````
|
||
*
|
||
* note: hostname should not include a {@link port}
|
||
*/
|
||
hostname?: string;
|
||
/**
|
||
* If set, the HTTP server will listen on a unix socket instead of a port.
|
||
* (Cannot be used with hostname+port)
|
||
*/
|
||
unix?: never;
|
||
/**
|
||
* Handle HTTP requests
|
||
*
|
||
* Respond to {@link Request} objects with a {@link Response} object.
|
||
*/
|
||
fetch(this: Server, request: Request, server: Server): Response | Promise<Response>;
|
||
routes: Record<string, Function | Response | Record<string, Function | Response>>;
|
||
}
|
||
export type Serve = Equal<BunServe.Options<unknown>, unknown> extends false ? BunServe.Options<unknown> : ServeOptions;
|
||
export type Server = Equal<BunServer<unknown>, unknown> extends false ? BunServer<unknown> : ServerOptions;
|
||
export type ServerWebSocketSendStatus = number;
|
||
export interface SocketAddress {
|
||
/**
|
||
* The IP address of the client.
|
||
*/
|
||
address: string;
|
||
/**
|
||
* The port of the client.
|
||
*/
|
||
port: number;
|
||
/**
|
||
* The IP family ("IPv4" or "IPv6").
|
||
*/
|
||
family: 'IPv4' | 'IPv6';
|
||
}
|
||
export interface ServerOptions extends Disposable {
|
||
/**
|
||
* Stop listening to prevent new connections from being accepted.
|
||
*
|
||
* By default, it does not cancel in-flight requests or websockets. That means it may take some time before all network activity stops.
|
||
*
|
||
* @param closeActiveConnections Immediately terminate in-flight requests, websockets, and stop accepting new connections.
|
||
* @default false
|
||
*/
|
||
stop(closeActiveConnections?: boolean): void;
|
||
/**
|
||
* Update the `fetch` and `error` handlers without restarting the server.
|
||
*
|
||
* This is useful if you want to change the behavior of your server without
|
||
* restarting it or for hot reloading.
|
||
*
|
||
* @example
|
||
*
|
||
* ```js
|
||
* // create the server
|
||
* const server = Bun.serve({
|
||
* fetch(request) {
|
||
* return new Response("Hello World v1")
|
||
* }
|
||
* });
|
||
*
|
||
* // Update the server to return a different response
|
||
* server.reload({
|
||
* fetch(request) {
|
||
* return new Response("Hello World v2")
|
||
* }
|
||
* });
|
||
* ```
|
||
*
|
||
* Passing other options such as `port` or `hostname` won't do anything.
|
||
*/
|
||
reload(options: Serve): void;
|
||
/**
|
||
* Mock the fetch handler for a running server.
|
||
*
|
||
* This feature is not fully implemented yet. It doesn't normalize URLs
|
||
* consistently in all cases and it doesn't yet call the `error` handler
|
||
* consistently. This needs to be fixed
|
||
*/
|
||
fetch(request: Request | string): Response | Promise<Response>;
|
||
/**
|
||
* Upgrade a {@link Request} to a {@link ServerWebSocket}
|
||
*
|
||
* @param request The {@link Request} to upgrade
|
||
* @param options Pass headers or attach data to the {@link ServerWebSocket}
|
||
*
|
||
* @returns `true` if the upgrade was successful and `false` if it failed
|
||
*
|
||
* @example
|
||
* ```js
|
||
* import { serve } from "bun";
|
||
* serve({
|
||
* websocket: {
|
||
* open: (ws) => {
|
||
* console.log("Client connected");
|
||
* },
|
||
* message: (ws, message) => {
|
||
* console.log("Client sent message", message);
|
||
* },
|
||
* close: (ws) => {
|
||
* console.log("Client disconnected");
|
||
* },
|
||
* },
|
||
* fetch(req, server) {
|
||
* const url = new URL(req.url);
|
||
* if (url.pathname === "/chat") {
|
||
* const upgraded = server.upgrade(req);
|
||
* if (!upgraded) {
|
||
* return new Response("Upgrade failed", { status: 400 });
|
||
* }
|
||
* }
|
||
* return new Response("Hello World");
|
||
* },
|
||
* });
|
||
* ```
|
||
* What you pass to `data` is available on the {@link ServerWebSocket.data} property
|
||
*/
|
||
upgrade<T = undefined>(request: Request, options?: {
|
||
/**
|
||
* Send any additional headers while upgrading, like cookies
|
||
*/
|
||
headers?: Bun.HeadersInit;
|
||
/**
|
||
* This value is passed to the {@link ServerWebSocket.data} property
|
||
*/
|
||
data?: T;
|
||
}): boolean;
|
||
/**
|
||
* Send a message to all connected {@link ServerWebSocket} subscribed to a topic
|
||
*
|
||
* @param topic The topic to publish to
|
||
* @param data The data to send
|
||
* @param compress Should the data be compressed? Ignored if the client does not support compression.
|
||
*
|
||
* @returns 0 if the message was dropped, -1 if backpressure was applied, or the number of bytes sent.
|
||
*
|
||
* @example
|
||
*
|
||
* ```js
|
||
* server.publish("chat", "Hello World");
|
||
* ```
|
||
*
|
||
* @example
|
||
* ```js
|
||
* server.publish("chat", new Uint8Array([1, 2, 3, 4]));
|
||
* ```
|
||
*
|
||
* @example
|
||
* ```js
|
||
* server.publish("chat", new ArrayBuffer(4), true);
|
||
* ```
|
||
*
|
||
* @example
|
||
* ```js
|
||
* server.publish("chat", new DataView(new ArrayBuffer(4)));
|
||
* ```
|
||
*/
|
||
publish(topic: string, data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, compress?: boolean): ServerWebSocketSendStatus;
|
||
/**
|
||
* Returns the client IP address and port of the given Request. If the request was closed or is a unix socket, returns null.
|
||
*
|
||
* @example
|
||
* ```js
|
||
* export default {
|
||
* async fetch(request, server) {
|
||
* return new Response(server.requestIP(request));
|
||
* }
|
||
* }
|
||
* ```
|
||
*/
|
||
requestIP(request: Request): SocketAddress | null;
|
||
/**
|
||
* Reset the idleTimeout of the given Request to the number in seconds. 0 means no timeout.
|
||
*
|
||
* @example
|
||
* ```js
|
||
* export default {
|
||
* async fetch(request, server) {
|
||
* server.timeout(request, 60);
|
||
* await Bun.sleep(30000);
|
||
* return new Response("30 seconds have passed");
|
||
* }
|
||
* }
|
||
* ```
|
||
*/
|
||
timeout(request: Request, seconds: number): void;
|
||
/**
|
||
* Undo a call to {@link Server.unref}
|
||
*
|
||
* If the Server has already been stopped, this does nothing.
|
||
*
|
||
* If {@link Server.ref} is called multiple times, this does nothing. Think of it as a boolean toggle.
|
||
*/
|
||
ref(): void;
|
||
/**
|
||
* Don't keep the process alive if this server is the only thing left.
|
||
* Active connections may continue to keep the process alive.
|
||
*
|
||
* By default, the server is ref'd.
|
||
*
|
||
* To prevent new connections from being accepted, use {@link Server.stop}
|
||
*/
|
||
unref(): void;
|
||
/**
|
||
* How many requests are in-flight right now?
|
||
*/
|
||
readonly pendingRequests: number;
|
||
/**
|
||
* How many {@link ServerWebSocket}s are in-flight right now?
|
||
*/
|
||
readonly pendingWebSockets: number;
|
||
readonly url: URL;
|
||
readonly port: number;
|
||
/**
|
||
* The hostname the server is listening on. Does not include the port
|
||
* @example
|
||
* ```js
|
||
* "localhost"
|
||
* ```
|
||
*/
|
||
readonly hostname: string;
|
||
/**
|
||
* Is the server running in development mode?
|
||
*
|
||
* In development mode, `Bun.serve()` returns rendered error messages with
|
||
* stack traces instead of a generic 500 error. This makes debugging easier,
|
||
* but development mode shouldn't be used in production or you will risk
|
||
* leaking sensitive information.
|
||
*/
|
||
readonly development: boolean;
|
||
/**
|
||
* An identifier of the server instance
|
||
*
|
||
* When bun is started with the `--hot` flag, this ID is used to hot reload the server without interrupting pending requests or websockets.
|
||
*
|
||
* When bun is not started with the `--hot` flag, this ID is currently unused.
|
||
*/
|
||
readonly id: string;
|
||
}
|
||
export type ListenCallback = (server: Server) => MaybePromise<void>;
|