Skip to content

Commit

Permalink
chore: update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Feb 24, 2024
1 parent b51b01c commit 52c0e4f
Show file tree
Hide file tree
Showing 5 changed files with 319 additions and 286 deletions.
319 changes: 35 additions & 284 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
# ⛨ CrossWS

[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
<!-- automd:badges -->

👉 Elegant, typed, and simple interface to implement platform-agnostic WebSocket servers
[![npm version](https://flat.badgen.net/npm/v/crossws)](https://npmjs.com/package/crossws)
[![npm downloads](https://flat.badgen.net/npm/dm/crossws)](https://npmjs.com/package/crossws)

<!-- /automd -->

Elegant, typed, and simple interface to implement platform-agnostic WebSocket servers.

👉 [📖 documentation](https://crossws.unjs.io)

## Features

🧩 Seamlessly integrates with, [Bun](https://bun.sh/), [Deno](https://deno.com/), [Cloudflare Workers](https://workers.cloudflare.com/) and [Node.js](https://nodejs.org/en) ([ws](https://github.com/websockets/ws) || [uWebSockets](https://github.com/uNetworking/uWebSockets.js)).

Expand All @@ -15,298 +23,41 @@

🔍 Developer-friendly object logging

> [!WARNING]
> [!IMPORTANT]
> This project and API are under development.
## Install

```sh
# npm
npm install crossws

# yarn
yarn add crossws

# pnpm
pnpm install crossws

# bun
bun install crossws
```

## Unified Hooks

CrossWS provides a cross-platform API to define WebSocket servers. An implementation with these hooks works across runtimes without needing you to go into details of any of them (while you always have the power to control low-level hooks). You can only define the life-cycle hooks that you only need and only those will be called run runtime.

> [!NOTE]
> For type support and IDE auto-completion, you can use `defineWebSocketHooks` utility or `WebSocketHooks` type export from the main.
```ts
import { defineWebSocketHooks } from "crossws";

const websocketHooks = defineWebSocketHooks({
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);
},

// ... platform hooks such as bun:drain ...
});
```

### Peer Object

Websocket hooks always accept a peer instance as the first argument. `peer`, keeps the state of the connected client.

**Properties:**

- `peer.id?`: The peer address or unique id (might be `undefined`)
- `peer.readyState`: The connection status ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState))
- `peer.ctx[name]`: Keeps the state of native client connection

**Methods:**

- `send(message, compress)`: Send a message to the connected client

> [!TIP]
> You can safely log a peer instance to the console using `console.log` it will be automatically stringified with useful information including the remote address and connection status!
### Message Object

on `message` hook, you receive a message object containing an incoming message from the client.

**Properties:**

- `message.rawData`: Raw message data
- `message.isBinary`: Indicates if the message is binary (might be `undefined`)

**Methods:**

- `message.text()`: Get stringified version of the message

> [!TIP]
> You can safely log `message` object to the console using `console.log` it will be automatically stringified!
## Error handling

You can catch errors using `error` hook. The second argument is error wrapped into a `WebSocketError` class.

## Universal WebSocket client

CrossWS also exposes a universal way to use [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) API in order to connect to the server. For all runtimes, except Node.js, native implementation is used, and for Node.js, a bundled version of [`ws.WebSocket`](https://www.npmjs.com/package/ws) is bundled.

```js
import WebSocket from "crossws/websocket";
```

> [!NOTE]
> Using export conditions, the correct version will be always used so you don't have to worry about picking the right build!
## Integrations

CrossWS allows integrating your WebSocket hooks with different runtimes and platforms using built-in adapters. Each runtime has a specific method of integrating WebSocket. Once integrated, your custom hooks (such as `message` and `close`) will work consistently even if you change the runtime!

### Integration with **Node.js** (ws)

To integrate CrossWS with your Node.js HTTP server, you need to connect the `upgrade` event to the `handleUpgrade` method returned from the adapter. Behind the scenes, CrossWS uses an embedded version of [ws](https://github.com/websockets/ws).

```ts
import { createServer } from "node:http";
import wsAdapter from "crossws/adapters/node";

const server = createServer((req, res) => {
res.end(
`<script>new WebSocket("ws://localhost:3000").addEventListener('open', (e) => e.target.send("Hello from client!"));</script>`,
);
}).listen(3000);

const { handleUpgrade } = wsAdapter({ message: console.log });
server.on("upgrade", handleUpgrade);
```

**Adapter hooks:**
## Contribution

- `node:open (peer)`
- `node:message (peer, data, isBinary)`
- `node:close (peer, code, reason)`
- `node:error (peer, error)`
- `node:ping (peer)`
- `node:pong (peer)`
- `node:unexpected-response (peer, req, res)`
- `node:upgrade (peer, req)`

See [`playground/node.ts`](./playground/node.ts) for demo and [`src/adapters/node.ts`](./src/adapters/node.ts) for implementation.

### Integration with **Node.js** (uWebSockets)

You can alternatively use [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js) server for Node.js WebSockets.

```ts
import { App } from "uWebSockets.js";
import wsAdapter from "crossws/adapters/node-uws";

const { websocket } = wsAdapter({ message: console.log });

const server = App().ws("/*", websocket);

server.get("/*", (res, req) => {
res.writeStatus("200 OK").writeHeader("Content-Type", "text/html");
res.end(
`<script>new WebSocket("ws://localhost:3000").addEventListener('open', (e) => e.target.send("Hello from client!"));</script>`,
);
});

server.listen(3001, () => {
console.log("Listening to port 3001");
});
```

**Adapter hooks:**

- `uws:open(ws)`
- `uws:message(ws, message, isBinary)`
- `uws:close(ws, code, message)`
- `uws:ping(ws, message)`
- `uws:pong(ws, message)`
- `uws:drain(ws)`
- `uws:upgrade (res, req, context)`
- `subscription(ws, topic, newCount, oldCount)`

See [`playground/node-uws.ts`](./playground/node-uws.ts) for demo and [`src/adapters/node-uws.ts`](./src/adapters/node-uws.ts) for implementation.

### Integration with **Bun**

To integrate CrossWS with your Bun server, you need to check for `server.upgrade` and also pass the `websocket` object returned from the adapter to server options. CrossWS leverages native Bun WebSocket API.

```ts
import wsAdapter from "./dist/adapters/bun";

const { websocket } = wsAdapter({ message: console.log });

Bun.serve({
port: 3000,
websocket,
fetch(req, server) {
if (server.upgrade(req)) {
return;
}
return new Response(
`<script>new WebSocket("ws://localhost:3000").addEventListener('open', (e) => e.target.send("Hello from client!"));</script>`,
{ headers: { "content-type": "text/html" } },
);
},
});
```

**Adapter hooks:**

- `bun:message (peer, ws,message)`
- `bun:open (peer, ws)`
- `bun:close (peer, ws)`
- `bun:drain (peer)`
- `bun:error (peer, ws, error)`
- `bun:ping (peer, ws, data)`
- `bun:pong (peer, ws, data)`

See [`playground/bun.ts`](./playground/bun.ts) for demo and [`src/adapters/bun.ts]`(./src/adapters/bun.ts) for implementation.

### Integration with **Deno**

To integrate CrossWS with your Deno server, you need to check for the `upgrade` header and then call `handleUpgrade` method from the adapter passing the incoming request object. The returned value is the server upgrade response.

```ts
import wsAdapter from "crossws/adapters/deno";

const { handleUpgrade } = wsAdapter({ message: console.log });

Deno.serve({ port: 3000 }, (request) => {
if (request.headers.get("upgrade") === "websocket") {
return handleUpgrade(request);
}
return new Response(
`<script>new WebSocket("ws://localhost:3000").addEventListener("open", (e) => e.target.send("Hello from client!"));</script>`,
{ headers: { "content-type": "text/html" } },
);
});
```

**Adapter hooks:**

- `deno:open (peer)`
- `deno:message (peer, event)`
- `deno:close (peer)`
- `deno:error (peer, error)`

See [`playground/deno.ts`](./playground/deno.ts) for demo and [`src/adapters/deno.ts`](./src/adapters/deno.ts) for implementation.

### Integration with **Cloudflare Workers**

To integrate CrossWS with your Cloudflare Workers, you need to check for the `upgrade` header

```ts
import wsAdapter from "crossws/adapters/cloudflare";

const { handleUpgrade } = wsAdapter({ message: console.log });

export default {
async fetch(request, env, context) {
if (request.headers.get("upgrade") === "websocket") {
return handleUpgrade(request, env, context);
}
return new Response(
`<script>new WebSocket("ws://localhost:3000").addEventListener("open", (e) => e.target.send("Hello from client!"));</script>`,
{ headers: { "content-type": "text/html" } },
);
},
};
```

**Adapter hooks:**

- `cloudflare:accept(peer)`
- `cloudflare:message(peer, event)`
- `cloudflare:error(peer, event)`
- `cloudflare:close(peer, event)`

See [`playground/cloudflare.ts`](./playground/cloudflare.ts) for demo and [`src/adapters/cloudflare.ts`](./src/adapters/cloudflare.ts) for implementation.

### Integration with other runtimes

You can define your custom adapters using `defineWebSocketAdapter` wrapper.

See other adapter implementations in [./src/adapters](./src/adapters/) to get an idea of how adapters can be implemented and feel free to directly make a Pull Request to support your environment in CrossWS!

## Development
<details>
<summary>Local development</summary>

- Clone this repository
- Install the latest LTS version of [Node.js](https://nodejs.org/en/)
- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
- Install dependencies using `pnpm install`
- Run interactive tests using `pnpm dev`
- Run examples using `pnpm play:` scripts

</details>

<!-- /automd -->

## License

Made with 💛
<!-- automd:contributors license=MIT author="pi0" -->

Published under the [MIT](https://github.com/unjs/crossws/blob/main/LICENSE) license.
Made by [@pi0](https://github.com/pi0) and [community](https://github.com/unjs/crossws/graphs/contributors) 💛
<br><br>
<a href="https://github.com/unjs/crossws/graphs/contributors">
<img src="https://contrib.rocks/image?repo=unjs/crossws" />
</a>

<!-- /automd -->

<!-- automd:with-automd -->

Published under [MIT License](./LICENSE).
---

<!-- Badges -->
_🤖 auto updated with [automd](https://automd.unjs.io)_

[npm-version-src]: https://img.shields.io/npm/v/crossws?style=flat&colorA=18181B&colorB=F0DB4F
[npm-version-href]: https://npmjs.com/package/crossws
[npm-downloads-src]: https://img.shields.io/npm/dm/crossws?style=flat&colorA=18181B&colorB=F0DB4F
[npm-downloads-href]: https://npmjs.com/package/crossws
<!-- /automd -->
2 changes: 1 addition & 1 deletion docs/2.adapters/bun.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ icon: simple-icons:bun
To integrate CrossWS with your Bun server, you need to handle upgrade with `handleUpgrade` util and also pass the `websocket` object returned from the adapter to server options. CrossWS leverages native Bun WebSocket API.

```ts
import wsAdapter from "./dist/adapters/bun";
import wsAdapter from "crossws/adapters/bun";

const { websocket, handleUpgrade } = wsAdapter({ message: console.log });

Expand Down
2 changes: 1 addition & 1 deletion docs/2.adapters/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Instead of [using `ws`](/adapters/node-ws) you can use [uWebSockets.js](https://

```ts
import { App } from "uWebSockets.js";
import wsAdapter from "crossws/adapters/node-uws";
import wsAdapter from "crossws/adapters/uws";

const { websocket } = wsAdapter({ message: console.log });

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"@types/node": "^20.11.10",
"@types/web": "^0.0.135",
"@types/ws": "^8.5.10",
"automd": "^0.3.6",
"changelogen": "^0.5.5",
"consola": "^3.2.3",
"eslint": "^8.56.0",
Expand Down
Loading

0 comments on commit 52c0e4f

Please sign in to comment.