Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | 8x 8x 5x 5x 5x 5x 8x 8x 4x 4x 3x 3x 3x 9x 5x 9x | /**
* HMR WebSocket Server
*
* Attaches a WebSocket server to an existing HTTP server (sharing the
* same port), tracks connected dev clients, and exposes a broadcast()
* helper that serializes a message once and fan-outs to every live
* client. Used by the Coherent dev server to push hot-update events
* to browser-side HMR clients.
*
* Wire protocol matches packages/client/src/hmr/client.js — server
* sends `{type, filePath?, webPath?, error?, updateType?}` objects;
* client switches on `type` and handles updates, reloads, errors.
*
* @module @coherent.js/cli/dev-server/hmr-server
*/
import { WebSocketServer } from 'ws';
/**
* @typedef {Object} HmrServer
* @property {(message: object) => void} broadcast - Serialize and send a JSON message to every live client.
* @property {() => void} close - Close the WebSocket server and drop all clients.
* @property {() => number} clientCount - Current number of live clients (for tests / diagnostics).
*/
/**
* Create and attach an HMR WebSocket server to an existing HTTP server.
*
* The WS server shares the HTTP server's port — clients connect to
* `ws://host:port` (no separate port to manage). New clients receive
* a `{type: 'connected'}` ack on open. Dead clients are pruned on
* the next broadcast.
*
* @param {import('node:http').Server} httpServer - HTTP server to attach to.
* @returns {HmrServer}
*/
export function createHmrServer(httpServer) {
const wss = new WebSocketServer({ server: httpServer });
wss.on('connection', (socket) => {
// Defer the initial ack by one event-loop turn so the client-side
// 'open' event has time to resolve before the message arrives.
// Without this, the 'message' event fires synchronously during the
// WS handshake — before the client can attach its first listener.
setTimeout(() => {
Eif (socket.readyState === socket.OPEN) {
try {
socket.send(JSON.stringify({ type: 'connected' }));
} catch {
// Client may have disconnected mid-handshake; ignore.
}
}
}, 0);
});
// Surface listener errors instead of crashing the dev server.
wss.on('error', (err) => {
console.warn('[coherent dev] HMR server error:', err.message);
});
return {
broadcast(message) {
const frame = JSON.stringify(message); // throws on circular — surfaces caller bug loudly
for (const client of wss.clients) {
Eif (client.readyState === client.OPEN) {
try {
client.send(frame);
} catch {
// Dead socket — let `ws` clean it up on its own close event.
}
}
}
},
close() {
for (const client of wss.clients) {
try { client.close(); } catch { /* ignore */ }
}
wss.close();
},
clientCount() {
let n = 0;
for (const c of wss.clients) if (c.readyState === c.OPEN) n++;
return n;
},
};
}
|