Releases: h3js/crossws
v0.4.1
v0.4.0
Noticeable Changes
🪄 Unified server powered by srvx (experimental)
Previously, you had to use manual adapter integrations or listhen for setting up a functional websocket server.
Powered by our new standard server building block srvx (experimental) and conditional exports, you can write a single server entry that magically works and natively integrates with any supported runtime.
// Magically works with Bun, Deno, and Node.js!
// (also Cloudflare or SSE as fallback)
import { serve } from "crossws/server";
serve({
websocket: {
message: console.log
}
});
🗂️ Namespaced pub/sub peers
Relevant PR: #162
In the previous versions of crossws, there was one peers
global Set in the server. Publishing to the same "topic", published to all peers subscribed to the same topic, even if they were connected to different WebSocket endpoints (as a result, we had to make sure topics are unique).
While it works in simple servers with a single set of hooks, when using dynamic hooks (using resolver), it makes sense that each peer only subscribes and publishes to relevant peers, and topics don't overlap.
With the new version, connected websocket peers have an assigned namespace
. By default, the request "pathname" is used for namespace, so peers connected to /_ws/room1
and /_ws/room2
are isolated from each other.
It is possible to have customized namespace logic based on request using the adapter option getNamespace: (req: Request) => "<namespace>"
. The default behavior is req => new URL(req.url).pathname
. You can use () => "default"
for behavior similar to previous versions.
It is also possible to use a dynamically determined namespace per upgrade connection by returning { namespace }
from the upgrade(req)
hook.
Global ws.publish(topic, message)
still publishes to all peers, but can specify a namespace using ws.publish(topic, message, { namespace })
🔥 Better cloudflare support
cloudflare
and cloudflare-durable
adapters are merged (#165) into single cloudflare
. In case a Durable object is not exported or not resolvable, crossws will fallback to a simple WebSocket upgrade without pub/sub support.
With this release, Cloudflare (durable) also supports global publish()
via RPC (#166). With this enhancement, all adapters now support global publish!
⚠️ Other Breaking Changes
⚠️ Support returning context from upgrade hook (#163)⚠️ Do not automatically accept firstsec-webSocket-protocol
header (#142)⚠️ Always passRequest
as first param to resolve (#160)⚠️ Always terminate upgrade if aResponse
is returned (#164)⚠️ Remove uncrypto dependency (Requires Node.js >= 20) (#153)
🚀 Enhancements
- Emulate full
Request
interface (#156) - Universal server powered by srvx (experimental) (#158)
- Expose
PeerContext
interface for type augmentation (#159)
💅 Refactors
- Simplify console inspected values (aa49668)
- Throw error when running adapters in an incompatible environment (b5fcf2a)
- Narrow down upgrade return type for
upgrade
hook (d843cd0) - cloudflare: Show warning when pub/sub is not supported (#144)
📖 Documentation
- Change org from
unjs
toh3js
(#155) - Add docs for augmenting
PeerContext
type (#161) - Prepare for v0.4 (#168)
📦 Build
❤️ Contributors
- Pooya Parsa (@pi0)
- Tee Ming (@eltigerchino)
- Luke Nelson (@luc122c)
- @iiio2
v0.3.5
🚀 Enhancements
- node: Support
closeAll
withforce
flag (#147)
🩹 Fixes
- node: Destroy socket on upgrade abort (#140)
📦 Build
- Export
AdapterInternal
type (#149)
🌊 Types
- Mark
NodeAdapter.handleUpgrade
as async (#136)
❤️ Contributors
- Pooya Parsa (@pi0)
- M1212e (@m1212e)
- Tee Ming (@eltigerchino)
- James Garbutt (@43081j)
v0.3.4
🚀 Enhancements
- cloudflare: Support
resolveDurableStub
(#130)
🩹 Fixes
- Global publish via first subscribed peer (#103)
- Define
request.context
as read-only (#133) - node, uws: Send data as blob only if it is not string (#124)
- bun: Pass
code
andreason
toclose
hook (#132)
💅 Types
📖 Documentation
- node: Check
upgrade === "websocket"
in example (#131) - Add dynamic example for changeable resolve (e5daf22)
❤️ Contributors
- Pooya Parsa (@pi0)
- Sandro Circi (@sandros94)
- Flo (@TecToast)
- Tee Ming (@eltigerchino)
v0.3.3
🚀 Enhancements
- Allow throwing error with
.response
prop inupgrade
(#113)
❤️ Contributors
- Luke Hagar (@LukeHagar)
v0.3.2
Note
🔑 This release includes enhancements to make authentication and session handling easier.
- You can validate the
request
param in theupgrade
hook andthrow
aResponse
as an error to terminate the upgrade (#91) - You can access a shared context from
request.context
in theupgrade
hook and frompeer.context
in the other hooks to preserve session data (#110, #111 (Note: context can be volatile in some environments likecloudflare-durable
)
🩹 Fixes
- types:
peer.request
always has.headers
if defined (e915f8d) - types: Mark
peer.request
as always defined (8fbb59b)
📖 Documentation
🏡 Chore
- examples: Fix typo (#107)
❤️ Contributors
- Pooya Parsa (@pi0)
- Luke Hagar (@LukeHagar)
- 39sho (@39sho)
- Sandro Circi (@sandros94)
- Jamaluddin Rumi (@jamaluddinrumi)
v0.3.1
v0.3.0
Important
Since this is a major version, ecosystem migration takes time. Stay tuned for listhen, h3 and nitro dependency upgrades.
🌟 What is new?
Better stability
Crossws 0.3.x includes an overhaul of refactors, stability improvements, and new features. A new codebase and testing matrix had been implemented (#55) to make sure all supported adapters and runtimes work as expected and are consistent with each other.
Refined Peer API
The peer object allows easy interaction with connected WebSocket clients from server route hooks (peer docs).
To improve Web standards compatibility, accessing upgrade URL and headers is now possible with peer.request.url
and peer.request.headers
(breaking change), and peer.addr
is also renamed to peer.remoteAddress
to improve readability (breaking change) and support is increased across providers. You can also use new lazy-generated and secure peer.id
(UUID v4) for various purposes including temporary sessions or persistent state.
Two new methods are now supported to close connected peers using peer.close(code, reason)
and peer.terminate()
. With this new version, you can access a standard WebSocket
interface using peer.websocket
.
Note
Today many of the server runtimes don't provide a spec-compliant WebSocket
API. Crossws uses an internal proxy to polyfill consistent access to extensions
, protocol
, and readyState
. See compatibility table for more details.
Refined Message API
On message
hook, you receive a message object containing data from the client (message docs).
Parsing incoming messages can be tricky across runtimes. Message object now has stable methods .text()
, .json()
, .uint8Array()
, .arrayBuffer()
, .blob()
to safely read message as desired format. If you need, you can also access .rawData
, .peer
, .event
(if available), and lazy generated secure UUID v4 .id
Authentication via upgrade
hook
When you need to authenticate and validate WebSocket clients before they can upgrade, you can now easily use the upgrade
hook to check incoming URLs and headers/cookies and return a Web Standard Response in case you need to abort the upgrade.
Pubsub with Deno and Cloudflare Durable Objects
One of the common use cases of WebSockets is pubsub. This release adds pub-sub support to Deno provider and also you can globally broadcast messages using ws.publish
for advanced use cases.
Normally with cloudflare workers, it is not possible to connect multiple peers with each other. Cloudflare Durable Objects (available on paid plans) allows building collaborative editing tools, interactive chat, multiplayer games, and applications that need coordination among multiple clients.
Crossws provides a new composable method to easily integrate WebSocket handlers with Durable Objects. Hibernation is supported out of the box to reduce billing costs when connected clients are inactive. (durable object peer docs)
Changelog
🚀 Enhancements
⚠️ Overhaul internal implementation (#55)⚠️ Overhaul peer and message interface (#70)- node, uws: Automatically detect binary message type (#53)
- peer: Add
peer.close()
andpeer.terminate()
support (#36) - Cloudflare durable objects support (#54) (docs)
- deno: Support pub/sub (#58)
- Universal access to all peers (#60)
- Global publish using
ws.publish
(#61) - Experimental SSE-based adapter to support websocket in limited runtimes (#62, #66, #68) (docs
- peer: Use secure lazy random UUID v4 (#64)
🩹 Fixes
- Should not serailize binary messages (#39)
- cloudflare-durable: Restore peer url and id after hibernation (#71)
💅 Refactors
⚠️ Movepeer.ctx
topeer._internal
(#59)⚠️ Remove adapter hooks (#72)- Rename internal crossws to hooks (bb4c917)
- Better internal organization (2744f21)
📖 Documentation
#22, 76fc105, 7dacb00, #46, #45, #44, a96dca3, 898ab49, 2e49cc3
📦 Build
✅ Tests
- Add adapter tests (#56)
- cloudflare: Use random port for wrangler inspector (a46265c)
- Run tests with web standard
WebSocket
andEventSource
(#67)
❤️ Contributors
- Pooya Parsa (@pi0)
- Eduardo San Martin Morote (@posva)
- Alex (@alexzhang1030)
- 39sho (@39sho)
- @beer (@iiio2)
- Sébastien Chopin (@atinux)
- Pierre Golfier [email protected]
v0.2.4
🚀 Enhancements
💅 Refactors
- Improve peer inspect message (9f7e1f0)
📖 Documentation
- Update content (6d78e12)
🏡 Chore
- Use seperate playground index (889b37b)
- Update lockfile (c119028)
- Update docs (54e0dca)
- Update playground (a6879bd)
- Update example (0ce11c5)
- Update playground (cbeb472)
❤️ Contributors
- Pooya Parsa (@pi0)