import { decode } from "./protocol"; import { type Client, getOrCreateSession, getSession, type WsData, } from "./session"; const PORT = Number(process.env.PORT) || 4040; function isValidRoomName(name: unknown): name is string { if (typeof name !== "string") return false; if (name.length === 0 || name.length > 64) return false; return /^[a-zA-Z0-9_-]+$/.test(name); } export const server = Bun.serve({ port: PORT, fetch(req, server) { const url = new URL(req.url); if (url.pathname === "/ws") { const upgraded = server.upgrade(req, { data: { room: null, client: null }, }); if (!upgraded) { return new Response("websocket upgrade failed", { status: 400 }); } return; } return new Response("collabd running"); }, websocket: { open(ws) { // create client object once and store in ws.data const client: Client = { ws }; ws.data.client = client; console.debug("client connected"); }, message(ws, raw) { const msg = decode(raw.toString()); if (!msg) return; // reuse the client object from ws.data const client = ws.data.client; if (!client) return; switch (msg.type) { case "join": { if (!isValidRoomName(msg.room)) { try { ws.send( JSON.stringify({ type: "error", message: "invalid room name" }), ); } catch (err) { console.debug("failed to send error to client:", err); } break; } const session = getOrCreateSession(msg.room); ws.data.room = msg.room; session.join(client); console.debug(`client joined room: ${msg.room}`); break; } case "leave": { if (ws.data.room) { const session = getSession(ws.data.room); session?.leave(client); ws.data.room = null; } break; } case "update": { if (ws.data.room) { const session = getSession(ws.data.room); session?.applyUpdate(new Uint8Array(msg.data), client); } break; } case "awareness": { if (ws.data.room) { const session = getSession(ws.data.room); if (session && "data" in msg) { session.broadcastAwareness(client, msg.data); } } break; } } }, close(ws) { if (ws.data.room && ws.data.client) { const session = getSession(ws.data.room); session?.leave(ws.data.client); } console.debug("client disconnected"); }, }, }); console.log(`collabd listening on :${PORT}`);