Releases: ueberdosis/hocuspocus
v4.3.0
What's Changed
- feat: add
afterHandleMessagehook to run after message handling completion by @janthurau in #1112 - feat: enforce pre-auth resource limits to safeguard server stability by @janthurau in #1113
Full Changelog: v4.2.0...v4.3.0
v4.2.0
What's Changed
- feat: add
unloadImmediatelyoption todisconnect()for configurable document persistence behavior by @janthurau in #1111
Full Changelog: v4.1.2...v4.2.0
v4.1.2
What's Changed
- fix: delete debouncer timer entry before awaiting the in-flight execution by @patrick-atticus in #1110
New Contributors
- @patrick-atticus made their first contribution in #1110
Full Changelog: v4.1.1...v4.1.2
v4.1.1
What's Changed
- fix: client-initiated broadcastStateless by @janthurau in #1103
- Fix memory leak: destroy scratch Awareness objects in MessageReceiver by @Copilot in #1109
- Feature: expose sessionAwareness in provider-react HocuspocusRoom by @t-schorn in #1104
New Contributors
Full Changelog: v4.1.0...v4.1.1
v4.1.0
v4.0.0
Hocuspocus v4.0 Release Notes
Hocuspocus v4 is a major release that brings cross-runtime support, improved type safety, and important bug fixes. This release focuses on making Hocuspocus run beyond Node.js -- on Bun, Deno, Cloudflare Workers, and Node with uWebSockets -- while improving the developer experience with generic Context typing and structured transaction origins.
Backward Compatibility
A v3 provider can connect to a v4 server, and a v4 provider can connect to a v3 server. The wire protocol remains compatible in both directions:
- v3 provider -> v4 server: The server accepts plain document names (no session routing key), does not require Pong responses, and handles auth messages without a provider version string.
- v4 provider -> v3 server: The provider defaults to
sessionAwareness: false, so it sends plain document names. The extra version string in the auth message is ignored by the v3 server as trailing data. The provider does not require server-initiated Ping messages. - Session awareness caveat: If
sessionAwareness: trueis explicitly enabled on a v4 provider connecting to a v3 server, the server will treat the composite routing key (documentName\0sessionId) as a literal document name, creating unintended documents. KeepsessionAwareness: false(the default) when connecting to a v3 server.
Highlights
Cross-Runtime Support
Hocuspocus is no longer tied to the Node.js ws library. The server now uses crossws, a universal WebSocket adapter, enabling Hocuspocus to run on:
- Node.js (with
wsoruWebSockets.js) - Bun
- Deno
- Cloudflare Workers
The built-in Server class continues to work as before for Node.js users. For other runtimes, use Hocuspocus directly with handleConnection(), which now accepts any WebSocketLike object and a web-standard Request.
Generic Context Type
All core classes and hook payloads now accept a generic Context type parameter, enabling end-to-end type safety:
interface MyContext {
userId: string;
permissions: string[];
}
const server = Server.configure<MyContext>({
async onAuthenticate({ context, token }) {
// context is typed as MyContext
return { userId: '123', permissions: ['read', 'write'] };
},
async onChange({ context }) {
// context.userId is typed as string
console.log(context.userId);
},
});The generic defaults to any, so existing code without explicit typing continues to work.
Ordered Message Processing
Document updates are now processed sequentially in the order they are received. Previously, concurrent messages could be processed out of order if async hooks were involved. A new internal message queue ensures CRDT updates are applied consistently.
Web Standard Request/Headers
Hook payloads now use the web-standard Request and Headers objects instead of Node.js IncomingMessage and IncomingHttpHeaders. This aligns with the cross-runtime goal and provides a consistent API across all environments.
New Features
Server
- Cross-runtime WebSocket support via
crossws-- Bun, Deno, Cloudflare Workers, Node/uWebSockets all supported (non-breaking) - Generic
Contexttype parameter onHocuspocus,Server,Extension,Connection,ClientConnection,DirectConnection, and all hook payloads (non-breaking -- defaults toany) - Structured transaction origins -- new
TransactionOriginunion type (ConnectionTransactionOrigin | RedisTransactionOrigin | LocalTransactionOrigin) with helper functionsisTransactionOrigin()andshouldSkipStoreHooks()(breaking -- see upgrade guide) onLoadDocumentnow acceptsUint8Arrayreturns -- extensions can return raw Yjs updates instead of constructing a fullY.Doc, simplifying storage extensions (non-breaking)handleConnection()returnsClientConnection-- enables programmatic access to the connection lifecycle for custom integrations (non-breaking)- Ordered message processing -- messages are queued and processed sequentially per connection (non-breaking)
- Session awareness -- the server supports session-aware multiplexing, allowing multiple providers with the same document name on a single WebSocket. Each provider gets a unique
sessionIdrouted via a composite key. The server transparently falls back to plain document names for v3 providers (non-breaking) - Auth retry support -- failed authentication now properly cleans up state, allowing clients to retry without reconnecting (non-breaking)
- DirectConnection context --
openDirectConnection(documentName, context)now accepts and propagates a context object (non-breaking) - Store hooks on all changes --
onStoreDocumentis now triggered on any document change (not just WebSocket-originated ones), with explicit opt-out viaskipStoreHooksonLocalTransactionOrigin(non-breaking) - Provider version awareness -- the provider version is available on
Connection.providerVersionand in hook payloads (onConnect,onAuthenticate,connected), making it easier to introduce protocol changes in a backward-compatible way (non-breaking) SkipFurtherHooksError-- extensions can throwSkipFurtherHooksError(from@hocuspocus/common) inonStoreDocumentto signal that persistence was handled and remaining hooks should be skipped (non-breaking)
Provider
- Session awareness -- new
sessionAwarenessoption (default:false). When enabled, the provider embeds a uniquesessionIdin the document name field of every message, enabling multiple providers with the same document name on one WebSocket. Keep disabled when connecting to a v3 server (non-breaking) - Provider version sent during auth -- the provider now sends its package version to the server in the authentication message. The extra field is safely ignored by v3 servers (non-breaking)
- Awareness message deduplication -- when the WebSocket is not yet open and messages are queued, duplicate awareness messages for the same document are deduplicated, keeping only the latest one (non-breaking)
- Application-level Ping/Pong -- new
MessageType.Ping(9) andMessageType.Pong(10). The provider responds to server Ping messages with Pong, replacing WebSocket-level ping/pong which is not available in all runtimes. The provider works fine without receiving Pings (e.g., when connected to a v3 server) (non-breaking) wspackage types removed -- the provider no longer importsEvent,MessageEvent, orCloseEventfrom thewspackage. It uses web-standard types and types from@hocuspocus/commoninstead (breaking for TypeScript users who relied onwstypes being re-exported)CloseEventshape simplified -- theCloseEventpassed toonClosecallbacks no longer includestargetandtypefields. Onlycodeandreasonremain (breaking if youronClosehandler readsevent.targetorevent.type)- Attach collision detection --
HocuspocusProviderWebsocket.attach()now throws an error if you try to attach two authenticated providers with the same effective name. Previously it silently overwrote the existing provider (non-breaking for correct usage; may surface existing bugs) - Unknown message types handled gracefully -- unknown message types now log
console.errorinstead of throwing. This makes rolling out future protocol additions easier (non-breaking)
Bug Fixes
Server
- Auth state reset on failure -- when authentication fails, document state is cleaned up so the client can send a new auth message without reconnecting (#944) (non-breaking)
onLoadDocumentacceptsUint8Array-- the callback now correctly handles bothY.DocandUint8Arrayreturns (#795, #271) (non-breaking)- Close code type check -- close event codes are now properly validated as numbers (#1062) (non-breaking)
- Store hooks reliability --
onStoreDocumentnow triggers on any document change, preventing accidental data loss when updates lacked a Yjs origin (non-breaking) onStoreDocumentpayload type -- the Database extension and Logger extension now correctly type the parameter asonStoreDocumentPayloadinstead of the incorrectonChangePayload/onDisconnectPayload(non-breaking)- Document name validation -- empty and whitespace-only document names are now rejected on both WebSocket connections and direct connections (non-breaking)
- Store hook retry on failure -- when
onStoreDocumenthooks throw, the document stays in memory and retries are attempted to avoid data loss (non-breaking) - Graceful shutdown flushes pending stores --
Server.destroy()now immediately executes all pending debouncedonStoreDocumentcalls, ensuring documents are persisted before the server exits (even whenunloadImmediately: false) (non-breaking) - Memory optimization -- outgoing Yjs update messages are now encoded once and shared across connections instead of being re-created per connection (non-breaking)
Provider
- Unknown message types no longer crash the provider --
console.errorinstead ofthrow. This makes future protocol additions easier (non-breaking)
Infrastructure Changes
- Package manager: migrated from npm to pnpm workspaces
- Bundler: migrated from Rollup to Rolldown
- SQLite extension: migrated from
sqlite3to better-sqlite3 (synchronous API, actively maintained) - Node.js requirement:
>=22(specified in@hocuspocus/server) - Default timeout: increased from 30s to 60s
- Lerna: upgraded to v9 with pnpm as npm client
Upgrade Guide: v3 to v4
1. Update Dependencies
# Install v4
npm install @hocuspocus/server@^4.0.0 @hocuspocus/provid...v3.4.4
What's Changed
-
fix: strict types for authorizedScope by @mattkrick in #1029
-
fix: fixes hono types in playground by @janthurau in #1045
-
fixes flaky redis test by @janthurau in #1047
-
fix: fixes #1041: websocket URL ending with / wont work by @janthurau in #1046
-
build(deps): bump actions/cache from 4.3.0 to 5.0.1 by @dependabot[bot] in #1038
-
build(deps): bump hono from 4.10.3 to 4.11.4 by @dependabot[bot] in #1042
-
build(deps): bump actions/cache from 5.0.1 to 5.0.2 by @dependabot[bot] in #1043
-
build(deps): bump lodash from 4.17.21 to 4.17.23 by @dependabot[bot] in #1044
Full Changelog: v3.4.3...v3.4.4
v3.4.3
- trusted publishing test
Full Changelog: v3.4.2...v3.4.3
v3.4.2
What's Changed
- fix: fixes memory leak under high load
- chore: testing new publish workflow
Full Changelog: v3.4.1...v3.4.2
v3.4.1
What's Changed
-
fix: extension-redis: on awareness crasah prevention and memory leak onStoreDocument by @matteotarantino-algor in #1032
-
build(deps): bump hono from 4.10.2 to 4.10.3 by @dependabot[bot] in #1016
-
WIP: chore: remove docs as they are now present in @ueberdosis/tiptap-docs by @janthurau in #952
-
build(deps): bump actions/checkout from 5 to 6 by @dependabot[bot] in #1023
-
build(deps): bump glob from 10.4.5 to 10.5.0 by @dependabot[bot] in #1021
-
Add the missing test for not unloading before ongoing save is finished by @raimohanska in #1025
-
build(deps): bump next from 15.4.7 to 15.4.8 by @dependabot[bot] in #1030
-
build(deps): bump next from 15.4.7 to 15.4.8 in /playground/frontend by @dependabot[bot] in #1026
New Contributors
- @matteotarantino-algor made their first contribution in #1032
Full Changelog: v3.4.0...v3.4.1