UNPKG

svelte-adapter-bun

Version:

Adapter for SvelteKit apps that generates a standalone Bun.js server.

566 lines (531 loc) 14.7 kB
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;