@epic-web/test-server
Version:
Utility for creating HTTP and WebSocket servers for testing
83 lines (80 loc) • 2.44 kB
JavaScript
import { kEmitter, kServers, kServer, toRelativePathname, getServerUrl } from './chunk-VF6J4Q2K.js';
import { randomUUID } from 'node:crypto';
import { WebSocketServer } from 'ws';
import '@hono/node-server';
import { DeferredPromise } from '@open-draft/deferred-promise';
function buildWebSocketApi(protocol, server, pathname) {
const baseUrl = new URL(
toRelativePathname(pathname),
getServerUrl(protocol, server)
);
baseUrl.protocol = baseUrl.protocol.replace("http", "ws");
return {
url() {
return baseUrl;
}
};
}
function createWebSocketMiddleware(options) {
const emitter = Reflect.get(options.server, kEmitter);
const pathname = options.pathname ?? `/ws/${randomUUID()}`;
const wss = new WebSocketServer({
noServer: true,
path: pathname
});
const handleUpgrade = (request, socket, head) => {
if (request.url !== pathname) {
return;
}
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit("connection", ws, request);
});
};
const subscriptions = [];
const addUpgradeListener = (server) => {
server.on("upgrade", handleUpgrade);
subscriptions.push(() => server.removeListener("upgrade", handleUpgrade));
};
const servers = Reflect.get(options.server, kServers);
servers.forEach((server) => {
addUpgradeListener(server);
});
emitter.on("listen", (server) => {
addUpgradeListener(server);
});
return {
async [Symbol.asyncDispose]() {
await this.close();
},
raw: wss,
on: wss.on.bind(wss),
once: wss.once.bind(wss),
off: wss.off.bind(wss),
get ws() {
const server = Reflect.get(options.server.http, kServer);
return buildWebSocketApi("http", server, pathname);
},
get wss() {
const server = Reflect.get(options.server.https, kServer);
return buildWebSocketApi("https", server, pathname);
},
async close() {
let effect;
while (effect = subscriptions.pop()) {
effect();
}
if (wss.clients.size === 0) {
return;
}
const pendingClientClosures = [];
wss.clients.forEach((client) => {
client.close();
const clientClosedPromise = new DeferredPromise();
pendingClientClosures.push(clientClosedPromise);
client.once("close", clientClosedPromise.resolve);
});
await Promise.all(pendingClientClosures);
}
};
}
export { createWebSocketMiddleware };