Realtime WebSocket Notification Drawer
Overview
A notification drawer built from a resilient WebSocket client, an accessible shadow-DOM web component and a matching demo server, with no runtime dependencies on either side. The client handles jittered exponential-backoff reconnect, application-level heartbeats, an outbound queue that survives disconnection, de-duplication by id, at-least-once acks and resume-from-last-seen-id. The server is a hand-written RFC 6455 implementation on node:http covering the Sec-WebSocket-Accept handshake, frame masking, 16- and 64-bit payload lengths, continuation frames, close codes and ping/pong, plus a topic broadcast hub and a POST /publish endpoint. The drawer's focus trap, keyboard navigation, aria-live announcements, style isolation and reconnect behaviour are exercised in headless Chrome by an included test suite that drives the DevTools Protocol directly. It does not support permessage-deflate, the hub is in-memory and single-process, and the drawer list is capped rather than virtualised.
Source preview
_drain() {
/** @type {Frame[]} */
const frames = [];
for (;;) {
const buf = this._buffer;
if (buf.length < 2) break;
const b0 = buf[0];
const b1 = buf[1];
const fin = (b0 & 0x80) !== 0;
if ((b0 & 0x70) !== 0) {
throw new WsProtocolError('RSV bits set but no extension was negotiated');
}
const opcode = b0 & 0x0f;
if (!KNOWN_OPCODES.has(opcode)) {
throw new WsProtocolError(`unknown opcode 0x${opcode.toString(16)}`);
}
const masked = (b1 & 0x80) !== 0;
let length = b1 & 0x7f;
let offset = 2;
if (length === 126) {
if (buf.length < offset + 2) break;
length = buf.readUInt16BE(offset);
if (length < 126) {
throw new WsProtocolError('payload length not minimally encoded');
}
offset += 2;Excludes tax, added at checkout where it applies.