UNPKG

@epic-web/test-server

Version:

Utility for creating HTTP and WebSocket servers for testing

83 lines (80 loc) 2.44 kB
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 };