Combining HMR with Another Upgrade Consumer
This page applies only when server.hmrTransport: 'mediated' and your application already has
its own WebSocket consumer on the same Fastify instance. If HMR is the only thing offered
upgrades on your server, you do not need it - see the
server.hmrTransport reference instead.
The generic rule
Section titled “The generic rule”Node delivers every upgrade event on an HTTP server to every listener registered for it - no
listener can stop the others from also seeing it. Two consumers on one server therefore need:
- one dispatcher you own, installed on the server’s
upgradeevent; - a separate, never-
listen()ed source per consumer, each consumer’s own listener installed on its own source.
τjs supplies its own source internally; you never see or manage it. The dispatcher offers τjs first refusal, and only falls through to the other consumer when τjs declines:
import type { IncomingMessage } from 'node:http';import type { Duplex } from 'node:stream';
const onUpgrade = (req: IncomingMessage, socket: Duplex, head: Buffer): void => { if (tau.dev.hmr.tryHandleUpgrade(req, socket, head)) return; otherSource.emit('upgrade', req, socket, head); // the other consumer listens on otherSource};app.server.on('upgrade', onUpgrade);app.addHook('onClose', async () => { app.server.off('upgrade', onUpgrade); });tryHandleUpgrade never throws and never writes to a socket it does not claim - it either
returns true (the socket is τjs’s HMR channel; do nothing more with it) or false (offer it
elsewhere). Whether the other consumer accepts a caller-supplied server to listen on, rather than
insisting on the Fastify instance itself, is a property of that consumer - not something τjs
can arrange on its behalf.