Skip to content

Commit

Permalink
feat: add cloudflare support (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Jan 28, 2024
1 parent ed2e522 commit 339b34b
Show file tree
Hide file tree
Showing 13 changed files with 772 additions and 51 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": ["eslint-config-unjs"],
"rules": {
"@typescript-eslint/no-unused-vars": 0,
"no-useless-constructor": 0
"no-useless-constructor": 0,
"unicorn/consistent-function-scoping": 0
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ dist
.eslintcache
*.log*
*.env*
.wrangler
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Cross-platform WebSocket Servers:

📦 No external dependencies, includes [ws](https://github.com/websockets/ws) types and Node.js support

🔗 Seamlessly integrates with [Bun](https://bun.sh/) and [Deno](https://deno.com/)
🔗 Seamlessly integrates with, [Node.js](https://nodejs.org/en), [Bun](https://bun.sh/), [Deno](https://deno.com/) and [Cloudflare Workers](https://workers.cloudflare.com/)!

💡 Extremely lightweight and tree-shakable packaging with ESM and CJS support

Expand Down Expand Up @@ -115,6 +115,30 @@ Deno.serve({ port: 3000 }, (req) => {

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 cloudflareAdapter from "crossws/adapters/cloudflare";

const { handleUpgrade } = cloudflareAdapter({ onMessage: 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" } },
);
},
};
```

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.
Expand Down
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
"import": "./dist/adapters/deno.mjs",
"require": "./dist/adapters/deno.cjs"
},
"./adapters/cloudflare": {
"types": "./dist/adapters/cloudflare.d.ts",
"import": "./dist/adapters/cloudflare.mjs",
"require": "./dist/adapters/cloudflare.cjs"
},
"./adapters/node": {
"types": "./dist/adapters/node.d.ts",
"import": "./dist/adapters/node.mjs",
Expand All @@ -40,6 +45,7 @@
"play:node": "jiti playground/node.ts",
"play:bun": "bun playground/bun.ts",
"play:deno": "deno run -A playground/deno.ts",
"play:cf": "wrangler dev",
"lint": "eslint --cache --ext .ts,.js,.mjs,.cjs . && prettier -c src test",
"lint:fix": "eslint --cache --ext .ts,.js,.mjs,.cjs . --fix && prettier -c src test -w",
"prepack": "pnpm run build",
Expand All @@ -48,6 +54,7 @@
"test:types": "tsc --noEmit --skipLibCheck"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20240117.0",
"@types/node": "^20.11.8",
"@types/web": "^0.0.135",
"@types/ws": "^8.5.10",
Expand All @@ -61,7 +68,8 @@
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.33.0",
"unbuild": "^2.0.0",
"vitest": "^1.2.2",
"wrangler": "^3.25.0",
"ws": "^8.16.0"
},
"packageManager": "pnpm@8.15.0"
}
}
14 changes: 8 additions & 6 deletions playground/_common.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
defineWebSocketHandler,
type WebSocketAdapter,
type WebSocketHandler,
} from "../src";
import { defineWebSocketHandler } from "../src";
import type { WebSocketAdapter } from "../src";

export const indexHTMLURL = new URL("_index.html", import.meta.url);
export const getIndexHTMLURL = () =>
new URL("public/index.html", import.meta.url);

export const importIndexHTML = () =>
import("./public/index.html" as string).then((r) => r.default);

export const log = (arg0: string, ...args) =>
console.log(`[ws] [${arg0}]`, ...args);
Expand All @@ -21,6 +22,7 @@ const websocketHandler = defineWebSocketHandler({
},
onOpen: (peer) => {
log("open", peer);
peer.send("hello!");
},
onClose: (peer, code, reason) => {
log("close", peer, code, reason);
Expand Down
4 changes: 2 additions & 2 deletions playground/bun.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// You can run this demo using `bun --bun ./bun.ts` or `npm run play:bun` in repo

import bunAdapter from "../src/adapters/bun";
import { createDemo, indexHTMLURL } from "./_common";
import { createDemo, getIndexHTMLURL } from "./_common";

const adapter = createDemo(bunAdapter);

Expand All @@ -11,7 +11,7 @@ Bun.serve({
if (server.upgrade(req)) {
return;
}
return new Response(Bun.file(indexHTMLURL), {
return new Response(Bun.file(getIndexHTMLURL()), {
headers: { "Content-Type": "text/html" },
});
},
Expand Down
25 changes: 25 additions & 0 deletions playground/cloudflare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// You can run this demo using `npm run play:cf` in repo

/// <reference types="@cloudflare/workers-types" />

import cloudflareAdapter from "../src/adapters/cloudflare";

import { createDemo, importIndexHTML } from "./_common.ts";

const { handleUpgrade } = createDemo(cloudflareAdapter);

export default {
async fetch(
request: Request,
env: Record<string, any>,
context: ExecutionContext,
) {
if (request.headers.get("upgrade") === "websocket") {
return handleUpgrade(request, env, context);
}

return new Response(await importIndexHTML(), {
headers: { "content-type": "text/html" },
});
},
};
4 changes: 2 additions & 2 deletions playground/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import denoAdapter from "../dist/adapters/deno.mjs";
// @ts-ignore
import type * as _Deno from "../types/lib.deno.d.ts";

import { createDemo, indexHTMLURL } from "./_common.ts";
import { createDemo, getIndexHTMLURL } from "./_common.ts";

const adapter = createDemo(denoAdapter);

Expand All @@ -14,7 +14,7 @@ Deno.serve({ port: 3001 }, (req) => {
return adapter.handleUpgrade(req);
}

return new Response(Deno.readFileSync(indexHTMLURL), {
return new Response(Deno.readFileSync(getIndexHTMLURL()), {
headers: { "Content-Type": "text/html" },
});
});
4 changes: 2 additions & 2 deletions playground/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { createServer } from "node:http";
import { readFileSync } from "node:fs";

import nodeAdapter from "../src/adapters/node";
import { createDemo, indexHTMLURL } from "./_common";
import { createDemo, getIndexHTMLURL } from "./_common";

const adapter = createDemo(nodeAdapter);

const server = createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/html" });
const indexHTML = readFileSync(indexHTMLURL, "utf8");
const indexHTML = readFileSync(getIndexHTMLURL(), "utf8");
res.end(indexHTML);
});

Expand Down
2 changes: 1 addition & 1 deletion playground/_index.html → playground/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<body>
<div id="logs"></div>
<script type="module">
const url = `ws://${location.host}`;
const url = `ws://${location.host}/_ws`;
const logsEl = document.querySelector("#logs");
const log = (...args) => {
console.log("[ws]", ...args);
Expand Down
Loading

0 comments on commit 339b34b

Please sign in to comment.