### Basic WebSocket Proxy Setup Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Use `createWebSocketProxy` to forward connections to a static upstream URL. This is suitable for simple proxying scenarios. ```typescript import crossws from "crossws/adapters/"; import { createWebSocketProxy } from "crossws"; const websocket = crossws({ hooks: createWebSocketProxy("wss://echo.websocket.org"), }); ``` -------------------------------- ### Create a Simple WebSocket Server Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/1.index.md This example demonstrates how to set up a basic WebSocket server using crossws. It includes handlers for connection open, message reception, closure, and errors. The server also fetches an HTML file to serve as a fallback. ```js // Works with Bun, Deno and Node.js (also Cloudflare or SSE as fallback) import { serve } from "crossws/server"; serve({ websocket: { open(peer) { console.log("[ws] open", peer); peer.send({ user: "server", message: `Welcome ${peer}!` }); }, message(peer, message) { console.log("[ws] message", message); if (message.text().includes("ping")) { peer.send({ user: "server", message: "pong" }); } else { peer.send({ user: peer.toString(), message: message.toString() }); } }, close(peer, event) { console.log("[ws] close", peer, event); }, error(peer, error) { console.log("[ws] error", peer, error); }, }, fetch: () => fetch( "https://raw.githubusercontent.com/h3js/crossws/refs/heads/main/playground/public/index.html", ).then((res) => new Response(res.body, { headers: { "Content-Type": "text/html" } })), }); ``` -------------------------------- ### Basic Resolver Setup Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/6.resolver.md Set up a basic resolver to dynamically route WebSocket events. Import the adapter and define the `resolve` function to determine which hooks to return based on the request. Return `undefined` if no match is found. ```js import crossws from "crossws/adapters/"; import { defineHooks } from "crossws"; const websocket = crossws({ async resolve(req) { // TODO: Resolve hooks based on req.url, req.headers // You can return undefined in case there is no match return { /* resolved hooks */ }; }, }); ``` -------------------------------- ### uWebSockets.js Integration Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/node.md Integrate crossws with uWebSockets.js for Node.js servers. This setup uses the App class from uWebSockets.js and configures crossws for WebSocket handling. ```typescript import { App } from "uWebSockets.js"; import crossws from "crossws/adapters/uws"; const ws = crossws({ hooks: { message: console.log, }, }); const server = App().ws("/*", ws.websocket); server.get("/*", (res, req) => { res.writeStatus("200 OK").writeHeader("Content-Type", "text/html"); res.end( ``, ); }); server.listen(3001, () => { console.log("Listening to port 3001"); }); ``` -------------------------------- ### Handle Node.js Upgrade Request in Vercel Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/vercel.draft.md Use `handleNodeUpgrade` for Vercel Node.js handlers that accept an `IncomingMessage` and `ServerResponse`. This example configures crossws with `message` hooks to echo received messages. ```typescript import crossws from "crossws/adapters/vercel"; const ws = crossws({ hooks: { message(peer, message) { peer.send(message.text()); }, }, }); export default async function handler(req, res) { if (await ws.handleNodeUpgrade(req, res)) { return; } res.end("ok"); } ``` -------------------------------- ### Run WebSocket Server with Different Runtimes Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/1.index.md Commands to run the crossws WebSocket server using Node.js, Deno, or Bun. ```bash node server.mjs ``` ```bash deno run --allow-env --allow-net server.mjs ``` ```bash bun run server.mjs ``` -------------------------------- ### Initialize WebsocketSSE Client Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/sse.md Instantiate the `WebsocketSSE` client for communication with the server. Ensure the server address is correctly specified and `bdir: true` is set if bidirectional communication is enabled. ```javascript import { WebsocketSSE } from "crossws/websocket/sse"; const ws = new WebsocketSSE("https://", { bdir: true }); ws.addEventListener("open", () => { ws.send("ping"); }); ws.addEventListener("message", (event) => { console.log("Received:", event.data); }); ``` -------------------------------- ### Initialize and Mount Vue App with WebSocket Logic Source: https://github.com/h3js/crossws/blob/main/playground/public/index.html This snippet initializes a Vue.js application, sets up a reactive store for messages, and defines functions for WebSocket connection, message handling, and UI interactions. It mounts the app and initiates the WebSocket connection. ```javascript import { createApp, reactive, nextTick } from "https://esm.sh/petite-vue@0.4.1"; let ws; const store = reactive({ message: "", messages: [], }); const scroll = () => { nextTick(() => { const el = document.querySelector("#messages"); el.scrollTop = el.scrollHeight; el.scrollTo({ top: el.scrollHeight, behavior: "smooth", }); }); }; const format = async () => { for (const message of store.messages) { if (!message._fmt && message.text.startsWith("{")) { message._fmt = true; const { codeToHtml } = await import("https://esm.sh/shiki@1.0.0"); const str = JSON.stringify(JSON.parse(message.text), null, 2); message.formattedText = await codeToHtml(str, { lang: "json", theme: "dark-plus", }); } } }; const log = (user, ...args) => { console.log("[ws]", user, ...args); store.messages.push({ text: args.join(" "), formattedText: "", user: user, date: new Date().toLocaleString(), }); scroll(); format(); }; const connect = async () => { const isSecure = location.protocol === "https:"; const url = (isSecure ? "wss://" : "ws://") + location.host + "/_ws"; if (ws) { log("ws", "Closing previous connection before reconnecting..."); ws.close(); clear(); } log("ws", "Connecting to", url, "..."); ws = new WebSocket(url); ws.addEventListener("message", async (event) => { let data = typeof event.data === "string" ? event.data : await event.data.text(); const { user = "system", message = "" } = data.startsWith("{") ? JSON.parse(data) : { message: data }; log(user, typeof message === "string" ? message : JSON.stringify(message)); }); await new Promise((resolve) => ws.addEventListener("open", resolve)); log("ws", "Connected!"); }; const clear = () => { store.messages.splice(0, store.messages.length); log("system", "previous messages cleared"); }; const send = () => { console.log("sending message..."); if (store.message) { ws.send(store.message); } store.message = ""; }; const ping = () => { log("ws", "Sending ping"); ws.send("ping"); }; createApp({ store, send, ping, clear, connect, rand: Math.random(), }).mount(); await connect(); ``` -------------------------------- ### Handle Web Upgrade Request in Vercel Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/vercel.draft.md Use `handleWebUpgrade` for runtimes that process Web `Request` objects and return `Response` objects. This snippet demonstrates setting up crossws with basic `open` hooks. ```typescript import crossws from "crossws/adapters/vercel"; const ws = crossws({ hooks: { open(peer) { peer.send("hello"); }, }, }); export default { async fetch(request) { const response = await ws.handleWebUpgrade(request); if (response) { return response; } return new Response("ok"); }, }; ``` -------------------------------- ### Manual Bun Server Integration with Crossws Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/bun.md Use this snippet to manually integrate crossws with your Bun server. It handles WebSocket upgrades using `handleUpgrade` and passes the `ws.websocket` object to `Bun.serve`. Ensure you have the `crossws/adapters/bun` module imported. ```typescript import crossws from "crossws/adapters/bun"; const ws = crossws({ hooks: { message: console.log, }, }); Bun.serve({ port: 3000, websocket: ws.websocket, fetch(request, server) { if (request.headers.get("upgrade") === "websocket") { return ws.handleUpgrade(request, server); } return new Response( ``, { headers: { "content-type": "text/html" } }, ); }, }); ``` -------------------------------- ### Manual Deno Server Integration with Crossws Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/deno.md Use this snippet to manually integrate crossws with your Deno server. It checks for the 'upgrade' header and calls `handleUpgrade` for WebSocket connections. Ensure the client connects to ws://localhost:3000. ```typescript import crossws from "crossws/adapters/deno"; const ws = crossws({ hooks: { message: console.log, }, }); Deno.serve({ port: 3000 }, (request, info) => { if (request.headers.get("upgrade") === "websocket") { return ws.handleUpgrade(request, info); } return new Response( ``, { headers: { "content-type": "text/html" } }, ); }); ``` -------------------------------- ### Define Pub/Sub Hooks Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/5.pubsub.md Use `defineHooks` to set up event handlers for peer lifecycle events like upgrade, open, message, and close. The `open` hook subscribes new clients to the 'chat' channel and publishes a join message. The `message` hook rebroadcasts incoming messages to the 'chat' channel. The `close` hook publishes a leave message and unsubscribes the peer. ```javascript import { defineHooks } from "crossws"; const hooks = defineHooks({ upgrade(req) { return { // namespace: new URL(req.url).pathname }; }, open(peer) { // Send welcome to the new client peer.send("Welcome to the server!"); // Join new client to the "chat" channel peer.subscribe("chat"); // Notify every other connected client peer.publish("chat", `[system] ${peer} joined בצs!`); }, message(peer, message) { // The server re-broadcasts incoming messages to everyone peer.publish("chat", `[${peer}] ${message}`); }, close(peer) { peer.publish("chat", `[system] ${peer} has left the chat בצs!`); peer.unsubscribe("chat"); }, }); ``` -------------------------------- ### Handle SSE Requests in Web Server Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/sse.md This code snippet shows how to integrate the SSE adapter into your web server's fetch handler. It checks for SSE-specific headers to determine if the request should be handled by the adapter. ```javascript async fetch(request) { // Handle crossws upgrade if ( request.headers.get("accept") === "text/event-stream" || request.headers.has("x-crossws-id") ) { return ws.fetch(request); } // Your normal application logic return new Response("default page") } ``` -------------------------------- ### createWebSocketProxy Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Creates a WebSocket proxy that forwards connections to a target URL. It supports various configurations for target resolution, subprotocol handling, custom headers, buffer limits, connection timeouts, and WebSocket constructors. ```APIDOC ## `createWebSocketProxy(target)` ### Description Creates a WebSocket proxy that forwards connections to a target URL. It supports various configurations for target resolution, subprotocol handling, custom headers, buffer limits, connection timeouts, and WebSocket constructors. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **`target`** (`string | URL | (peer: Peer) => string | URL`) - The upstream WebSocket URL, or a function that resolves it per peer. The proxy does not enforce a scheme allowlist; any URL the configured `WebSocket` constructor accepts (including `ws+unix:` with `ws`) works. See the [SSRF warning](#dynamic-target) before using a dynamic resolver. - **`forwardProtocol`** (`boolean | string | string[] | Record | (peer: Peer) => string | string[] | undefined`) - (default `true`). Controls the subprotocol(s) offered to the upstream. `true` forwards the client's `sec-websocket-protocol` header verbatim; `false` offers none. A `string`/`string[]` offers a fixed value upstream regardless of the client. A `Record` rewrites matching client tokens to their mapped values, passing unmapped tokens through. A function resolves the value per peer. See [rewriting the upstream subprotocol](#rewriting-the-upstream-subprotocol). In all cases the value echoed back to the client is the first client-offered token (RFC 6455), and values that are not valid RFC 7230 tokens are dropped from the echo. - **`headers`** (`HeadersInit | (peer: Peer) => HeadersInit`) - Extra headers to send on the upstream handshake. Only honored when a custom `WebSocket` constructor that accepts a third options argument is supplied — the WHATWG global ignores it. - **`maxBufferSize`** (`number`) - (default `1048576`, i.e. 1 MiB). Maximum number of bytes buffered per peer while the upstream is still connecting. String frames are accounted at their UTF-8 worst case (3 bytes per UTF-16 code unit) to avoid undercounting multi-byte content. When exceeded, the peer is closed with code `1009` (Message Too Big). Set to `0` to disable. - **`connectTimeout`** (`number`) - (default `10000`). Milliseconds to wait for the upstream WebSocket handshake to complete. If exceeded, the peer is closed with code `1011`. Set to `0` to disable. - **`WebSocket`** (`typeof WebSocket`) - (default `globalThis.WebSocket`). Custom `WebSocket` constructor used to dial the upstream. Falls back to the global when omitted; throws at setup time if neither is available. ### Returns A `Partial` object containing `upgrade`, `open`, `message`, `close`, and `error` hooks. ``` -------------------------------- ### Basic Bunny.net WebSocket Integration Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/bunny.md Integrates crossws with Bunny.net Edge Scripting by checking for the 'upgrade' header and handling WebSocket requests. Includes basic message, open, and close hooks. ```typescript import * as BunnySDK from "@bunny.net/edgescript-sdk"; import crossws from "crossws/adapters/bunny"; const ws = crossws({ hooks: { message: (peer, message) => { console.log("Received:", message.text()); peer.send(`Echo: ${message.text()}`); }, open: (peer) => { console.log("Client connected"); }, close: (peer) => { console.log("Client disconnected"); }, }, }); BunnySDK.net.http.serve(async (request: Request) => { if (request.headers.get("upgrade") === "websocket") { return ws.handleUpgrade(request); } return new Response( ``, { headers: { "content-type": "text/html" } }, ); }); ``` -------------------------------- ### Manual Node.js HTTP Server Integration Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/node.md Manually connect the 'upgrade' event of a Node.js HTTP server to crossws's handleUpgrade method for WebSocket integration. Ensure the 'upgrade' header is 'websocket'. ```typescript import { createServer } from "node:http"; import crossws from "crossws/adapters/node"; const ws = crossws({ hooks: { message: console.log, }, }); const server = createServer((req, res) => { res.end( ``, ); }).listen(3000); server.on("upgrade", (req, socket, head) => { if (req.headers.upgrade === "websocket") { ws.handleUpgrade(req, socket, head); } }); ``` -------------------------------- ### Bunny.net Protocol Negotiation with Upgrade Hook Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/bunny.md Controls WebSocket protocol negotiation using the 'upgrade' hook. It checks for an authorization token and negotiates subprotocols like 'graphql-ws' or 'graphql-transport-ws' based on the client's request. ```typescript const ws = crossws({ hooks: { upgrade(req) { // Check for authorization token const token = req.headers.get("authorization"); if (!token) { return new Response("Unauthorized", { status: 401 }); } // Negotiate protocol based on client request const requestedProtocol = req.headers.get("sec-websocket-protocol"); const supportedProtocols = ["graphql-ws", "graphql-transport-ws"]; const protocol = requestedProtocol ?.split(",") .map((p) => p.trim()) .find((p) => supportedProtocols.includes(p)); return { headers: { "sec-websocket-protocol": protocol || "", }, }; }, }, }); ``` -------------------------------- ### Define WebSocket Server Hooks with Crossws Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/2.hooks.md Use `defineHooks` to create a set of lifecycle hooks for your WebSocket server. This wrapper is optional but provides type support and auto-completion. Implement hooks like `upgrade`, `open`, `message`, `close`, and `error` to handle WebSocket events. ```typescript import { defineHooks } from "crossws"; const hooks = defineHooks({ upgrade(req) { console.log(`[ws] upgrading ${req.url}...`); return { // namespace: new URL(req.url).pathname headers: {}, }; }, open(peer) { console.log(`[ws] open: ${peer}`); }, message(peer, message) { console.log("[ws] message", peer, message); if (message.text().includes("ping")) { peer.send("pong"); } }, close(peer, event) { console.log("[ws] close", peer, event); }, error(peer, error) { console.log("[ws] error", peer, error); }, }); ``` -------------------------------- ### Define SSE Adapter with Bidirectional Support Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/sse.md Configure the SSE adapter for bidirectional messaging. The `upgrade` hook can be used for additional authentication, and `open` and `message` hooks handle connection and message events. ```typescript import sseAdapter from "crossws/adapters/sse"; const ws = sseAdapter({ bidir: true, // Enable bidirectional messaging support hooks: { upgrade(request) { // In case of bidirectional mode, extra auth is recommended based on request // You can return a new Response() instead to abort return { headers: {}, }; }, open(peer) { // Use this hook to send messages to peer peer.send(`Welcome ${peer}`); }, message(peer, message) { // Accepting messages from peer (bidirectional mode) console.log(`Message from ${peer}: ${message}`); // Message from : ping }, }, }); ``` -------------------------------- ### Configure WebSocket Subprotocol for Bunny.net Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/bunny.md Sets a specific WebSocket subprotocol for the connection using the 'protocol' option. This can be overridden by the 'upgrade' hook for dynamic negotiation. ```typescript const ws = crossws({ protocol: "graphql-ws", }); ``` -------------------------------- ### Adapter Support for Message Event Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/4.message.md Table indicating adapter support for the message event across different environments. ```APIDOC ## Adapter Support | | [Bun][bun] | [Cloudflare][cfw] | [Cloudflare (durable)][cfd] | [Deno][deno] | [Node (ws)][nodews] | [Node (μWebSockets)][nodeuws] | [SSE][sse] | | ------- | ---------- | ----------------- | --------------------------- | ------------ | ------------------- | ----------------------------- | ---------- | | `event` | ⨉ | ✓ | ⨉ | ✓ | ⨉ | ⨉ | ⨉ | [bun]: /adapters/bun [cfw]: /adapters/cloudflare [cfd]: /adapters/cloudflare#durable-objects [deno]: /adapters/deno [nodews]: /adapters/node [nodeuws]: /adapters/node#uwebsockets [sse]: adapters/sse ``` -------------------------------- ### Peer Object Methods Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/3.peer.md Methods available on a Peer instance to send messages, manage subscriptions, and control the connection. ```APIDOC ## Peer Object Methods ### `peer.send(message, { compress? })` Send a message to the connected client. ### `peer.subscribe(channel)` Join a broadcast channel. :read-more{to="/guide/pubsub"} ### `peer.unsubscribe(channel)` Leave a broadcast channel. :read-more{to="/guide/pubsub"} ### `peer.publish(channel, message)` Broadcast a message to the channel. The peer that the publish is called on itself is excluded from the broadcast. :read-more{to="/guide/pubsub"} ### `peer.close(code?, number?)` Gracefully closes the connection. Here is a list of close codes: - `1000` means "normal closure" (default) - `1009` means a message was too big and was rejected - `1011` means the server encountered an error - `1012` means the server is restarting - `1013` means the server is too busy or the client is rate-limited - `4000` through `4999` are reserved for applications (you can use it!) To close the connection abruptly, use `peer.terminate()`. ### `peer.terminate()` Abruptly close the connection. To gracefully close the connection, use `peer.close()`. ``` -------------------------------- ### Client-side Socket.IO Communication Source: https://github.com/h3js/crossws/blob/main/examples/socket.io/public/index.html Connects to a Socket.IO server, logs connection status, and handles incoming messages. Use this to establish real-time communication with a server and display updates. ```javascript const socket = io(); const logEl = document.getElementById("log"); const log = (line) => { const li = document.createElement("li"); li.textContent = line; logEl.prepend(li); }; socket.on("connect", () => log(`[connect] ${socket.id}`)); socket.on("disconnect", (reason) => log(`[disconnect] ${reason}`)); socket.on("welcome", (m) => log(`[welcome] ${JSON.stringify(m)}`)); socket.on("message", (m) => log(`[message] ${JSON.stringify(m)}`)); document.getElementById("form").addEventListener("submit", (e) => { e.preventDefault(); const input = document.getElementById("input"); if (!input.value) return; socket.emit("message", input.value); input.value = ""; }); ``` -------------------------------- ### Authenticated WebSocket Proxy Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Wrap the default `upgrade` hook to add authentication logic before proxying. This prevents unauthorized access to the upstream. ```typescript import { createWebSocketProxy } from "crossws"; const proxyHooks = createWebSocketProxy("wss://backend.example.com"); const hooks = { ...proxyHooks, async upgrade(req) { const token = req.headers.get("authorization"); if (!(await isValidToken(token))) { return new Response("Unauthorized", { status: 401 }); } // Delegate to the proxy's own `upgrade` so subprotocol echoing still works. return proxyHooks.upgrade?.(req); }, }; ``` -------------------------------- ### Dynamic WebSocket Target Resolution Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Provide a function to `createWebSocketProxy` to dynamically determine the upstream URL based on the incoming peer's request. This is useful for routing. ```typescript import { createWebSocketProxy } from "crossws"; const hooks = createWebSocketProxy({ target: (peer) => { const { pathname } = new URL(peer.request.url); return pathname.startsWith("/admin") ? "wss://admin.internal/ws" : "wss://public.internal/ws"; }, }); ``` -------------------------------- ### Delegate Node.js Upgrade Handler Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/node.md Route Node.js WebSocket upgrade requests through crossws to an existing library's upgrade handler using fromNodeUpgradeHandler. Crossws manages upgrade-time requests, while the existing library handles the WebSocket lifecycle. ```typescript import { WebSocketServer } from "ws"; import { fromNodeUpgradeHandler } from "crossws/adapters/node"; import { serve } from "crossws/server/node"; const wss = new WebSocketServer({ noServer: true }); wss.on("connection", (ws) => { ws.on("message", (data) => ws.send(data)); }); serve({ fetch: () => new Response("ok"), websocket: fromNodeUpgradeHandler((req, socket, head) => { wss.handleUpgrade(req, socket, head, (ws) => { wss.emit("connection", ws, req); }); }), }); ``` -------------------------------- ### Proxy with Custom WebSocket Constructor Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Overrides the global WebSocket constructor with a specific implementation, useful for Node.js environments or testing. Requires a constructor that accepts options. ```typescript import { WebSocket } from "ws"; import { createWebSocketProxy } from "crossws"; const hooks = createWebSocketProxy({ target: "wss://backend.example.com", WebSocket: WebSocket as unknown as typeof globalThis.WebSocket, }); ``` -------------------------------- ### Cloudflare `wrangler.toml` configuration for Durable Objects Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/cloudflare.md This configuration specifies the Durable Object binding and class name in `wrangler.toml`, which is necessary for crossws to correctly manage WebSocket connections with Durable Objects. It also includes migration information for deploying the Durable Object. ```toml [[durable_objects.bindings]] name = "$DurableObject" class_name = "$DurableObject" [[migrations]] tag = "v1" new_classes = ["$DurableObject"] ``` -------------------------------- ### Proxy to Unix Domain Socket Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Connects to a backend via a Unix domain socket using a custom WebSocket constructor that supports the 'ws+unix:' scheme. ```typescript import { WebSocket } from "ws"; import { createWebSocketProxy } from "crossws"; const hooks = createWebSocketProxy({ target: "ws+unix:/var/run/backend.sock:/chat", WebSocket: WebSocket as unknown as typeof globalThis.WebSocket, }); ``` -------------------------------- ### Set Idle Timeout for Bunny.net WebSocket Connections Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/bunny.md Configures the idle timeout in seconds for waiting for a pong response before closing the connection. Defaults to 30 seconds. Connections are also closed if no data is transmitted for 2 minutes. ```typescript const ws = crossws({ idleTimeout: 60, }); ``` -------------------------------- ### Display Message Timestamp Source: https://github.com/h3js/crossws/blob/main/playground/public/index.html Shows the date and time when a message was received or sent. ```html {{ message.date }} ``` -------------------------------- ### Cloudflare Worker with Durable Object and crossws Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/cloudflare.md This code sets up a Cloudflare Worker to handle WebSocket upgrades using crossws. It integrates with Durable Objects for state management and pub/sub capabilities. Ensure the Durable Object class is exported and bound in `wrangler.toml`. The fallback mode is used if the Durable Object binding is unavailable. ```javascript import { DurableObject } from "cloudflare:workers"; import crossws from "crossws/adapters/cloudflare"; const ws = crossws({ // bindingName: "$DurableObject", // instanceName: "crossws", hooks: { message: console.log, open(peer) { peer.subscribe("chat"); peer.publish("chat", { user: "server", message: `${peer} joined!` }); }, }, }); export default { async fetch(request, env, context) { if (request.headers.get("upgrade") === "websocket") { return ws.handleUpgrade(request, env, context); } return new Response( ``, { headers: { "content-type": "text/html" } }, ); }, }; export class $DurableObject extends DurableObject { constructor(state, env) { super(state, env); ws.handleDurableInit(this, state, env); } fetch(request) { return ws.handleDurableUpgrade(this, request); } webSocketMessage(client, message) { return ws.handleDurableMessage(this, client, message); } webSocketPublish(topic, message, opts) { return ws.handleDurablePublish(this, topic, message, opts); } webSocketClose(client, code, reason, wasClean) { return ws.handleDurableClose(this, client, code, reason, wasClean); } } ``` -------------------------------- ### Peer Object Properties Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/3.peer.md Properties available on a Peer instance to access client information and connection details. ```APIDOC ## Peer Object Properties ### `peer.id` Unique random identifier ([uuid v4](https://developer.mozilla.org/en-US/docs/Glossary/UUID)) for the peer. ### `peer.request?` Access to the upgrade request info. You can use it to do authentication and access users headers and cookies. > [!NOTE] > This property is compatible with web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) interface, However interface is emulated for Node.js and sometimes unavailable. Refer to the [compatibility table](#compatibility) for more info. ### `peer.remoteAddress?` The IP address of the client. > [!NOTE] > Not all adapters provide this. Refer to the [compatibility table](#compatibility) for more info. ### `peer.websocket` Direct access to the [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) instance. > [!NOTE] > WebSocket properties vary across runtimes. When accessing `peer.websocket`, a lightweight proxy increases stability. Refer to the [compatibility table](#compatibility) for more info. ### `peer.context` The context is an object that contains arbitrary information about the request. You can augment the `PeerContext` interface types to add your properties. ```ts declare module "crossws" { interface PeerContext { customData?: string[]; } } ``` > [!NOTE] > context data can be volatile in some runtimes. ### `peer.topics` All topics, this peer has been subscribed to. ### `peer.namespace` Peer's pubsub namespace. ``` -------------------------------- ### Proxy with Dynamic Subprotocol Rewrite Function Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Dynamically rewrites subprotocols based on the client's offered protocols using a function. This allows for complex logic in selecting or transforming subprotocols. ```typescript createWebSocketProxy({ target: "wss://backend.example.com", forwardProtocol: (peer) => (peer.request.headers.get("sec-websocket-protocol") ?? "") .split(",") .map((p) => p.trim()) .filter(Boolean) .map((p) => (p.startsWith("proxied-") ? p.slice("proxied-".length) : p)), }); ``` -------------------------------- ### Integrate Socket.IO with Crossws Node.js Source: https://github.com/h3js/crossws/blob/main/docs/2.adapters/node.md Use this snippet to integrate Socket.IO with crossws on Node.js. It handles both WebSocket upgrades and HTTP long-polling by delegating requests to Socket.IO's listener. ```typescript import { createServer } from "node:http"; import { Server as SocketIOServer } from "socket.io"; import { fromNodeUpgradeHandler } from "crossws/adapters/node"; import { serve } from "crossws/server/node"; import { fetchNodeHandler } from "srvx/node"; // Attach socket.io to a throwaway http.Server — it never `.listen()`s, // but the dummy server now holds socket.io's own `request` listener, // which handles the client bundle AND delegates polling to engine.io. const dummyServer = createServer(); const io = new SocketIOServer(dummyServer, { serveClient: true }); const [socketIoRequestListener] = dummyServer.listeners("request"); io.on("connection", (socket) => { socket.emit("welcome", { id: socket.id }); socket.on("message", (text) => io.emit("message", { from: socket.id, text })); }); const server = serve({ port: 3000, async fetch(req) { const url = new URL(req.url); if (url.pathname.startsWith("/socket.io/")) { return fetchNodeHandler(socketIoRequestListener, req); } return new Response("ok"); }, websocket: fromNodeUpgradeHandler((req, socket, head) => { io.engine.handleUpgrade(req, socket, head); }), }); await server.ready(); ``` -------------------------------- ### Proxy with Fixed Upstream Subprotocol Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Use when the upstream always expects a single, known subprotocol. The client's offered subprotocols are ignored in favor of this fixed value. ```typescript createWebSocketProxy({ target: "wss://backend.example.com", forwardProtocol: "vite-hmr", }); ``` -------------------------------- ### Message Object Methods Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/4.message.md Methods available on the message object for data retrieval and parsing. ```APIDOC ## Message Object Methods ### `message.text()` Get stringified text version of the message. If raw data is in any other format, it will be automatically converted or decoded. ### `message.json()` Get parsed version of the message text with [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse). You can optionally use [`unjs/destr`](https://github.com/unjs/destr) to safely parse the message object, which does not throw an error if the input is not valid JSON but falls back to text and also removes any fields that could potentially cause prototype pollution vulnerabilities. ```ts const data = destr(await message.text()); ``` ### `message.uint8Array()` Get data as [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) value. If raw data is in any other format or string, it will be automatically converted or encoded. ### `message.arrayBuffer()` Get data as [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) or [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) value. If raw data is in any other format or string, it will be automatically converted or encoded. ### `message.blob()` Get data as [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) value. If raw data is in any other format or string, it will be automatically converted or encoded. ``` -------------------------------- ### Proxy with Subprotocol Rewrite Map Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Rewrites specific client-offered subprotocols to upstream-expected values using a map. Other subprotocols are passed through. ```typescript createWebSocketProxy({ target: "wss://backend.example.com", forwardProtocol: { "proxied-vite-hmr": "vite-hmr" }, }); ``` -------------------------------- ### Display Message Text Source: https://github.com/h3js/crossws/blob/main/playground/public/index.html Renders the content of a message in the chat interface. Supports formatted JSON display. ```html {{ message.text }} ``` -------------------------------- ### Display Message User Source: https://github.com/h3js/crossws/blob/main/playground/public/index.html Displays the username associated with a message in the chat interface. ```html {{ message.user }} ``` -------------------------------- ### Disable Subprotocol Forwarding Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Set `forwardProtocol` to `false` in `createWebSocketProxy` options to disable automatic subprotocol negotiation between the client and upstream. Use with caution. ```typescript createWebSocketProxy({ target: "wss://backend.example.com", forwardProtocol: false, }); ``` -------------------------------- ### Proxy with Custom Handshake Headers Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/7.proxy.md Attaches custom headers to the upstream WebSocket handshake. This is useful for forwarding identity information or injecting secrets. Requires a WebSocket constructor that accepts options. ```typescript import { WebSocket } from "ws"; import { createWebSocketProxy } from "crossws"; const hooks = createWebSocketProxy({ target: "wss://backend.example.com", WebSocket: WebSocket as unknown as typeof globalThis.WebSocket, headers: (peer) => ({ cookie: peer.request.headers.get("cookie") ?? "", "x-forwarded-for": peer.remoteAddress ?? "", }), }); ``` -------------------------------- ### Augment PeerContext Interface Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/3.peer.md Extend the PeerContext interface to add custom data properties for request-specific information. ```typescript declare module "crossws" { interface PeerContext { customData?: string[]; } } ``` -------------------------------- ### Message Object Properties Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/4.message.md Properties available on the message object received via the 'message' hook. ```APIDOC ## Message Object Properties On `message` [hook](/guide/hooks), you receive a message object containing data from the client. The message object is API-compatible with the standard Websocket [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/message_event) with a convenient superset of utilities. ### `message.id` Unique random identifier ([uuid v4](https://developer.mozilla.org/en-US/docs/Glossary/UUID)) for the message. ### `message.event` Access to the original [message event](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/message_event) if available. ### `message.peer` Access to the [peer instance](/guide/peer) that emitted the message. ### `message.rawData` Raw message data (can be of any type). ### `message.data` Message data (value varies based on [`peer.binaryType`](/guide/peer#peerbinarytype)). ``` -------------------------------- ### Dynamic Resolver Update Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/6.resolver.md Update the resolver function dynamically, for instance, to support Hot Module Replacement (HMR). Define a variable that holds the current resolve function and update it as needed. ```ts let resolveWebSocketHooks = (req) => /* ... */ const websocket = crossws({ async resolve(req) { return resolveWebSocketHooks(req) }, }); // Update reference to `resolveWebSocketHooks` later. ``` -------------------------------- ### Safely Parse JSON Message Data Source: https://github.com/h3js/crossws/blob/main/docs/1.guide/4.message.md Use `destr` for safe JSON parsing of message text, preventing errors on invalid JSON and mitigating prototype pollution vulnerabilities. This is recommended when dealing with potentially untrusted input. ```typescript const data = destr(await message.text()); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.