From a9abe896f6fc43e2f2afc01b47aa9f20ed24ce95 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 14:40:36 +0200 Subject: [PATCH 01/12] begin --- package.json | 1 + src/use/bun.ts | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 8 ++++++ 3 files changed, 76 insertions(+) create mode 100644 src/use/bun.ts diff --git a/package.json b/package.json index 3cca5680..16f0c8b2 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "@typescript-eslint/eslint-plugin": "^5.59.5", "@typescript-eslint/parser": "^5.59.5", "babel-jest": "^29.5.0", + "bun-types": "^0.5.8", "eslint": "^8.40.0", "eslint-config-prettier": "^8.8.0", "fastify": "^4.17.0", diff --git a/src/use/bun.ts b/src/use/bun.ts new file mode 100644 index 00000000..0fdedd59 --- /dev/null +++ b/src/use/bun.ts @@ -0,0 +1,67 @@ +/// + +import type { WebSocketHandler, ServerWebSocket } from 'bun'; +import { ConnectionInitMessage } from '../common'; +import { makeServer, ServerOptions } from '../server'; + +export interface Extra { + ws: ServerWebSocket; +} + +export function makeHandler< + P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], + E extends Record = Record, +>(options: ServerOptions>): WebSocketHandler { + const server = makeServer(options); + + interface Client { + handleMessage: (data: string) => Promise; + closed: (code: number, reason: string) => Promise; + } + const clients = new WeakMap(); + + return { + open(ws) { + console.log(ws); + const client: Client = { + handleMessage: () => { + throw new Error('Message received before handler was registered'); + }, + closed: () => { + throw new Error('Closed before handler was registered'); + }, + }; + + client.closed = server.opened( + { + protocol: 'graphql-transport-ws', // TODO: read actual + send: async (message) => { + // ws might have been destroyed in the meantime, send only if exists + if (clients.has(ws)) { + ws.sendText(message); + } + }, + close: (code, reason) => { + if (clients.has(ws)) { + ws.close(code, reason); + } + }, + onMessage: (cb) => (client.handleMessage = cb), + }, + { ws } as Extra & Partial, + ); + + clients.set(ws, client); + }, + message(ws, message) { + const client = clients.get(ws); + if (!client) throw new Error('Message received for a missing client'); + return client.handleMessage(String(message)); + }, + close(ws, code, message) { + const client = clients.get(ws); + if (!client) throw new Error('Closing a missing client'); + return client.closed(code, message); + }, + }; +} diff --git a/yarn.lock b/yarn.lock index 941f4a61..ea0aabc3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4415,6 +4415,13 @@ __metadata: languageName: node linkType: hard +"bun-types@npm:^0.5.8": + version: 0.5.8 + resolution: "bun-types@npm:0.5.8" + checksum: 8a4a7652b1b877381549195f1fa8d23c7f62f60f39af58b5ca060946576ed4fb728e9f249518e68a0c678a1953b85fe509b50956d7fefe5fcece5abcc6ee6a94 + languageName: node + linkType: hard + "busboy@npm:1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" @@ -7064,6 +7071,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ^5.59.5 "@typescript-eslint/parser": ^5.59.5 babel-jest: ^29.5.0 + bun-types: ^0.5.8 eslint: ^8.40.0 eslint-config-prettier: ^8.8.0 fastify: ^4.17.0 From 6a16322a96d556dd019f1c94720d131e8eca53b2 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 15:02:06 +0200 Subject: [PATCH 02/12] somehow theres no error here anymore --- scripts/post-gendocs.mjs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/scripts/post-gendocs.mjs b/scripts/post-gendocs.mjs index c0a89db9..b597e2b8 100644 --- a/scripts/post-gendocs.mjs +++ b/scripts/post-gendocs.mjs @@ -29,12 +29,7 @@ async function fixLinksInDir(dirPath) { } const contents = await fsp.readFile(filePath); const src = contents.toString(); - await fsp.writeFile( - filePath, - src - // @ts-expect-error we're running this on modern node - .replaceAll('.md', ''), - ); + await fsp.writeFile(filePath, src.replaceAll('.md', '')); } } @@ -70,9 +65,7 @@ async function createRecursiveMetaFiles(dirPath) { if (nameNoExt === 'use__fastify_websocket') { meta[nameNoExt] = 'use/@fastify/websocket'; } else { - meta[nameNoExt] = nameNoExt - // @ts-expect-error we're running this on modern node - .replaceAll('_', '/'); + meta[nameNoExt] = nameNoExt.replaceAll('_', '/'); } } From 989237af21577dbf9457e3d2523c3b5ecb8b7e03 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 19:22:20 +0200 Subject: [PATCH 03/12] document and use socket --- src/use/bun.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/use/bun.ts b/src/use/bun.ts index 0fdedd59..9271ecd8 100644 --- a/src/use/bun.ts +++ b/src/use/bun.ts @@ -4,10 +4,24 @@ import type { WebSocketHandler, ServerWebSocket } from 'bun'; import { ConnectionInitMessage } from '../common'; import { makeServer, ServerOptions } from '../server'; +/** + * The extra that will be put in the `Context`. + * + * @category Server/bun + */ export interface Extra { - ws: ServerWebSocket; + /** + * The actual socket connection between the server and the client. + */ + readonly socket: ServerWebSocket; } +/** + * Use the server with [Bun](https://bun.sh/). + * This is a basic starter, feel free to copy the code over and adjust it to your needs + * + * @category Server/bun + */ export function makeHandler< P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record = Record, @@ -48,7 +62,7 @@ export function makeHandler< }, onMessage: (cb) => (client.handleMessage = cb), }, - { ws } as Extra & Partial, + { socket: ws } as Extra & Partial, ); clients.set(ws, client); From 2452b19f084b2467f001c3c6efaf0f862f74f099 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 19:34:56 +0200 Subject: [PATCH 04/12] bun export path --- package.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 16f0c8b2..3180ab85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphql-ws", - "version": "5.12.1", + "version": "5.13.1-rc.0", "description": "Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client", "keywords": [ "protocol", @@ -59,6 +59,12 @@ "require": "./lib/use/fastify-websocket.js", "import": "./lib/use/fastify-websocket.mjs" }, + "./lib/use/bun": { + "bun": "./lib/use/bun.d.ts", + "types": "./lib/use/bun.d.ts", + "require": "./lib/use/bun.js", + "import": "./lib/use/bun.mjs" + }, "./package.json": "./package.json" }, "files": [ From f3835e4eca4f4c0ef3b65486ed50c931a850cf50 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 20:21:58 +0200 Subject: [PATCH 05/12] no consolelog --- src/use/bun.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/use/bun.ts b/src/use/bun.ts index 9271ecd8..bb3b0278 100644 --- a/src/use/bun.ts +++ b/src/use/bun.ts @@ -36,7 +36,6 @@ export function makeHandler< return { open(ws) { - console.log(ws); const client: Client = { handleMessage: () => { throw new Error('Message received before handler was registered'); From febf9934ca3c38ee228837bf854ffc4c1ff3439f Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 20:25:08 +0200 Subject: [PATCH 06/12] bun benchmark --- .github/workflows/benchmark.yml | 26 ++++++++++++++++++++++++++ benchmark/servers/bun.ts | 18 ++++++++++++++++++ benchmark/servers/ports.mjs | 1 + 3 files changed, 45 insertions(+) create mode 100644 benchmark/servers/bun.ts diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 9c72ca58..e87eb143 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -120,3 +120,29 @@ jobs: run: | NODE_ENV=production node benchmark/servers/legacy_ws7.mjs & LEGACY=1 SERVER=legacy_ws7 ./k6 run benchmark/k6.mjs + bun: + name: bun + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: yarn + - name: Install + run: yarn install --immutable + - name: Download k6 + run: | + curl https://github.com/grafana/k6/releases/download/v0.39.0/k6-v0.39.0-linux-amd64.tar.gz -L | tar xvz --strip-components 1 + - name: Build + run: yarn run build:esm + - name: Set up bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: latest + - name: Run + run: | + bun benchmark/servers/bun.ts & + SERVER=bun ./k6 run benchmark/k6.mjs diff --git a/benchmark/servers/bun.ts b/benchmark/servers/bun.ts new file mode 100644 index 00000000..f7b0aeda --- /dev/null +++ b/benchmark/servers/bun.ts @@ -0,0 +1,18 @@ +/// + +import { schema } from './schema.mjs'; +import { makeHandler } from '../../src/use/bun'; +import { ports } from './ports.mjs'; + +Bun.serve({ + fetch(req, server) { + if (server.upgrade(req)) { + return new Response(); + } + return new Response('Upgrade failed :(', { status: 500 }); + }, + websocket: makeHandler({ schema }), + port: ports.bun, +}); + +console.log(`bun - listening on port ${ports.bun}...`); diff --git a/benchmark/servers/ports.mjs b/benchmark/servers/ports.mjs index 5f2a1eb8..608e4bbc 100644 --- a/benchmark/servers/ports.mjs +++ b/benchmark/servers/ports.mjs @@ -4,4 +4,5 @@ export const ports = { uWebSockets: 6541, legacy_ws7: 6542, '@fastify/websocket': 6544, + bun: 6545, }; From b22532b3d536ba553a97096c99a52f7a19cedf25 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 22:47:10 +0200 Subject: [PATCH 07/12] servers --- benchmark/servers/bun.ts | 2 +- src/use/bun.ts | 5 +++++ website/src/pages/get-started.mdx | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/benchmark/servers/bun.ts b/benchmark/servers/bun.ts index f7b0aeda..fe62979b 100644 --- a/benchmark/servers/bun.ts +++ b/benchmark/servers/bun.ts @@ -9,7 +9,7 @@ Bun.serve({ if (server.upgrade(req)) { return new Response(); } - return new Response('Upgrade failed :(', { status: 500 }); + return new Response(null, { status: 500 }); }, websocket: makeHandler({ schema }), port: ports.bun, diff --git a/src/use/bun.ts b/src/use/bun.ts index bb3b0278..b0c37bb5 100644 --- a/src/use/bun.ts +++ b/src/use/bun.ts @@ -4,6 +4,11 @@ import type { WebSocketHandler, ServerWebSocket } from 'bun'; import { ConnectionInitMessage } from '../common'; import { makeServer, ServerOptions } from '../server'; +/** + * Convenience export for checking the WebSocket protocol on the request in `Bun.serve`. + */ +export { handleProtocols } from '../server'; + /** * The extra that will be put in the `Context`. * diff --git a/website/src/pages/get-started.mdx b/website/src/pages/get-started.mdx index 9cc81716..b83c152f 100644 --- a/website/src/pages/get-started.mdx +++ b/website/src/pages/get-started.mdx @@ -113,6 +113,34 @@ fastify.listen(4000, (err) => { }); ``` +#### With [Bun](https://bun.sh) + +```ts +import { makeHandler, handleProtocols } from 'graphql-ws/lib/use/lib/bun'; +import { schema } from './previous-step'; + +Bun.serve({ + fetch(req, server) { + const [path, _search] = req.url.split('?'); + if ( + path.endsWith('/graphql') && + handleProtocols(req.headers.get('sec-websocket-protocol') || '') + ) { + if (server.upgrade(req)) { + return new Response(); + } else { + return new Response('Internal Server Error', { status: 500 }); + } + } + return new Response('Upgrade Required', { status: 426 }); + }, + websocket: makeHandler({ schema }), + port: 4000, +}); + +console.log('Listening to port 4000'); +``` + ### Use the client ```ts From 8c6b54fbfa80f9dff01e8fee133c72e54f274918 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 23:16:17 +0200 Subject: [PATCH 08/12] not an rc --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3180ab85..cdff745a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphql-ws", - "version": "5.13.1-rc.0", + "version": "5.13.1", "description": "Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client", "keywords": [ "protocol", From 4d7bd6a95fc8a86f83aa42ce54e17e3981fb0473 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 23:16:43 +0200 Subject: [PATCH 09/12] less --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cdff745a..dc6ed975 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphql-ws", - "version": "5.13.1", + "version": "5.12.1", "description": "Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client", "keywords": [ "protocol", From d3cf139f5eb3b511df72864a4e74b01d026acc56 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 23:19:57 +0200 Subject: [PATCH 10/12] protocol --- src/use/bun.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/use/bun.ts b/src/use/bun.ts index b0c37bb5..be6e49d1 100644 --- a/src/use/bun.ts +++ b/src/use/bun.ts @@ -1,7 +1,10 @@ /// import type { WebSocketHandler, ServerWebSocket } from 'bun'; -import { ConnectionInitMessage } from '../common'; +import { + ConnectionInitMessage, + GRAPHQL_TRANSPORT_WS_PROTOCOL, +} from '../common'; import { makeServer, ServerOptions } from '../server'; /** @@ -52,7 +55,8 @@ export function makeHandler< client.closed = server.opened( { - protocol: 'graphql-transport-ws', // TODO: read actual + // TODO: use protocol on socket once Bun exposes it + protocol: GRAPHQL_TRANSPORT_WS_PROTOCOL, send: async (message) => { // ws might have been destroyed in the meantime, send only if exists if (clients.has(ws)) { From 0219e46142d21402ca1bfbfcb6b80a7f0e7cf334 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 23:47:59 +0200 Subject: [PATCH 11/12] use mjs for bun --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc6ed975..fceace3f 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "import": "./lib/use/fastify-websocket.mjs" }, "./lib/use/bun": { - "bun": "./lib/use/bun.d.ts", + "bun": "./lib/use/bun.mjs", "types": "./lib/use/bun.d.ts", "require": "./lib/use/bun.js", "import": "./lib/use/bun.mjs" From 387586b886cc4cae6968bde7ffd1eb5f1e90d335 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Thu, 11 May 2023 23:56:39 +0200 Subject: [PATCH 12/12] explain --- src/use/bun.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/use/bun.ts b/src/use/bun.ts index be6e49d1..8b2b6632 100644 --- a/src/use/bun.ts +++ b/src/use/bun.ts @@ -28,6 +28,9 @@ export interface Extra { * Use the server with [Bun](https://bun.sh/). * This is a basic starter, feel free to copy the code over and adjust it to your needs * + * The keep-alive logic _seems_ to be handled by Bun seeing that + * they default [`sendPingsAutomatically` to `true`](https://github.com/oven-sh/bun/blob/6a163cf933542506354dc836bd92693bcae5939b/src/deps/uws.zig#L893). + * * @category Server/bun */ export function makeHandler<