svelte-adapter-bun
Version:
Adapter for SvelteKit apps that generates a standalone Bun.js server.
566 lines (531 loc) • 14.7 kB
TypeScript
import { Adapter } from "@sveltejs/kit";
declare global {
const ENV_PREFIX: string;
const BUILD_OPTIONS: BuildOptions;
}
declare module "SERVER" {
export { Server } from "@sveltejs/kit";
}
declare module "MANIFEST" {
import { SSRManifest } from "@sveltejs/kit";
export const manifest: SSRManifest;
}
interface BuildOptions {
/**
* Render contextual errors? This enables bun's error page
* @default false
*/
development?: boolean;
/**
* If enabled use `PROTOCOL_HEADER` `HOST_HEADER` like origin.
* @default false
*/
dynamic_origin?: boolean;
/**
* The default value of XFF_DEPTH if environment is not set.
* @default 1
*/
xff_depth?: number;
/**
* Browse a static assets
* @default true
*/
assets?: boolean;
}
type BuildOptionsMap = keyof BuildOptions;
interface MimeTypes {
[key: string]: string;
}
interface CompressOptions {
/**
* @default false
*/
gzip?: boolean;
/**
* @default false
*/
brotli?: boolean;
/**
* @default html,js,json,css,svg,xml,wasm
*/
files?: string[];
}
interface AdapterOptions extends BuildOptions {
/**
* The directory to build the server to. It defaults to build — i.e. node build would start the server locally after it has been created.
* @default "build"
*/
out?: string;
/**
* Enables precompressing using gzip and brotli for assets and prerendered pages. It defaults to false.
* @default false
*/
precompress?: boolean | CompressOptions;
/**
* If you need to change the name of the environment variables used to configure the deployment (for example, to deconflict with environment variables you don't control), you can specify a prefix: envPrefix: 'MY_CUSTOM_';
* @default ''
*/
envPrefix?: string;
}
/**
* **0** means the message was **dropped**
*
* **-1** means **backpressure**
*
* **> 0** is the **number of bytes sent**
*
*/
type ServerWebSocketSendStatus = 0 | -1 | number;
/**
* Fast WebSocket API designed for server environments.
*
* Features:
* - **Message compression** - Messages can be compressed
* - **Backpressure** - If the client is not ready to receive data, the server will tell you.
* - **Dropped messages** - If the client cannot receive data, the server will tell you.
* - **Topics** - Messages can be {@link ServerWebSocket.publish}ed to a specific topic and the client can {@link ServerWebSocket.subscribe} to topics
*
* This is slightly different than the browser {@link WebSocket} which Bun supports for clients.
*
* Powered by [uWebSockets](https://github.com/uNetworking/uWebSockets)
*/
interface ServerWebSocket<T = undefined> {
/**
*
* Send a message to the client.
*
* @param data The message 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
* const status = ws.send("Hello World");
* if (status === 0) {
* console.log("Message was dropped");
* } else if (status === -1) {
* console.log("Backpressure was applied");
* } else {
* console.log(`Message sent! ${status} bytes sent`);
* }
* ```
*
* @example
*
* ```js
* ws.send("Feeling very compressed", true);
* ```
*
* @example
*
* ```js
* ws.send(new Uint8Array([1, 2, 3, 4]));
* ```
*
* @example
*
* ```js
* ws.send(new ArrayBuffer(4));
* ```
*
* @example
*
* ```js
* ws.send(new DataView(new ArrayBuffer(4)));
* ```
*
*/
send(data: string | ArrayBufferView | ArrayBuffer, compress?: boolean): ServerWebSocketSendStatus;
/**
*
* Send a message to the client.
*
* This function is the same as {@link ServerWebSocket.send} but it only accepts a string. This function includes a fast path.
*
* @param data The message 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
* const status = ws.send("Hello World");
* if (status === 0) {
* console.log("Message was dropped");
* } else if (status === -1) {
* console.log("Backpressure was applied");
* } else {
* console.log(`Message sent! ${status} bytes sent`);
* }
* ```
*
* @example
*
* ```js
* ws.send("Feeling very compressed", true);
* ```
*
*
*/
sendText(data: string, compress?: boolean): ServerWebSocketSendStatus;
/**
*
* Send a message to the client.
*
* This function is the same as {@link ServerWebSocket.send} but it only accepts Uint8Array.
*
* @param data The message 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.
*
*
* ```js
* ws.sendBinary(new Uint8Array([1, 2, 3, 4]));
* ```
*
* @example
*
* ```js
* ws.sendBinary(new ArrayBuffer(4));
* ```
*
* @example
*
* ```js
* ws.sendBinary(new DataView(new ArrayBuffer(4)));
* ```
*
*/
sendBinary(data: Uint8Array, compress?: boolean): ServerWebSocketSendStatus;
/**
* Gently close the connection.
*
* @param code The close code
*
* @param reason The close reason
*
* To close the connection abruptly, use `close(0, "")`
*/
close(code?: number, reason?: string): void;
/**
* Send a message to all subscribers of 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
* ws.publish("chat", "Hello World");
* ```
*
* @example
* ```js
* ws.publish("chat", new Uint8Array([1, 2, 3, 4]));
* ```
*
* @example
* ```js
* ws.publish("chat", new ArrayBuffer(4), true);
* ```
*
* @example
* ```js
* ws.publish("chat", new DataView(new ArrayBuffer(4)));
* ```
*/
publish(
topic: string,
data: string | ArrayBufferView | ArrayBuffer,
compress?: boolean,
): ServerWebSocketSendStatus;
/**
* Send a message to all subscribers of a topic
*
* This function is the same as {@link publish} but only accepts string input. This function has a fast path.
*
* @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
* ws.publishText("chat", "Hello World");
* ```
*
*/
publishText(topic: string, data: string, compress?: boolean): ServerWebSocketSendStatus;
/**
* Send a message to all subscribers of a topic
*
* This function is the same as {@link publish} but only accepts a Uint8Array. This function has a fast path.
*
* @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
* ws.publishBinary("chat", "Hello World");
* ```
*
* @example
* ```js
* ws.publishBinary("chat", new Uint8Array([1, 2, 3, 4]));
* ```
*
* @example
* ```js
* ws.publishBinary("chat", new ArrayBuffer(4), true);
* ```
*
* @example
* ```js
* ws.publishBinary("chat", new DataView(new ArrayBuffer(4)));
* ```
*/
publishBinary(topic: string, data: Uint8Array, compress?: boolean): ServerWebSocketSendStatus;
/**
* Subscribe to a topic
* @param topic The topic to subscribe to
*
* @example
* ```js
* ws.subscribe("chat");
* ```
*/
subscribe(topic: string): void;
/**
* Unsubscribe from a topic
* @param topic The topic to unsubscribe from
*
* @example
* ```js
* ws.unsubscribe("chat");
* ```
*
*/
unsubscribe(topic: string): void;
/**
* Is the socket subscribed to a topic?
* @param topic The topic to check
*
* @returns `true` if the socket is subscribed to the topic, `false` otherwise
*/
isSubscribed(topic: string): boolean;
/**
* The remote address of the client
* @example
* ```js
* console.log(socket.remoteAddress); // "127.0.0.1"
* ```
*/
readonly remoteAddress: string;
/**
* Ready state of the socket
*
* @example
* ```js
* console.log(socket.readyState); // 1
* ```
*/
readonly readyState: -1 | 0 | 1 | 2 | 3;
/**
* The data from the {@link Server.upgrade} function
*
* Put any data you want to share between the `fetch` function and the websocket here.
*
* You can read/write to this property at any time.
*/
data: T;
/**
* Batch data sent to a {@link ServerWebSocket}
*
* This makes it significantly faster to {@link ServerWebSocket.send} or {@link ServerWebSocket.publish} multiple messages
*
* The `message`, `open`, and `drain` callbacks are automatically corked, so
* you only need to call this if you are sending messages outside of those
* callbacks or in async functions
*/
cork: (callback: (ws: ServerWebSocket<T>) => any) => void | Promise<void>;
/**
* Configure the {@link WebSocketHandler.message} callback to return a {@link ArrayBuffer} instead of a {@link Uint8Array}
*
* @default "uint8array"
*/
binaryType?: "arraybuffer" | "uint8array";
}
type WebSocketCompressor =
| "disable"
| "shared"
| "dedicated"
| "3KB"
| "4KB"
| "8KB"
| "16KB"
| "32KB"
| "64KB"
| "128KB"
| "256KB";
/**
* Create a server-side {@link ServerWebSocket} handler for use with {@link Bun.serve}
*
* @example
* ```ts
* import { websocket, serve } from "bun";
*
* serve({
* port: 3000,
* websocket: websocket<{name: string}>({
* open: (ws) => {
* console.log("Client connected");
* },
* message: (ws, message) => {
* console.log(`${ws.data.name}: ${message}`);
* },
* close: (ws) => {
* console.log("Client disconnected");
* },
* }),
*
* fetch(req, server) {
* if (req.url === "/chat") {
* const upgraded = server.upgrade(req, {
* data: {
* name: new URL(req.url).searchParams.get("name"),
* },
* });
* if (!upgraded) {
* return new Response("Upgrade failed", { status: 400 });
* }
* return;
* }
* return new Response("Hello World");
* },
* });
*/
export interface WebSocketHandler<T = undefined> {
upgrade: (
request: Request,
upgrade: (
request: Request,
options?: {
/**
* Send any additional headers while upgrading, like cookies
*/
headers?: HeadersInit;
/**
* This value is passed to the {@link ServerWebSocket.data} property
*/
data?: T;
},
) => boolean,
) => boolean | Promise<boolean>;
/**
* Handle an incoming message to a {@link ServerWebSocket}
*
* @param ws The {@link ServerWebSocket} that received the message
* @param message The message received
*
* To change `message` to be an `ArrayBuffer` instead of a `Uint8Array`, set `ws.binaryType = "arraybuffer"`
*/
message: (ws: ServerWebSocket<T>, message: string | Uint8Array) => void | Promise<void>;
/**
* The {@link ServerWebSocket} has been opened
*
* @param ws The {@link ServerWebSocket} that was opened
*/
open?: (ws: ServerWebSocket<T>) => void | Promise<void>;
/**
* The {@link ServerWebSocket} is ready for more data
*
* @param ws The {@link ServerWebSocket} that is ready
*/
drain?: (ws: ServerWebSocket<T>) => void | Promise<void>;
/**
* The {@link ServerWebSocket} is being closed
* @param ws The {@link ServerWebSocket} that was closed
* @param code The close code
* @param message The close message
*/
close?: (ws: ServerWebSocket<T>, code: number, message: string) => void | Promise<void>;
/**
* Enable compression for clients that support it. By default, compression is disabled.
*
* @default false
*
* `true` is equivalent to `"shared"
*/
perMessageDeflate?:
| true
| false
| {
/**
* Enable compression on the {@link ServerWebSocket}
*
* @default false
*
* `true` is equivalent to `"shared"
*/
compress?: WebSocketCompressor | false | true;
/**
* Configure decompression
*
* @default false
*
* `true` is equivalent to `"shared"
*/
decompress?: WebSocketCompressor | false | true;
};
/**
* The maximum size of a message
*/
maxPayloadLength?: number;
/**
* After a connection has not received a message for this many seconds, it will be closed.
* @default 120 (2 minutes)
*/
idleTimeout?: number;
/**
* The maximum number of bytes that can be buffered for a single connection.
* @default 16MB
*/
backpressureLimit?: number;
/**
* Close the connection if the backpressure limit is reached.
* @default false
* @see {@link backpressureLimit}
* @see {@link ServerWebSocketSendStatus}
* @see {@link ServerWebSocket.send}
* @see {@link ServerWebSocket.publish}
*/
closeOnBackpressureLimit?: boolean;
/**
* Control whether or not ws.publish() should include the ServerWebSocket
* that published the message. This is enabled by default, but it was an API
* design mistake. A future version of Bun will change this default to
* `false` and eventually remove this option entirely. The better way to publish to all is to use {@link Server.publish}.
*
* if `true` or `undefined`, {@link ServerWebSocket.publish} will publish to all subscribers, including the websocket publishing the message.
*
* if `false`, {@link ServerWebSocket.publish} will publish to all subscribers excluding the websocket publishing the message.
*
* @default true
*
*/
publishToSelf?: boolean;
}
export default function plugin(options?: AdapterOptions): Adapter;