Conversation
|
All contributors have signed the CLA ✍️ ✅ |
commit: |
|
|
I have read the CLA Document and I hereby sign the CLA |
|
recheck |
|
In package.json we currently have a special separate build for workerd that includes the workers-only stuff. Maybe we should have a separate build for bun, too? Avoid code bloat for other runtimes. |
|
@kentonv thanks for the review.
Yeah that makes sense. I've done that.
Yeh, that's fair. It was testing against the interface of the
I've not created a test matrix to keep things simple, but happy to do so if you'd prefer. Now we now have a |
Bun uses callback-based WebSocket handlers registered on `Bun.serve()` rather than the standard `addEventListener` interface. Passing a Bun ServerWebSocket to `newWebSocketRpcSession()` errors because the `addEventListener` method does not exist (cloudflare#61). A new BunWebSocketTransport implements the RpcTransport interface using the same resolver pattern as the existing WebSocket and MessagePort transports. It exposes dispatchMessage, dispatchClose, and dispatchError methods that Bun handler callbacks invoke to feed messages into the transport. Two convenience functions sit on top of the transport. `newBunWebSocketRpcHandler()` returns a complete handler object you can pass straight to `Bun.serve()`, wiring everything up automatically via `ws.data`. `newBunWebSocketRpcSession()` returns the stub and transport for users who need custom handler logic. The Bun-specific code ships in a separate entry point (index-bun.ts) mapped via a "bun" conditional export in package.json, following the same pattern as the existing workerd build. Other runtimes never load this code. The test suite in __tests__/bun.test.ts runs under Bun native test runner against real `Bun.serve()` instances with actual WebSocket connections. Run with `npm run test:bun` or `npm run test:ci`. Closes cloudflare#61
Adds the oven-sh/setup-bun action so the Bun runtime is available in the Playwright container. The test step now runs `test:ci` which chains Vitest and Bun tests in sequence.
Adds an "HTTP server on Bun" section to the readme showing both the handler helper and the manual wiring escape hatch. Notes that the Bun helpers ship via a conditional export and are never loaded by other runtimes. Adds a development section documenting the test scripts: npm test for vitest only, npm run test:bun for the Bun suite, and npm run test:ci for both together.
Previously, "bun" and "workerd" were nested inside "import"/"require", which meant TypeScript always fell through to the single top-level "types" entry and picked up index.d.ts regardless of the active condition. Each platform block is now a top-level condition with its own "types", "import", and "require" keys: - "bun" resolves to index-bun.d.ts, which exposes BunWebSocketTransport, BunServerWebSocket, and the Bun-specific session helpers - "workerd" shares the same declarations as the default but keeps a distinct runtime (index-workers.js) that imports cloudflare:workers and registers it for native RPC interop - The bare "types"/"import"/"require" fallback covers Node.js and every other environment
|
|
||
| Bun's server-side WebSocket API uses [callback-based handlers](https://bun.sh/docs/runtime/http/websockets) instead of the standard `addEventListener` interface. Cap'n Web provides `newBunWebSocketRpcHandler()` which returns a handler object you can pass directly to `Bun.serve()`. | ||
|
|
||
| > **Note:** The Bun-specific helpers (`newBunWebSocketRpcHandler`, `newBunWebSocketRpcSession`, and `BunWebSocketTransport`) are shipped in a separate build entry point that Bun resolves automatically via the `"bun"` [conditional export](https://nodejs.org/api/packages.html#conditional-exports) in `package.json`. Other runtimes never load this code, keeping bundle size minimal. |
There was a problem hiding this comment.
I think we can leave out this line, it probably isn't worth the cognitive overhead for people to be aware of this.
| }); | ||
| ``` | ||
|
|
||
| If you need to attach custom data to connections or add your own logic to the WebSocket handlers, use `newBunWebSocketRpcSession()` instead, which gives you direct access to the transport: |
There was a problem hiding this comment.
Let's leave this out (here to the end of the section). It can be documented in doc comments in the code, but I don't think we want to spend prime README space on it.
| @@ -0,0 +1,140 @@ | |||
| // Copyright (c) 2025 Cloudflare, Inc. | |||
| BunWebSocketTransport } from "./bun.js"; | ||
|
|
||
| export { newBunWebSocketRpcHandler, BunWebSocketTransport }; | ||
| export type { BunServerWebSocket } from "./bun.js"; |
There was a problem hiding this comment.
I don't think we should export this type -- people should use the official types from @types/bun, ours is just a minimal subset.
There was a problem hiding this comment.
Is it possible for us to use @types/bun here without forcing non-bun dependents to depend on it? Some way to declare dependencies that only apply to one platform?
| : Disposable; | ||
| ``` | ||
|
|
||
| Note: Bun's `ServerWebSocket` does not implement the standard `WebSocket` `addEventListener` interface. If you are using Bun, use the dedicated `newBunWebSocketRpcHandler()` or `newBunWebSocketRpcSession()` described above. |
There was a problem hiding this comment.
Remove this line, it's redundant.
| // Now we can call methods on the stub. | ||
| ``` | ||
|
|
||
| Cap'n Web's built-in transports (WebSocket, MessagePort, HTTP batch, and Bun ServerWebSocket) are all implemented on top of this interface. |
There was a problem hiding this comment.
Remove. Don't add unrelated stuff to the readme.
|
|
||
| Note that sessions are entirely symmetric: neither side is defined as the "client" nor the "server". Each side can optionally expose a "main interface" to the other. In typical scenarios with a logical client and server, the server exposes a main interface but the client does not. | ||
|
|
||
| ## Development |
Add Bun ServerWebSocket support to capnweb.
Bun's server-side WebSocket uses callback-based handlers registered on
Bun.serve()rather than the standardaddEventListenerinterface. Passing a BunServerWebSockettonewWebSocketRpcSession()silently fails because the event listeners never fire. (#61)A new
BunWebSocketTransportimplements theRpcTransportinterface using the same resolver pattern as the existing WebSocket and MessagePort transports. It exposesdispatchMessage,dispatchClose, anddispatchErrormethods that Bun's handler callbacks invoke to feed messages into the transport.Two convenience functions sit on top of the transport.
newBunWebSocketRpcHandler()returns a complete handler object you can pass straight toBun.serve(), wiring everything up automatically viaws.data.newBunWebSocketRpcSession()returns the stub and transport for users who need custom handler logic.The readme now documents both approaches and notes that Bun is a supported runtime.
Quick start with the handler helper
Escape hatch with manual wiring
Testing locally
Run the test suite with
npm test. The new tests in__tests__/bun.test.tsexercise the transport, both convenience functions, and full round-trip calls through a loopback pair without needing a real Bun server.Unit tests cover message queuing, close and error propagation, abort semantics, buffer-to-string conversion, and end-to-end calls including bidirectional callbacks and error forwarding. All existing tests continue to pass.
Closes #61