aboutsummaryrefslogtreecommitdiff
path: root/docs/api
diff options
context:
space:
mode:
authorGravatar Colin McDonnell <colinmcd94@gmail.com> 2023-06-20 18:57:37 -0700
committerGravatar GitHub <noreply@github.com> 2023-06-20 18:57:37 -0700
commitadb451eec6b8286a4ee18b16b5b87644d5ef3020 (patch)
treef1bca25150bf1c9ad9b1ac2d5703b71e5953fc51 /docs/api
parent62639081c1b8ffef0dbf8d729d9d21e10ed41734 (diff)
downloadbun-adb451eec6b8286a4ee18b16b5b87644d5ef3020.tar.gz
bun-adb451eec6b8286a4ee18b16b5b87644d5ef3020.tar.zst
bun-adb451eec6b8286a4ee18b16b5b87644d5ef3020.zip
Docs for DOM testing and FileSink (#3330)
* Update websocket docs & jsdoc * Add info about user-specific data in ws * Document FileSink * Docs for happydom test * Updates
Diffstat (limited to 'docs/api')
-rw-r--r--docs/api/file-io.md57
-rw-r--r--docs/api/websockets.md113
2 files changed, 122 insertions, 48 deletions
diff --git a/docs/api/file-io.md b/docs/api/file-io.md
index 07336d071..effc57580 100644
--- a/docs/api/file-io.md
+++ b/docs/api/file-io.md
@@ -202,6 +202,53 @@ const response = await fetch("https://bun.sh");
await Bun.write("index.html", response);
```
+## Incremental writing with `FileSink`
+
+Bun provides a native incremental file writing API called `FileSink`. To retrieve a `FileSink` instance from a `BunFile`:
+
+```ts
+const file = Bun.file("output.txt");
+const writer = file.writer();
+```
+
+To incrementally write to the file, call `.write()`.
+
+```ts
+const file = Bun.file("output.txt");
+const writer = file.writer();
+
+writer.write("it was the best of times\n");
+writer.write("it was the worst of times\n");
+```
+
+These chunks will be buffered internally. To flush the buffer to disk, use `.flush()`. This returns the number of flushed bytes.
+
+```ts
+writer.flush(); // write buffer to disk
+```
+
+The buffer will also auto-flush when the `FileSink`'s _high water mark_ is reached; that is, when its internal buffer is full. This value can be configured.
+
+```ts
+const file = Bun.file("output.txt");
+const writer = file.writer({ highWaterMark: 1024 * 1024 }); // 1MB
+```
+
+To flush the buffer and close the file:
+
+```ts
+writer.end();
+```
+
+Note that, by default, the `bun` process will stay alive until this `FileSink` is explicitly closed with `.end()`. To opt out of this behavior, you can "unref" the instance.
+
+```ts
+writer.unref();
+
+// to "re-ref" it later
+writer.ref();
+```
+
## Benchmarks
The following is a 3-line implementation of the Linux `cat` command.
@@ -250,5 +297,15 @@ interface BunFile {
stream(): Promise<ReadableStream>;
arrayBuffer(): Promise<ArrayBuffer>;
json(): Promise<any>;
+ writer(params: { highWaterMark?: number }): FileSink;
+}
+
+export interface FileSink {
+ write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
+ flush(): number | Promise<number>;
+ end(error?: Error): number | Promise<number>;
+ start(options?: { highWaterMark?: number }): void;
+ ref(): void;
+ unref(): void;
}
```
diff --git a/docs/api/websockets.md b/docs/api/websockets.md
index d1e1d3831..b8b7f7a8e 100644
--- a/docs/api/websockets.md
+++ b/docs/api/websockets.md
@@ -12,41 +12,7 @@
Internally Bun's WebSocket implementation is built on [uWebSockets](https://github.com/uNetworking/uWebSockets).
{% /callout %}
-## Connect to a WebSocket server
-
-To connect to an external socket server, create an instance of `WebSocket` with the constructor.
-
-```ts
-const socket = new WebSocket("ws://localhost:3000");
-```
-
-Bun supports setting custom headers. This is a Bun-specific extension of the `WebSocket` standard. _This will not work in browsers._
-
-```ts
-const socket = new WebSocket("ws://localhost:3000", {
- headers: {
- // custom headers
- },
-});
-```
-
-To add event listeners to the socket:
-
-```ts
-// message is received
-socket.addEventListener("message", event => {});
-
-// socket opened
-socket.addEventListener("open", event => {});
-
-// socket closed
-socket.addEventListener("close", event => {});
-
-// error handler
-socket.addEventListener("error", event => {});
-```
-
-## Create a WebSocket server
+## Start a WebSocket server
Below is a simple WebSocket server built with `Bun.serve`, in which all incoming requests are [upgraded](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) to WebSocket connections in the `fetch` handler. The socket handlers are declared in the `websocket` parameter.
@@ -109,7 +75,7 @@ Bun.serve({
});
```
-## Sending messages
+### Sending messages
Each `ServerWebSocket` instance has a `.send()` method for sending messages to the client. It supports a range of input types.
@@ -119,7 +85,7 @@ ws.send(response.arrayBuffer()); // ArrayBuffer
ws.send(new Uint8Array([1, 2, 3])); // TypedArray | DataView
```
-## Headers
+### Headers
Once the upgrade succeeds, Bun will send a `101 Switching Protocols` response per the [spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism). Additional `headers` can be attched to this `Response` in the call to `server.upgrade()`.
@@ -137,7 +103,7 @@ Bun.serve({
});
```
-## Contextual data
+### Contextual data
Contextual `data` can be attached to a new WebSocket in the `.upgrade()` call. This data is made available on the `ws.data` property inside the WebSocket handlers.
@@ -145,14 +111,16 @@ Contextual `data` can be attached to a new WebSocket in the `.upgrade()` call. T
type WebSocketData = {
createdAt: number;
channelId: string;
+ authToken: string;
};
// TypeScript: specify the type of `data`
Bun.serve<WebSocketData>({
fetch(req, server) {
+ // use a library to parse cookies
const cookies = parseCookies(req.headers.get("Cookie"));
server.upgrade(req, {
- // TS: this object must conform to WebSocketData
+ // this object must conform to WebSocketData
data: {
createdAt: Date.now(),
channelId: new URL(req.url).searchParams.get("channelId"),
@@ -165,10 +133,12 @@ Bun.serve<WebSocketData>({
websocket: {
// handler called when a message is received
async message(ws, message) {
- ws.data; // WebSocketData
+ const user = getUserFromToken(ws.data.authToken);
+
await saveMessageToDatabase({
channel: ws.data.channelId,
message: String(message),
+ userId: user.id,
});
},
},
@@ -185,9 +155,11 @@ socket.addEventListener("message", event => {
})
```
-The cookies that are currently set on the page will be sent with the WebSocket upgrade request and available on `req.headers` in the `fetch` handler. Parse these cookies to determine the identity of the connecting user and set the value of `data` accordingly.
+{% callout %}
+**Identifying users** — The cookies that are currently set on the page will be sent with the WebSocket upgrade request and available on `req.headers` in the `fetch` handler. Parse these cookies to determine the identity of the connecting user and set the value of `data` accordingly.
+{% /callout %}
-## Pub/Sub
+### Pub/Sub
Bun's `ServerWebSocket` implementation implements a native publish-subscribe API for topic-based broadcasting. Individual sockets can `.subscribe()` to a topic (specified with a string identifier) and `.publish()` messages to all other subscribers to that topic. This topic-based broadcast API is similar to [MQTT](https://en.wikipedia.org/wiki/MQTT) and [Redis Pub/Sub](https://redis.io/topics/pubsub).
@@ -199,7 +171,9 @@ const server = Bun.serve<{ username: string }>({
console.log(`upgrade!`);
const username = getUsernameFromReq(req);
const success = server.upgrade(req, { data: { username } });
- return success ? undefined : new Response("WebSocket upgrade error", { status: 400 });
+ return success
+ ? undefined
+ : new Response("WebSocket upgrade error", { status: 400 });
}
return new Response("Hello world");
@@ -226,9 +200,9 @@ const server = Bun.serve<{ username: string }>({
console.log(`Listening on ${server.hostname}:${server.port}`);
```
-Calling `.publish(data)` will send the message to all subscribers of a topic (excluding the socket that called `.publish()`).
+Calling `.publish(data)` will send the message to all subscribers of a topic _except_ the socket that called `.publish()`.
-## Compression
+### Compression
Per-message [compression](https://websockets.readthedocs.io/en/stable/topics/compression.html) can be enabled with the `perMessageDeflate` parameter.
@@ -250,7 +224,7 @@ ws.send("Hello world", true);
For fine-grained control over compression characteristics, refer to the [Reference](#reference).
-## Backpressure
+### Backpressure
The `.send(message)` method of `ServerWebSocket` returns a `number` indicating the result of the operation.
@@ -260,6 +234,42 @@ The `.send(message)` method of `ServerWebSocket` returns a `number` indicating t
This gives you better control over backpressure in your server.
+## Connect to a `Websocket` server
+
+To connect to an external socket server, either from a browser or from Bun, create an instance of `WebSocket` with the constructor.
+
+```ts
+const socket = new WebSocket("ws://localhost:3000");
+```
+
+In browsers, the cookies that are currently set on the page will be sent with the WebSocket upgrade request. This is a standard feature of the `WebSocket` API.
+
+For convenience, Bun lets you setting custom headers directly in the constructor. This is a Bun-specific extension of the `WebSocket` standard. _This will not work in browsers._
+
+```ts
+const socket = new WebSocket("ws://localhost:3000", {
+ headers: {
+ // custom headers
+ },
+});
+```
+
+To add event listeners to the socket:
+
+```ts
+// message is received
+socket.addEventListener("message", event => {});
+
+// socket opened
+socket.addEventListener("open", event => {});
+
+// socket closed
+socket.addEventListener("close", event => {});
+
+// error handler
+socket.addEventListener("error", event => {});
+```
+
## Reference
```ts
@@ -267,7 +277,10 @@ namespace Bun {
export function serve(params: {
fetch: (req: Request, server: Server) => Response | Promise<Response>;
websocket?: {
- message: (ws: ServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void;
+ message: (
+ ws: ServerWebSocket,
+ message: string | ArrayBuffer | Uint8Array,
+ ) => void;
open?: (ws: ServerWebSocket) => void;
close?: (ws: ServerWebSocket) => void;
error?: (ws: ServerWebSocket, error: Error) => void;
@@ -297,7 +310,11 @@ type Compressor =
interface Server {
pendingWebsockets: number;
- publish(topic: string, data: string | ArrayBufferView | ArrayBuffer, compress?: boolean): number;
+ publish(
+ topic: string,
+ data: string | ArrayBufferView | ArrayBuffer,
+ compress?: boolean,
+ ): number;
upgrade(
req: Request,
options?: {